Jelajahi Sumber

批量关联页面

huangxw 1 Minggu lalu
induk
melakukan
4072d44787

+ 7 - 0
src/assets/styles/uview-plus.ts

@@ -13,4 +13,11 @@ export const formItemBtnListStyle = {
     lineHeight: '68rpx',
     borderRadius: '10rpx',
     padding: '0 20rpx',
+};
+export const formItemBtnCardStyle = {
+    height: '78rpx',
+    fontSize: '36rpx',
+    lineHeight: '78rpx',
+    borderRadius: '10rpx',
+    padding: '0 20rpx',
 };

+ 14 - 0
src/pages.json

@@ -464,6 +464,13 @@
                     "style": {
                         "navigationBarTitleText": "选择包装对象"
                     }
+                },
+                // 批量关联
+                {
+                    "path": "batch-link/index",
+                    "style": {
+                        "navigationBarTitleText": "批量关联"
+                    }
                 }
             ]
         },
@@ -645,6 +652,13 @@
                         "navigationBarTitleText": "view-html页面",
                         "navigationStyle": "custom"
                     }
+                },
+                // 选择追溯码码段
+                {
+                    "path": "select-code-section/index",
+                    "style": {
+                        "navigationBarTitleText": "选择追溯码码段"
+                    }
                 }
             ]
         },

+ 306 - 0
src/plant/packaging/batch-link/index.vue

