@shwfed/config 3.0.12 → 3.0.13
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/module.json +1 -1
- package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/config.d.vue.ts +18 -18
- package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/config.vue.d.ts +18 -18
- package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/runtime.d.vue.ts +18 -18
- package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/runtime.vue.d.ts +18 -18
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.d.vue.ts +8 -8
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.vue +264 -286
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.vue.d.ts +8 -8
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.d.vue.ts +8 -8
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.vue +91 -291
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.vue.d.ts +8 -8
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/schema.d.ts +3 -28
- package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/schema.js +9 -33
- package/dist/runtime/components/config/index.d.vue.ts +2 -2
- package/dist/runtime/components/config/index.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.textarea/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.textarea/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.d.vue.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.vue.d.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.d.vue.ts +26 -26
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.vue.d.ts +26 -26
- package/dist/runtime/components/form/utils/validation.js +9 -7
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
- package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/config.vue +57 -247
- package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/prompt.md +2 -2
- package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/runtime.vue +21 -124
- package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/schema.d.ts +5 -27
- package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/schema.js +10 -37
- package/dist/runtime/components/ui/invoice/InvoiceSheet.d.vue.ts +66 -0
- package/dist/runtime/components/ui/invoice/InvoiceSheet.vue +182 -0
- package/dist/runtime/components/ui/invoice/InvoiceSheet.vue.d.ts +66 -0
- package/dist/runtime/components/ui/invoice/index.d.ts +1 -0
- package/dist/runtime/components/ui/invoice/index.js +1 -0
- package/dist/runtime/components/ui/invoice-config/InvoiceConfigShell.d.vue.ts +36 -0
- package/dist/runtime/components/ui/invoice-config/InvoiceConfigShell.vue +41 -0
- package/dist/runtime/components/ui/invoice-config/InvoiceConfigShell.vue.d.ts +36 -0
- package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/expr-field.vue → ui/invoice-config/InvoiceExprField.vue} +4 -4
- package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/json-scope-provider.vue → ui/invoice-config/InvoiceJsonScope.vue} +3 -3
- package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/row-scope-provider.vue → ui/invoice-config/InvoiceRowScope.vue} +2 -2
- package/dist/runtime/components/ui/invoice-config/index.d.ts +4 -0
- package/dist/runtime/components/ui/invoice-config/index.js +4 -0
- package/dist/runtime/share/invoice-eval.d.ts +13 -0
- package/dist/runtime/share/invoice-eval.js +88 -0
- package/dist/runtime/share/invoice.d.ts +68 -0
- package/dist/runtime/share/invoice.js +35 -0
- package/package.json +1 -1
- /package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/expr-field.d.vue.ts → ui/invoice-config/InvoiceExprField.d.vue.ts} +0 -0
- /package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/expr-field.vue.d.ts → ui/invoice-config/InvoiceExprField.vue.d.ts} +0 -0
- /package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/json-scope-provider.d.vue.ts → ui/invoice-config/InvoiceJsonScope.d.vue.ts} +0 -0
- /package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/json-scope-provider.vue.d.ts → ui/invoice-config/InvoiceJsonScope.vue.d.ts} +0 -0
- /package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/row-scope-provider.d.vue.ts → ui/invoice-config/InvoiceRowScope.d.vue.ts} +0 -0
- /package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/row-scope-provider.vue.d.ts → ui/invoice-config/InvoiceRowScope.vue.d.ts} +0 -0
- /package/dist/runtime/{components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital → share}/rmb.d.ts +0 -0
- /package/dist/runtime/{components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital → share}/rmb.js +0 -0
package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/runtime.vue
CHANGED
|
@@ -48,24 +48,6 @@ const EXTENSIONS = {
|
|
|
48
48
|
"application/msword": [".doc"],
|
|
49
49
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"]
|
|
50
50
|
};
|
|
51
|
-
const SHORT_NAMES = {
|
|
52
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "XLSX",
|
|
53
|
-
"application/vnd.ms-excel": "XLS",
|
|
54
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "PPTX",
|
|
55
|
-
"application/vnd.ms-powerpoint": "PPT",
|
|
56
|
-
"application/zip": "ZIP",
|
|
57
|
-
"application/x-zip-compressed": "ZIP",
|
|
58
|
-
"application/vnd.rar": "RAR",
|
|
59
|
-
"application/x-rar-compressed": "RAR",
|
|
60
|
-
"application/pdf": "PDF",
|
|
61
|
-
"application/ofd": "OFD",
|
|
62
|
-
"application/xml": "XML",
|
|
63
|
-
"image/png": "PNG",
|
|
64
|
-
"image/jpg": "JPG",
|
|
65
|
-
"image/jpeg": "JPEG",
|
|
66
|
-
"application/msword": "DOC",
|
|
67
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX"
|
|
68
|
-
};
|
|
69
51
|
defineOptions({ name: "ShwfedTableUploadRendererRuntime" });
|
|
70
52
|
const props = defineProps({
|
|
71
53
|
column: { type: null, required: true },
|
|
@@ -76,7 +58,6 @@ const { locale, t } = useI18n({
|
|
|
76
58
|
messages: {
|
|
77
59
|
zh: {
|
|
78
60
|
"upload-add-file": "\u6DFB\u52A0\u6587\u4EF6",
|
|
79
|
-
"upload-accept-hint": "\u4EC5\u63A5\u53D7 {types}",
|
|
80
61
|
"upload-rejected-mime": "\u300C{name}\u300D\u7C7B\u578B\u4E0D\u88AB\u5141\u8BB8",
|
|
81
62
|
"upload-rejected-size": "\u300C{name}\u300D\u8D85\u51FA\u5355\u6587\u4EF6\u5927\u5C0F\u9650\u5236",
|
|
82
63
|
"upload-rejected-total": "\u300C{name}\u300D\u52A0\u5165\u540E\u5C06\u8D85\u51FA\u603B\u5927\u5C0F\u9650\u5236",
|
|
@@ -86,7 +67,6 @@ const { locale, t } = useI18n({
|
|
|
86
67
|
},
|
|
87
68
|
en: {
|
|
88
69
|
"upload-add-file": "Add file",
|
|
89
|
-
"upload-accept-hint": "Only {types} are accepted",
|
|
90
70
|
"upload-rejected-mime": "{name} is not an allowed file type",
|
|
91
71
|
"upload-rejected-size": "{name} exceeds the maximum file size",
|
|
92
72
|
"upload-rejected-total": "{name} would exceed the total size limit",
|
|
@@ -96,7 +76,6 @@ const { locale, t } = useI18n({
|
|
|
96
76
|
},
|
|
97
77
|
ja: {
|
|
98
78
|
"upload-add-file": "\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0",
|
|
99
|
-
"upload-accept-hint": "{types} \u306E\u307F\u53D7\u3051\u4ED8\u3051\u307E\u3059",
|
|
100
79
|
"upload-rejected-mime": "\u300C{name}\u300D\u306F\u8A31\u53EF\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
|
|
101
80
|
"upload-rejected-size": "\u300C{name}\u300D\u306F\u30D5\u30A1\u30A4\u30EB\u30B5\u30A4\u30BA\u306E\u4E0A\u9650\u3092\u8D85\u3048\u3066\u3044\u307E\u3059",
|
|
102
81
|
"upload-rejected-total": "\u300C{name}\u300D\u3092\u8FFD\u52A0\u3059\u308B\u3068\u5408\u8A08\u30B5\u30A4\u30BA\u306E\u4E0A\u9650\u3092\u8D85\u3048\u307E\u3059",
|
|
@@ -110,9 +89,6 @@ const formState = useFormState();
|
|
|
110
89
|
const inherited = injectCELContext();
|
|
111
90
|
const $cel = (expression, context) => _rawCel(expression, celScope(inherited, context));
|
|
112
91
|
const eventChannel = useEventChannel();
|
|
113
|
-
const placeholderText = computed(
|
|
114
|
-
() => props.column.placeholder ? getLocalizedText(props.column.placeholder, locale.value) : void 0
|
|
115
|
-
);
|
|
116
92
|
function evalBool(expression, label) {
|
|
117
93
|
if (!expression) return false;
|
|
118
94
|
try {
|
|
@@ -160,19 +136,24 @@ function entryIcon(entry) {
|
|
|
160
136
|
if (entry instanceof File) return ICONS[entry.type] ?? "fluent:document-20-regular";
|
|
161
137
|
return "fluent:document-20-regular";
|
|
162
138
|
}
|
|
139
|
+
const addIcon = computed(() => props.column.addIcon ?? "fluent:add-20-regular");
|
|
140
|
+
const addLabel = computed(
|
|
141
|
+
() => (props.column.addLabel ? getLocalizedText(props.column.addLabel, locale.value) : void 0) ?? t("upload-add-file")
|
|
142
|
+
);
|
|
163
143
|
const inputRef = ref();
|
|
164
144
|
const acceptAttr = computed(() => {
|
|
165
145
|
const list = props.column.accept;
|
|
166
146
|
return list && list.length > 0 ? list.join(",") : void 0;
|
|
167
147
|
});
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
return
|
|
148
|
+
const effectiveMaxFiles = computed(() => {
|
|
149
|
+
const configured = props.column.maxFiles;
|
|
150
|
+
if (props.column.multiple !== true) return Math.min(1, configured ?? 1);
|
|
151
|
+
return configured;
|
|
152
|
+
});
|
|
153
|
+
const isFull = computed(() => {
|
|
154
|
+
const cap = effectiveMaxFiles.value;
|
|
155
|
+
return cap !== void 0 && entries.value.length >= cap;
|
|
172
156
|
});
|
|
173
|
-
const isFull = computed(
|
|
174
|
-
() => props.column.maxFiles !== void 0 && entries.value.length >= props.column.maxFiles
|
|
175
|
-
);
|
|
176
157
|
const canAdd = computed(() => !isDisabled.value && !isReadonly.value && !isFull.value && !uploading.value);
|
|
177
158
|
function isMimeAllowed(file) {
|
|
178
159
|
const list = props.column.accept;
|
|
@@ -197,7 +178,7 @@ function partition(incoming) {
|
|
|
197
178
|
rejected.push({ file, reason: "size" });
|
|
198
179
|
continue;
|
|
199
180
|
}
|
|
200
|
-
if (
|
|
181
|
+
if (effectiveMaxFiles.value !== void 0 && count >= effectiveMaxFiles.value) {
|
|
201
182
|
rejected.push({ file, reason: "count" });
|
|
202
183
|
continue;
|
|
203
184
|
}
|
|
@@ -264,13 +245,6 @@ function onAddClick() {
|
|
|
264
245
|
if (!canAdd.value) return;
|
|
265
246
|
inputRef.value?.click();
|
|
266
247
|
}
|
|
267
|
-
function onKeydown(event) {
|
|
268
|
-
if (!canAdd.value) return;
|
|
269
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
270
|
-
event.preventDefault();
|
|
271
|
-
inputRef.value?.click();
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
248
|
function onDelete(index) {
|
|
275
249
|
if (isDisabled.value || isReadonly.value || uploading.value) return;
|
|
276
250
|
const next = [...entries.value];
|
|
@@ -287,34 +261,6 @@ function saveFile(file) {
|
|
|
287
261
|
a.remove();
|
|
288
262
|
URL.revokeObjectURL(url);
|
|
289
263
|
}
|
|
290
|
-
const templates = computed(() => props.column.templates ?? []);
|
|
291
|
-
function templateIcon(template) {
|
|
292
|
-
return template.icon ?? "fluent:arrow-download-20-regular";
|
|
293
|
-
}
|
|
294
|
-
function templateLabelText(template) {
|
|
295
|
-
return (template.label ? getLocalizedText(template.label, locale.value) : void 0) ?? "\u4E0B\u8F7D\u6A21\u677F";
|
|
296
|
-
}
|
|
297
|
-
async function onDownloadTemplate(template) {
|
|
298
|
-
const program = Effect.gen(function* () {
|
|
299
|
-
const requestBuilder = yield* $cel(template.request, {});
|
|
300
|
-
if (!template.download) {
|
|
301
|
-
return yield* requestBuilder.file();
|
|
302
|
-
}
|
|
303
|
-
const json = yield* requestBuilder.json();
|
|
304
|
-
const downloadBuilder = yield* $cel(template.download, { json });
|
|
305
|
-
return yield* downloadBuilder.file();
|
|
306
|
-
});
|
|
307
|
-
try {
|
|
308
|
-
const file = await Effect.runPromise(Effect.provide(program, Fetch.layer));
|
|
309
|
-
if (!(file instanceof File)) {
|
|
310
|
-
console.error(`[shwfed-table] template request for ${props.column.id} did not produce a File`);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
saveFile(file);
|
|
314
|
-
} catch (err) {
|
|
315
|
-
console.error(`[shwfed-table] failed to download template for ${props.column.id}:`, err);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
264
|
const downloadEnabled = computed(() => props.column.download != null);
|
|
319
265
|
const downloadIcon = computed(() => props.column.download?.icon ?? "fluent:arrow-download-20-regular");
|
|
320
266
|
const downloadLabel = computed(
|
|
@@ -418,53 +364,26 @@ async function onDownloadEntry(entry) {
|
|
|
418
364
|
</ul>
|
|
419
365
|
|
|
420
366
|
<!--
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
367
|
+
Add button — the only affordance for picking files, empty cell or not.
|
|
368
|
+
A cell is one grid row tall, so the dashed drop zone this replaces made
|
|
369
|
+
empty rows tower over their neighbours. `isFull` hides it (nothing left
|
|
370
|
+
to add); `isDisabled` keeps it rendered but inert, per the schema.
|
|
424
371
|
-->
|
|
425
|
-
<div
|
|
426
|
-
v-else
|
|
427
|
-
role="button"
|
|
428
|
-
:tabindex="canAdd ? 0 : -1"
|
|
429
|
-
data-slot="upload-zone"
|
|
430
|
-
:data-disabled="isDisabled || void 0"
|
|
431
|
-
:aria-disabled="isDisabled || void 0"
|
|
432
|
-
class="flex min-h-16 w-full flex-col items-center justify-center gap-1 rounded border border-dashed border-zinc-200 bg-zinc-50/60 px-2 py-2 outline-none transition-colors duration-180 ease-out focus-visible:border-(--primary) data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-60"
|
|
433
|
-
:class="canAdd && 'cursor-pointer hover:border-zinc-400 hover:bg-zinc-100'"
|
|
434
|
-
@click="onAddClick"
|
|
435
|
-
@keydown="onKeydown"
|
|
436
|
-
>
|
|
437
|
-
<Icon
|
|
438
|
-
:icon="uploading ? 'fluent:spinner-ios-20-regular' : 'fluent:cloud-arrow-up-20-regular'"
|
|
439
|
-
class="size-5 text-zinc-400"
|
|
440
|
-
:class="uploading && 'animate-spin'"
|
|
441
|
-
/>
|
|
442
|
-
<p class="text-[0.7rem] text-zinc-500">
|
|
443
|
-
{{ uploading ? t("upload-uploading") : placeholderText ?? t("upload-add-file") }}
|
|
444
|
-
</p>
|
|
445
|
-
<p
|
|
446
|
-
v-if="acceptHint"
|
|
447
|
-
class="text-[0.65rem] text-zinc-400"
|
|
448
|
-
>
|
|
449
|
-
{{ t("upload-accept-hint", { types: acceptHint }) }}
|
|
450
|
-
</p>
|
|
451
|
-
</div>
|
|
452
|
-
|
|
453
|
-
<!-- Add button — only when the list is non-empty and adding is allowed. -->
|
|
454
372
|
<Button
|
|
455
|
-
v-if="
|
|
373
|
+
v-if="!isReadonly && !isFull"
|
|
456
374
|
type="button"
|
|
457
375
|
variant="default"
|
|
458
376
|
size="sm"
|
|
459
377
|
data-slot="upload-zone-add"
|
|
460
378
|
class="h-6 w-full shrink-0 text-[0.75rem]"
|
|
379
|
+
:disabled="isDisabled || uploading"
|
|
461
380
|
@click="onAddClick"
|
|
462
381
|
>
|
|
463
382
|
<Icon
|
|
464
|
-
:icon="uploading ? 'fluent:spinner-ios-20-regular' :
|
|
383
|
+
:icon="uploading ? 'fluent:spinner-ios-20-regular' : addIcon"
|
|
465
384
|
:class="uploading && 'animate-spin'"
|
|
466
385
|
/>
|
|
467
|
-
{{ uploading ? t("upload-uploading") :
|
|
386
|
+
{{ uploading ? t("upload-uploading") : addLabel }}
|
|
468
387
|
</Button>
|
|
469
388
|
|
|
470
389
|
<input
|
|
@@ -476,28 +395,6 @@ async function onDownloadEntry(entry) {
|
|
|
476
395
|
:disabled="isDisabled || isReadonly"
|
|
477
396
|
@change="onPickerChange"
|
|
478
397
|
>
|
|
479
|
-
|
|
480
|
-
<div
|
|
481
|
-
v-if="templates.length > 0 && !isReadonly"
|
|
482
|
-
class="flex flex-wrap justify-end gap-1"
|
|
483
|
-
>
|
|
484
|
-
<Button
|
|
485
|
-
v-for="(template, index) in templates"
|
|
486
|
-
:key="index"
|
|
487
|
-
type="button"
|
|
488
|
-
variant="link"
|
|
489
|
-
size="sm"
|
|
490
|
-
data-slot="upload-zone-template"
|
|
491
|
-
class="h-5 px-1 text-[0.7rem]"
|
|
492
|
-
@click="onDownloadTemplate(template)"
|
|
493
|
-
>
|
|
494
|
-
<Icon
|
|
495
|
-
:icon="templateIcon(template)"
|
|
496
|
-
class="size-3.5"
|
|
497
|
-
/>
|
|
498
|
-
{{ templateLabelText(template) }}
|
|
499
|
-
</Button>
|
|
500
|
-
</div>
|
|
501
398
|
</div>
|
|
502
399
|
</div>
|
|
503
400
|
</template>
|
package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/schema.d.ts
CHANGED
|
@@ -9,11 +9,6 @@ export declare const metadata: {
|
|
|
9
9
|
readonly icon: "fluent:cloud-arrow-up-20-regular";
|
|
10
10
|
readonly description: "可编辑的文件上传单元格,值为文件描述对象列表,写回行数据";
|
|
11
11
|
};
|
|
12
|
-
export declare const JSON_VAR: {
|
|
13
|
-
readonly type: "dyn";
|
|
14
|
-
readonly label: "HTTP 响应体";
|
|
15
|
-
readonly description: "第一步 `请求` 的响应体(已解析 JSON);用于构造第二步下载请求";
|
|
16
|
-
};
|
|
17
12
|
export declare const FILES_VAR: {
|
|
18
13
|
readonly type: "list<dyn>";
|
|
19
14
|
readonly label: "已选文件";
|
|
@@ -30,7 +25,11 @@ export declare const FILE_VAR: {
|
|
|
30
25
|
readonly description: "行数据中的单个文件项(服务端回填或上传响应映射所得);返回其展示文件名";
|
|
31
26
|
};
|
|
32
27
|
export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
|
|
33
|
-
|
|
28
|
+
disabled: Schema.optional<Schema.Schema<string, string, never>>;
|
|
29
|
+
readonly: Schema.optional<Schema.Schema<string, string, never>>;
|
|
30
|
+
multiple: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
31
|
+
addIcon: Schema.optional<Schema.refine<string, typeof Schema.String>>;
|
|
32
|
+
addLabel: Schema.optional<Schema.suspend<readonly [{
|
|
34
33
|
readonly locale: "zh";
|
|
35
34
|
readonly message: string;
|
|
36
35
|
}, ...{
|
|
@@ -43,9 +42,6 @@ export declare function schema(configure: (env: Environment) => void): Schema.St
|
|
|
43
42
|
readonly locale: "en" | "ja" | "ko";
|
|
44
43
|
readonly message: string;
|
|
45
44
|
}[]], never>>;
|
|
46
|
-
disabled: Schema.optional<Schema.Schema<string, string, never>>;
|
|
47
|
-
readonly: Schema.optional<Schema.Schema<string, string, never>>;
|
|
48
|
-
multiple: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
49
45
|
accept: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
50
46
|
maxFileSize: Schema.optional<Schema.refine<number, typeof Schema.Number>>;
|
|
51
47
|
maxTotalSize: Schema.optional<Schema.refine<number, typeof Schema.Number>>;
|
|
@@ -55,24 +51,6 @@ export declare function schema(configure: (env: Environment) => void): Schema.St
|
|
|
55
51
|
handle: Schema.Schema<string, string, never>;
|
|
56
52
|
filename: Schema.optional<Schema.Schema<string, string, never>>;
|
|
57
53
|
}>>;
|
|
58
|
-
templates: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
59
|
-
request: Schema.Schema<string, string, never>;
|
|
60
|
-
download: Schema.optional<Schema.Schema<string, string, never>>;
|
|
61
|
-
icon: Schema.optional<Schema.refine<string, typeof Schema.String>>;
|
|
62
|
-
label: Schema.optional<Schema.suspend<readonly [{
|
|
63
|
-
readonly locale: "zh";
|
|
64
|
-
readonly message: string;
|
|
65
|
-
}, ...{
|
|
66
|
-
readonly locale: "en" | "ja" | "ko";
|
|
67
|
-
readonly message: string;
|
|
68
|
-
}[]], string | readonly [{
|
|
69
|
-
readonly locale: "zh";
|
|
70
|
-
readonly message: string;
|
|
71
|
-
}, ...{
|
|
72
|
-
readonly locale: "en" | "ja" | "ko";
|
|
73
|
-
readonly message: string;
|
|
74
|
-
}[]], never>>;
|
|
75
|
-
}>>>;
|
|
76
54
|
download: Schema.optional<Schema.Struct<{
|
|
77
55
|
operations: Schema.Array$<Schema.Struct<{
|
|
78
56
|
id: Schema.optional<Schema.refine<string, typeof Schema.String>>;
|
package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/schema.js
CHANGED
|
@@ -16,11 +16,6 @@ export const metadata = {
|
|
|
16
16
|
icon: "fluent:cloud-arrow-up-20-regular",
|
|
17
17
|
description: "\u53EF\u7F16\u8F91\u7684\u6587\u4EF6\u4E0A\u4F20\u5355\u5143\u683C\uFF0C\u503C\u4E3A\u6587\u4EF6\u63CF\u8FF0\u5BF9\u8C61\u5217\u8868\uFF0C\u5199\u56DE\u884C\u6570\u636E"
|
|
18
18
|
};
|
|
19
|
-
export const JSON_VAR = {
|
|
20
|
-
type: "dyn",
|
|
21
|
-
label: "HTTP \u54CD\u5E94\u4F53",
|
|
22
|
-
description: "\u7B2C\u4E00\u6B65 `\u8BF7\u6C42` \u7684\u54CD\u5E94\u4F53\uFF08\u5DF2\u89E3\u6790 JSON\uFF09\uFF1B\u7528\u4E8E\u6784\u9020\u7B2C\u4E8C\u6B65\u4E0B\u8F7D\u8BF7\u6C42"
|
|
23
|
-
};
|
|
24
19
|
export const FILES_VAR = {
|
|
25
20
|
type: "list<dyn>",
|
|
26
21
|
label: "\u5DF2\u9009\u6587\u4EF6",
|
|
@@ -45,8 +40,6 @@ function withRowAndVar(configure, name, spec) {
|
|
|
45
40
|
}
|
|
46
41
|
export function schema(configure) {
|
|
47
42
|
const CelBool = CelRowAccess(configure, { resultType: "bool" });
|
|
48
|
-
const CelHttpRequest = CelRowAccess(configure, { resultType: "HttpRequest" });
|
|
49
|
-
const CelDownloadRequest = Expression({ configure: withRowAndVar(configure, "json", JSON_VAR), resultType: "HttpRequest" });
|
|
50
43
|
const CelUploadRequest = Expression({ configure: withRowAndVar(configure, "files", FILES_VAR), resultType: "HttpRequest" });
|
|
51
44
|
const CelUploadHandle = Expression({ configure: withRowAndVar(configure, "json", UPLOAD_JSON_VAR), resultType: "list" });
|
|
52
45
|
const CelItemName = Expression({ configure: withRowAndVar(configure, "file", FILE_VAR), resultType: "string" });
|
|
@@ -54,13 +47,9 @@ export function schema(configure) {
|
|
|
54
47
|
type: Schema.Literal(type),
|
|
55
48
|
compatibilityDate: Schema.Literal(compatibilityDate),
|
|
56
49
|
...editableColumnFields(),
|
|
57
|
-
placeholder: Schema.optional(Locale.annotations({
|
|
58
|
-
title: "\u5360\u4F4D\u7B26",
|
|
59
|
-
description: "\u5355\u5143\u683C\u4E3A\u7A7A\u65F6\u4E0A\u4F20\u533A\u57DF\u7684\u63D0\u793A\u6587\u672C"
|
|
60
|
-
})),
|
|
61
50
|
disabled: Schema.optional(CelBool.annotations({
|
|
62
51
|
title: "\u7981\u7528\u6761\u4EF6",
|
|
63
|
-
description: "\u8FD4\u56DE `true` \u65F6\
|
|
52
|
+
description: "\u8FD4\u56DE `true` \u65F6\u6DFB\u52A0\u6309\u94AE\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u64CD\u4F5C"
|
|
64
53
|
})),
|
|
65
54
|
readonly: Schema.optional(CelBool.annotations({
|
|
66
55
|
title: "\u53EA\u8BFB\u6761\u4EF6",
|
|
@@ -68,7 +57,15 @@ export function schema(configure) {
|
|
|
68
57
|
})),
|
|
69
58
|
multiple: Schema.optional(Schema.Boolean.annotations({
|
|
70
59
|
title: "\u5141\u8BB8\u591A\u6587\u4EF6",
|
|
71
|
-
description: "\u5F00\u542F\u540E\u53EF\u4E00\u6B21\u9009\u62E9
|
|
60
|
+
description: "\u5F00\u542F\u540E\u53EF\u4E00\u6B21\u9009\u62E9\u591A\u4E2A\u6587\u4EF6\uFF1B\u5173\u95ED\u65F6\u6700\u591A\u4FDD\u7559\u4E00\u4E2A\u6587\u4EF6\uFF0C\u9700\u5148\u5220\u9664\u5DF2\u6709\u6587\u4EF6\u624D\u80FD\u91CD\u65B0\u6DFB\u52A0"
|
|
61
|
+
})),
|
|
62
|
+
addIcon: Schema.optional(Schema.String.pipe(Schema.minLength(1)).annotations({
|
|
63
|
+
title: "\u6DFB\u52A0\u6309\u94AE\u56FE\u6807",
|
|
64
|
+
description: "Iconify \u56FE\u6807\u6807\u8BC6\u7B26\uFF0C\u4F8B\u5982 `fluent:add-20-regular`\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807"
|
|
65
|
+
})),
|
|
66
|
+
addLabel: Schema.optional(Locale.annotations({
|
|
67
|
+
title: "\u6DFB\u52A0\u6309\u94AE\u6587\u672C",
|
|
68
|
+
description: "\u6DFB\u52A0\u6587\u4EF6\u6309\u94AE\u4E0A\u7684\u672C\u5730\u5316\u6587\u672C\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528\u300C\u6DFB\u52A0\u6587\u4EF6\u300D"
|
|
72
69
|
})),
|
|
73
70
|
accept: Schema.optional(Schema.Array(Schema.String).annotations({
|
|
74
71
|
title: "\u5141\u8BB8\u7684\u6587\u4EF6\u7C7B\u578B",
|
|
@@ -108,30 +105,6 @@ export function schema(configure) {
|
|
|
108
105
|
title: "\u5373\u65F6\u4E0A\u4F20",
|
|
109
106
|
description: "\u914D\u7F6E\u540E\u6587\u4EF6\u5728\u9009\u62E9\u65F6\u7ACB\u5373\u4E0A\u4F20\uFF0C\u7ED1\u5B9A\u503C\u5B58\u50A8\u6620\u5C04\u540E\u7684\u6587\u4EF6\u9879\uFF08\u800C\u975E\u539F\u59CB `File`\uFF09\uFF1B\u4E0D\u914D\u7F6E\u5219\u4FDD\u7559\u6587\u4EF6\u7B49\u5F85\u6574\u884C\u63D0\u4EA4"
|
|
110
107
|
})),
|
|
111
|
-
// Download templates — each entry renders one button next to the upload
|
|
112
|
-
// zone. `request` is the (possibly only) step; `download` is an optional
|
|
113
|
-
// second step that may reference the first response via `json`.
|
|
114
|
-
templates: Schema.optional(Schema.Array(Schema.Struct({
|
|
115
|
-
request: CelHttpRequest.annotations({
|
|
116
|
-
title: "\u8BF7\u6C42",
|
|
117
|
-
description: "\u8FD4\u56DE `HttpRequest` \u7684 CEL \u8868\u8FBE\u5F0F\u3002\u672A\u914D\u7F6E\u300C\u4E0B\u8F7D\u8BF7\u6C42\u300D\u65F6\u76F4\u63A5\u53D1\u8D77\u5E76\u4E0B\u8F7D\u5176\u54CD\u5E94\uFF1B\u914D\u7F6E\u4E86\u300C\u4E0B\u8F7D\u8BF7\u6C42\u300D\u65F6\u4F5C\u4E3A\u83B7\u53D6\u4E0B\u8F7D\u51ED\u636E\u7684\u7B2C\u4E00\u6B65\u8BF7\u6C42"
|
|
118
|
-
}),
|
|
119
|
-
download: Schema.optional(CelDownloadRequest.annotations({
|
|
120
|
-
title: "\u4E0B\u8F7D\u8BF7\u6C42",
|
|
121
|
-
description: "\u53EF\u9009\u7684\u7B2C\u4E8C\u6B65\uFF1A\u8FD4\u56DE `HttpRequest` \u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u901A\u8FC7 `json` \u5F15\u7528\u7B2C\u4E00\u6B65\u7684\u54CD\u5E94\u4F53\uFF0C\u4F8B\u5982\u7528 `json.data.key` \u6784\u9020\u771F\u6B63\u7684\u4E0B\u8F7D\u8BF7\u6C42"
|
|
122
|
-
})),
|
|
123
|
-
icon: Schema.optional(Schema.String.pipe(Schema.minLength(1)).annotations({
|
|
124
|
-
title: "\u6A21\u677F\u6309\u94AE\u56FE\u6807",
|
|
125
|
-
description: "Iconify \u56FE\u6807\u6807\u8BC6\u7B26\uFF0C\u4F8B\u5982 `fluent:arrow-download-20-regular`\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807"
|
|
126
|
-
})),
|
|
127
|
-
label: Schema.optional(Locale.annotations({
|
|
128
|
-
title: "\u6A21\u677F\u6309\u94AE\u6587\u672C",
|
|
129
|
-
description: "\u4E0B\u8F7D\u6A21\u677F\u6309\u94AE\u4E0A\u7684\u672C\u5730\u5316\u6587\u672C\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528\u300C\u4E0B\u8F7D\u6A21\u677F\u300D"
|
|
130
|
-
}))
|
|
131
|
-
})).annotations({
|
|
132
|
-
title: "\u4E0B\u8F7D\u6A21\u677F",
|
|
133
|
-
description: "\u4E0B\u8F7D\u6A21\u677F\u5217\u8868\uFF1B\u6BCF\u4E00\u9879\u5728\u4E0A\u4F20\u533A\u57DF\u65C1\u6E32\u67D3\u4E00\u4E2A\u4E0B\u8F7D\u6309\u94AE"
|
|
134
|
-
})),
|
|
135
108
|
// Per-file download — a 触发操作 (trigger-operation) list run when the user
|
|
136
109
|
// clicks a file item's 下载 button, with the clicked stored item in scope as
|
|
137
110
|
// `file` (the same `FILE_VAR` as `upload.filename`), on top of the row scope.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数电票的票面骨架。国家税务总局的数电票是**一张底版 + 逐业务的差异**:抬头(双线标题 /
|
|
3
|
+
* 发票号码 / 开票日期)、购销双方、明细三表对齐、合计、价税合计大小写、备注框、开票人
|
|
4
|
+
* 在全家族逐像素相同;只有「业务标签」「明细有哪些列」「备注里有哪些栏」逐票不同。
|
|
5
|
+
* 这里收口相同的那部分,差异走 slot。
|
|
6
|
+
*
|
|
7
|
+
* 列宽(`colgroup`)刻意由调用方给:明细列逐业务不同,但**同一张票的三张表必须共用同
|
|
8
|
+
* 一份 colgroup** 才对得齐,所以它属于「块自己写一份、三处都用」,不属于骨架。同理
|
|
9
|
+
* 合计行(`items-foot`)—— 金额 / 税额落在第几列取决于该票的列,骨架不该猜。
|
|
10
|
+
*/
|
|
11
|
+
type __VLS_Props = {
|
|
12
|
+
/** 票面标题全文,如 `电子发票(增值税专用发票)`。骨架不认识票种。 */
|
|
13
|
+
title: string;
|
|
14
|
+
invoiceNumber: string;
|
|
15
|
+
issueDate: string;
|
|
16
|
+
buyer: {
|
|
17
|
+
name: string;
|
|
18
|
+
taxId: string;
|
|
19
|
+
};
|
|
20
|
+
seller: {
|
|
21
|
+
name: string;
|
|
22
|
+
taxId: string;
|
|
23
|
+
};
|
|
24
|
+
/** 价税合计(小写)。已格式化,空串即不打印 `¥`。 */
|
|
25
|
+
grandTotal: string;
|
|
26
|
+
/** 价税合计(大写)。空串即留空 —— 见 `useInvoiceEval` 的 `capital`。 */
|
|
27
|
+
grandTotalCapital: string;
|
|
28
|
+
issuer?: string;
|
|
29
|
+
/** 为真时票面中央叠加「红字发票」水印。 */
|
|
30
|
+
redLetter?: boolean;
|
|
31
|
+
/** 明细列数 —— 只用来给填充空行的 `colspan` 定宽。 */
|
|
32
|
+
columnCount: number;
|
|
33
|
+
/** 实际明细行数 —— 骨架据此补足填充空行。 */
|
|
34
|
+
rowCount: number;
|
|
35
|
+
/** 明细区最大高度(CSS 长度)。留空则明细区随行数增长,滚动条永不出现。 */
|
|
36
|
+
itemsMaxHeight?: string;
|
|
37
|
+
};
|
|
38
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_13: {}, __VLS_15: {}, __VLS_17: {}, __VLS_19: {}, __VLS_21: {};
|
|
39
|
+
type __VLS_Slots = {} & {
|
|
40
|
+
business?: (props: typeof __VLS_1) => any;
|
|
41
|
+
} & {
|
|
42
|
+
colgroup?: (props: typeof __VLS_3) => any;
|
|
43
|
+
} & {
|
|
44
|
+
'items-head'?: (props: typeof __VLS_5) => any;
|
|
45
|
+
} & {
|
|
46
|
+
colgroup?: (props: typeof __VLS_13) => any;
|
|
47
|
+
} & {
|
|
48
|
+
'items-body'?: (props: typeof __VLS_15) => any;
|
|
49
|
+
} & {
|
|
50
|
+
colgroup?: (props: typeof __VLS_17) => any;
|
|
51
|
+
} & {
|
|
52
|
+
'items-foot'?: (props: typeof __VLS_19) => any;
|
|
53
|
+
} & {
|
|
54
|
+
remark?: (props: typeof __VLS_21) => any;
|
|
55
|
+
};
|
|
56
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
57
|
+
redLetter: boolean;
|
|
58
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
59
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
60
|
+
declare const _default: typeof __VLS_export;
|
|
61
|
+
export default _default;
|
|
62
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
63
|
+
new (): {
|
|
64
|
+
$slots: S;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { ScrollArea } from "../scroll-area";
|
|
4
|
+
defineOptions({ name: "ShwfedInvoiceSheet" });
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
title: { type: String, required: true },
|
|
7
|
+
invoiceNumber: { type: String, required: true },
|
|
8
|
+
issueDate: { type: String, required: true },
|
|
9
|
+
buyer: { type: Object, required: true },
|
|
10
|
+
seller: { type: Object, required: true },
|
|
11
|
+
grandTotal: { type: String, required: true },
|
|
12
|
+
grandTotalCapital: { type: String, required: true },
|
|
13
|
+
issuer: { type: String, required: false },
|
|
14
|
+
redLetter: { type: Boolean, required: false, default: false },
|
|
15
|
+
columnCount: { type: Number, required: true },
|
|
16
|
+
rowCount: { type: Number, required: true },
|
|
17
|
+
itemsMaxHeight: { type: String, required: false }
|
|
18
|
+
});
|
|
19
|
+
const MIN_ROWS = 6;
|
|
20
|
+
const fillerRows = computed(() => Math.max(0, MIN_ROWS - props.rowCount));
|
|
21
|
+
const itemsStyle = computed(
|
|
22
|
+
() => props.itemsMaxHeight ? { maxHeight: props.itemsMaxHeight } : void 0
|
|
23
|
+
);
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<div class="invoice">
|
|
28
|
+
<!-- 抬头:标题相对整张票居中,业务标签 / 号码块靠两侧 -->
|
|
29
|
+
<header class="head">
|
|
30
|
+
<div class="business">
|
|
31
|
+
<slot name="business" />
|
|
32
|
+
</div>
|
|
33
|
+
<div class="title-block">
|
|
34
|
+
<h1 class="title">
|
|
35
|
+
{{ title }}
|
|
36
|
+
</h1>
|
|
37
|
+
<div
|
|
38
|
+
class="title-rule"
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
<dl class="meta">
|
|
43
|
+
<div>
|
|
44
|
+
<dt>发票号码:</dt>
|
|
45
|
+
<dd>{{ invoiceNumber }}</dd>
|
|
46
|
+
</div>
|
|
47
|
+
<div>
|
|
48
|
+
<dt>开票日期:</dt>
|
|
49
|
+
<dd>{{ issueDate }}</dd>
|
|
50
|
+
</div>
|
|
51
|
+
</dl>
|
|
52
|
+
</header>
|
|
53
|
+
|
|
54
|
+
<div class="frame">
|
|
55
|
+
<!-- 购销双方 -->
|
|
56
|
+
<div class="parties">
|
|
57
|
+
<div class="party">
|
|
58
|
+
<div class="side-label">
|
|
59
|
+
购买方信息
|
|
60
|
+
</div>
|
|
61
|
+
<div class="party-body">
|
|
62
|
+
<div class="party-row">
|
|
63
|
+
<span class="party-key">名称:</span>
|
|
64
|
+
<span class="party-value">{{ buyer.name }}</span>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="party-row">
|
|
67
|
+
<span class="party-key">统一社会信用代码/纳税人识别号:</span>
|
|
68
|
+
<span class="party-value tax-id">{{ buyer.taxId }}</span>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="party">
|
|
73
|
+
<div class="side-label">
|
|
74
|
+
销售方信息
|
|
75
|
+
</div>
|
|
76
|
+
<div class="party-body">
|
|
77
|
+
<div class="party-row">
|
|
78
|
+
<span class="party-key">名称:</span>
|
|
79
|
+
<span class="party-value">{{ seller.name }}</span>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="party-row">
|
|
82
|
+
<span class="party-key">统一社会信用代码/纳税人识别号:</span>
|
|
83
|
+
<span class="party-value tax-id">{{ seller.taxId }}</span>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<!-- 明细。列标题 / 内容 / 合计拆成三张 table,只有中间那张进 ScrollArea ——
|
|
90
|
+
ScrollArea 的滚动条是覆盖整个滚动容器高度的绝对定位条,表头表尾若也在里面
|
|
91
|
+
就会被它横穿。三张靠调用方给的同一份 colgroup 对齐(都是 table-layout: fixed)。 -->
|
|
92
|
+
<div class="items">
|
|
93
|
+
<!-- 水印:在明细内容**之下**(纸质票的红字是印在票面底纹上、被文字压住的),
|
|
94
|
+
不参与布局,也不拦截选中 -->
|
|
95
|
+
<div
|
|
96
|
+
v-if="redLetter"
|
|
97
|
+
class="watermark"
|
|
98
|
+
aria-hidden="true"
|
|
99
|
+
>
|
|
100
|
+
红字发票
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<table class="items-head">
|
|
104
|
+
<slot name="colgroup" />
|
|
105
|
+
<thead>
|
|
106
|
+
<slot name="items-head" />
|
|
107
|
+
</thead>
|
|
108
|
+
</table>
|
|
109
|
+
|
|
110
|
+
<!-- `overscroll-none`:明细滚到头就停住,既不橡皮筋回弹、也不把滚动传给外层页面
|
|
111
|
+
—— 票是嵌在页面里的一小块,滚它不该顺带滚走整张页面。 -->
|
|
112
|
+
<ScrollArea
|
|
113
|
+
class="items-body"
|
|
114
|
+
viewport-class="overscroll-none"
|
|
115
|
+
:style="itemsStyle"
|
|
116
|
+
>
|
|
117
|
+
<table>
|
|
118
|
+
<slot name="colgroup" />
|
|
119
|
+
<tbody>
|
|
120
|
+
<slot name="items-body" />
|
|
121
|
+
<!-- 撑满票面的空行,纸质票同款 -->
|
|
122
|
+
<tr
|
|
123
|
+
v-for="n in fillerRows"
|
|
124
|
+
:key="`filler-${n}`"
|
|
125
|
+
class="filler"
|
|
126
|
+
>
|
|
127
|
+
<td :colspan="columnCount">
|
|
128
|
+
|
|
129
|
+
</td>
|
|
130
|
+
</tr>
|
|
131
|
+
</tbody>
|
|
132
|
+
</table>
|
|
133
|
+
</ScrollArea>
|
|
134
|
+
|
|
135
|
+
<table class="items-foot">
|
|
136
|
+
<slot name="colgroup" />
|
|
137
|
+
<tfoot>
|
|
138
|
+
<slot name="items-foot" />
|
|
139
|
+
</tfoot>
|
|
140
|
+
</table>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<!-- 价税合计 -->
|
|
144
|
+
<div class="grand">
|
|
145
|
+
<div class="grand-label">
|
|
146
|
+
价税合计(大写)
|
|
147
|
+
</div>
|
|
148
|
+
<div class="grand-capital">
|
|
149
|
+
<span class="cross">⊗</span>{{ grandTotalCapital }}
|
|
150
|
+
</div>
|
|
151
|
+
<div class="grand-lower">
|
|
152
|
+
<span class="grand-lower-key">(小写)</span>
|
|
153
|
+
<template v-if="grandTotal">
|
|
154
|
+
¥{{ grandTotal }}
|
|
155
|
+
</template>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- 备注。框与竖排栏标是全家族共用的,栏内怎么排逐票不同(通用票是一段自由文本,
|
|
160
|
+
不动产系是「标签:值;」的内联流),故走 slot。 -->
|
|
161
|
+
<div class="remark">
|
|
162
|
+
<div class="side-label">
|
|
163
|
+
备 注
|
|
164
|
+
</div>
|
|
165
|
+
<div class="remark-body">
|
|
166
|
+
<slot name="remark" />
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<footer
|
|
172
|
+
v-if="issuer"
|
|
173
|
+
class="foot"
|
|
174
|
+
>
|
|
175
|
+
<span class="foot-label">开票人:</span>{{ issuer }}
|
|
176
|
+
</footer>
|
|
177
|
+
</div>
|
|
178
|
+
</template>
|
|
179
|
+
|
|
180
|
+
<style scoped>
|
|
181
|
+
.invoice{--invoice-ink:maroon;--invoice-data:#000;--invoice-font-song:"Songti SC",SimSun,STSong,"Source Han Serif SC","Noto Serif CJK SC","Noto Serif SC",serif;--invoice-font-kai:KaiTi,"Kaiti SC",STKaiti,"AR PL UKai CN","LXGW WenKai",var(--invoice-font-song);background:#fff;color:var(--invoice-data);container-type:inline-size;font-family:var(--invoice-font-song);font-size:clamp(9px,1.15cqw,14px);line-height:1.5;padding:1rem}.foot-label,.grand-label,.grand-lower-key,.items :deep(.total-label),.items :deep(th),.meta dt,.party-key,.side-label,.title{color:var(--invoice-ink);font-family:var(--invoice-font-kai)}.head{display:flex;justify-content:center;padding:1.8em 0 2em;position:relative}.business{left:0;position:absolute;top:50%;transform:translateY(-50%)}.title-block{display:flex;flex-direction:column}.title{font-size:2.6em;font-weight:400;letter-spacing:.1em;white-space:nowrap}.title-rule{border-bottom:1.5px solid var(--invoice-ink);border-top:1.5px solid var(--invoice-ink);height:.32em;margin-top:.45em}.meta{flex-direction:column;gap:.35em;position:absolute;right:0;top:50%;transform:translateY(-50%);white-space:nowrap}.meta,.meta div{display:flex}.meta dt{font-weight:400}.meta dd{letter-spacing:.02em;margin:0}.frame{border:1px solid var(--invoice-ink)}.parties{border-bottom:1px solid var(--invoice-ink);display:grid;grid-template-columns:1fr 1fr}.party{display:grid;grid-template-columns:auto 1fr}.party+.party{border-left:1px solid var(--invoice-ink)}.side-label{align-items:center;border-right:1px solid var(--invoice-ink);display:flex;justify-content:center;letter-spacing:.25em;padding:.5em .4em;text-orientation:upright;white-space:nowrap;writing-mode:vertical-rl}.party-body{flex-direction:column;justify-content:space-around;padding:.6em .8em}.party-body,.party-row{display:flex;min-width:0}.party-row{align-items:baseline}.party-key{white-space:nowrap}.party-value{min-width:0;word-break:break-all}.tax-id{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,Courier New,monospace}.items{position:relative}.items table{border-collapse:collapse;table-layout:fixed;width:100%}.items-body,.items-foot,.items-head{position:relative;z-index:1}.items :deep(th){font-weight:400;letter-spacing:.05em;padding:.4em .5em;text-align:center}.items :deep(td){padding:.25em .5em;vertical-align:top}.items :deep(.cell-text){white-space:pre-wrap;word-break:break-all}.items :deep(.cell-center){text-align:center}.items :deep(.cell-num){font-variant-numeric:tabular-nums;text-align:right}.items :deep(.total-label){letter-spacing:.05em;text-align:center}.items :deep(tfoot td){padding-bottom:.35em;padding-top:.35em}.watermark{color:transparent;display:flex;font-size:3.4em;inset:0;justify-content:center;letter-spacing:.5em;pointer-events:none;position:absolute;z-index:0;-webkit-text-stroke:1px color-mix(in srgb,var(--invoice-ink) 40%,#fff);font-family:var(--invoice-font-kai);opacity:.85}.grand,.watermark{align-items:center}.grand{border-bottom:1px solid var(--invoice-ink);border-top:1px solid var(--invoice-ink);display:grid;grid-template-columns:22% 1fr auto}.grand-label{border-right:1px solid var(--invoice-ink);letter-spacing:.1em;padding:.5em 1.2em;text-align:center;white-space:nowrap}.grand-capital{min-width:0;padding:.5em .8em;word-break:break-all}.cross{margin-right:.3em}.grand-lower{font-variant-numeric:tabular-nums;padding:.5em 1.2em;white-space:nowrap}.grand-lower-key{margin-right:.4em}.remark{display:grid;grid-template-columns:auto 1fr;min-height:5em}.remark-body{padding:.5em .8em;white-space:pre-wrap;word-break:break-all}.foot{padding-left:3em;padding-top:.75em}
|
|
182
|
+
</style>
|