@veltra/desktop 1.7.13 → 1.7.16

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.
Files changed (89) hide show
  1. package/dist/components/batch-edit/batch-edit-form.js +9 -3
  2. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  3. package/dist/components/batch-edit/batch-edit-list.js +2 -1
  4. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  5. package/dist/components/batch-edit/batch-edit.js +57 -30
  6. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  7. package/dist/components/batch-edit/style.d.ts +1 -0
  8. package/dist/components/batch-edit/style.js +1 -0
  9. package/dist/components/batch-edit/style2.css +14 -0
  10. package/dist/components/batch-edit/use-dialog-effect.js +20 -0
  11. package/dist/components/batch-edit/use-dialog-effect.js.map +1 -0
  12. package/dist/components/batch-edit/use-edit-state.js +22 -3
  13. package/dist/components/batch-edit/use-edit-state.js.map +1 -1
  14. package/dist/components/batch-edit/use-handlers.js +5 -0
  15. package/dist/components/batch-edit/use-handlers.js.map +1 -1
  16. package/dist/components/batch-edit/use-shortcut-key.js +5 -2
  17. package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
  18. package/dist/components/button/button.js +2 -2
  19. package/dist/components/button/button.js.map +1 -1
  20. package/dist/components/file-viewer/file-viewer.js +21 -14
  21. package/dist/components/file-viewer/file-viewer.js.map +1 -1
  22. package/dist/components/file-viewer/helper.js +2 -0
  23. package/dist/components/file-viewer/helper.js.map +1 -1
  24. package/dist/components/file-viewer/previewers/ofd-previewer.js +196 -0
  25. package/dist/components/file-viewer/previewers/ofd-previewer.js.map +1 -0
  26. package/dist/components/file-viewer/style2.css +27 -0
  27. package/dist/components/form-item/style2.css +17 -2
  28. package/dist/components/table-editor/style2.css +48 -5
  29. package/dist/components/table-editor/table-editor.js +228 -63
  30. package/dist/components/table-editor/table-editor.js.map +1 -1
  31. package/dist/components/tip/style2.css +63 -0
  32. package/dist/components/tip/tip.js +6 -1
  33. package/dist/components/tip/tip.js.map +1 -1
  34. package/dist/index.d.ts +3 -3
  35. package/dist/packages/ofd-core/dist/color.js +69 -0
  36. package/dist/packages/ofd-core/dist/color.js.map +1 -0
  37. package/dist/packages/ofd-core/dist/ctm.js +55 -0
  38. package/dist/packages/ofd-core/dist/ctm.js.map +1 -0
  39. package/dist/packages/ofd-core/dist/error.js +14 -0
  40. package/dist/packages/ofd-core/dist/error.js.map +1 -0
  41. package/dist/packages/ofd-core/dist/font.js +384 -0
  42. package/dist/packages/ofd-core/dist/font.js.map +1 -0
  43. package/dist/packages/ofd-core/dist/index.js +6 -0
  44. package/dist/packages/ofd-core/dist/number.js +13 -0
  45. package/dist/packages/ofd-core/dist/number.js.map +1 -0
  46. package/dist/packages/ofd-core/dist/page-objects.js +181 -0
  47. package/dist/packages/ofd-core/dist/page-objects.js.map +1 -0
  48. package/dist/packages/ofd-core/dist/parse.js +65 -0
  49. package/dist/packages/ofd-core/dist/parse.js.map +1 -0
  50. package/dist/packages/ofd-core/dist/render.js +361 -0
  51. package/dist/packages/ofd-core/dist/render.js.map +1 -0
  52. package/dist/packages/ofd-core/dist/xml.js +199 -0
  53. package/dist/packages/ofd-core/dist/xml.js.map +1 -0
  54. package/dist/packages/ofd-core/dist/zip.js +93 -0
  55. package/dist/packages/ofd-core/dist/zip.js.map +1 -0
  56. package/dist/style.d.ts +1 -1
  57. package/dist/style.js +1 -1
  58. package/dist/types/batch-edit.d.ts +6 -0
  59. package/dist/types/file-viewer.d.ts +1 -1
  60. package/dist/types/index.d.ts +3 -3
  61. package/dist/types/table-editor.d.ts +28 -7
  62. package/dist/types/tip.d.ts +5 -1
  63. package/package.json +8 -7
  64. package/src/components/batch-edit/__test__/batch-edit.test.ts +191 -4
  65. package/src/components/batch-edit/batch-edit-form.vue +17 -3
  66. package/src/components/batch-edit/batch-edit-list.vue +2 -1
  67. package/src/components/batch-edit/batch-edit.vue +22 -4
  68. package/src/components/batch-edit/style.scss +20 -0
  69. package/src/components/batch-edit/style.ts +1 -0
  70. package/src/components/batch-edit/use-dialog-effect.ts +9 -3
  71. package/src/components/batch-edit/use-edit-state.ts +32 -4
  72. package/src/components/batch-edit/use-handlers.ts +10 -0
  73. package/src/components/batch-edit/use-shortcut-key.ts +6 -2
  74. package/src/components/button/button.vue +3 -2
  75. package/src/components/file-viewer/__test__/helper.test.ts +13 -0
  76. package/src/components/file-viewer/file-viewer.vue +23 -16
  77. package/src/components/file-viewer/helper.ts +2 -0
  78. package/src/components/file-viewer/previewers/ofd-previewer.vue +216 -0
  79. package/src/components/file-viewer/style.scss +33 -0
  80. package/src/components/form-item/style.scss +11 -1
  81. package/src/components/table-editor/__test__/table-editor.test.ts +479 -0
  82. package/src/components/table-editor/style.scss +57 -5
  83. package/src/components/table-editor/table-editor.vue +286 -38
  84. package/src/components/tip/style.scss +15 -0
  85. package/src/components/tip/tip.vue +1 -1
  86. package/src/types/batch-edit.ts +6 -0
  87. package/src/types/file-viewer.ts +1 -1
  88. package/src/types/table-editor.ts +29 -7
  89. package/src/types/tip.ts +6 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml.js","names":[],"sources":["../../../../../ofd-core/dist/xml.js"],"sourcesContent":["import { OfdParseError } from \"./error.js\";\nimport { parseNumberList } from \"./ctm.js\";\nimport { parseOfdColor } from \"./color.js\";\n//#region src/xml.ts\nfunction childElements(element) {\n\tconst out = [];\n\tfor (let i = 0; i < element.childNodes.length; i++) {\n\t\tconst node = element.childNodes[i];\n\t\tif (node && node.nodeType === 1) out.push(node);\n\t}\n\treturn out;\n}\nfunction localNameOf(element) {\n\tif (element.localName) return element.localName;\n\tconst tag = element.nodeName;\n\treturn tag.includes(\":\") ? tag.slice(tag.indexOf(\":\") + 1) : tag;\n}\nfunction childrenNamed(element, name) {\n\treturn childElements(element).filter((child) => localNameOf(child) === name);\n}\nfunction firstChildNamed(element, name) {\n\treturn childrenNamed(element, name)[0] ?? null;\n}\nfunction childText(element, name) {\n\treturn firstChildNamed(element, name)?.textContent ?? null;\n}\nfunction normalizedText(value) {\n\tconst text = value?.trim();\n\treturn text ? text : null;\n}\nfunction parseXml(xml, source) {\n\tlet doc;\n\ttry {\n\t\tdoc = new DOMParser().parseFromString(xml, \"application/xml\");\n\t} catch (cause) {\n\t\tthrow new OfdParseError(\"invalid-xml\", `${source} 不是合法的 XML`, { cause });\n\t}\n\tif (doc.getElementsByTagName(\"parsererror\").length > 0) throw new OfdParseError(\"invalid-xml\", `${source} 不是合法的 XML`);\n\treturn doc;\n}\nfunction rootElementOf(doc, names, source) {\n\tconst expected = typeof names === \"string\" ? [names] : names;\n\tconst root = doc.documentElement;\n\tconst lowered = expected.map((name) => name.toLowerCase());\n\tif (!root || !lowered.includes(localNameOf(root).toLowerCase())) throw new OfdParseError(\"invalid-structure\", `${source} 根节点不是 ${expected.join(\" / \")}`);\n\treturn root;\n}\n/** 解析 OFD.xml:每个 DocBody 产出一条入口,支持多文档容器 */\nfunction parseOfdRoot(xml, source = \"OFD.xml\") {\n\treturn childrenNamed(rootElementOf(parseXml(xml, source), \"Ofd\", source), \"DocBody\").map((body) => {\n\t\tconst docRoot = normalizedText(childText(body, \"DocRoot\"));\n\t\tif (!docRoot) throw new OfdParseError(\"invalid-structure\", `${source} 中 DocBody 缺少 DocRoot`);\n\t\tconst info = firstChildNamed(body, \"DocInfo\");\n\t\treturn {\n\t\t\tdocRoot,\n\t\t\tinfo: {\n\t\t\t\tdocId: info ? normalizedText(childText(info, \"DocID\")) : null,\n\t\t\t\ttitle: info ? normalizedText(childText(info, \"Title\")) : null\n\t\t\t}\n\t\t};\n\t});\n}\n/** 解析 Document.xml:默认页尺寸、公共资源引用、模板页声明、页面树 */\nfunction parseDocumentXml(xml, source) {\n\tconst root = rootElementOf(parseXml(xml, source), \"Document\", source);\n\tconst commonData = firstChildNamed(root, \"CommonData\");\n\tconst pages = firstChildNamed(root, \"Pages\");\n\treturn {\n\t\tpageSize: commonData ? parsePageSize(commonData) ?? physicalBoxSize(commonData, \"PageArea\") : null,\n\t\tresLocation: resDeclarationLocation(root, commonData, \"DocumentRes\"),\n\t\tpublicResLocation: resDeclarationLocation(root, commonData, \"PublicRes\"),\n\t\ttemplates: commonData ? childrenNamed(commonData, \"TemplatePage\").map((template) => ({\n\t\t\tid: template.getAttribute(\"ID\"),\n\t\t\tlocation: normalizedText(template.getAttribute(\"BaseLoc\") ?? template.textContent)\n\t\t})) : [],\n\t\tpageLocations: pages ? childrenNamed(pages, \"Page\").map((page) => normalizedText(page.getAttribute(\"BaseLoc\"))).filter((value) => value !== null) : []\n\t};\n}\n/**\n* 资源声明路径兼容两种产出形态:声明元素挂在 Document 根或 CommonData 下,\n* 路径写在 ResLoc 属性或元素文本中(WPS / 数科 / 数电发票产出均为文本形态)。\n*/\nfunction resDeclarationLocation(root, commonData, name) {\n\tconst node = firstChildNamed(root, name) ?? (commonData && firstChildNamed(commonData, name));\n\tif (!node) return null;\n\treturn normalizedText(node.getAttribute(\"ResLoc\")) ?? normalizedText(node.textContent);\n}\n/** 物理盒尺寸兜底(\"x y w h\" 取宽高);主流生成器以 PhysicalBox 代替 PageWidth/PageHeight */\nfunction physicalBoxSize(scope, wrapper) {\n\tconst container = firstChildNamed(scope, wrapper);\n\tconst box = container ? firstChildNamed(container, \"PhysicalBox\") : null;\n\tconst numbers = parseNumberList(box?.textContent ?? null);\n\treturn numbers.length === 4 ? {\n\t\twidth: numbers[2],\n\t\theight: numbers[3]\n\t} : null;\n}\n/** 解析 DocumentRes.xml / PublicRes.xml:字体、多媒体资源与绘制参数声明 */\nfunction parseDocumentRes(xml, source) {\n\tconst root = rootElementOf(parseXml(xml, source), \"Res\", source);\n\tconst baseLoc = normalizedText(root.getAttribute(\"BaseLoc\"));\n\tconst fonts = [];\n\tconst medias = [];\n\tconst drawParams = [];\n\tfor (const group of childrenNamed(root, \"Fonts\")) for (const font of childrenNamed(group, \"Font\")) {\n\t\tconst id = font.getAttribute(\"ID\");\n\t\tif (id !== null) fonts.push({\n\t\t\tid,\n\t\t\tfontName: normalizedText(font.getAttribute(\"FontName\")),\n\t\t\tfontFile: fontFileLocation(baseLoc, font)\n\t\t});\n\t}\n\tfor (const group of childrenNamed(root, \"MultiMedias\")) for (const media of childrenNamed(group, \"MultiMedia\")) {\n\t\tconst id = media.getAttribute(\"ID\");\n\t\tif (id !== null) medias.push({\n\t\t\tid,\n\t\t\ttype: media.getAttribute(\"Type\"),\n\t\t\tlocation: normalizedText(media.getAttribute(\"ResLoc\")) ?? mediaFileLocation(baseLoc, media)\n\t\t});\n\t}\n\tfor (const group of childrenNamed(root, \"DrawParams\")) for (const param of childrenNamed(group, \"DrawParam\")) {\n\t\tconst id = param.getAttribute(\"ID\");\n\t\tif (id !== null) drawParams.push({\n\t\t\tid,\n\t\t\tlineWidth: parseNumber(param.getAttribute(\"LineWidth\")),\n\t\t\trelative: normalizedText(param.getAttribute(\"Relative\")),\n\t\t\tfillColor: declaredColor(param, \"FillColor\"),\n\t\t\tstrokeColor: declaredColor(param, \"StrokeColor\")\n\t\t});\n\t}\n\treturn {\n\t\tfonts,\n\t\tmedias,\n\t\tdrawParams\n\t};\n}\n/** DrawParam 子元素颜色:<FillColor Value=\"128 0 0\"/> 按 Value 属性解析 */\nfunction declaredColor(param, name) {\n\treturn parseOfdColor(firstChildNamed(param, name)?.getAttribute(\"Value\") ?? null);\n}\n/**\n* 字体文件位置:FontFile 兼容属性与子元素两种声明(新版数电发票实证为子元素),\n* 路径相对资源声明根的 BaseLoc 解析,未声明 BaseLoc 时相对文档根。\n*/\nfunction fontFileLocation(baseLoc, font) {\n\tconst file = normalizedText(font.getAttribute(\"FontFile\")) ?? normalizedText(childText(font, \"FontFile\"));\n\treturn file === null ? null : joinZipPath(baseLoc ?? \"\", file);\n}\n/** 媒体位置兜底:MediaFile 子元素声明文件名,相对资源声明根的 BaseLoc 解析 */\nfunction mediaFileLocation(baseLoc, media) {\n\tconst file = normalizedText(firstChildNamed(media, \"MediaFile\")?.textContent ?? null);\n\treturn file === null ? null : joinZipPath(baseLoc ?? \"\", file);\n}\n/**\n* 拼接并规范化容器内路径:去空段与 './',统一 '/' 分隔(页面定位、图片资源定位共用)。\n* 段以 '/' 开头时按容器绝对路径处理(个别产出在相对文档根的位置写绝对路径)。\n*/\nfunction joinZipPath(...segments) {\n\tconst parts = [];\n\tfor (const segment of segments) {\n\t\tif (segment.startsWith(\"/\")) parts.length = 0;\n\t\tfor (const piece of segment.split(\"/\")) if (piece !== \"\" && piece !== \".\") parts.push(piece);\n\t}\n\treturn parts.join(\"/\");\n}\n/** 解析 Page.xml:页面尺寸覆盖与图层声明 */\nfunction parsePageXml(xml, source) {\n\tconst root = rootElementOf(parseXml(xml, source), \"Page\", source);\n\tconst commonData = firstChildNamed(root, \"CommonData\");\n\tconst layerParent = firstChildNamed(root, \"Content\") ?? root;\n\treturn {\n\t\tsize: (commonData ? parsePageSize(commonData) ?? physicalBoxSize(commonData, \"PageArea\") : null) ?? physicalBoxSize(root, \"Area\"),\n\t\tlayers: childrenNamed(layerParent, \"Layer\").map((layer) => ({\n\t\t\tid: layer.getAttribute(\"ID\"),\n\t\t\ttype: parseLayerType(layer.getAttribute(\"Type\"))\n\t\t}))\n\t};\n}\nfunction parsePageSize(commonData) {\n\tconst width = parseNumber(childText(commonData, \"PageWidth\"));\n\tconst height = parseNumber(childText(commonData, \"PageHeight\"));\n\treturn width !== null && height !== null ? {\n\t\twidth,\n\t\theight\n\t} : null;\n}\nfunction parseNumber(value) {\n\tif (value === null || value.trim() === \"\") return null;\n\tconst parsed = Number(value);\n\treturn Number.isFinite(parsed) ? parsed : null;\n}\nfunction parseLayerType(value) {\n\tconst lowered = value?.toLowerCase();\n\treturn lowered === \"background\" || lowered === \"body\" || lowered === \"foreground\" || lowered === \"annotation\" ? lowered : null;\n}\n//#endregion\nexport { childElements, childrenNamed, firstChildNamed, joinZipPath, localNameOf, parseDocumentRes, parseDocumentXml, parseLayerType, parseOfdRoot, parsePageXml, parseXml, rootElementOf };\n\n//# sourceMappingURL=xml.js.map"],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { OfdParseError } from "./error.js";