@@ -0,0 +1,306 @@
+<template>
+    <z-paging ref="paging" bgColor="#F7F7F7" safe-area-inset-bottom paging-class="paging-btm-shadow" refresher-only @onRefresh="onRefresh" scroll-with-animation>
+        <template #top>
+            <ut-navbar title="批量关联" :fixed="false" border></ut-navbar>
+            <view class="pd-5"></view>
+        </template>
+        <view class="pd-24">
+            <view class="mb-30 p-rtv batch-wapper">
+                <view class="d-flex a-c">
+                    <image class="w-50 h-50 mr-10" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images-plt/print-codes/pcxx_icon.png" mode="widthFix" />
+                    <view class="f-s-28 c-#333 f-w-5">需关联追溯码的批次信息</view>
+                </view>
+                <view class="pl-50">
+                    <view v-if="item" class="border-#8FC1EC b-radius bg-#fff ov-hd">
+                        <pack-card :item="item" :dict="{ pt_pack_ref_type }"></pack-card>
+                    </view>
+                    <view class="pd-30 d-flex j-c">
+                        <view class="c-primary f-s-28">
+                            <span>待关联数量:</span>
+                            <span class="f-s-46 f-w-600">{{ item?.planCount - item?.actualCount || 0 }}</span>
+                            <span>{{ item?.unit }}</span>
+                        </view>
+                    </view>
+                </view>
+                <view class="d-flex a-c">
+                    <image class="w-50 h-50 mr-10" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images-plt/print-codes/glm_icon.png" mode="widthFix" />
+                    <view class="f-s-28 c-#333 f-w-5">本批次信息要关联到哪些(提前预制的)追溯码上?</view>
+                </view>
+            </view>
+            <view class="pl-50">
+                <template v-for="(row, index) in relations" :key="index">
+                    <view :id="`unchecked-row-${index}`" class="p-rtv form-codes-item bg-fff border-#9DD3AB mb-20 b-radius">
+                        <view class="d-flex j-sb">
+                            <view class="no-num-tag">NO.{{ index + 1 }}</view>
+                        </view>
+                        <view @click="deleteRow(index)" class="pd-10 close-icon">
+                            <up-icon name="close" color="#F74C30" size="34rpx"></up-icon>
+                        </view>
+                        <view class="pd2-20-30">
+                            <view class="f-s-24 c-#999 pd2-4-0">生成批号:{{ row?.item?.batchSn }}</view>
+                            <view class="d-flex f-s-24 c-#999 pd2-4-0">
+                                <view>码段:</view>
+                                <view>
+                                    <view>{{ row?.item?.traceCodeStart || '-' }} 至 </view>
+                                    <view>{{ row?.item?.traceCodeEnd || '-' }}</view>
+                                </view>
+                            </view>
+                            <view class="d-flex flex-wrap">
+                                <view class="hcol-14 f-s-24 pd2-4-0">
+                                    <span class="c-#999">生成数量:</span>
+                                    <span class="c-#999">{{ row?.item?.sumCount || '-' }}个</span>
+                                </view>
+                                <view class="hcol-16 f-s-24 pd2-4-0">
+                                    <span class="c-#999">剩余数量:</span>
+                                    <span class="c-#999">{{ +row?.item?.sumCount - +row?.item?.useCount - +row?.item?.voidCount || '-' }}</span>
+                                </view>
+                                <view class="hcol-20 f-s-24 pd2-4-0">
+                                    <span class="c-#999">生成时间:</span>
+                                    <span class="c-#999">{{ parseTime(row?.item?.createTime, '{y}-{m}-{d} {h}:{i}') || '-' }}</span>
+                                </view>
+                            </view>
+                        </view>
+                        <view class="h-1 bg-#F7F7F7 mg2-0-10"></view>
+                        <view class="pd2-20-30">
+                            <view class="f-s-28 c-#333 f-w-5 mb-16">请输入要关联的预制码编号:</view>
+                            <view class="d-flex a-c j-c mb-24">
+                                <up-input placeholder="7位起始编号" maxlength="7" v-model="row.form.startSn" @change="markRowUnchecked(row)"></up-input>
+                                <view class="pd2-0-20">至</view>
+                                <up-input placeholder="7位结束编号" maxlength="7" v-model="row.form.endSn" @change="markRowUnchecked(row)"></up-input>
+                            </view>
+                            <view class="d-flex j-c pl-120 pr-120 mb-30">
+                                <up-button @click="checkCodesValidItem(row)" :customStyle="formItemBtnCardStyle" type="primary">校验是否可用</up-button>
+                            </view>
+                            <view v-if="row?.sumnCount">
+                                <view class="d-flex a-c">
+                                    <up-icon class="mr-10" name="checkmark-circle-fill" color="#3FAD5B" size="26rpx"></up-icon>
+                                    <span class="f-s-24 c-#999">此次共关联{{ row?.sumnCount }}个追溯码,关联明细如下: </span>
+                                </view>
+                                <view v-for="(col, i) in row?.validCodes" :key="i" class="f-s-24 c-#999 pl-36">
+                                    <span>{{ i + 1 }}、</span>
+                                    <span>编号区间为{{ col.startSn.toString().padStart(7, '0') }} - {{ col.endSn.toString().padStart(7, '0') }};</span>
+                                    <span>共{{ col.count }}个</span>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </template>
+                <up-button @click="selectCodes" type="primary" plain customStyle="background: #F5F7F5;">
+                    <image class="w-36 h-36 mr-10" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images-plt/common/select_push_icon.png" mode="widthFix" />
+                    <span>请选择追溯码</span>
+                </up-button>
+            </view>
+        </view>
+        <template #bottom>
+            <view class="pd3-10-24-20">
+                <view class="f-s-28 c-#333 text-center mb-5">此次共关联{{ totalSumCount }}个追溯码</view>
+                <up-button @click="batchLinkCodes" type="primary">批量关联</up-button>
+            </view>
+        </template>
+    </z-paging>
+</template>
+<script setup lang="ts">
+import { useClientRequest } from '@/utils/request';
+import { getUrlParams, parseTime, recursiveDecodeURIComponent } from '@/utils/ruoyi';
+import PackCard from '@/plant/models/pack-card.vue';
+import { formItemBtnCardStyle } from '@/assets/styles/uview-plus';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { pt_pack_ref_type } = toRefs<any>(proxy?.useDict('pt_pack_ref_type'));
+const paging = ref<any>(null);
+const item = ref<any>(null);
+const did = ref('');
+const relations = ref<any[]>([]);
+const callbackName = 'refreshCodesRange';
+// 根据id获取基地详情
+const getDetailById = async (id: string) => {
+    const res = await useClientRequest.get(`/plt-api/app/packTask/${id}`);
+    if (res && res.code === 200) {
+        item.value = res.data || null;
+    }
+};
+const onRefresh = () => {
+    getDetailById(did.value);
+    paging.value?.complete();
+};
+const totalSumCount = computed(() => {
+    return relations.value.reduce((sum, item) => sum + +(item?.sumnCount || 0), 0);
+});
+// 判断节点是否在可视区域内如果在则切换tab createIntersectionObserver
+onLoad((options: any) => {
+    did.value = options?.packId || getUrlParams(recursiveDecodeURIComponent(options?.q))?.packId || '';
+    getDetailById(did.value);
+    uni.$on('refreshBase', () => {
+        onRefresh();
+    });
+    uni.$on(callbackName, onCodeRangeSelected);
+});
+onUnload(() => {
+    uni.$off(callbackName, onCodeRangeSelected);
+});
+const deleteRow = (index: number) => {
+    relations.value.splice(index, 1);
+};
+const markRowUnchecked = (row: any) => {
+    row.hasChecked = false;
+    row.validCodes = [];
+    row.sumnCount = 0;
+};
+// 校验单个码段是否有用
+const checkCodesValidItem = async (row: any) => {
+    if (!row?.form?.startSn || !row?.form?.endSn) {
+        uni.showToast({
+            title: '请输入完整的起始编号和结束编号',
+            icon: 'none',
+        });
+        return;
+    }
+    if (Number(row.form.startSn) > Number(row.form.endSn)) {
+        uni.showToast({
+            title: '起始编号不能大于结束编号',
+            icon: 'none',
+        });
+        return;
+    }
+    if (Number(row.form.startSn) < Number(row.item?.startSn)) {
+        uni.showToast({
+            title: '起始编号不能小于已有编号',
+            icon: 'none',
+        });
+        return;
+    }
+    if (Number(row.form.endSn) > Number(row.item?.endSn)) {
+        uni.showToast({
+            title: '结束编号不能大于已有编号',
+            icon: 'none',
+        });
+        return;
+    }
+    const res = await useClientRequest.get(`/plt-api/app/traceCodeLog/checkCodeRange/${row?.form?.id}`, {
+        ...row?.form,
+    });
+    console.log(res);
+    if (!res || res.code !== 200) {
+        row.hasChecked = false;
+        return;
+    }
+    const validCodes = res.data;
+    row.validCodes = validCodes.map((item:any) => ({ ...item, logId: row?.form?.id }));
+    row.sumnCount = validCodes.reduce((sum: number, item: any) => sum + item.count, 0);
+    row.hasChecked = true;
+};
+// 校验码段是否有用
+const checkCodesValid = async (data: any) => {
+    if (relations.value.some((relation) => relation?.item?.id === data?.id)) {
+        uni.showToast({
+            title: '该追溯码段已选择',
+            icon: 'none',
+        });
+        return;
+    }
+    const res = await useClientRequest.get(`/plt-api/app/traceCodeLog/checkCodeRange/${data?.id}`, {
+        ...data,
+    });
+    if (res && res.code === 200) {
+        // return res.data || [];
+        relations.value.push({
+            item: data,
+            form: {
+                id: data?.id,
+                startSn: res.data[0]?.startSn?.toString().padStart(7, '0')
+            },
+            hasChecked: false,
+            validCodes: [],
+            sumnCount: 0,
+        });
+        console.log(relations.value);
+    }
+    // return [];
+};
+const onCodeRangeSelected = (data: any) => {
+    console.log('选中的码段数据:', data);
+    checkCodesValid(data);
+};
+const selectCodes = () => {
+    const selectedIds = relations.value.map((row) => row?.item?.id).filter((id) => id !== null && id !== undefined);
+    uni.$u.route({
+        type: 'navigateTo',
+        url: '/tools/select-code-section/index',
+        params: {
+            // 监听函数字符串
+            callback: callbackName,
+            selectedIds: encodeURIComponent(JSON.stringify(selectedIds)),
+        },
+    });
+};
+const batchLinkCodes = async () => {
+    if (relations.value.length === 0) {
+        uni.showToast({
+            title: '请至少选择一个追溯码段进行关联',
+            icon: 'none',
+        });
+        return;
+    }
+    const uncheckedIndex = relations.value.findIndex((row) => !row?.hasChecked);
+    if (uncheckedIndex !== -1) {
+        paging.value?.scrollIntoViewById(`unchecked-row-${uncheckedIndex}`, 30, true);
+        uni.showToast({
+            title: `请先校验第${uncheckedIndex + 1}项`,
+            icon: 'none',
+        });
+        return;
+    }
+    try {
+        await uni.showLoading({
+            title: '关联中...',
+            mask: true,
+        });
+        const params = {
+            packId: did.value,
+            relations: relations.value.map((row: any) => row?.validCodes || [])?.flat(),
+        };
+        await useClientRequest.post('/plt-api/app/packTask/batchRelaction', params);
+        uni.hideLoading();
+        uni.showToast({
+            title: '关联成功',
+            icon: 'success',
+        });
+        onRefresh();
+    } catch (error) {
+        console.error('批量关联失败:', error);
+        uni.hideLoading();
+        uni.showToast({
+            title: '关联失败,请重试',
+            icon: 'none',
+        });
+    }
+};
+</script>
+<style lang="scss" scoped>
+.batch-wapper {
+    &::before {
+        content: '';
+        position: absolute;
+        left: 25rpx;
+        top: 50rpx;
+        bottom: 50rpx;
+        width: 1px;
+        background: #37a954;
+        z-index: -1;
+    }
+}
+.no-num-tag {
+    padding: 10rpx 14rpx;
+    font-size: 20rpx;
+    line-height: 1;
+    font-weight: 600;
+    color: $u-primary;
+    background-color: #d9efde;
+    border-radius: 16rpx 0 16rpx 0;
+}
+.close-icon {
+    position: absolute;
+    right: 0rpx;
+    top: 0rpx;
+    z-index: 1;
+}
+</style>

