smart-admin-web 10.25.0 → 10.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.ts +170 -5
- package/dist/api/index.js +206 -116
- package/dist/api/index.js.map +1 -1
- package/dist/api/schema.d.ts +1477 -113
- package/dist/assets/icons/ph-subset.json.js +1 -1
- package/dist/components/FilePathUpload/index.vue.d.ts +35 -0
- package/dist/components/FilePathUpload/index.vue.js +47 -0
- package/dist/components/FilePathUpload/index.vue.js.map +1 -0
- package/dist/components/FilePathUpload/index2.vue.js +5 -0
- package/dist/components/FilePathUpload/index2.vue.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +302 -294
- package/dist/index.js.map +1 -1
- package/dist/layouts/NoticeBell.vue.js +2 -2
- package/dist/layouts/NoticeBell2.vue.js +203 -146
- package/dist/layouts/NoticeBell2.vue.js.map +1 -1
- package/dist/lib/runtime.js +1 -1
- package/dist/locales/en-US.d.ts +144 -0
- package/dist/locales/en-US.js +139 -0
- package/dist/locales/en-US.js.map +1 -1
- package/dist/locales/kernelMenuTitles.js +10 -0
- package/dist/locales/kernelMenuTitles.js.map +1 -1
- package/dist/locales/zh-CN.d.ts +144 -0
- package/dist/locales/zh-CN.js +139 -0
- package/dist/locales/zh-CN.js.map +1 -1
- package/dist/router/viewRegistry.js +8 -8
- package/dist/router/viewRegistry.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/utils/error.js +22 -18
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/filePathUpload.d.ts +14 -0
- package/dist/utils/filePathUpload.js +84 -0
- package/dist/utils/filePathUpload.js.map +1 -0
- package/dist/views/system/file-path/access.d.ts +15 -0
- package/dist/views/system/file-path/access.js +49 -0
- package/dist/views/system/file-path/access.js.map +1 -0
- package/dist/views/system/file-path/components/PathForm.vue.d.ts +18 -0
- package/dist/views/system/file-path/components/PathForm.vue.js +8 -0
- package/dist/views/system/file-path/components/PathForm.vue.js.map +1 -0
- package/dist/views/system/file-path/components/PathForm2.vue.js +529 -0
- package/dist/views/system/file-path/components/PathForm2.vue.js.map +1 -0
- package/dist/views/system/file-path/components/PathTab.vue.d.ts +3 -0
- package/dist/views/system/file-path/components/PathTab.vue.js +8 -0
- package/dist/views/system/file-path/components/PathTab.vue.js.map +1 -0
- package/dist/views/system/file-path/components/PathTab2.vue.js +226 -0
- package/dist/views/system/file-path/components/PathTab2.vue.js.map +1 -0
- package/dist/views/system/file-path/components/ServerForm.vue.d.ts +20 -0
- package/dist/views/system/file-path/components/ServerForm.vue.js +8 -0
- package/dist/views/system/file-path/components/ServerForm.vue.js.map +1 -0
- package/dist/views/system/file-path/components/ServerForm2.vue.js +328 -0
- package/dist/views/system/file-path/components/ServerForm2.vue.js.map +1 -0
- package/dist/views/system/file-path/components/ServerTab.vue.d.ts +7 -0
- package/dist/views/system/file-path/components/ServerTab.vue.js +8 -0
- package/dist/views/system/file-path/components/ServerTab.vue.js.map +1 -0
- package/dist/views/system/file-path/components/ServerTab2.vue.js +181 -0
- package/dist/views/system/file-path/components/ServerTab2.vue.js.map +1 -0
- package/dist/views/system/file-path/index.vue.d.ts +3 -0
- package/dist/views/system/file-path/index.vue.js +8 -0
- package/dist/views/system/file-path/index.vue.js.map +1 -0
- package/dist/views/system/file-path/index2.vue.js +61 -0
- package/dist/views/system/file-path/index2.vue.js.map +1 -0
- package/dist/views/system/file-path/rules.d.ts +61 -0
- package/dist/views/system/file-path/rules.js +53 -0
- package/dist/views/system/file-path/rules.js.map +1 -0
- package/package.json +1 -1
package/dist/utils/error.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t
|
|
2
|
-
import { ApiError as
|
|
3
|
-
const
|
|
1
|
+
import { t, i18n as i } from "../locales/index.js";
|
|
2
|
+
import { ApiError as s } from "../api/index.js";
|
|
3
|
+
const n = {
|
|
4
4
|
41002: "error.perm.demoReadOnly",
|
|
5
5
|
44001: "error.file.empty",
|
|
6
6
|
44002: "error.file.tooLarge",
|
|
@@ -19,32 +19,36 @@ const e = {
|
|
|
19
19
|
46012: "error.export.tooManyRows",
|
|
20
20
|
46013: "error.export.columnInvalid"
|
|
21
21
|
};
|
|
22
|
-
function
|
|
22
|
+
function l(r) {
|
|
23
23
|
if (r instanceof Error) {
|
|
24
24
|
if (r.name === "TimeoutError") return "error.network.timeout";
|
|
25
25
|
if (r.name === "AbortError") return "error.network.aborted";
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
function
|
|
29
|
-
const
|
|
30
|
-
|
|
28
|
+
function a(r) {
|
|
29
|
+
const o = r ?? {}, e = typeof o.reason == "string" ? `error.reason.${o.reason}` : "";
|
|
30
|
+
return e && i.global.te(e) ? { ...o, reason: t(e) } : o;
|
|
31
|
+
}
|
|
32
|
+
function m(r) {
|
|
33
|
+
const o = l(r);
|
|
34
|
+
if (o) return t(o);
|
|
31
35
|
if (typeof r == "number") {
|
|
32
|
-
const
|
|
33
|
-
return
|
|
36
|
+
const e = n[r];
|
|
37
|
+
return e && i.global.te(e) ? t(e) : t("error._fallback");
|
|
34
38
|
}
|
|
35
|
-
if (r instanceof
|
|
36
|
-
if (r.msgKey && i.global.te(r.msgKey)) return
|
|
37
|
-
if (!r.msgKey &&
|
|
38
|
-
return
|
|
39
|
+
if (r instanceof s) {
|
|
40
|
+
if (r.msgKey && i.global.te(r.msgKey)) return t(r.msgKey, a(r.args));
|
|
41
|
+
if (!r.msgKey && n[r.code] && i.global.te(n[r.code]))
|
|
42
|
+
return t(n[r.code], a(r.args));
|
|
39
43
|
if (r.message) return r.message;
|
|
40
44
|
}
|
|
41
|
-
return r instanceof Error && r.message ? r.message :
|
|
45
|
+
return r instanceof Error && r.message ? r.message : t("error._fallback");
|
|
42
46
|
}
|
|
43
|
-
function
|
|
44
|
-
return r instanceof
|
|
47
|
+
function u(r) {
|
|
48
|
+
return r instanceof s && r.message && r.msgKey && r.message !== r.msgKey ? r.message : m(r);
|
|
45
49
|
}
|
|
46
50
|
export {
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
m as translateError,
|
|
52
|
+
u as translateErrorDetail
|
|
49
53
|
};
|
|
50
54
|
//# sourceMappingURL=error.js.map
|
package/dist/utils/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sources":["../../src/utils/error.ts"],"sourcesContent":["import { i18n, t } from '#/locales'\nimport { ApiError } from '#/api'\n\n/**\n * 数值 ErrorCode → msgKey(镜像后端 [MsgKey];CellError 只带码不带文案)。\n * 只列导入预览会在单元格/列错误里出现的码 + 常见下载失败码;其余走兜底。\n */\nconst CODE_MSG_KEY: Record<number, string> = {\n 41002: 'error.perm.demoReadOnly',\n 44001: 'error.file.empty',\n 44002: 'error.file.tooLarge',\n 44003: 'error.file.extNotAllowed',\n 46001: 'error.excel.providerMissing',\n 46002: 'error.import.fileEmpty',\n 46003: 'error.import.rowLimitExceeded',\n 46004: 'error.import.columnMissing',\n 46005: 'error.import.cellRequired',\n 46006: 'error.import.cellDictInvalid',\n 46007: 'error.import.cellRefNotFound',\n 46008: 'error.import.cellFormatInvalid',\n 46009: 'error.import.duplicateInFile',\n 46010: 'error.import.duplicateInDb',\n 46011: 'error.import.orgOutOfScope',\n 46012: 'error.export.tooManyRows',\n 46013: 'error.export.columnInvalid',\n}\n\n/**\n * 请求被中止:超时闸(client.ts 的 timeoutMiddleware)抛 TimeoutError,主动取消抛 AbortError。\n * 二者都是 DOMException,原始 message 是浏览器的英文原话,不能直接给用户看。\n */\nfunction abortMsgKey(err: unknown): string | undefined {\n if (!(err instanceof Error)) return undefined\n if (err.name === 'TimeoutError') return 'error.network.timeout'\n if (err.name === 'AbortError') return 'error.network.aborted'\n return undefined\n}\n\n/**\n * 把任意错误翻成用户可读文案:\n * - 数字 ErrorCode(CellError.code) → 按码查 i18n\n * - 超时 / 主动取消 → 专门文案(否则用户看到浏览器的英文原话)\n * - ApiError.msgKey 命中 i18n → 本地化文案;否则退回 message;再退回通用兜底。\n * 纯函数,不依赖 Naive —— 视图拿到字符串后自行 useMessage 弹出。\n */\nexport function translateError(err: unknown): string {\n const abortKey = abortMsgKey(err)\n if (abortKey) return t(abortKey)\n if (typeof err === 'number') {\n const key = CODE_MSG_KEY[err]\n if (key && i18n.global.te(key)) return t(key)\n return t('error._fallback')\n }\n if (err instanceof ApiError) {\n // args 必须带上:后端 AdminException 的 args 就是文案占位符的实参(如 {max}),\n // 丢了它界面上会显示占位符原文,最该看到的那个数反而没了。无占位符的文案传 args 无害。\n if (err.msgKey && i18n.global.te(err.msgKey)) return t(err.msgKey, err.args
|
|
1
|
+
{"version":3,"file":"error.js","sources":["../../src/utils/error.ts"],"sourcesContent":["import { i18n, t } from '#/locales'\nimport { ApiError } from '#/api'\n\n/**\n * 数值 ErrorCode → msgKey(镜像后端 [MsgKey];CellError 只带码不带文案)。\n * 只列导入预览会在单元格/列错误里出现的码 + 常见下载失败码;其余走兜底。\n */\nconst CODE_MSG_KEY: Record<number, string> = {\n 41002: 'error.perm.demoReadOnly',\n 44001: 'error.file.empty',\n 44002: 'error.file.tooLarge',\n 44003: 'error.file.extNotAllowed',\n 46001: 'error.excel.providerMissing',\n 46002: 'error.import.fileEmpty',\n 46003: 'error.import.rowLimitExceeded',\n 46004: 'error.import.columnMissing',\n 46005: 'error.import.cellRequired',\n 46006: 'error.import.cellDictInvalid',\n 46007: 'error.import.cellRefNotFound',\n 46008: 'error.import.cellFormatInvalid',\n 46009: 'error.import.duplicateInFile',\n 46010: 'error.import.duplicateInDb',\n 46011: 'error.import.orgOutOfScope',\n 46012: 'error.export.tooManyRows',\n 46013: 'error.export.columnInvalid',\n}\n\n/**\n * 请求被中止:超时闸(client.ts 的 timeoutMiddleware)抛 TimeoutError,主动取消抛 AbortError。\n * 二者都是 DOMException,原始 message 是浏览器的英文原话,不能直接给用户看。\n */\nfunction abortMsgKey(err: unknown): string | undefined {\n if (!(err instanceof Error)) return undefined\n if (err.name === 'TimeoutError') return 'error.network.timeout'\n if (err.name === 'AbortError') return 'error.network.aborted'\n return undefined\n}\n\n/**\n * args.reason 是后端的原因码(accessDenied 这类,文件服务器访问失败的原因),文案在 `error.reason.<码>`。\n * 先换成翻译再填占位符;语言包里没有的码原样保留,总比丢掉原因强。\n */\nfunction translatedArgs(args?: Record<string, unknown> | null): Record<string, unknown> {\n // 后端无实参时信封里是 \"args\": null,默认参数只管 undefined,所以这里用 ?? 兜\n const a = args ?? {}\n const key = typeof a.reason === 'string' ? `error.reason.${a.reason}` : ''\n return key && i18n.global.te(key) ? { ...a, reason: t(key) } : a\n}\n\n/**\n * 把任意错误翻成用户可读文案:\n * - 数字 ErrorCode(CellError.code) → 按码查 i18n\n * - 超时 / 主动取消 → 专门文案(否则用户看到浏览器的英文原话)\n * - ApiError.msgKey 命中 i18n → 本地化文案;否则退回 message;再退回通用兜底。\n * 纯函数,不依赖 Naive —— 视图拿到字符串后自行 useMessage 弹出。\n */\nexport function translateError(err: unknown): string {\n const abortKey = abortMsgKey(err)\n if (abortKey) return t(abortKey)\n if (typeof err === 'number') {\n const key = CODE_MSG_KEY[err]\n if (key && i18n.global.te(key)) return t(key)\n return t('error._fallback')\n }\n if (err instanceof ApiError) {\n // args 必须带上:后端 AdminException 的 args 就是文案占位符的实参(如 {max}),\n // 丢了它界面上会显示占位符原文,最该看到的那个数反而没了。无占位符的文案传 args 无害。\n if (err.msgKey && i18n.global.te(err.msgKey)) return t(err.msgKey, translatedArgs(err.args))\n // 无 msgKey 时用数字码兜底(下载失败信封偶发只有 code)\n if (!err.msgKey && CODE_MSG_KEY[err.code] && i18n.global.te(CODE_MSG_KEY[err.code])) {\n return t(CODE_MSG_KEY[err.code], translatedArgs(err.args))\n }\n if (err.message) return err.message\n }\n if (err instanceof Error && err.message) return err.message\n return t('error._fallback')\n}\n\n/**\n * 同 translateError,但后端在 AdminException 上附了明细(`new AdminException(code, null, \"…\")`)时优先显示明细。\n * 对接外部系统、批量导入这类场景,对端原话 / 重复项清单比一句泛化的 i18n 文案更有用。\n * 判据:AdminExceptionFilter 无明细时把 Message 置成 msgKey,所以 message !== msgKey 即为明细。\n */\nexport function translateErrorDetail(err: unknown): string {\n if (err instanceof ApiError && err.message && err.msgKey && err.message !== err.msgKey) {\n return err.message\n }\n return translateError(err)\n}\n"],"names":["CODE_MSG_KEY","abortMsgKey","err","translatedArgs","args","a","key","i18n","translateError","abortKey","ApiError","translateErrorDetail"],"mappings":";;AAOA,MAAMA,IAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAMA,SAASC,EAAYC,GAAkC;AACrD,MAAMA,aAAe,OACrB;AAAA,QAAIA,EAAI,SAAS,eAAgB,QAAO;AACxC,QAAIA,EAAI,SAAS,aAAc,QAAO;AAAA;AAExC;AAMA,SAASC,EAAeC,GAAgE;AAEtF,QAAMC,IAAID,KAAQ,CAAA,GACZE,IAAM,OAAOD,EAAE,UAAW,WAAW,gBAAgBA,EAAE,MAAM,KAAK;AACxE,SAAOC,KAAOC,EAAK,OAAO,GAAGD,CAAG,IAAI,EAAE,GAAGD,GAAG,QAAQ,EAAEC,CAAG,MAAMD;AACjE;AASO,SAASG,EAAeN,GAAsB;AACnD,QAAMO,IAAWR,EAAYC,CAAG;AAChC,MAAIO,EAAU,QAAO,EAAEA,CAAQ;AAC/B,MAAI,OAAOP,KAAQ,UAAU;AAC3B,UAAMI,IAAMN,EAAaE,CAAG;AAC5B,WAAII,KAAOC,EAAK,OAAO,GAAGD,CAAG,IAAU,EAAEA,CAAG,IACrC,EAAE,iBAAiB;AAAA,EAC5B;AACA,MAAIJ,aAAeQ,GAAU;AAG3B,QAAIR,EAAI,UAAUK,EAAK,OAAO,GAAGL,EAAI,MAAM,EAAG,QAAO,EAAEA,EAAI,QAAQC,EAAeD,EAAI,IAAI,CAAC;AAE3F,QAAI,CAACA,EAAI,UAAUF,EAAaE,EAAI,IAAI,KAAKK,EAAK,OAAO,GAAGP,EAAaE,EAAI,IAAI,CAAC;AAChF,aAAO,EAAEF,EAAaE,EAAI,IAAI,GAAGC,EAAeD,EAAI,IAAI,CAAC;AAE3D,QAAIA,EAAI,QAAS,QAAOA,EAAI;AAAA,EAC9B;AACA,SAAIA,aAAe,SAASA,EAAI,UAAgBA,EAAI,UAC7C,EAAE,iBAAiB;AAC5B;AAOO,SAASS,EAAqBT,GAAsB;AACzD,SAAIA,aAAeQ,KAAYR,EAAI,WAAWA,EAAI,UAAUA,EAAI,YAAYA,EAAI,SACvEA,EAAI,UAENM,EAAeN,CAAG;AAC3B;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FilePathUploadOutput } from '../api';
|
|
2
|
+
/** 超过这个大小自动分片;正好等于时仍走单次上传。 */
|
|
3
|
+
export declare const FILE_PATH_CHUNK_THRESHOLD: number;
|
|
4
|
+
/**
|
|
5
|
+
* 把文件传进编码对应的文件路径,返回相对文件夹的路径(业务存它,之后凭它下载)。
|
|
6
|
+
* @param opts.subPath 仅「业务指定路径」命名规则使用,其余规则服务端忽略。
|
|
7
|
+
* @param opts.onProgress 进度回调(0–100)。仅分片上传会报:传片阶段最多到 99,服务端合并完成后才报 100。
|
|
8
|
+
* @param opts.signal 中止这次上传(组件卸载等):在途的请求一并取消,不再重试。
|
|
9
|
+
*/
|
|
10
|
+
export declare function uploadToFilePath(code: string, file: File, opts?: {
|
|
11
|
+
subPath?: string;
|
|
12
|
+
onProgress?: (percent: number) => void;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}): Promise<FilePathUploadOutput>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { filePathFileApi as h, ApiError as T } from "../api/index.js";
|
|
2
|
+
const H = 20 * 1024 * 1024, N = 3, S = [1e3, 2e3, 4e3], R = 44114, _ = 44115;
|
|
3
|
+
async function I(t) {
|
|
4
|
+
const n = await crypto.subtle.digest("SHA-256", await t.arrayBuffer());
|
|
5
|
+
return [...new Uint8Array(n)].map((a) => a.toString(16).padStart(2, "0")).join("");
|
|
6
|
+
}
|
|
7
|
+
function x(t) {
|
|
8
|
+
return t instanceof T ? t.code === R || t.code >= 500 && t.code < 600 : t instanceof TypeError || (t == null ? void 0 : t.name) === "TimeoutError";
|
|
9
|
+
}
|
|
10
|
+
function M(t, n) {
|
|
11
|
+
return new Promise((a, o) => {
|
|
12
|
+
if (n.aborted) {
|
|
13
|
+
o(n.reason);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const r = () => {
|
|
17
|
+
clearTimeout(i), o(n.reason);
|
|
18
|
+
}, i = setTimeout(() => {
|
|
19
|
+
n.removeEventListener("abort", r), a();
|
|
20
|
+
}, t);
|
|
21
|
+
n.addEventListener("abort", r, { once: !0 });
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function C(t, n) {
|
|
25
|
+
for (let a = 0; ; a++) {
|
|
26
|
+
try {
|
|
27
|
+
return await t();
|
|
28
|
+
} catch (o) {
|
|
29
|
+
if (n.aborted || a >= S.length || !x(o)) throw o;
|
|
30
|
+
}
|
|
31
|
+
await M(S[a], n);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function v(t, n, a = {}) {
|
|
35
|
+
var E;
|
|
36
|
+
const { subPath: o, onProgress: r, signal: i } = a;
|
|
37
|
+
if (n.size <= H)
|
|
38
|
+
return h.upload(t, n, o, i);
|
|
39
|
+
const L = await I(n), { uploadId: d, chunkSize: f, chunkCount: l } = await h.chunkInit(
|
|
40
|
+
t,
|
|
41
|
+
{ fileName: n.name, size: n.size, sha256: L, subPath: o },
|
|
42
|
+
i
|
|
43
|
+
), m = new AbortController(), c = i ? AbortSignal.any([i, m.signal]) : m.signal, p = (e) => C(
|
|
44
|
+
() => h.chunk(
|
|
45
|
+
t,
|
|
46
|
+
d,
|
|
47
|
+
e,
|
|
48
|
+
n.slice(e * f, (e + 1) * f),
|
|
49
|
+
c
|
|
50
|
+
),
|
|
51
|
+
c
|
|
52
|
+
);
|
|
53
|
+
let y = 0;
|
|
54
|
+
r == null || r(0);
|
|
55
|
+
let A = 0, u;
|
|
56
|
+
async function k() {
|
|
57
|
+
for (; !u && A < l; ) {
|
|
58
|
+
const e = A++;
|
|
59
|
+
try {
|
|
60
|
+
await p(e);
|
|
61
|
+
} catch (s) {
|
|
62
|
+
throw u ?? (u = { error: s }), m.abort(), u.error;
|
|
63
|
+
}
|
|
64
|
+
if (u) return;
|
|
65
|
+
y++, r == null || r(Math.min(99, Math.round(y / l * 100)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
await Promise.all(Array.from({ length: Math.min(N, l) }, k));
|
|
69
|
+
const b = () => C(() => h.chunkComplete(t, d, c), c);
|
|
70
|
+
let w;
|
|
71
|
+
try {
|
|
72
|
+
w = await b();
|
|
73
|
+
} catch (e) {
|
|
74
|
+
const s = e instanceof T && e.code === _ ? (E = e.args) == null ? void 0 : E.index : void 0;
|
|
75
|
+
if (typeof s != "number") throw e;
|
|
76
|
+
await p(s), w = await b();
|
|
77
|
+
}
|
|
78
|
+
return r == null || r(100), w;
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
H as FILE_PATH_CHUNK_THRESHOLD,
|
|
82
|
+
v as uploadToFilePath
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=filePathUpload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filePathUpload.js","sources":["../../src/utils/filePathUpload.ts"],"sourcesContent":["// 文件路径上传编排:不超过阈值走单次上传,超过的自动分片(算整文件 SHA-256 → init → 并发传片 → complete)。\n// 与 chunkUpload.ts 是两条互不相干的路:那边对应 sys_file 的断点续传和秒传,这边按文件路径编码落到配置好的文件夹,没有这两项。\n// 分片模式在同一次上传之内自愈:分片和合并遇到网络错误、超时、5xx、文件夹暂时访问不了时退避重试,合并报缺片时补传那一片再合并一次。\nimport { ApiError, filePathFileApi, type FilePathUploadOutput } from '#/api'\n\n/** 超过这个大小自动分片;正好等于时仍走单次上传。 */\nexport const FILE_PATH_CHUNK_THRESHOLD = 20 * 1024 * 1024\n/** 并发上传的分片数。 */\nconst CONCURRENCY = 3\n/** 重试前的等待(毫秒):第 n 次重试前等第 n 项,用完就放弃。 */\nconst RETRY_DELAYS_MS = [1000, 2000, 4000]\n/** 文件夹无法访问(服务器暂时不通、繁忙):服务端保留分片会话,稍后再试可能成功。 */\nconst FOLDER_UNAVAILABLE = 44114\n/** 合并时缺分片,args.index 是缺的那一片。 */\nconst CHUNK_MISSING = 44115\n\n/**\n * 算整文件 SHA-256(hex,小写),服务端合并后重算比对。\n * shortcut: 一次性读入内存做全量 hash,正确优先;超大文件想省内存要改分块流式 hash,需引库,当前不引依赖。\n */\nasync function sha256Hex(file: File): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', await file.arrayBuffer())\n return [...new Uint8Array(digest)].map(b => b.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * 再试一次可能成功的失败:fetch 连不上(TypeError)、客户端超时闸到点(TimeoutError)、5xx、文件夹暂时访问不了。\n * 业务校验类错误(会话无效、哈希不符、规则不允许)再试结果也一样,不在其列;中止(AbortError)也不重试。\n */\nfunction isTransient(e: unknown): boolean {\n if (e instanceof ApiError) return e.code === FOLDER_UNAVAILABLE || (e.code >= 500 && e.code < 600)\n return e instanceof TypeError || (e as { name?: unknown } | null)?.name === 'TimeoutError'\n}\n\n/** 等 ms 毫秒;signal 中止时立即以它的原因失败。 */\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n reject(signal.reason)\n return\n }\n const onAbort = () => {\n clearTimeout(timer)\n reject(signal.reason)\n }\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n signal.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/** 按 RETRY_DELAYS_MS 退避重试暂时性失败;signal 中止后不再重试。 */\nasync function withRetry<T>(op: () => Promise<T>, signal: AbortSignal): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await op()\n } catch (e) {\n if (signal.aborted || attempt >= RETRY_DELAYS_MS.length || !isTransient(e)) throw e\n }\n await sleep(RETRY_DELAYS_MS[attempt], signal)\n }\n}\n\n/**\n * 把文件传进编码对应的文件路径,返回相对文件夹的路径(业务存它,之后凭它下载)。\n * @param opts.subPath 仅「业务指定路径」命名规则使用,其余规则服务端忽略。\n * @param opts.onProgress 进度回调(0–100)。仅分片上传会报:传片阶段最多到 99,服务端合并完成后才报 100。\n * @param opts.signal 中止这次上传(组件卸载等):在途的请求一并取消,不再重试。\n */\nexport async function uploadToFilePath(\n code: string,\n file: File,\n opts: { subPath?: string; onProgress?: (percent: number) => void; signal?: AbortSignal } = {},\n): Promise<FilePathUploadOutput> {\n const { subPath, onProgress, signal } = opts\n if (file.size <= FILE_PATH_CHUNK_THRESHOLD) {\n return filePathFileApi.upload(code, file, subPath, signal)\n }\n\n const sha256 = await sha256Hex(file)\n // 分片大小以服务端为准:它按自己的配置算出片数,客户端照切,最后一片可以短。\n const { uploadId, chunkSize, chunkCount } = await filePathFileApi.chunkInit(\n code,\n { fileName: file.name, size: file.size, sha256, subPath },\n signal,\n )\n\n // 本次上传自己的中止:任一片最终失败就取消其余在途的分片;调用方的 signal 一并生效\n const stop = new AbortController()\n const abort = signal ? AbortSignal.any([signal, stop.signal]) : stop.signal\n const sendChunk = (i: number) =>\n withRetry(\n () =>\n filePathFileApi.chunk(\n code,\n uploadId,\n i,\n file.slice(i * chunkSize, (i + 1) * chunkSize),\n abort,\n ),\n abort,\n )\n\n let uploaded = 0\n onProgress?.(0)\n // 限并发的 worker 池:共享游标,各自领片上传;任一片失败后其余 worker 不再领新片,在途的也取消,免得出错后还在往服务端灌数据。\n let next = 0\n let failure: { error: unknown } | undefined\n async function worker() {\n while (!failure && next < chunkCount) {\n const i = next++\n try {\n await sendChunk(i)\n } catch (e) {\n // 被取消的分片也走到这里:一律抛最先失败的那一片自己的错误,不抛取消引出的 AbortError\n failure ??= { error: e }\n stop.abort()\n throw failure.error\n }\n // 别的分片已经失败:这片即使传完也不再计数、不报进度,否则 n-upload 会把已失败的文件改回「上传中」\n if (failure) return\n uploaded++\n onProgress?.(Math.min(99, Math.round((uploaded / chunkCount) * 100)))\n }\n }\n await Promise.all(Array.from({ length: Math.min(CONCURRENCY, chunkCount) }, worker))\n\n const complete = () =>\n withRetry(() => filePathFileApi.chunkComplete(code, uploadId, abort), abort)\n let out: FilePathUploadOutput\n try {\n out = await complete()\n } catch (e) {\n // 缺片(分片落在了没共享暂存目录的副本上、被清扫过):补传缺的那一片再合并一次,只补这一轮\n const index = e instanceof ApiError && e.code === CHUNK_MISSING ? e.args?.index : undefined\n if (typeof index !== 'number') throw e\n await sendChunk(index)\n out = await complete()\n }\n onProgress?.(100)\n return out\n}\n"],"names":["FILE_PATH_CHUNK_THRESHOLD","CONCURRENCY","RETRY_DELAYS_MS","FOLDER_UNAVAILABLE","CHUNK_MISSING","sha256Hex","file","digest","b","isTransient","e","ApiError","sleep","ms","signal","resolve","reject","onAbort","timer","withRetry","op","attempt","uploadToFilePath","code","opts","subPath","onProgress","filePathFileApi","sha256","uploadId","chunkSize","chunkCount","stop","abort","sendChunk","i","uploaded","next","failure","worker","complete","out","index","_a"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FileAccessStatus } from '../../../api';
|
|
2
|
+
/** 原因码换成 `error.reason.*` 的文案;语言包里没有的码原样显示,总比丢掉原因强。 */
|
|
3
|
+
export declare function reasonText(reason: string | null | undefined): string;
|
|
4
|
+
/**
|
|
5
|
+
* 列表在线状态:一页取完后,用这一页的 Id 问一次,不轮询,翻页或刷新时再问。
|
|
6
|
+
* 还没有结果的行显示「检测中」;整批请求失败显示「未知」,不弹错(列表本身是好的),悬停能看到失败原因。
|
|
7
|
+
*/
|
|
8
|
+
export declare function useAccessStatus(fetchStatus: (ids: number[]) => Promise<FileAccessStatus[]>): {
|
|
9
|
+
load: (rows: {
|
|
10
|
+
id: number | string;
|
|
11
|
+
}[]) => Promise<void>;
|
|
12
|
+
render: (id: number | string) => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ref as g, h as a } from "vue";
|
|
2
|
+
import { NTooltip as p, NTag as y } from "naive-ui";
|
|
3
|
+
import { i18n as b, t as u } from "../../../locales/index.js";
|
|
4
|
+
import { translateError as k } from "../../../utils/error.js";
|
|
5
|
+
function x(e) {
|
|
6
|
+
if (!e) return "";
|
|
7
|
+
const n = `error.reason.${e}`;
|
|
8
|
+
return b.global.te(n) ? u(n) : e;
|
|
9
|
+
}
|
|
10
|
+
const d = (e) => a("span", { style: { color: "var(--text-3)" } }, e);
|
|
11
|
+
function N(e) {
|
|
12
|
+
const n = g({}), f = g("");
|
|
13
|
+
let m = 0;
|
|
14
|
+
async function h(c) {
|
|
15
|
+
const l = ++m;
|
|
16
|
+
n.value = {};
|
|
17
|
+
const t = c.map((i) => Number(i.id));
|
|
18
|
+
if (t.length === 0) return;
|
|
19
|
+
let r, o = "";
|
|
20
|
+
try {
|
|
21
|
+
const i = await e(t);
|
|
22
|
+
r = Object.fromEntries(i.map((s) => [String(s.id), s]));
|
|
23
|
+
} catch (i) {
|
|
24
|
+
r = Object.fromEntries(t.map((s) => [String(s), null])), o = k(i);
|
|
25
|
+
}
|
|
26
|
+
l === m && (n.value = r, f.value = o);
|
|
27
|
+
}
|
|
28
|
+
function v(c) {
|
|
29
|
+
const l = String(c);
|
|
30
|
+
if (!(l in n.value)) return d(u("filePath.status.checking"));
|
|
31
|
+
const t = n.value[l];
|
|
32
|
+
if (!t) {
|
|
33
|
+
const o = d(u("filePath.status.unknown"));
|
|
34
|
+
return f.value ? a(p, null, { trigger: () => o, default: () => f.value }) : o;
|
|
35
|
+
}
|
|
36
|
+
const r = a(
|
|
37
|
+
y,
|
|
38
|
+
{ size: "small", bordered: !1, type: t.online ? "success" : "error" },
|
|
39
|
+
() => u(t.online ? "filePath.status.online" : "filePath.status.offline")
|
|
40
|
+
);
|
|
41
|
+
return t.online || !t.reason ? r : a(p, null, { trigger: () => r, default: () => x(t.reason) });
|
|
42
|
+
}
|
|
43
|
+
return { load: h, render: v };
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
x as reasonText,
|
|
47
|
+
N as useAccessStatus
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.js","sources":["../../../../src/views/system/file-path/access.ts"],"sourcesContent":["// 在线状态与失败原因的展示,文件路径、文件服务器两个页签共用。\nimport { h, ref } from 'vue'\nimport { NTag, NTooltip } from 'naive-ui'\nimport { i18n, t } from '#/locales'\nimport type { FileAccessStatus } from '#/api'\nimport { translateError } from '#/utils/error'\n\n/** 原因码换成 `error.reason.*` 的文案;语言包里没有的码原样显示,总比丢掉原因强。 */\nexport function reasonText(reason: string | null | undefined): string {\n if (!reason) return ''\n const key = `error.reason.${reason}`\n return i18n.global.te(key) ? t(key) : reason\n}\n\nconst faint = (text: string) => h('span', { style: { color: 'var(--text-3)' } }, text)\n\n/**\n * 列表在线状态:一页取完后,用这一页的 Id 问一次,不轮询,翻页或刷新时再问。\n * 还没有结果的行显示「检测中」;整批请求失败显示「未知」,不弹错(列表本身是好的),悬停能看到失败原因。\n */\nexport function useAccessStatus(fetchStatus: (ids: number[]) => Promise<FileAccessStatus[]>) {\n // 没有条目 = 检测中;null = 这一批检测失败\n const results = ref<Record<string, FileAccessStatus | null>>({})\n // 整批检测失败的原因(已翻译),「未知」的悬停提示\n const failure = ref('')\n let latest = 0\n\n async function load(rows: { id: number | string }[]) {\n const mine = ++latest\n results.value = {}\n const ids = rows.map(r => Number(r.id))\n if (ids.length === 0) return\n let next: Record<string, FileAccessStatus | null>\n let error = ''\n try {\n const list = await fetchStatus(ids)\n next = Object.fromEntries(list.map(s => [String(s.id), s]))\n } catch (e) {\n next = Object.fromEntries(ids.map(id => [String(id), null]))\n error = translateError(e)\n }\n // 翻页太快时只认最后一次\n if (mine !== latest) return\n results.value = next\n failure.value = error\n }\n\n function render(id: number | string) {\n const key = String(id)\n if (!(key in results.value)) return faint(t('filePath.status.checking'))\n const s = results.value[key]\n if (!s) {\n const unknown = faint(t('filePath.status.unknown'))\n return failure.value\n ? h(NTooltip, null, { trigger: () => unknown, default: () => failure.value })\n : unknown\n }\n const tag = h(\n NTag,\n { size: 'small', bordered: false, type: s.online ? 'success' : 'error' },\n () => t(s.online ? 'filePath.status.online' : 'filePath.status.offline'),\n )\n if (s.online || !s.reason) return tag\n return h(NTooltip, null, { trigger: () => tag, default: () => reasonText(s.reason) })\n }\n\n return { load, render }\n}\n"],"names":["reasonText","reason","key","i18n","t","faint","text","h","useAccessStatus","fetchStatus","results","ref","failure","latest","load","rows","mine","ids","r","next","error","list","e","id","translateError","render","s","unknown","NTooltip","tag","NTag"],"mappings":";;;;AAQO,SAASA,EAAWC,GAA2C;AACpE,MAAI,CAACA,EAAQ,QAAO;AACpB,QAAMC,IAAM,gBAAgBD,CAAM;AAClC,SAAOE,EAAK,OAAO,GAAGD,CAAG,IAAIE,EAAEF,CAAG,IAAID;AACxC;AAEA,MAAMI,IAAQ,CAACC,MAAiBC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,kBAAgB,GAAKD,CAAI;AAM9E,SAASE,EAAgBC,GAA6D;AAE3F,QAAMC,IAAUC,EAA6C,EAAE,GAEzDC,IAAUD,EAAI,EAAE;AACtB,MAAIE,IAAS;AAEb,iBAAeC,EAAKC,GAAiC;AACnD,UAAMC,IAAO,EAAEH;AACf,IAAAH,EAAQ,QAAQ,CAAA;AAChB,UAAMO,IAAMF,EAAK,IAAI,OAAK,OAAOG,EAAE,EAAE,CAAC;AACtC,QAAID,EAAI,WAAW,EAAG;AACtB,QAAIE,GACAC,IAAQ;AACZ,QAAI;AACF,YAAMC,IAAO,MAAMZ,EAAYQ,CAAG;AAClC,MAAAE,IAAO,OAAO,YAAYE,EAAK,IAAI,CAAA,MAAK,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;AAAA,IAC5D,SAASC,GAAG;AACV,MAAAH,IAAO,OAAO,YAAYF,EAAI,IAAI,CAAAM,MAAM,CAAC,OAAOA,CAAE,GAAG,IAAI,CAAC,CAAC,GAC3DH,IAAQI,EAAeF,CAAC;AAAA,IAC1B;AAEA,IAAIN,MAASH,MACbH,EAAQ,QAAQS,GAChBP,EAAQ,QAAQQ;AAAA,EAClB;AAEA,WAASK,EAAOF,GAAqB;AACnC,UAAMrB,IAAM,OAAOqB,CAAE;AACrB,QAAI,EAAErB,KAAOQ,EAAQ,eAAeL,EAAMD,EAAE,0BAA0B,CAAC;AACvE,UAAMsB,IAAIhB,EAAQ,MAAMR,CAAG;AAC3B,QAAI,CAACwB,GAAG;AACN,YAAMC,IAAUtB,EAAMD,EAAE,yBAAyB,CAAC;AAClD,aAAOQ,EAAQ,QACXL,EAAEqB,GAAU,MAAM,EAAE,SAAS,MAAMD,GAAS,SAAS,MAAMf,EAAQ,MAAA,CAAO,IAC1Ee;AAAA,IACN;AACA,UAAME,IAAMtB;AAAA,MACVuB;AAAA,MACA,EAAE,MAAM,SAAS,UAAU,IAAO,MAAMJ,EAAE,SAAS,YAAY,QAAA;AAAA,MAC/D,MAAMtB,EAAEsB,EAAE,SAAS,2BAA2B,yBAAyB;AAAA,IAAA;AAEzE,WAAIA,EAAE,UAAU,CAACA,EAAE,SAAeG,IAC3BtB,EAAEqB,GAAU,MAAM,EAAE,SAAS,MAAMC,GAAK,SAAS,MAAM7B,EAAW0B,EAAE,MAAM,GAAG;AAAA,EACtF;AAEA,SAAO,EAAE,MAAAZ,GAAM,QAAAW,EAAA;AACjB;"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FilePathView } from '../../../../api';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
editing: FilePathView | null;
|
|
4
|
+
};
|
|
5
|
+
type __VLS_ModelProps = {
|
|
6
|
+
'show'?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
9
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
10
|
+
"update:show": (value: boolean) => any;
|
|
11
|
+
} & {
|
|
12
|
+
saved: () => any;
|
|
13
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
14
|
+
onSaved?: (() => any) | undefined;
|
|
15
|
+
"onUpdate:show"?: ((value: boolean) => any) | undefined;
|
|
16
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import o from "./PathForm2.vue.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import t from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
const f = /* @__PURE__ */ t(o, [["__scopeId", "data-v-3824eb1f"]]);
|
|
5
|
+
export {
|
|
6
|
+
f as default
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=PathForm.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PathForm.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|