2
+ //#region ../ofd-core/dist/zip.js
3
+ const EOCD_SIGNATURE = 101010256;
4
+ const CEN_SIGNATURE = 33639248;
5
+ const LOC_SIGNATURE = 67324752;
6
+ const EOCD_LENGTH = 22;
7
+ const CEN_HEADER_LENGTH = 46;
8
+ const LOC_HEADER_LENGTH = 30;
9
+ /** EOCD 注释区最大长度,找 EOCD 时最多向前扫这么多字节 */
10
+ const EOCD_COMMENT_MAX = 65535;
11
+ const STORED_METHOD = 0;
12
+ const DEFLATE_METHOD = 8;
13
+ /** 解析 ZIP 结构(EOCD / 中央目录);条目数据在 read 时才解压 */
14
+ function openOfdZip(data) {
15
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
16
+ const eocdOffset = findEocdOffset(data);
17
+ const records = parseCentralDirectory(data, view, eocdOffset, view.getUint16(eocdOffset + 10, true));
18
+ const byName = new Map(records.map((record) => [record.name.toLowerCase(), record]));
19
+ return {
20
+ entries: records,
21
+ has: (name) => byName.has(name.toLowerCase()),
22
+ read: (name) => readEntry(data, view, byName.get(name.toLowerCase()), name),
23
+ text: async (name) => new TextDecoder().decode(await readEntry(data, view, byName.get(name.toLowerCase()), name))
24
+ };
25
+ }
26
+ function findEocdOffset(data) {
27
+ const minStart = Math.max(0, data.length - EOCD_LENGTH - EOCD_COMMENT_MAX);
28
+ for (let offset = data.length - EOCD_LENGTH; offset >= minStart; offset--) if (readU32(data, offset) === EOCD_SIGNATURE) return offset;
29
+ throw new OfdParseError("not-zip", "未找到 ZIP End of Central Directory,输入不是合法的 ZIP 容器");
30
+ }
31
+ function parseCentralDirectory(data, view, eocdOffset, entryCount) {
32
+ let offset = view.getUint32(eocdOffset + 16, true);
33
+ const records = [];
34
+ for (let index = 0; index < entryCount; index++) {
35
+ if (offset + CEN_HEADER_LENGTH > data.length) throw new OfdParseError("truncated", `中央目录第 ${index} 个条目越界,数据被截断`);
36
+ if (view.getUint32(offset, true) !== CEN_SIGNATURE) throw new OfdParseError("bad-central-directory", `中央目录第 ${index} 个条目签名不合法`);
37
+ const method = view.getUint16(offset + 10, true);
38
+ const compressedSize = view.getUint32(offset + 20, true);
39
+ const size = view.getUint32(offset + 24, true);
40
+ const nameLength = view.getUint16(offset + 28, true);
41
+ const extraLength = view.getUint16(offset + 30, true);
42
+ const commentLength = view.getUint16(offset + 32, true);
43
+ const localHeaderOffset = view.getUint32(offset + 42, true);
44
+ const recordLength = CEN_HEADER_LENGTH + nameLength + extraLength + commentLength;
45
+ if (offset + recordLength > data.length) throw new OfdParseError("truncated", `中央目录第 ${index} 个条目不完整,数据被截断`);
46
+ const name = new TextDecoder().decode(data.subarray(offset + CEN_HEADER_LENGTH, offset + CEN_HEADER_LENGTH + nameLength));
47
+ records.push({
48
+ name,
49
+ method,
50
+ compressedSize,
51
+ size,
52
+ localHeaderOffset
53
+ });
54
+ offset += recordLength;
55
+ }
56
+ return records;
57
+ }
58
+ function readEntry(data, view, record, name) {
59
+ if (!record) throw new OfdParseError("missing-entry", `容器中不存在条目 ${name}`);
60
+ const start = entryDataStart(data, view, record);
61
+ const compressed = data.subarray(start, start + record.compressedSize);
62
+ if (record.method === STORED_METHOD) return Promise.resolve(compressed);
63
+ if (record.method === DEFLATE_METHOD) return inflateRaw(compressed, name);
64
+ return Promise.reject(new OfdParseError("unsupported-compression", `条目 ${name} 使用不支持的压缩方法 ${record.method}`));
65
+ }
66
+ /** 定位条目数据起点:跳过本地文件头(长度以本地头声明的为准) */
67
+ function entryDataStart(data, view, record) {
68
+ const offset = record.localHeaderOffset;
69
+ if (offset + LOC_HEADER_LENGTH > data.length) throw new OfdParseError("truncated", `条目 ${record.name} 的本地文件头越界`);
70
+ if (view.getUint32(offset, true) !== LOC_SIGNATURE) throw new OfdParseError("bad-local-header", `条目 ${record.name} 的本地文件头签名不合法`);
71
+ const start = offset + LOC_HEADER_LENGTH + view.getUint16(offset + 26, true) + view.getUint16(offset + 28, true);
72
+ if (start + record.compressedSize > data.length) throw new OfdParseError("truncated", `条目 ${record.name} 的数据被截断`);
73
+ return start;
74
+ }
75
+ /** 用原生 DecompressionStream 解压 deflate-raw 数据 */
76
+ async function inflateRaw(compressed, name) {
77
+ try {
78
+ const stream = new ReadableStream({ start(controller) {
79
+ controller.enqueue(compressed);
80
+ controller.close();
81
+ } }).pipeThrough(new DecompressionStream("deflate-raw"));
82
+ return new Uint8Array(await new Response(stream).arrayBuffer());
83
+ } catch (cause) {
84
+ throw new OfdParseError("truncated", `条目 ${name} 的 deflate 数据无法解压`, { cause });
85
+ }
86
+ }
87
+ function readU32(data, offset) {
88
+ return (data[offset + 3] << 24 | data[offset + 2] << 16 | data[offset + 1] << 8 | data[offset]) >>> 0;
89
+ }
90
+ //#endregion
91
+ export { openOfdZip };
92
+
93
+ //# sourceMappingURL=zip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zip.js","names":[],"sources":["../../../../../ofd-core/dist/zip.js"],"sourcesContent":["import { OfdParseError } from \"./error.js\";\n//#region src/zip.ts\nconst EOCD_SIGNATURE = 101010256;\nconst CEN_SIGNATURE = 33639248;\nconst LOC_SIGNATURE = 67324752;\nconst EOCD_LENGTH = 22;\nconst CEN_HEADER_LENGTH = 46;\nconst LOC_HEADER_LENGTH = 30;\n/** EOCD 注释区最大长度,找 EOCD 时最多向前扫这么多字节 */\nconst EOCD_COMMENT_MAX = 65535;\nconst STORED_METHOD = 0;\nconst DEFLATE_METHOD = 8;\n/** 解析 ZIP 结构(EOCD / 中央目录);条目数据在 read 时才解压 */\nfunction openOfdZip(data) {\n\tconst view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\tconst eocdOffset = findEocdOffset(data);\n\tconst records = parseCentralDirectory(data, view, eocdOffset, view.getUint16(eocdOffset + 10, true));\n\tconst byName = new Map(records.map((record) => [record.name.toLowerCase(), record]));\n\treturn {\n\t\tentries: records,\n\t\thas: (name) => byName.has(name.toLowerCase()),\n\t\tread: (name) => readEntry(data, view, byName.get(name.toLowerCase()), name),\n\t\ttext: async (name) => new TextDecoder().decode(await readEntry(data, view, byName.get(name.toLowerCase()), name))\n\t};\n}\nfunction findEocdOffset(data) {\n\tconst minStart = Math.max(0, data.length - EOCD_LENGTH - EOCD_COMMENT_MAX);\n\tfor (let offset = data.length - EOCD_LENGTH; offset >= minStart; offset--) if (readU32(data, offset) === EOCD_SIGNATURE) return offset;\n\tthrow new OfdParseError(\"not-zip\", \"未找到 ZIP End of Central Directory,输入不是合法的 ZIP 容器\");\n}\nfunction parseCentralDirectory(data, view, eocdOffset, entryCount) {\n\tlet offset = view.getUint32(eocdOffset + 16, true);\n\tconst records = [];\n\tfor (let index = 0; index < entryCount; index++) {\n\t\tif (offset + CEN_HEADER_LENGTH > data.length) throw new OfdParseError(\"truncated\", `中央目录第 ${index} 个条目越界,数据被截断`);\n\t\tif (view.getUint32(offset, true) !== CEN_SIGNATURE) throw new OfdParseError(\"bad-central-directory\", `中央目录第 ${index} 个条目签名不合法`);\n\t\tconst method = view.getUint16(offset + 10, true);\n\t\tconst compressedSize = view.getUint32(offset + 20, true);\n\t\tconst size = view.getUint32(offset + 24, true);\n\t\tconst nameLength = view.getUint16(offset + 28, true);\n\t\tconst extraLength = view.getUint16(offset + 30, true);\n\t\tconst commentLength = view.getUint16(offset + 32, true);\n\t\tconst localHeaderOffset = view.getUint32(offset + 42, true);\n\t\tconst recordLength = CEN_HEADER_LENGTH + nameLength + extraLength + commentLength;\n\t\tif (offset + recordLength > data.length) throw new OfdParseError(\"truncated\", `中央目录第 ${index} 个条目不完整,数据被截断`);\n\t\tconst name = new TextDecoder().decode(data.subarray(offset + CEN_HEADER_LENGTH, offset + CEN_HEADER_LENGTH + nameLength));\n\t\trecords.push({\n\t\t\tname,\n\t\t\tmethod,\n\t\t\tcompressedSize,\n\t\t\tsize,\n\t\t\tlocalHeaderOffset\n\t\t});\n\t\toffset += recordLength;\n\t}\n\treturn records;\n}\nfunction readEntry(data, view, record, name) {\n\tif (!record) throw new OfdParseError(\"missing-entry\", `容器中不存在条目 ${name}`);\n\tconst start = entryDataStart(data, view, record);\n\tconst compressed = data.subarray(start, start + record.compressedSize);\n\tif (record.method === STORED_METHOD) return Promise.resolve(compressed);\n\tif (record.method === DEFLATE_METHOD) return inflateRaw(compressed, name);\n\treturn Promise.reject(new OfdParseError(\"unsupported-compression\", `条目 ${name} 使用不支持的压缩方法 ${record.method}`));\n}\n/** 定位条目数据起点:跳过本地文件头(长度以本地头声明的为准) */\nfunction entryDataStart(data, view, record) {\n\tconst offset = record.localHeaderOffset;\n\tif (offset + LOC_HEADER_LENGTH > data.length) throw new OfdParseError(\"truncated\", `条目 ${record.name} 的本地文件头越界`);\n\tif (view.getUint32(offset, true) !== LOC_SIGNATURE) throw new OfdParseError(\"bad-local-header\", `条目 ${record.name} 的本地文件头签名不合法`);\n\tconst start = offset + LOC_HEADER_LENGTH + view.getUint16(offset + 26, true) + view.getUint16(offset + 28, true);\n\tif (start + record.compressedSize > data.length) throw new OfdParseError(\"truncated\", `条目 ${record.name} 的数据被截断`);\n\treturn start;\n}\n/** 用原生 DecompressionStream 解压 deflate-raw 数据 */\nasync function inflateRaw(compressed, name) {\n\ttry {\n\t\tconst stream = new ReadableStream({ start(controller) {\n\t\t\tcontroller.enqueue(compressed);\n\t\t\tcontroller.close();\n\t\t} }).pipeThrough(new DecompressionStream(\"deflate-raw\"));\n\t\treturn new Uint8Array(await new Response(stream).arrayBuffer());\n\t} catch (cause) {\n\t\tthrow new OfdParseError(\"truncated\", `条目 ${name} 的 deflate 数据无法解压`, { cause });\n\t}\n}\nfunction readU32(data, offset) {\n\treturn (data[offset + 3] << 24 | data[offset + 2] << 16 | data[offset + 1] << 8 | data[offset]) >>> 0;\n}\n//#endregion\nexport { openOfdZip };\n\n//# sourceMappingURL=zip.js.map"],"mappings":"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"}
package/dist/style.d.ts CHANGED
@@ -4,6 +4,7 @@ import "./components/action/style.js";
4
4
  import "./components/dropdown/style.js";