+ 1 - 1
src/plant/packaging/list/index.vue

@@ -43,7 +43,7 @@
                                         <up-button :customStyle="formItemBtnListStyle" v-if="+item?.printCount" @click="$u.route({ url: '/plant/packaging/detail/index', params: { id: item?.id, viewMode: 'print' } })" class="ml-10" color="#37A954">查看打印记录</up-button>
                                     </template>
                                     <template v-if="item.refType == '1'">
-                                        <up-button :customStyle="formItemBtnListStyle" v-if="+item?.planCount - item?.actualCount" class="ml-10" color="#91C747">批量关联</up-button>
+                                        <up-button :customStyle="formItemBtnListStyle" @click="$u.route({ url: '/plant/packaging/batch-link/index', params: { packId: item?.id } })" v-if="(+item?.planCount > +item?.actualCount)" class="ml-10" color="#91C747">批量关联</up-button>
                                         <up-button :customStyle="formItemBtnListStyle" v-if="+item?.actualCount" class="ml-10" color="#91C747">查看关联记录</up-button>
                                     </template>
                                 </view>

+ 152 - 0
src/tools/select-code-section/index.vue

@@ -0,0 +1,152 @@
+<template>
+    <z-paging ref="paging" safe-area-inset-bottom v-model="list" bottom-bg-color="#fff" paging-class="paging-btm-shadow" @query="query">
+        <template #top>
+            <ut-navbar title="请选择追溯码" :fixed="false" :breadcrumb="false"></ut-navbar>
+            <view class="pd-20">
+                <ut-search ref="searchRef" v-model="form.keyword" @search="onRefresh" margin="0" :border="false" placeholder="搜生成批号、操作人" bgColor="#fff" height="86rpx" borderRadius="10rpx"></ut-search>
+            </view>
+        </template>
+        <view class="base-content pd-20">
+            <view :class="{ active: item.id === checkedId, disabled: isDisabled(item) }" v-for="(item, index) in list" :key="item.id" @click="clickItem(item)" class="b-radius bg-#fff pd-20 p-rtv select-item-card mb-20">
+                <image v-if="item.id === checkedId" class="w-40 h-40 checked-icon" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images-lm/price/checked1.png" mode="widthFix" />
+                <view v-if="isDisabled(item)" class="disabled-tag">已选择</view>
+                <view class="f-s-28 pd2-5-0">
+                    <span class="c-#666">生成批号:</span>
+                    <span class="c-#333 f-w-500">{{ item?.batchSn || '-' }}</span>
+                </view>
+                <view class="f-s-28 pd2-5-0 d-flex">
+                    <span class="c-#666">码段:</span>
+                    <view class="f-w-500 flex1 c-#333 f-w-500">
+                        <view>{{ item?.traceCodeStart || '-' }}<span class="c-#999"> 至 </span></view>
+                        <view>{{ item?.traceCodeEnd || '-' }}</view>
+                    </view>
+                </view>
+                <view class="d-flex">
+                    <view class="f-s-28 pd2-5-0 hcol-14">
+                        <span class="c-#666">生成数量:</span>
+                        <span class="c-#333 f-w-500">{{ item?.sumCount || '-' }}个</span>
+                    </view>
+                    <view class="f-s-28 pd2-5-0 hcol-16">
+                        <span class="c-#666">剩余数量:</span>
+                        <span class="c-#333 f-w-500">{{ +item?.sumCount - +item?.useCount - +item?.voidCount || '-' }}个</span>
+                    </view>
+                </view>
+                <view class="d-flex">
+                    <view class="f-s-24 pd2-5-0 hcol-14 up-line-1">
+                        <span class="c-#666">操作人:</span>
+                        <span class="c-#666">{{ item?.operatorName || '-' }}</span>
+                    </view>
+                    <view class="f-s-24 pd2-5-0 hcol-16 up-line-1">
+                        <span class="c-#666">生成时间:</span>
+                        <span class="c-#666">{{ parseTime(item?.createTime, '{y}-{m}-{d} {h}:{i}') || '-' }}</span>
+                    </view>
+                </view>
+            </view>
+        </view>
+        <template #bottom>
+            <view class="pd-20 d-flex">
+                <up-button type="primary" @click="confirmSelect">确认选择</up-button>
+            </view>
+        </template>
+    </z-paging>
+</template>
+<script setup lang="ts">
+import { useClientRequest } from '@/utils/request';
+import { parseTime } from '@/utils/ruoyi';
+const paging = ref<any>(null);
+const list = ref<any[]>([]);
+const form = ref({
+    keyword: '',
+    restFlag: '1',
+});
+const query = async (pageNum: number, pageSize: number) => {
+    const res = await useClientRequest.get('/plt-api/app/traceCodeLog/list', {
+        ...form.value,
+        pageNum: pageNum,
+        pageSize: pageSize,
+    });
+    if (res.code == 200) {
+        paging.value.complete(res.rows || []);
+    }
+};
+const checkedId = ref(null);
+const selectedIds = ref<number[]>([]);
+const isDisabled = (item: any) => selectedIds.value.includes(item?.id);
+const clickItem = (item: any) => {
+    if (isDisabled(item)) {
+        uni.showToast({
+            title: '该追溯码段已被选择',
+            icon: 'none',
+        });
+        return;
+    }
+    checkedId.value = item.id;
+};
+// 确认选择
+const confirmSelect = () => {
+    const selectedItem = list.value.find((item) => item.id === checkedId.value);
+    if (selectedItem && !isDisabled(selectedItem)) {
+        uni.$emit(opts.value.callback, selectedItem);
+        uni.navigateBack();
+    } else {
+        uni.showToast({
+            title: '请选择一项追溯码段信息',
+            icon: 'none',
+        });
+    }
+};
+const onRefresh = () => {
+    try {
+        paging.value?.reload();
+    } catch (e) {
+        console.error('Error refreshing address list:', e);
+    }
+};
+const opts = ref({
+    // 监听函数字符串
+    callback: 'refreshCodesRange',
+});
+onLoad((options: any) => {
+    console.log(options);
+    
+    opts.value.callback = options?.callback || 'refreshCodesRange';
+    if (!options?.selectedIds) return;
+    try {
+        const ids = JSON.parse(decodeURIComponent(options.selectedIds));
+        selectedIds.value = ids || [];
+    } catch (error) {
+        selectedIds.value = [];
+    }
+});
+</script>
+<style lang="scss" scoped>
+.select-item-card {
+    position: relative;
+    border: 1rpx solid #fff;
+    border-radius: 16rpx;
+    overflow: hidden;
+    .checked-icon {
+        position: absolute;
+        right: 0;
+        bottom: 0;
+    }
+    &.active {
+        border-color: $u-primary;
+        background-color: #ebf6ee;
+    }
+    &.disabled {
+        opacity: 0.55;
+        background-color: #f5f5f5;
+    }
+    .disabled-tag {
+        position: absolute;
+        right: 20rpx;
+        top: 20rpx;
+        padding: 4rpx 12rpx;
+        border-radius: 999rpx;
+        font-size: 22rpx;
+        color: #999;
+        background-color: #ececec;
+    }
+}
+</style>

+ 1 - 1
src/tools/select-gap-info/index.vue

@@ -60,7 +60,7 @@ const query = async (pageNum: number, pageSize: number) => {
     const res = await useClientRequest.get('/plt-api/app/gapCertificationInfo/pageList', {
         ...form.value,
         pageNum: pageNum,
-        pageSize: 1000,
+        pageSize,
     });
     if (res.code == 200) {
         paging.value.complete(res.rows || []);