From 77e4c3d0201a83cf41b73d7bd7a79255759575f4 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 17 十月 2026 11:19:23 +0800
Subject: [PATCH] fix(mdm): 修正计量单位类型选项和API文档
---
src/api/mdm/unit/index.ts | 2
src/views/mes/pro/qrexport/qr-export-progress.vue | 10 +++
src/views/basicData/mdm/unit/data.ts | 6 +-
src/api/mes/pro/bagCode/index.ts | 6 +-
src/views/mes/pro/bagCode/modules/generate-form.vue | 18 +++---
src/views/mes/pro/qrexport/use-qr-export.ts | 119 +++++++++++++++++++++++++++++++--------
6 files changed, 120 insertions(+), 41 deletions(-)
diff --git a/src/api/mdm/unit/index.ts b/src/api/mdm/unit/index.ts
index 084cdee..2135f01 100644
--- a/src/api/mdm/unit/index.ts
+++ b/src/api/mdm/unit/index.ts
@@ -8,7 +8,7 @@
id?: number; // 鍗曚綅缂栧彿
code: string; // 鍗曚綅缂栫爜
name: string; // 鍗曚綅鍚嶇О
- type?: string; // 绫诲瀷锛�48鍗冨厠/100鍗冨厠/鍚級
+ type?: string; // 绫诲瀷锛�48琚�/100琚�/鍚級
primaryFlag?: boolean; // 鏄惁涓诲崟浣�
primaryId?: number; // 涓诲崟浣嶇紪鍙�
changeRate?: number; // 涓庝富鍗曚綅鎹㈢畻姣斾緥
diff --git a/src/api/mes/pro/bagCode/index.ts b/src/api/mes/pro/bagCode/index.ts
index 57f2d3b..7ea1122 100644
--- a/src/api/mes/pro/bagCode/index.ts
+++ b/src/api/mes/pro/bagCode/index.ts
@@ -41,17 +41,17 @@
createTime?: string[];
}
- /** 鐢熸垚鏁版嵁鍖呰姹傦紙鍝佺涓昏閲忓崟浣嶇被鍨嬩负 N鍗冨厠 鏃舵寜鎵樼洏鏁版崲绠楄鏁帮紱涓� 鍚� 鏃舵棤鎵樼洏銆佺洿鎺ヨ緭鍏ヨ鐮佹暟锛� */
+ /** 鐢熸垚鏁版嵁鍖呰姹傦紙鍝佺涓昏閲忓崟浣嶇被鍨嬩负 N琚� 鏃舵寜鎵樼洏鏁版崲绠楄鏁帮紱涓� 鍚� 鏃舵棤鎵樼洏銆佺洿鎺ヨ緭鍏ヨ鐮佹暟锛� */
export interface GenerateParams {
itemId: number; // 鍝佺锛堢墿鏂欙級缂栧彿
- palletCount?: number; // 鎵樼洏鏁帮紙48鍗冨厠/100鍗冨厠 鍝佺蹇呭~锛�
+ palletCount?: number; // 鎵樼洏鏁帮紙48琚�/100琚� 鍝佺蹇呭~锛�
bagCount?: number; // 琚嬬爜鏁帮紙鍚ㄥ搧绉嶅繀濉級
remark?: string; // 澶囨敞
}
export interface GenerateResult {
uuid?: string; // 鐢熸垚缁� UUID
- type?: string; // 鍝佺涓昏閲忓崟浣嶇被鍨嬶紙48鍗冨厠/100鍗冨厠/鍚級
+ type?: string; // 鍝佺涓昏閲忓崟浣嶇被鍨嬶紙48琚�/100琚�/鍚級
perPallet?: number; // 姣忔墭琚嬫暟锛堝惃鍝佺涓� 0锛�
totalCount?: number; // 鐢熸垚琚嬬爜鎬绘暟
palletIds?: number[]; // 鑷姩鐢熸垚鐨勬墭鐩樼紪鍙峰垪琛紙鍚ㄥ搧绉嶄负绌猴級
diff --git a/src/views/basicData/mdm/unit/data.ts b/src/views/basicData/mdm/unit/data.ts
index aed8206..0712e11 100644
--- a/src/views/basicData/mdm/unit/data.ts
+++ b/src/views/basicData/mdm/unit/data.ts
@@ -8,8 +8,8 @@
/** 璁¢噺鍗曚綅绫诲瀷閫夐」 */
export const UNIT_TYPE_OPTIONS = [
- { label: '48鍗冨厠', value: '48鍗冨厠' },
- { label: '100鍗冨厠', value: '100鍗冨厠' },
+ { label: '48琚�', value: '48琚�' },
+ { label: '100琚�', value: '100琚�' },
{ label: '鍚�', value: '鍚�' },
];
@@ -152,4 +152,4 @@
slots: { default: 'actions' },
},
];
-}
\ No newline at end of file
+}
diff --git a/src/views/mes/pro/bagCode/modules/generate-form.vue b/src/views/mes/pro/bagCode/modules/generate-form.vue
index 46ba37e..a9bf722 100644
--- a/src/views/mes/pro/bagCode/modules/generate-form.vue
+++ b/src/views/mes/pro/bagCode/modules/generate-form.vue
@@ -18,9 +18,9 @@
const emit = defineEmits(["success"]);
- // 鎵�閫夊搧绉嶄富璁¢噺鍗曚綅绫诲瀷锛�48鍗冨厠/100鍗冨厠 -> 姣忔墭 N 琚嬶紱鍚� -> 鏃犳墭鐩樼洿鎺ヨ緭琚嬬爜鏁�
+ // 鎵�閫夊搧绉嶄富璁¢噺鍗曚綅绫诲瀷锛�48琚�/100琚� -> 姣忔墭 N 琚嬶紱鍚� -> 鏃犳墭鐩樼洿鎺ヨ緭琚嬬爜鏁�
const unitType = ref("");
- const kgPerPallet = ref<number | null>(null);
+ const bagsPerPallet = ref<number | null>(null);
const isTon = ref(false);
const unitTypeError = ref(false);
const palletCountVal = ref<number | null>(null);
@@ -29,7 +29,7 @@
/** 鎹㈢畻/閰嶇疆鎻愮ず鏂囨 */
const tipText = computed(() => {
if (unitTypeError.value) {
- return "璇ュ搧绉嶄富璁¢噺鍗曚綅鏈厤缃被鍨嬶紙浠呮敮鎸� 48鍗冨厠/100鍗冨厠/鍚級锛岃鍏堝墠寰�銆岃閲忓崟浣嶃�嶇淮鎶ゅ悗閲嶈瘯";
+ return "璇ュ搧绉嶄富璁¢噺鍗曚綅鏈厤缃被鍨嬶紙浠呮敮鎸� 48琚�/100琚�/鍚級锛岃鍏堝墠寰�銆岃閲忓崟浣嶃�嶇淮鎶ゅ悗閲嶈瘯";
}
if (!unitType.value) {
return "";
@@ -39,7 +39,7 @@
bagCountVal.value ? `锛屽皢鐢熸垚 ${bagCountVal.value} 涓鐮乣 : ""
}`;
}
- const per = kgPerPallet.value ?? 0;
+ const per = bagsPerPallet.value ?? 0;
const count = palletCountVal.value ?? 0;
return `璁¢噺鍗曚綅绫诲瀷涓恒��${unitType.value}銆嶏細姣忔墭 ${per} 琚�${
count > 0
@@ -61,7 +61,7 @@
rules: "selectRequired",
},
];
- if (kgPerPallet.value) {
+ if (bagsPerPallet.value) {
schema.push({
fieldName: "palletCount",
label: "鎵樼洏鏁�",
@@ -110,7 +110,7 @@
/** 閫夋嫨鍝佺鍚庤鍙栦富璁¢噺鍗曚綅绫诲瀷锛屽姩鎬佸垏鎹㈡墭鐩樻暟/琚嬬爜鏁拌緭鍏� */
async function handleItemChange(item?: MdmItemApi.Item) {
unitType.value = "";
- kgPerPallet.value = null;
+ bagsPerPallet.value = null;
isTon.value = false;
unitTypeError.value = false;
palletCountVal.value = null;
@@ -123,9 +123,9 @@
if (type === "鍚�") {
isTon.value = true;
} else {
- const matcher = /^(\d{1,5})鍗冨厠$/.exec(type);
+ const matcher = /^(\d{1,5})琚�$/.exec(type);
if (matcher) {
- kgPerPallet.value = Number(matcher[1]);
+ bagsPerPallet.value = Number(matcher[1]);
} else {
unitTypeError.value = true;
}
@@ -190,7 +190,7 @@
modalApi.lock();
try {
unitType.value = "";
- kgPerPallet.value = null;
+ bagsPerPallet.value = null;
isTon.value = false;
unitTypeError.value = false;
palletCountVal.value = null;
diff --git a/src/views/mes/pro/qrexport/qr-export-progress.vue b/src/views/mes/pro/qrexport/qr-export-progress.vue
index 3d9281d..5edbe3f 100644
--- a/src/views/mes/pro/qrexport/qr-export-progress.vue
+++ b/src/views/mes/pro/qrexport/qr-export-progress.vue
@@ -7,7 +7,7 @@
defineOptions({ name: 'QrExportProgress' });
- const { state, cancel, minimize, expand, close } = useQrExport();
+ const { state, cancel, retryDownload, minimize, expand, close } = useQrExport();
/** 闈㈡澘鏄惁灞曠ず锛坕dle 琛ㄧず娌℃湁浠讳綍浠诲姟锛� */
const visible = computed(() => state.phase !== 'idle');
@@ -143,6 +143,14 @@
{{ title }}
</span>
<div class="flex items-center gap-1">
+ <Button
+ v-if="state.phase === 'failed' && state.downloadRetryable"
+ type="link"
+ size="small"
+ @click="retryDownload"
+ >
+ 閲嶈瘯涓嬭浇
+ </Button>
<!-- 杩涜涓彁渚涖�岄殣钘忋�嶏紝浠诲姟杞叆鍚庡彴缁х画锛屼笉浼氳涓柇 -->
<Button
v-if="state.phase !== 'done' && state.phase !== 'failed' && state.phase !== 'canceled'"
diff --git a/src/views/mes/pro/qrexport/use-qr-export.ts b/src/views/mes/pro/qrexport/use-qr-export.ts
index fe78856..61275b2 100644
--- a/src/views/mes/pro/qrexport/use-qr-export.ts
+++ b/src/views/mes/pro/qrexport/use-qr-export.ts
@@ -34,6 +34,8 @@
export interface QrExportState {
bizType?: MesProQrExportApi.BizType;
+ /** 涓嬭浇澶辫触鍚庯紝鏈嶅姟绔枃浠朵粛鍙敤鏃跺厑璁哥洿鎺ラ噸璇� */
+ downloadRetryable: boolean;
/** 宸蹭笅杞藉瓧鑺� */
downloadLoaded: number;
/** 浜х墿鎬诲瓧鑺傦紙鍙栬嚜 Content-Length锛屽彇涓嶅埌鏃朵负 0 琛ㄧず鏈煡锛� */
@@ -94,6 +96,7 @@
function createInitialState(): QrExportState {
return {
downloadLoaded: 0,
+ downloadRetryable: false,
downloadTotal: 0,
errorMsg: '',
finished: 0,
@@ -253,15 +256,55 @@
}
}
-/** 涓嬭浇浜х墿锛氫紭鍏堟祦寮忓啓鍏ョ鐩橈紝閬垮厤鏁扮櫨 MB 鐨勫唴瀹规暣鍧楅┗鐣欏唴瀛� */
+/** 姣忔鍙姹� 2 MB锛岄伩鍏嶄唬鐞嗗鍗曚釜澶ф枃浠跺搷搴旇秴鏃躲�� */
+const DOWNLOAD_CHUNK_SIZE = 2 * 1024 * 1024;
+
+async function fetchDownloadChunk(
+ url: string,
+ token: string | undefined,
+ start: number,
+ end: number,
+ total: number,
+ signal: AbortSignal,
+): Promise<Uint8Array> {
+ for (let attempt = 0; attempt < 3; attempt++) {
+ try {
+ const response = await fetch(url, {
+ headers: {
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ Range: `bytes=${start}-${end}`,
+ },
+ signal,
+ });
+ if (response.status !== 206) {
+ throw new Error(`鍒嗘涓嬭浇澶辫触锛圚TTP ${response.status}锛塦);
+ }
+ if (response.headers.get('content-range') !== `bytes ${start}-${end}/${total}`) {
+ throw new Error('涓嬭浇鍝嶅簲鐨勫瓧鑺傝寖鍥翠笉姝g‘');
+ }
+ const bytes = new Uint8Array(await response.arrayBuffer());
+ if (bytes.byteLength !== end - start + 1) {
+ throw new Error('涓嬭浇鍐呭涓嶅畬鏁�');
+ }
+ return bytes;
+ } catch (error) {
+ if (signal.aborted || attempt === 2) throw error;
+ await new Promise((resolve) => setTimeout(resolve, 750 * (attempt + 1)));
+ }
+ }
+ throw new Error('鍒嗘涓嬭浇澶辫触');
+}
+
+/** 涓嬭浇浜х墿锛氫紭鍏堝啓鍏ョ鐩橈紝姣忔鐙珛璇锋眰骞跺彲閲嶈瘯銆� */
async function downloadProduct(task: MesProQrExportApi.ExportTask) {
state.phase = 'downloading';
+ state.downloadRetryable = false;
state.message = '姝e湪涓嬭浇鈥�';
state.downloadLoaded = 0;
state.downloadTotal = task.fileSize ?? 0;
const fileName = `${task.taskName ?? '浜岀淮鐮佸鍑�'}.zip`;
const url = `${apiURL}${getQrExportDownloadPath(task.id)}`;
- const token = useAccessStore().accessToken;
+ const token = useAccessStore().accessToken ?? undefined;
const controller = new AbortController();
let canceled = false;
@@ -272,33 +315,41 @@
};
try {
- const response = await fetch(url, {
- headers: token ? { Authorization: `Bearer ${token}` } : {},
- signal: controller.signal,
- });
- if (!response.ok || !response.body) {
- throw new Error(`涓嬭浇澶辫触锛圚TTP ${response.status}锛塦);
- }
- const contentLength = Number(response.headers.get('content-length') ?? 0);
- if (contentLength > 0) {
- state.downloadTotal = contentLength;
- }
- const reader = response.body.getReader();
- downloadAbort = () => {
- canceled = true;
- void reader.cancel();
- };
const useDisk = Boolean(saveHandle);
if (useDisk) {
writable = await saveHandle.createWritable();
}
- // 杈规敹杈瑰啓锛氳繘搴︽潵鑷湡瀹炲瓧鑺傛暟锛涜兘鍐欑洏灏卞彧鍗犱竴涓垎鐗囩殑鍐呭瓨
const chunks: Uint8Array[] = [];
let loaded = 0;
- for (;;) {
- const { done, value } = await reader.read();
- if (done) break;
- if (value) {
+ if (Number.isSafeInteger(task.fileSize) && (task.fileSize ?? 0) > 0) {
+ const total = task.fileSize!;
+ for (let start = 0; start < total; start += DOWNLOAD_CHUNK_SIZE) {
+ const end = Math.min(start + DOWNLOAD_CHUNK_SIZE, total) - 1;
+ const value = await fetchDownloadChunk(url, token, start, end, total, controller.signal);
+ if (useDisk) {
+ await writable.write(value);
+ } else {
+ chunks.push(value);
+ }
+ loaded += value.length;
+ state.downloadLoaded = loaded;
+ }
+ } else {
+ // 鍏煎鏈褰曟枃浠跺ぇ灏忕殑鏃т换鍔°��
+ const response = await fetch(url, {
+ headers: token ? { Authorization: `Bearer ${token}` } : {},
+ signal: controller.signal,
+ });
+ if (!response.ok || !response.body) {
+ throw new Error(`涓嬭浇澶辫触锛圚TTP ${response.status}锛塦);
+ }
+ const contentLength = Number(response.headers.get('content-length') ?? 0);
+ if (contentLength > 0) state.downloadTotal = contentLength;
+ const reader = response.body.getReader();
+ for (;;) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ if (!value) continue;
if (useDisk) {
await writable.write(value);
} else {
@@ -309,7 +360,6 @@
}
}
if (canceled) {
- if (useDisk) await writable.abort();
return;
}
if (useDisk) {
@@ -322,10 +372,30 @@
state.message = `宸插鍑� ${state.total} 寮燻;
} catch (error: any) {
if (canceled) return;
+ state.downloadRetryable = true;
failWith(error);
} finally {
+ if (writable && state.phase !== 'done') {
+ await writable.abort().catch(() => {});
+ }
downloadAbort = null;
saveHandle = null;
+ }
+}
+
+/** 閲嶆柊涓嬭浇宸茬敓鎴愮殑 ZIP锛屾棤闇�鍐嶆鐢熸垚浜岀淮鐮併�� */
+async function retryDownload() {
+ if (!state.downloadRetryable || !state.taskId || state.phase !== 'failed') return;
+ try {
+ // 淇濆瓨鏂囦欢閫夋嫨鍣ㄩ渶瑕佺敤鎴风偣鍑昏Е鍙戯紱鍏堝彇寰楀彞鏌勶紝鍐嶆煡璇换鍔$姸鎬併��
+ saveHandle = await tryPickSaveFile(`${state.taskName}.zip`);
+ const task = await getQrExportTask(state.taskId);
+ if (!task.downloadable) {
+ throw new Error('瀵煎嚭鏂囦欢宸茶繃鏈燂紝璇烽噸鏂扮敓鎴�');
+ }
+ await downloadProduct(task);
+ } catch (error: any) {
+ failWith(error);
}
}
@@ -380,6 +450,7 @@
isActive,
start,
cancel,
+ retryDownload,
minimize,
expand,
close,
--
Gitblit v1.9.3