5
5
  import "./components/input/style.js";
6
6
  import "./components/auto-complete/style.js";
7
+ import "./components/dialog/style.js";
7
8
  import "./components/checkbox/style.js";
8
9
  import "./components/radio/style.js";
9
10
  import "./components/table/style.js";
@@ -16,7 +17,6 @@ import "./components/condition-editor/style.js";
16
17
  import "./components/contextmenu/style.js";
17
18
  import "./components/date-picker/style.js";
18
19
  import "./components/date-range-picker/style.js";
19
- import "./components/dialog/style.js";
20
20
  import "./components/drawer/style.js";
21
21
  import "./components/dual-nav/style.js";
22
22
  import "./components/float-button/style.js";
package/dist/style.js CHANGED
@@ -9,6 +9,7 @@ import "./components/input/style.js";
9
9
  import "./components/tag/style.js";
10
10
  import "./components/auto-complete/style.js";
11
11
  import "./components/badge/style.js";
12
+ import "./components/dialog/style.js";
12
13
  import "./components/form-item/style.js";
13
14
  import "./components/grid/style.js";
14
15
  import "./components/form/style.js";
@@ -34,7 +35,6 @@ import "./components/contextmenu/style.js";
34
35
  import "./components/date-panel/style.js";
35
36
  import "./components/date-picker/style.js";
