@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.
Files changed (75) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/config.d.vue.ts +18 -18
  3. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/config.vue.d.ts +18 -18
  4. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/runtime.d.vue.ts +18 -18
  5. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/runtime.vue.d.ts +18 -18
  6. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.d.vue.ts +8 -8
  7. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.vue +264 -286
  8. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.vue.d.ts +8 -8
  9. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.d.vue.ts +8 -8
  10. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.vue +91 -291
  11. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.vue.d.ts +8 -8
  12. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/schema.d.ts +3 -28
  13. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/schema.js +9 -33
  14. package/dist/runtime/components/config/index.d.vue.ts +2 -2
  15. package/dist/runtime/components/config/index.vue.d.ts +2 -2
  16. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.textarea/config.d.vue.ts +2 -2
  17. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.textarea/config.vue.d.ts +2 -2
  18. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +26 -26
  19. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +26 -26
  20. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +26 -26
  21. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +26 -26
  22. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +26 -26
  23. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +26 -26
  24. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +26 -26
  25. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +26 -26
  26. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +26 -26
  27. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +26 -26
  28. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.d.vue.ts +26 -26
  29. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.vue.d.ts +26 -26
  30. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.d.vue.ts +26 -26
  31. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.vue.d.ts +26 -26
  32. package/dist/runtime/components/form/utils/validation.js +9 -7
  33. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
  34. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
  35. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
  36. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
  37. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
  38. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
  39. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
  40. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
  41. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
  42. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
  43. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
  44. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
  45. package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/config.vue +57 -247
  46. package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/prompt.md +2 -2
  47. package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/runtime.vue +21 -124
  48. package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/schema.d.ts +5 -27
  49. package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/schema.js +10 -37
  50. package/dist/runtime/components/ui/invoice/InvoiceSheet.d.vue.ts +66 -0
  51. package/dist/runtime/components/ui/invoice/InvoiceSheet.vue +182 -0
  52. package/dist/runtime/components/ui/invoice/InvoiceSheet.vue.d.ts +66 -0
  53. package/dist/runtime/components/ui/invoice/index.d.ts +1 -0
  54. package/dist/runtime/components/ui/invoice/index.js +1 -0
  55. package/dist/runtime/components/ui/invoice-config/InvoiceConfigShell.d.vue.ts +36 -0
  56. package/dist/runtime/components/ui/invoice-config/InvoiceConfigShell.vue +41 -0
  57. package/dist/runtime/components/ui/invoice-config/InvoiceConfigShell.vue.d.ts +36 -0
  58. package/dist/runtime/components/{config/blocks/2026-07-17/com.shwfed.block.invoice.digital/expr-field.vue → ui/invoice-config/InvoiceExprField.vue} +4 -4
  59. 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
  60. 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
  61. package/dist/runtime/components/ui/invoice-config/index.d.ts +4 -0
  62. package/dist/runtime/components/ui/invoice-config/index.js +4 -0
  63. package/dist/runtime/share/invoice-eval.d.ts +13 -0
  64. package/dist/runtime/share/invoice-eval.js +88 -0
  65. package/dist/runtime/share/invoice.d.ts +68 -0
  66. package/dist/runtime/share/invoice.js +35 -0
  67. package/package.json +1 -1
  68. /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
  69. /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
  70. /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
  71. /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
  72. /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
  73. /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
  74. /package/dist/runtime/{components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital → share}/rmb.d.ts +0 -0
  75. /package/dist/runtime/{components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital → share}/rmb.js +0 -0
@@ -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 acceptHint = computed(() => {
169
- const list = props.column.accept;
170
- if (!list || list.length === 0) return void 0;
171
- return Array.from(new Set(list.map((m) => SHORT_NAMES[m] ?? m))).join("\u3001");
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 (props.column.maxFiles !== void 0 && count >= props.column.maxFiles) {
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
- Empty editable state: a compact dashed add zone. Kept short (min-h-16)
422
- so an empty row does not tower over its neighbours; the non-empty list
423
- grows only as far as the files it holds.
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="entries.length > 0 && canAdd"
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' : 'fluent:add-20-regular'"
383
+ :icon="uploading ? 'fluent:spinner-ios-20-regular' : addIcon"
465
384
  :class="uploading && 'animate-spin'"
466
385
  />
467
- {{ uploading ? t("upload-uploading") : t("upload-add-file") }}
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>
@@ -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
- placeholder: Schema.optional<Schema.suspend<readonly [{
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>>;
@@ -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\u4E0A\u4F20\u533A\u57DF\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u64CD\u4F5C"
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/\u62D6\u5165\u591A\u4E2A\u6587\u4EF6\uFF1B\u5173\u95ED\u65F6\u65B0\u6587\u4EF6\u4F1A\u66FF\u6362\u5DF2\u9009\u6587\u4EF6"
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
+ &nbsp;
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>