index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <template>
  2. <z-paging ref="paging" bgColor="#F7F7F7" safe-area-inset-bottom paging-class="paging-btm-shadow" refresher-only @onRefresh="onRefresh" scroll-with-animation>
  3. <template #top>
  4. <ut-navbar title="批量关联" :fixed="false" border></ut-navbar>
  5. <view class="pd-5"></view>
  6. </template>
  7. <view class="pd-24">
  8. <view class="mb-30 p-rtv batch-wapper">
  9. <view class="d-flex a-c">
  10. <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" />
  11. <view class="f-s-28 c-#333 f-w-5">需关联追溯码的批次信息</view>
  12. </view>
  13. <view class="pl-50">
  14. <view v-if="item" class="border-#8FC1EC b-radius bg-#fff ov-hd">
  15. <pack-card :item="item" :dict="{ pt_pack_ref_type }"></pack-card>
  16. </view>
  17. <view class="pd-30 d-flex j-c">
  18. <view class="c-primary f-s-28">
  19. <span>待关联数量:</span>
  20. <span class="f-s-46 f-w-600">{{ item?.planCount - item?.actualCount || 0 }}</span>
  21. <span>{{ item?.unit }}</span>
  22. </view>
  23. </view>
  24. </view>
  25. <view class="d-flex a-c">
  26. <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" />
  27. <view class="f-s-28 c-#333 f-w-5">本批次信息要关联到哪些(提前预制的)追溯码上?</view>
  28. </view>
  29. </view>
  30. <view class="pl-50">
  31. <template v-for="(row, index) in relations" :key="index">
  32. <view :id="`unchecked-row-${index}`" class="p-rtv form-codes-item bg-fff border-#9DD3AB mb-20 b-radius">
  33. <view class="d-flex j-sb">
  34. <view class="no-num-tag">NO.{{ index + 1 }}</view>
  35. </view>
  36. <view @click="deleteRow(index)" class="pd-10 close-icon">
  37. <up-icon name="close" color="#F74C30" size="34rpx"></up-icon>
  38. </view>
  39. <view class="pd2-20-30">
  40. <view class="f-s-24 c-#999 pd2-4-0">生成批号:{{ row?.item?.batchSn }}</view>
  41. <view class="d-flex f-s-24 c-#999 pd2-4-0">
  42. <view>码段:</view>
  43. <view>
  44. <view>{{ row?.item?.traceCodeStart || '-' }} 至 </view>
  45. <view>{{ row?.item?.traceCodeEnd || '-' }}</view>
  46. </view>
  47. </view>
  48. <view class="d-flex flex-wrap">
  49. <view class="hcol-14 f-s-24 pd2-4-0">
  50. <span class="c-#999">生成数量:</span>
  51. <span class="c-#999">{{ row?.item?.sumCount || '-' }}个</span>
  52. </view>
  53. <view class="hcol-16 f-s-24 pd2-4-0">
  54. <span class="c-#999">剩余数量:</span>
  55. <span class="c-#999">{{ +row?.item?.sumCount - +row?.item?.useCount - +row?.item?.voidCount || '-' }}</span>
  56. </view>
  57. <view class="hcol-20 f-s-24 pd2-4-0">
  58. <span class="c-#999">生成时间:</span>
  59. <span class="c-#999">{{ parseTime(row?.item?.createTime, '{y}-{m}-{d} {h}:{i}') || '-' }}</span>
  60. </view>
  61. </view>
  62. </view>
  63. <view class="h-1 bg-#F7F7F7 mg2-0-10"></view>
  64. <view class="pd2-20-30">
  65. <view class="f-s-28 c-#333 f-w-5 mb-16">请输入要关联的预制码编号:</view>
  66. <view class="d-flex a-c j-c mb-24">
  67. <up-input placeholder="7位起始编号" maxlength="7" v-model="row.form.startSn" @change="markRowUnchecked(row)"></up-input>
  68. <view class="pd2-0-20">至</view>
  69. <up-input placeholder="7位结束编号" maxlength="7" v-model="row.form.endSn" @change="markRowUnchecked(row)"></up-input>
  70. </view>
  71. <view class="d-flex j-c pl-120 pr-120 mb-30">
  72. <up-button @click="checkCodesValidItem(row)" :customStyle="formItemBtnCardStyle" type="primary">校验是否可用</up-button>
  73. </view>
  74. <view v-if="row?.sumnCount">
  75. <view class="d-flex a-c">
  76. <up-icon class="mr-10" name="checkmark-circle-fill" color="#3FAD5B" size="26rpx"></up-icon>
  77. <span class="f-s-24 c-#999">此次共关联{{ row?.sumnCount }}个追溯码,关联明细如下: </span>
  78. </view>
  79. <view v-for="(col, i) in row?.validCodes" :key="i" class="f-s-24 c-#999 pl-36">
  80. <span>{{ i + 1 }}、</span>
  81. <span>编号区间为{{ col.startSn.toString().padStart(7, '0') }} - {{ col.endSn.toString().padStart(7, '0') }};</span>
  82. <span>共{{ col.count }}个</span>
  83. </view>
  84. </view>
  85. </view>
  86. </view>
  87. </template>
  88. <up-button @click="selectCodes" type="primary" plain customStyle="background: #F5F7F5;">
  89. <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" />
  90. <span>请选择追溯码</span>
  91. </up-button>
  92. </view>
  93. </view>
  94. <template #bottom>
  95. <view class="pd3-10-24-20">
  96. <view class="f-s-28 c-#333 text-center mb-5">此次共关联{{ totalSumCount }}个追溯码</view>
  97. <up-button @click="batchLinkCodes" type="primary">批量关联</up-button>
  98. </view>
  99. </template>
  100. </z-paging>
  101. </template>
  102. <script setup lang="ts">
  103. import { useClientRequest } from '@/utils/request';
  104. import { getUrlParams, parseTime, recursiveDecodeURIComponent } from '@/utils/ruoyi';
  105. import PackCard from '@/plant/models/pack-card.vue';
  106. import { formItemBtnCardStyle } from '@/assets/styles/uview-plus';
  107. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  108. const { pt_pack_ref_type } = toRefs<any>(proxy?.useDict('pt_pack_ref_type'));
  109. const paging = ref<any>(null);
  110. const item = ref<any>(null);
  111. const did = ref('');
  112. const relations = ref<any[]>([]);
  113. const callbackName = 'refreshCodesRange';
  114. // 根据id获取基地详情
  115. const getDetailById = async (id: string) => {
  116. const res = await useClientRequest.get(`/plt-api/app/packTask/${id}`);
  117. if (res && res.code === 200) {
  118. item.value = res.data || null;
  119. }
  120. };
  121. const onRefresh = () => {
  122. getDetailById(did.value);
  123. paging.value?.complete();
  124. };
  125. const totalSumCount = computed(() => {
  126. return relations.value.reduce((sum, item) => sum + +(item?.sumnCount || 0), 0);
  127. });
  128. // 判断节点是否在可视区域内如果在则切换tab createIntersectionObserver
  129. onLoad((options: any) => {
  130. did.value = options?.packId || getUrlParams(recursiveDecodeURIComponent(options?.q))?.packId || '';
  131. getDetailById(did.value);
  132. uni.$on('refreshBase', () => {
  133. onRefresh();
  134. });
  135. uni.$on(callbackName, onCodeRangeSelected);
  136. });
  137. onUnload(() => {
  138. uni.$off(callbackName, onCodeRangeSelected);
  139. });
  140. const deleteRow = (index: number) => {
  141. relations.value.splice(index, 1);
  142. };
  143. const markRowUnchecked = (row: any) => {
  144. row.hasChecked = false;
  145. row.validCodes = [];
  146. row.sumnCount = 0;
  147. };
  148. // 校验单个码段是否有用
  149. const checkCodesValidItem = async (row: any) => {
  150. if (!row?.form?.startSn || !row?.form?.endSn) {
  151. uni.showToast({
  152. title: '请输入完整的起始编号和结束编号',
  153. icon: 'none',
  154. });
  155. return;
  156. }
  157. if (Number(row.form.startSn) > Number(row.form.endSn)) {
  158. uni.showToast({
  159. title: '起始编号不能大于结束编号',
  160. icon: 'none',
  161. });
  162. return;
  163. }
  164. if (Number(row.form.startSn) < Number(row.item?.startSn)) {
  165. uni.showToast({
  166. title: '起始编号不能小于已有编号',
  167. icon: 'none',
  168. });
  169. return;
  170. }
  171. if (Number(row.form.endSn) > Number(row.item?.endSn)) {
  172. uni.showToast({
  173. title: '结束编号不能大于已有编号',
  174. icon: 'none',
  175. });
  176. return;
  177. }
  178. const res = await useClientRequest.get(`/plt-api/app/traceCodeLog/checkCodeRange/${row?.form?.id}`, {
  179. ...row?.form,
  180. });
  181. console.log(res);
  182. if (!res || res.code !== 200) {
  183. row.hasChecked = false;
  184. return;
  185. }
  186. const validCodes = res.data;
  187. row.validCodes = validCodes.map((item:any) => ({ ...item, logId: row?.form?.id }));
  188. row.sumnCount = validCodes.reduce((sum: number, item: any) => sum + item.count, 0);
  189. row.hasChecked = true;
  190. };
  191. // 校验码段是否有用
  192. const checkCodesValid = async (data: any) => {
  193. if (relations.value.some((relation) => relation?.item?.id === data?.id)) {
  194. uni.showToast({
  195. title: '该追溯码段已选择',
  196. icon: 'none',
  197. });
  198. return;
  199. }
  200. const res = await useClientRequest.get(`/plt-api/app/traceCodeLog/checkCodeRange/${data?.id}`, {
  201. ...data,
  202. });
  203. if (res && res.code === 200) {
  204. // return res.data || [];
  205. relations.value.push({
  206. item: data,
  207. form: {
  208. id: data?.id,
  209. startSn: res.data[0]?.startSn?.toString().padStart(7, '0')
  210. },
  211. hasChecked: false,
  212. validCodes: [],
  213. sumnCount: 0,
  214. });
  215. console.log(relations.value);
  216. }
  217. // return [];
  218. };
  219. const onCodeRangeSelected = (data: any) => {
  220. console.log('选中的码段数据:', data);
  221. checkCodesValid(data);
  222. };
  223. const selectCodes = () => {
  224. const selectedIds = relations.value.map((row) => row?.item?.id).filter((id) => id !== null && id !== undefined);
  225. uni.$u.route({
  226. type: 'navigateTo',
  227. url: '/tools/select-code-section/index',
  228. params: {
  229. // 监听函数字符串
  230. callback: callbackName,
  231. selectedIds: encodeURIComponent(JSON.stringify(selectedIds)),
  232. },
  233. });
  234. };
  235. const batchLinkCodes = async () => {
  236. if (relations.value.length === 0) {
  237. uni.showToast({
  238. title: '请至少选择一个追溯码段进行关联',
  239. icon: 'none',
  240. });
  241. return;
  242. }
  243. const uncheckedIndex = relations.value.findIndex((row) => !row?.hasChecked);
  244. if (uncheckedIndex !== -1) {
  245. paging.value?.scrollIntoViewById(`unchecked-row-${uncheckedIndex}`, 30, true);
  246. uni.showToast({
  247. title: `请先校验第${uncheckedIndex + 1}项`,
  248. icon: 'none',
  249. });
  250. return;
  251. }
  252. try {
  253. await uni.showLoading({
  254. title: '关联中...',
  255. mask: true,
  256. });
  257. const params = {
  258. packId: did.value,
  259. relations: relations.value.map((row: any) => row?.validCodes || [])?.flat(),
  260. };
  261. await useClientRequest.post('/plt-api/app/packTask/batchRelaction', params);
  262. uni.hideLoading();
  263. uni.showToast({
  264. title: '关联成功',
  265. icon: 'success',
  266. });
  267. uni.$emit('refreshPackTaskList');
  268. uni.$emit('refreshPackTaskDetail');
  269. uni.navigateBack({
  270. delta: 1,
  271. });
  272. } catch (error) {
  273. console.error('批量关联失败:', error);
  274. uni.hideLoading();
  275. uni.showToast({
  276. title: '关联失败,请重试',
  277. icon: 'none',
  278. });
  279. }
  280. };
  281. </script>
  282. <style lang="scss" scoped>
  283. .batch-wapper {
  284. &::before {
  285. content: '';
  286. position: absolute;
  287. left: 25rpx;
  288. top: 50rpx;
  289. bottom: 50rpx;
  290. width: 1px;
  291. background: #37a954;
  292. z-index: -1;
  293. }
  294. }
  295. .no-num-tag {
  296. padding: 10rpx 14rpx;
  297. font-size: 20rpx;
  298. line-height: 1;
  299. font-weight: 600;
  300. color: $u-primary;
  301. background-color: #d9efde;
  302. border-radius: 16rpx 0 16rpx 0;
  303. }
  304. .close-icon {
  305. position: absolute;
  306. right: 0rpx;
  307. top: 0rpx;
  308. z-index: 1;
  309. }
  310. </style>