36
37
  import "./components/date-range-picker/style.js";
37
- import "./components/dialog/style.js";
38
38
  import "./components/drawer/style.js";
39
39
  import "./components/file-picker/style.js";
40
40
  import "./components/file-viewer/style.js";
@@ -38,6 +38,12 @@ export interface BatchEditProps extends TableProps {
38
38
  cols?: string | [string, string];
39
39
  /** 只读模式 */
40
40
  readonly?: boolean;
41
+ /**
42
+ * 表单交互模式
43
+ * @description `'panel'`:右侧面板(默认);`'dialog'`:编辑 / 新增 / 查看 / 添加子级时以弹框呈现表单
44
+ * @default 'panel'
45
+ */
46
+ formMode?: 'panel' | 'dialog';
41
47
  /**
42
48
  * 开启快速编辑
43
49
  * @description 开启后,编辑行时表单实时写回 `row.data`(经 `model` 中转),且不调用 `saveMethod`
@@ -2,7 +2,7 @@ import { ShallowRef } from "vue";
2
2
  import { DeconstructValue } from "@veltra/utils";
3
3
  //#region src/types/file-viewer.d.ts
4
4
  /** 预览器类别:xlsx 与 csv 归一为 sheet */
5
- export type FileViewerKind = 'image' | 'video' | 'pdf' | 'sheet' | 'docx' | 'text';
5
+ export type FileViewerKind = 'image' | 'video' | 'pdf' | 'sheet' | 'docx' | 'ofd' | 'text';
6
6
  /** 单个预览文件定义 */
7
7
  export interface FileViewerItem {
8
8
  /** 唯一 id;未提供时组件内部按索引生成 */
@@ -11,7 +11,7 @@ import { ScrollEmits, ScrollExposed, ScrollPosition, ScrollProps, _ScrollExposed
11
11
  import { WatermarkEmits, WatermarkExposed, WatermarkProps, _WatermarkExposed } from "./watermark.js";
12
12
  import { ProgressEmits, ProgressExposed, ProgressProps, _ProgressExposed } from "./progress.js";
13
13
  import { ProgressNodesEmits, ProgressNodesExposed, ProgressNodesProps, _ProgressNodesExposed } from "./progress-nodes.js";
14
- import { TipAlign, TipDirection, TipEmits, TipExposed, TipProps, _TipExposed } from "./tip.js";
14
+ import { TipAlign, TipDirection, TipEmits, TipExposed, TipProps, TipType, _TipExposed } from "./tip.js";
15
15
  import { PopConfirmEmits, PopConfirmExposed, PopConfirmProps, _PopConfirmExposed } from "./pop-confirm.js";
16
16
  import { TabItem, TabsEmits, TabsExposed, TabsHorizontalEmits, TabsHorizontalExposed, TabsHorizontalProps, TabsProps, TabsVerticalEmits, TabsVerticalExposed, TabsVerticalProps, _TabsExposed, _TabsHorizontalExposed, _TabsVerticalExposed } from "./tabs.js";
17
17
  import { BarType, TextEditorEmits, TextEditorExposed, TextEditorProps, _TextEditorExposed } from "./text-editor.js";
@@ -45,7 +45,7 @@ import { MultiTreeSelectEmits, MultiTreeSelectExposed, MultiTreeSelectProps, _Mu
45
45
  import { CodeEditorEmits, CodeEditorExposed, CodeEditorLang, CodeEditorProps, _CodeEditorExposed } from "./code-editor.js";
46
46
  import { TextEmits, TextExposed, TextProps, _TextExposed } from "./text.js";
47
47
  import { CheckboxGroupEmits, CheckboxGroupExposed, CheckboxGroupProps } from "./checkbox-group.js";
48
- import { TableEditorEmits, TableEditorExposed, TableEditorProps, _TableEditorExposed } from "./table-editor.js";
48
+ import { TableEditorColumn, TableEditorEmits, TableEditorExposed, TableEditorProps, _TableEditorExposed } from "./table-editor.js";
49
49
  import { NavEmits, NavExposed, NavItem, NavProps, _NavExposed } from "./nav.js";
50
50
  import { DualNavEmits, DualNavExposed, DualNavProps, DualNavRailVariant, DualNavRootItem, _DualNavExposed } from "./dual-nav.js";
51
51
  import { GroupNavEmits, GroupNavGroup, GroupNavProps } from "./group-nav.js";
@@ -82,4 +82,4 @@ import { CollapseEmits, CollapseItemEmits, CollapseItemProps, CollapseModelValue
82
82
  import { SegmentEmits, SegmentExposed, SegmentItem, SegmentProps, _SegmentExposed } from "./segment.js";
83
83
  import { ImageCropperChangePayload, ImageCropperCropTransform, ImageCropperEmits, ImageCropperExposed, ImageCropperProps, ImageCropperResult, ImageCropperResultOptions, ImageCropperSelection, _ImageCropperExposed } from "./image-cropper.js";
84
84
  export type * from "@veltra/utils";
85
- export { ActionEmits, ActionExposed, ActionGroupExposed, ActionGroupProps, ActionProps, AnimationEmits, AnimationExposed, AnimationProps, AutoCompleteEmits, AutoCompleteExposed, AutoCompleteProps, BadgeEmits, BadgeExposed, BadgeProps, BarType, BatchEditColumn, BatchEditEmits, BatchEditExposed, BatchEditFeature, BatchEditFormStatus, BatchEditProps, BatchEditSlots, BatchEditStates, BreadcrumbEmits, BreadcrumbExposed, BreadcrumbItem, BreadcrumbProps, BreadcrumbSlotScope, BreakCols, Breakpoint, ButtonEmits, ButtonExposed, ButtonProps, ButtonType, CalendarDay, CalendarEmits, CalendarExposed, CalendarMonth, CalendarProps, CalendarYear, CardActionProps, CardContentProps, CardCoverProps, CardEmits, CardExposed, CardProps, CascadeEmits, CascadeExposed, CascadeNode, CascadeProps, CheckTagEmits, CheckTagExposed, CheckTagProps, CheckboxButtonEmits, CheckboxButtonProps, CheckboxEmits, CheckboxExposed, CheckboxGroupEmits, CheckboxGroupExposed, CheckboxGroupProps, CheckboxProps, CodeEditorEmits, CodeEditorExposed, CodeEditorLang, CodeEditorProps, CollapseEmits, CollapseItemEmits, CollapseItemProps, CollapseModelValue, CollapseProps, CollapseValue, ConditionConnector, ConditionEditorEmits, ConditionEditorExposed, ConditionEditorProps, ConditionExpression, ConditionField, ConditionGroup, ConditionLeaf, ConditionNode, ConditionValue, ContextmenuEmits, ContextmenuExposed, ContextmenuItem, ContextmenuProps, CssTransitionEmits, CssTransitionExposed, CssTransitionProps, DatePanelEmits, DatePanelProps, DatePickerDataType, DatePickerEmits, DatePickerExposed, DatePickerProps, DateRangePickerDataType, DateRangePickerEmits, DateRangePickerExposed, DateRangePickerProps, DateRangeValue, DialogEmits, DialogExposed, DialogProps, DialogTransition, DrawerDirection, DrawerEmits, DrawerExposed, DrawerMode, DrawerProps, DropdownEmits, DropdownExposed, DropdownProps, DualNavEmits, DualNavExposed, DualNavProps, DualNavRailVariant, DualNavRootItem, EmptyEmits, EmptyExposed, EmptyProps, ExpressionEditorEmits, ExpressionEditorExposed, ExpressionEditorProps, ExpressionSelectableLevels, FileViewerEmits, FileViewerExposed, FileViewerItem, FileViewerKind, FileViewerProps, FloatButtonEmits, FloatButtonExposed, FloatButtonItem, FloatButtonProps, FormEmits, FormExposed, FormItemEmits, FormItemExposed, FormItemProps, FormProps, GanttChartEmits, GanttChartExposed, GanttChartProps, GridEmits, GridExposed, GridInputEmits, GridInputExposed, GridInputProps, GridItemProps, GridProps, GroupInputEmits, GroupInputExposed, GroupInputProps, GroupNavEmits, GroupNavGroup, GroupNavProps, IconEmits, IconExposed, IconProps, ImageCropperChangePayload, ImageCropperCropTransform, ImageCropperEmits, ImageCropperExposed, ImageCropperProps, ImageCropperResult, ImageCropperResultOptions, ImageCropperSelection, InputEmits, InputExposed, InputProps, LayoutEmits, LayoutExposed, LayoutProps, ListEmits, ListExposed, ListProps, LoadingEmits, LoadingExposed, LoadingProps, LoadingType, Message, MessageConfirm, MessageConfirmAction, MessageConfirmEmits, MessageConfirmExposed, MessageConfirmInstance, MessageConfirmOptions, MessageConfirmProps, MessageEmits, MessageExposed, MessageInstance, MessageOptions, MessageProps, MessageType, MultiAutoCompleteEmits, MultiAutoCompleteExposed, MultiAutoCompleteProps, MultiSelectEmits, MultiSelectExposed, MultiSelectProps, MultiTreeSelectEmits, MultiTreeSelectExposed, MultiTreeSelectProps, NavEmits, NavExposed, NavItem, NavProps, NodeRenderExposed, NodeRenderProps, Notification, NotificationEmits, NotificationExposed, NotificationInstance, NotificationOptions, NotificationPosition, NotificationProps, NumberEmits, NumberExposed, NumberInputEmits, NumberInputExposed, NumberInputProps, NumberProps, NumberRangeInputEmits, NumberRangeInputExposed, NumberRangeInputProps, NumberRangeTuple, PaginatorEmits, PaginatorExposed, PaginatorProps, PaletteColorType, PaletteEmits, PaletteExposed, PaletteHSV, PaletteProps, PaletteRGB, PanelItem, PanelType, PasswordInputEmits, PasswordInputExposed, PasswordInputProps, PopConfirmEmits, PopConfirmExposed, PopConfirmProps, PopEmits, PopExposed, PopProps, ProgressEmits, ProgressExposed, ProgressNodesEmits, ProgressNodesExposed, ProgressNodesProps, ProgressProps, QuickBatchEditEmits, QuickBatchEditExposed, QuickBatchEditProps, RadioEmits, RadioExposed, RadioGroupEmits, RadioGroupExposed, RadioGroupProps, RadioProps, RichTextEditorEmits, RichTextEditorExposed, RichTextEditorProps, RichTextFormat, ScrollEmits, ScrollExposed, ScrollPosition, ScrollProps, SegmentEmits, SegmentExposed, SegmentItem, SegmentProps, SelectEmits, SelectExposed, SelectProps, SliderEmits, SliderExposed, SliderProps, StepsEmits, StepsExposed, StepsProps, StepsSlotScope, SwitchEmits, SwitchExposed, SwitchProps, TabItem, TableColumn, TableColumnAlign, TableColumnNode, TableColumnRenderContext, TableColumnSlotsScope, TableColumnStyle, TableEditorEmits, TableEditorExposed, TableEditorProps, TableEmits, TableExposed, TableProps, TableRow, TableRowSlotsScope, TableSummaryContext, TabsEmits, TabsExposed, TabsHorizontalEmits, TabsHorizontalExposed, TabsHorizontalProps, TabsProps, TabsVerticalEmits, TabsVerticalExposed, TabsVerticalProps, TagEmits, TagExposed, TagProps, TextEditorEmits, TextEditorExposed, TextEditorProps, TextEmits, TextExposed, TextProps, TextareaEmits, TextareaExposed, TextareaProps, ThemeExposed, ThemeProps, TipAlign, TipDirection, TipEmits, TipExposed, TipProps, ToolbarItem, TreeEmit, TreeExposed, TreeNode, TreeNodeProps, TreeProps, TreeSelectEmits, TreeSelectExposed, TreeSelectProps, UploaderEmits, UploaderExposed, UploaderProps, VariableItem, WatermarkEmits, WatermarkExposed, WatermarkProps, _ActionExposed, _ActionGroupExposed, _AnimationExposed, _AutoCompleteExposed, _BadgeExposed, _BatchEditExposed, _BreadcrumbExposed, _ButtonExposed, _CalendarExposed, _CardExposed, _CascadeExposed, _CheckTagExposed, _CodeEditorExposed, _ConditionEditorExposed, _ContextmenuExposed, _CssTransitionExposed, _DatePickerExposed, _DateRangePickerExposed, _DialogExposed, _DrawerExposed, _DropdownExposed, _DualNavExposed, _EmptyExposed, _ExpressionEditorExposed, _FileViewerExposed, _FloatButtonExposed, _FormExposed, _GanttChartExposed, _GridExposed, _GridInputExposed, _GroupInputExposed, _ImageCropperExposed, _InputExposed, _LayoutExposed, _ListExposed, _LoadingExposed, _MessageConfirmExposed, _MessageExposed, _MultiAutoCompleteExposed, _MultiSelectExposed, _MultiTreeSelectExposed, _NavExposed, _NotificationExposed, _PaginatorExposed, _PaletteExposed, _PasswordInputExposed, _PopConfirmExposed, _PopExposed, _ProgressExposed, _ProgressNodesExposed, _QuickBatchEditExposed, _RadioExposed, _RadioGroupExposed, _RichTextEditorExposed, _ScrollExposed, _SegmentExposed, _SelectExposed, _SliderExposed, _StepsExposed, _SwitchExposed, _TableEditorExposed, _TableExposed, _TabsExposed, _TabsHorizontalExposed, _TabsVerticalExposed, _TextEditorExposed, _TextExposed, _TextareaExposed, _ThemeExposed, _TipExposed, _TreeExposed, _TreeSelectExposed, _UploaderExposed, _WatermarkExposed };
85
+ export { ActionEmits, ActionExposed, ActionGroupExposed, ActionGroupProps, ActionProps, AnimationEmits, AnimationExposed, AnimationProps, AutoCompleteEmits, AutoCompleteExposed, AutoCompleteProps, BadgeEmits, BadgeExposed, BadgeProps, BarType, BatchEditColumn, BatchEditEmits, BatchEditExposed, BatchEditFeature, BatchEditFormStatus, BatchEditProps, BatchEditSlots, BatchEditStates, BreadcrumbEmits, BreadcrumbExposed, BreadcrumbItem, BreadcrumbProps, BreadcrumbSlotScope, BreakCols, Breakpoint, ButtonEmits, ButtonExposed, ButtonProps, ButtonType, CalendarDay, CalendarEmits, CalendarExposed, CalendarMonth, CalendarProps, CalendarYear, CardActionProps, CardContentProps, CardCoverProps, CardEmits, CardExposed, CardProps, CascadeEmits, CascadeExposed, CascadeNode, CascadeProps, CheckTagEmits, CheckTagExposed, CheckTagProps, CheckboxButtonEmits, CheckboxButtonProps, CheckboxEmits, CheckboxExposed, CheckboxGroupEmits, CheckboxGroupExposed, CheckboxGroupProps, CheckboxProps, CodeEditorEmits, CodeEditorExposed, CodeEditorLang, CodeEditorProps, CollapseEmits, CollapseItemEmits, CollapseItemProps, CollapseModelValue, CollapseProps, CollapseValue, ConditionConnector, ConditionEditorEmits, ConditionEditorExposed, ConditionEditorProps, ConditionExpression, ConditionField, ConditionGroup, ConditionLeaf, ConditionNode, ConditionValue, ContextmenuEmits, ContextmenuExposed, ContextmenuItem, ContextmenuProps, CssTransitionEmits, CssTransitionExposed, CssTransitionProps, DatePanelEmits, DatePanelProps, DatePickerDataType, DatePickerEmits, DatePickerExposed, DatePickerProps, DateRangePickerDataType, DateRangePickerEmits, DateRangePickerExposed, DateRangePickerProps, DateRangeValue, DialogEmits, DialogExposed, DialogProps, DialogTransition, DrawerDirection, DrawerEmits, DrawerExposed, DrawerMode, DrawerProps, DropdownEmits, DropdownExposed, DropdownProps, DualNavEmits, DualNavExposed, DualNavProps, DualNavRailVariant, DualNavRootItem, EmptyEmits, EmptyExposed, EmptyProps, ExpressionEditorEmits, ExpressionEditorExposed, ExpressionEditorProps, ExpressionSelectableLevels, FileViewerEmits, FileViewerExposed, FileViewerItem, FileViewerKind, FileViewerProps, FloatButtonEmits, FloatButtonExposed, FloatButtonItem, FloatButtonProps, FormEmits, FormExposed, FormItemEmits, FormItemExposed, FormItemProps, FormProps, GanttChartEmits, GanttChartExposed, GanttChartProps, GridEmits, GridExposed, GridInputEmits, GridInputExposed, GridInputProps, GridItemProps, GridProps, GroupInputEmits, GroupInputExposed, GroupInputProps, GroupNavEmits, GroupNavGroup, GroupNavProps, IconEmits, IconExposed, IconProps, ImageCropperChangePayload, ImageCropperCropTransform, ImageCropperEmits, ImageCropperExposed, ImageCropperProps, ImageCropperResult, ImageCropperResultOptions, ImageCropperSelection, InputEmits, InputExposed, InputProps, LayoutEmits, LayoutExposed, LayoutProps, ListEmits, ListExposed, ListProps, LoadingEmits, LoadingExposed, LoadingProps, LoadingType, Message, MessageConfirm, MessageConfirmAction, MessageConfirmEmits, MessageConfirmExposed, MessageConfirmInstance, MessageConfirmOptions, MessageConfirmProps, MessageEmits, MessageExposed, MessageInstance, MessageOptions, MessageProps, MessageType, MultiAutoCompleteEmits, MultiAutoCompleteExposed, MultiAutoCompleteProps, MultiSelectEmits, MultiSelectExposed, MultiSelectProps, MultiTreeSelectEmits, MultiTreeSelectExposed, MultiTreeSelectProps, NavEmits, NavExposed, NavItem, NavProps, NodeRenderExposed, NodeRenderProps, Notification, NotificationEmits, NotificationExposed, NotificationInstance, NotificationOptions, NotificationPosition, NotificationProps, NumberEmits, NumberExposed, NumberInputEmits, NumberInputExposed, NumberInputProps, NumberProps, NumberRangeInputEmits, NumberRangeInputExposed, NumberRangeInputProps, NumberRangeTuple, PaginatorEmits, PaginatorExposed, PaginatorProps, PaletteColorType, PaletteEmits, PaletteExposed, PaletteHSV, PaletteProps, PaletteRGB, PanelItem, PanelType, PasswordInputEmits, PasswordInputExposed, PasswordInputProps, PopConfirmEmits, PopConfirmExposed, PopConfirmProps, PopEmits, PopExposed, PopProps, ProgressEmits, ProgressExposed, ProgressNodesEmits, ProgressNodesExposed, ProgressNodesProps, ProgressProps, QuickBatchEditEmits, QuickBatchEditExposed, QuickBatchEditProps, RadioEmits, RadioExposed, RadioGroupEmits, RadioGroupExposed, RadioGroupProps, RadioProps, RichTextEditorEmits, RichTextEditorExposed, RichTextEditorProps, RichTextFormat, ScrollEmits, ScrollExposed, ScrollPosition, ScrollProps, SegmentEmits, SegmentExposed, SegmentItem, SegmentProps, SelectEmits, SelectExposed, SelectProps, SliderEmits, SliderExposed, SliderProps, StepsEmits, StepsExposed, StepsProps, StepsSlotScope, SwitchEmits, SwitchExposed, SwitchProps, TabItem, TableColumn, TableColumnAlign, TableColumnNode, TableColumnRenderContext, TableColumnSlotsScope, TableColumnStyle, TableEditorColumn, TableEditorEmits, TableEditorExposed, TableEditorProps, TableEmits, TableExposed, TableProps, TableRow, TableRowSlotsScope, TableSummaryContext, TabsEmits, TabsExposed, TabsHorizontalEmits, TabsHorizontalExposed, TabsHorizontalProps, TabsProps, TabsVerticalEmits, TabsVerticalExposed, TabsVerticalProps, TagEmits, TagExposed, TagProps, TextEditorEmits, TextEditorExposed, TextEditorProps, TextEmits, TextExposed, TextProps, TextareaEmits, TextareaExposed, TextareaProps, ThemeExposed, ThemeProps, TipAlign, TipDirection, TipEmits, TipExposed, TipProps, TipType, ToolbarItem, TreeEmit, TreeExposed, TreeNode, TreeNodeProps, TreeProps, TreeSelectEmits, TreeSelectExposed, TreeSelectProps, UploaderEmits, UploaderExposed, UploaderProps, VariableItem, WatermarkEmits, WatermarkExposed, WatermarkProps, _ActionExposed, _ActionGroupExposed, _AnimationExposed, _AutoCompleteExposed, _BadgeExposed, _BatchEditExposed, _BreadcrumbExposed, _ButtonExposed, _CalendarExposed, _CardExposed, _CascadeExposed, _CheckTagExposed, _CodeEditorExposed, _ConditionEditorExposed, _ContextmenuExposed, _CssTransitionExposed, _DatePickerExposed, _DateRangePickerExposed, _DialogExposed, _DrawerExposed, _DropdownExposed, _DualNavExposed, _EmptyExposed, _ExpressionEditorExposed, _FileViewerExposed, _FloatButtonExposed, _FormExposed, _GanttChartExposed, _GridExposed, _GridInputExposed, _GroupInputExposed, _ImageCropperExposed, _InputExposed, _LayoutExposed, _ListExposed, _LoadingExposed, _MessageConfirmExposed, _MessageExposed, _MultiAutoCompleteExposed, _MultiSelectExposed, _MultiTreeSelectExposed, _NavExposed, _NotificationExposed, _PaginatorExposed, _PaletteExposed, _PasswordInputExposed, _PopConfirmExposed, _PopExposed, _ProgressExposed, _ProgressNodesExposed, _QuickBatchEditExposed, _RadioExposed, _RadioGroupExposed, _RichTextEditorExposed, _ScrollExposed, _SegmentExposed, _SelectExposed, _SliderExposed, _StepsExposed, _SwitchExposed, _TableEditorExposed, _TableExposed, _TabsExposed, _TabsHorizontalExposed, _TabsVerticalExposed, _TextEditorExposed, _TextExposed, _TextareaExposed, _ThemeExposed, _TipExposed, _TreeExposed, _TreeSelectExposed, _UploaderExposed, _WatermarkExposed };
@@ -1,17 +1,38 @@
1
- import { TableProps } from "./table.js";
2
- import { DeconstructValue } from "@veltra/utils";
1
+ import { TableColumn, TableProps } from "./table.js";
2
+ import { DeconstructValue, ValidateRule } from "@veltra/utils";
3
3
  //#region src/types/table-editor.d.ts
4
- /** 表格型编辑器组件属性 */
5
- export interface TableEditorProps extends Omit<TableProps, 'data'> {
4
+ /** 表格编辑器列:在 TableColumn 基础上扩展按列校验 */
5
+ export interface TableEditorColumn extends TableColumn {
6
+ /** 列校验规则:配置后该列控件的 change 事件触发单元格校验,`required` 非空时表头列名前渲染红星 */
7
+ rules?: ValidateRule;
8
+ }
9
+ /**
10
+ * 表格型编辑器组件属性
11
+ *
12
+ * 单元格常驻渲染:声明了 `#column:key` 插槽的列,输入控件常驻挂载;
13
+ * 未声明编辑插槽的列渲染字段原始值。
14
+ */
15
+ export interface TableEditorProps extends Omit<TableProps, 'data' | 'columns'> {
6
16
  /** 表格数据 */
7
- modelValue?: any[];
17
+ modelValue?: Record<string, any>[];
18
+ /** 表格列 */
19
+ columns?: TableEditorColumn[];
20
+ /**
21
+ * 只读模式
22
+ * @default false
23
+ * @description 只读下输入控件经插槽 model 收到 readonly: true(值不可修改),操作列与空态「添加」按钮不渲染
24
+ */
25
+ readonly?: boolean;
8
26
  }
9
27
  /** 表格型编辑器组件定义的事件 */
10
28
  export interface TableEditorEmits {
11
- (e: 'update:modelValue', value: any[]): void;
29
+ (e: 'update:modelValue', value: Record<string, any>[]): void;
12
30
  }
13
31
  /** 表格型编辑器组件暴露的属性和方法(组件内部使用) */
14
- export interface _TableEditorExposed {}
32
+ export interface _TableEditorExposed {
33
+ /** 校验全表配置了 rules 的列,全部通过 resolve true,任一失败 resolve false */
34
+ validate(): Promise<boolean>;
35
+ }
15
36
  /** 表格型编辑器组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
16
37
  export type TableEditorExposed = DeconstructValue<_TableEditorExposed>;
17
38
  //#endregion
@@ -1,12 +1,16 @@
1
1
  import { CSSProperties } from "vue";
2
- import { DeconstructValue } from "@veltra/utils";
2
+ import { ColorType, DeconstructValue } from "@veltra/utils";
3
3
  //#region src/types/tip.d.ts
4
4
  export type TipDirection = 'top' | 'bottom' | 'left' | 'right';
5
5
  export type TipAlign = 'center' | 'start' | 'end';
6
+ /** tip类型 */
7
+ export type TipType = ColorType;
6
8
  /** tip提示组件组件属性 */
7
9
  export interface TipProps {
8
10
  /** 控制显影 */
9
11
  visible?: boolean;
12
+ /** tip类型 */
13
+ type?: TipType;
10
14
  /**提示内容 */
11
15
  content?: string;
12
16
  /** 自定义tip样式 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.7.13",
3
+ "version": "1.7.16",
4
4
  "homepage": "https://github.com/cabinet-fe/ultra-ui#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/cabinet-fe/ultra-ui/issues"
@@ -75,17 +75,18 @@
75
75
  "@codemirror/search": "6.7.1",
76
76
  "@codemirror/state": "6.7.1",
77
77
  "@codemirror/theme-one-dark": "6.1.3",
78
- "@codemirror/view": "6.43.9"
78
+ "@codemirror/view": "6.43.9",
79
+ "@veltra/ofd-core": "1.0.1"
79
80
  },
80
81
  "peerDependencies": {
81
82
  "@cat-kit/core": ">=1.2.1",
82
83
  "@cat-kit/fe": ">=1.2.1",
83
- "@veltra/compositions": "^1.7.13",
84
- "@veltra/directives": "^1.7.13",
84
+ "@veltra/compositions": "^1.7.14",
85
+ "@veltra/directives": "^1.7.14",
85
86
  "@veltra/icons": "^1.5.0",
86
- "@veltra/sheet-core": "^2.5.6",
87
- "@veltra/styles": "^1.7.13",
88
- "@veltra/utils": "^1.7.13",
87
+ "@veltra/sheet-core": "^2.5.7",
88
+ "@veltra/styles": "^1.7.14",
89
+ "@veltra/utils": "^1.7.14",
89
90
  "vue": ">=3.5.42"
90
91
  },
91
92
  "peerDependenciesMeta": {
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it, vi } from 'vitest'
2
2
  import { createApp, h, nextTick, reactive, ref } from 'vue'
3
3
 
4
4
  import { UInput } from '../../input'
@@ -6,7 +6,13 @@ import { USelect } from '../../select'
6
6
  import { defineTableColumns } from '../../table'
7
7
  import { UBatchEdit } from '../index'
8
8
 
9
- function mountBatchEdit() {
9
+ interface MountOptions {
10
+ props?: Record<string, any>
11
+ /** 给「名称」输入框附加校验规则 */
12
+ labelRules?: Record<string, any>
13
+ }
14
+
15
+ function mountBatchEdit(options: MountOptions = {}) {
10
16
  const host = document.createElement('div')
11
17
  document.body.appendChild(host)
12
18
 
@@ -42,11 +48,12 @@ function mountBatchEdit() {
42
48
  columns,
43
49
  model,
44
50
  rowKey: 'id',
45
- quickEdit: true
51
+ quickEdit: true,
52
+ ...options.props
46
53
  },
47
54
  {
48
55
  form: () => [
49
- h(UInput, { field: 'label', label: '名称' }),
56
+ h(UInput, { field: 'label', label: '名称', rules: options.labelRules }),
50
57
  h(USelect, {
51
58
  field: 'behavior',
52
59
  label: '行为',
@@ -197,3 +204,183 @@ describe('UBatchEdit quick-edit 切换编辑行', () => {
197
204
  unmount()
198
205
  })
199
206
  })
207
+
208
+ function queryDialog() {
209
+ return document.body.querySelector<HTMLElement>('.u-batch-edit__form-dialog')
210
+ }
211
+
212
+ /** 等弹框(teleport 到 body)的进入 / 离开过渡走完 */
213
+ async function flushDialog() {
214
+ await nextTick()
215
+ await nextTick()
216
+ await new Promise((resolve) => setTimeout(resolve, 20))
217
+ await nextTick()
218
+ }
219
+
220
+ function clickEl(el: HTMLElement) {
221
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
222
+ }
223
+
224
+ function setInputValue(input: HTMLInputElement, value: string) {
225
+ input.value = value
226
+ input.dispatchEvent(
227
+ new InputEvent('input', { bubbles: true, data: value, inputType: 'insertText' })
228
+ )
229
+ }
230
+
231
+ function querySaveButton(dialog: HTMLElement) {
232
+ return dialog.querySelector<HTMLElement>('.u-batch-edit__form-actions button[title="保存"]')
233
+ }
234
+
235
+ function queryCancelButton(dialog: HTMLElement) {
236
+ return [...dialog.querySelectorAll<HTMLElement>('.u-batch-edit__form-actions button')].find(
237
+ (btn) => btn.textContent?.includes('取消')
238
+ )
239
+ }
240
+
241
+ describe('UBatchEdit formMode', () => {
242
+ it('默认(不传 formMode)仍渲染右侧面板,不出现弹框', async () => {
243
+ const { host, unmount } = mountBatchEdit()
244
+
245
+ await clickRow(host, 0)
246
+ const aside = host.querySelector<HTMLElement>('.u-batch-edit__form')
247
+ expect(aside).toBeTruthy()
248
+ expect(aside!.style.display).not.toBe('none')
249
+ expect(queryDialog()).toBeNull()
250
+
251
+ unmount()
252
+ })
253
+
254
+ it('formMode="dialog" 时不渲染右列,触发编辑后以弹框打开表单', async () => {
255
+ const { host, model, unmount } = mountBatchEdit({
256
+ props: { formMode: 'dialog', quickEdit: false }
257
+ })
258
+
259
+ expect(host.querySelector('.u-batch-edit__form')).toBeNull()
260
+ expect(queryDialog()).toBeNull()
261
+
262
+ await clickRow(host, 0)
263
+ await flushDialog()
264
+
265
+ const dialog = queryDialog()
266
+ expect(dialog).toBeTruthy()
267
+ // 表格占满整行,右列不再渲染
268
+ expect(host.querySelector('.u-batch-edit__form')).toBeNull()
269
+ // 弹框内复用原表单头部 / 表单体 / 底部操作区
270
+ expect(dialog!.querySelector('.u-batch-edit__form-header')).toBeTruthy()
271
+ expect(dialog!.querySelector('.u-batch-edit__form-body')).toBeTruthy()
272
+ expect(dialog!.querySelector('.u-batch-edit__form-actions')).toBeTruthy()
273
+ // 编辑行已回显
274
+ expect(model.label).toBe('存草稿')
275
+
276
+ unmount()
277
+ })
278
+
279
+ it('弹框内保存走校验与 saveMethod,成功后关闭弹框', async () => {
280
+ const saveMethod = vi.fn()
281
+ const { host, data, unmount } = mountBatchEdit({
282
+ props: { formMode: 'dialog', quickEdit: false, saveMethod },
283
+ labelRules: { required: true }
284
+ })
285
+
286
+ // 打开新增弹框
287
+ clickEl(host.querySelector<HTMLElement>('.u-batch-edit__add-btn')!)
288
+ await flushDialog()
289
+ expect(queryDialog()).toBeTruthy()
290
+
291
+ // 必填校验失败:不调用 saveMethod,弹框保持打开
292
+ clickEl(querySaveButton(queryDialog()!)!)
293
+ await flushDialog()
294
+ expect(saveMethod).not.toHaveBeenCalled()
295
+ expect(queryDialog()).toBeTruthy()
296
+
297
+ // 填写后保存:走 saveMethod 并插入数据,成功后关闭弹框
298
+ setInputValue(queryDialog()!.querySelector<HTMLInputElement>('.u-input input')!, '新名称')
299
+ await nextTick()
300
+ await nextTick()
301
+ clickEl(querySaveButton(queryDialog()!)!)
302
+ await flushDialog()
303
+
304
+ expect(saveMethod).toHaveBeenCalledTimes(1)
305
+ expect(saveMethod.mock.calls[0]![0]).toMatchObject({ label: '新名称' })
306
+ expect(saveMethod.mock.calls[0]![1]).toBe('create')
307
+ expect(data.value).toHaveLength(3)
308
+ expect(data.value[2]).toMatchObject({ label: '新名称' })
309
+ expect(queryDialog()).toBeNull()
310
+
311
+ unmount()
312
+ })
313
+
314
+ it('弹框内取消 / 关闭按钮 / 遮罩点击均不保存,且 model 恢复初始值', async () => {
315
+ const saveMethod = vi.fn()
316
+ const { host, data, model, unmount } = mountBatchEdit({
317
+ props: { formMode: 'dialog', quickEdit: false, saveMethod }
318
+ })
319
+
320
+ // 编辑第 1 行后取消
321
+ await clickRow(host, 0)
322
+ await flushDialog()
323
+ setInputValue(queryDialog()!.querySelector<HTMLInputElement>('.u-input input')!, '改过的名称')
324
+ await nextTick()
325
+ await nextTick()
326
+ clickEl(queryCancelButton(queryDialog()!)!)
327
+ await flushDialog()
328
+
329
+ expect(saveMethod).not.toHaveBeenCalled()
330
+ expect(data.value[0]).toMatchObject({ label: '存草稿' })
331
+ // model 恢复初始快照,而不是残留上一行数据
332
+ expect(model.label).toBe('')
333
+ expect(model.submitType).toBeUndefined()
334
+ expect(queryDialog()).toBeNull()
335
+
336
+ // 取消后再新增:表单应为初始值(快照不被上一行污染)
337
+ clickEl(host.querySelector<HTMLElement>('.u-batch-edit__add-btn')!)
338
+ await flushDialog()
339
+ expect(queryDialog()!.querySelector<HTMLInputElement>('.u-input input')!.value).toBe('')
340
+
341
+ // 表单头部的关闭按钮同样不保存
342
+ setInputValue(queryDialog()!.querySelector<HTMLInputElement>('.u-input input')!, '随便写')
343
+ await nextTick()
344
+ await nextTick()
345
+ clickEl(queryDialog()!.querySelector<HTMLElement>('.u-batch-edit__form-close')!)
346
+ await flushDialog()
347
+ expect(saveMethod).not.toHaveBeenCalled()
348
+ expect(model.label).toBe('')
349
+ expect(data.value).toHaveLength(2)
350
+ expect(queryDialog()).toBeNull()
351
+
352
+ // 弹框自身关闭交互(点击遮罩)也不保存
353
+ await clickRow(host, 1)
354
+ await flushDialog()
355
+ setInputValue(queryDialog()!.querySelector<HTMLInputElement>('.u-input input')!, '改过的名称')
356
+ await nextTick()
357
+ await nextTick()
358
+ document
359
+ .querySelector<HTMLElement>('.u-dialog__overlay')!
360
+ .dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }))
361
+ await flushDialog()
362
+
363
+ expect(saveMethod).not.toHaveBeenCalled()
364
+ expect(data.value[1]).toMatchObject({ label: '调接口' })
365
+ expect(model.label).toBe('')
366
+ expect(queryDialog()).toBeNull()
367
+
368
+ unmount()
369
+ })
370
+ })
371
+
372
+ describe('UBatchEdit 属性透传', () => {
373
+ it('style / class 等非 props 属性透传到根布局元素(组件为片段根,需手动继承)', async () => {
374
+ const { host, unmount } = mountBatchEdit({
375
+ props: { style: 'height: 500px', class: 'custom-batch-edit' }
376
+ })
377
+ await nextTick()
378
+
379
+ const root = host.querySelector<HTMLElement>('.u-batch-edit')
380
+ expect(root).toBeTruthy()
381
+ expect(root!.classList.contains('custom-batch-edit')).toBe(true)
382
+ expect(root!.style.height).toBe('500px')
383
+
384
+ unmount()
385
+ })
386
+ })
@@ -1,6 +1,11 @@
1
1
  <template>
2
- <!-- 表单常驻挂载(快照只在挂载时对传入的 model 拍一次),仅用 v-show 控制显隐 -->
3
- <aside :class="cls.e('form')" v-if="!!props.model" v-show="state.formVisible">
2
+ <!-- 面板模式:表单常驻挂载(快照只在挂载时对传入的 model 拍一次),仅用 v-show 控制显隐;
3
+ 弹框模式:表单随弹框挂载 / 卸载,model 初始快照由 use-edit-state 维护 -->
4
+ <aside
5
+ :class="[cls.e('form'), bem.is('dialog', props.formMode === 'dialog')]"
6
+ v-if="!!props.model"
7
+ v-show="state.formVisible"
8
+ >
4
9
  <header :class="cls.e('form-header')">
5
10
  <span :class="[cls.e('form-icon'), bem.is(state.formActionType)]">
6
11
  <u-icon>
@@ -48,7 +53,7 @@
48
53
  <span :class="cls.e('form-hint')">
49
54
  <template v-if="props.readonly"> 只读模式 </template>
50
55
 
51
- <span :class="{ [bem.is('concealed')]: !focused }">
56
+ <span :class="{ [bem.is('concealed')]: !shortcutActive }">
52
57
  <template v-if="!(state.formActionType === 'update' && props.quickEdit)">
53
58
  <u-kbd>Ctrl + S</u-kbd> 保存 ·
54
59
  </template>
@@ -86,6 +91,7 @@ import { UKbd } from '../kbd'
86
91
  import { UScroll } from '../scroll'
87
92
  import { BatchEditDIKey } from './di'
88
93
  import { FORM_ACTION_HEADER_MAP } from './form-action-header'
94
+ import { useDialogEffect } from './use-dialog-effect'
89
95
 
90
96
  defineOptions({ name: 'UBatchEditForm' })
91
97
 
@@ -96,6 +102,14 @@ const { cls, props, state, handleClose, handleSave, staticFeatures, dynamicFeatu
96
102
 
97
103
  const focused = toRef(batchEditCtx, 'focused')
98
104
 
105
+ // 弹框模式下,弹框自身关闭(遮罩点击 / 右上角关闭)时同步走 handleClose,不保存
106
+ useDialogEffect({ closeForm: handleClose })
107
+
108
+ /** 快捷键提示是否可见:面板模式随聚焦显隐;弹框模式弹框打开期间快捷键始终可用 */
109
+ const shortcutActive = computed(() => {
110
+ return props.formMode === 'dialog' ? state.formVisible : focused.value
111
+ })
112
+
99
113
  const formComponentRef = shallowRef<FormExposed>()
100
114
 
101
115
  defineExpose<_FormExposed>({
@@ -98,7 +98,8 @@ const tableProps = computed(() => {
98
98
  'saveMethod',
99
99
  'features',
100
100
  'mode',
101
- 'beforeCreate'
101
+ 'beforeCreate',
102
+ 'formMode'
102
103
  ])
103
104
  })
104
105