djm-ui 0.67.4 → 0.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +38 -2
  3. package/dist/djm-ui.cjs +1032 -347
  4. package/dist/djm-ui.cjs.map +4 -4
  5. package/dist/djm-ui.css +752 -17
  6. package/dist/djm-ui.esm.js +1105 -420
  7. package/dist/djm-ui.esm.js.map +4 -4
  8. package/dist/djm-ui.esm.min.js +373 -227
  9. package/dist/djm-ui.min.cjs +373 -227
  10. package/dist/djm-ui.min.css +2 -2
  11. package/dist/djm-ui.umd.js +944 -259
  12. package/dist/djm-ui.umd.min.js +373 -227
  13. package/dist/mcp/manifest.json +12195 -0
  14. package/dist/mcp/server.js +90 -0
  15. package/package.json +12 -4
  16. package/src/components/icon/index.js +13 -1
  17. package/src/components/rich-editor/index.js +724 -61
  18. package/src/index.js +1 -1
  19. package/src/locale/en-US.js +33 -1
  20. package/src/locale/zh-CN.js +33 -1
  21. package/src/styles/base.css +2 -3
  22. package/src/styles/components/button.css +2 -1
  23. package/src/styles/components/console.css +5 -0
  24. package/src/styles/components/content.css +517 -0
  25. package/src/styles/components/form.css +5 -0
  26. package/src/styles/components/layout.css +326 -0
  27. package/src/styles/components/media-picker.css +3 -3
  28. package/src/styles/components/menu.css +2 -1
  29. package/src/styles/components/org-picker.css +1 -1
  30. package/src/styles/components/responsive.css +86 -0
  31. package/src/styles/components/rich-editor.css +206 -7
  32. package/src/styles/components/skin.css +54 -0
  33. package/src/styles/components/template.css +5 -0
  34. package/src/styles/index.css +1 -0
  35. package/src/styles/theme-dark.css +2 -0
  36. package/src/styles/tokens.css +8 -0
  37. package/src/theme/index.js +40 -0
  38. package/src/utils/sanitize-html.js +44 -6
  39. package/types/index.d.ts +8 -0
@@ -1,4 +1,4 @@
1
- /*! DJM UI v0.67.4 · MIT · 2026-10-26 */
1
+ /*! DJM UI v0.72.0 · MIT · 2026-10-26 */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __esm = (fn, res, err) => function __init() {
@@ -969,7 +969,19 @@ var PATHS = {
969
969
  activity: "M22 12h-4l-3 9L9 3l-3 9H2",
970
970
  database: "M12 8c4.42 0 8-1.34 8-3s-3.58-3-8-3-8 1.34-8 3 3.58 3 8 3zM4 5v14c0 1.66 3.58 3 8 3s8-1.34 8-3V5M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3",
971
971
  sun: "M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10zM12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42",
972
- moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"
972
+ moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z",
973
+ undo: "M3 10h11a5 5 0 1 1 0 10H8M3 10l4-4M3 10l4 4",
974
+ redo: "M21 10H10a5 5 0 1 0 0 10h6M21 10l-4-4M21 10l-4 4",
975
+ "align-left": "M4 6h16M4 12h10M4 18h13",
976
+ "align-center": "M4 6h16M7 12h10M6 18h12",
977
+ "align-right": "M4 6h16M10 12h10M7 18h13",
978
+ "align-justify": "M4 6h16M4 12h16M4 18h16",
979
+ list: "M9 6h11M9 12h11M9 18h11M4.5 6h.01M4.5 12h.01M4.5 18h.01",
980
+ "list-ordered": "M10 6h10M10 12h10M10 18h10M4 6h1v4M4 12h2M4 16h2v2H4",
981
+ outdent: "M4 6h16M10 12h10M10 18h10M8 9l-3 3 3 3",
982
+ indent: "M4 6h16M10 12h10M10 18h10M5 9l3 3-3 3",
983
+ unlink: "M10 13a5 5 0 0 0 7.5.5M14 11a5 5 0 0 0-7.5-.5M2 2l20 20",
984
+ images: "M8 3h12v12M4 7h12v12H4zM7 15l3-3 3 3"
973
985
  };
974
986
  var icon_default = {
975
987
  name: "DjmIcon",
@@ -1296,7 +1308,6 @@ var zh_CN_default = {
1296
1308
  "richEditor.italic": "\u659C\u4F53",
1297
1309
  "richEditor.underline": "\u4E0B\u5212\u7EBF",
1298
1310
  "richEditor.strike": "\u5220\u9664\u7EBF",
1299
- "richEditor.heading": "\u4E8C\u7EA7\u6807\u9898",
1300
1311
  "richEditor.paragraph": "\u6B63\u6587",
1301
1312
  "richEditor.bulletList": "\u65E0\u5E8F\u5217\u8868",
1302
1313
  "richEditor.numberList": "\u6709\u5E8F\u5217\u8868",
@@ -1307,6 +1318,39 @@ var zh_CN_default = {
1307
1318
  "richEditor.redo": "\u91CD\u505A",
1308
1319
  "richEditor.source": "\u6E90\u7801",
1309
1320
  "richEditor.visual": "\u53EF\u89C6\u5316",
1321
+ "richEditor.toolbar": "\u7F16\u8F91\u5DE5\u5177\u680F",
1322
+ "richEditor.block": "\u6BB5\u843D\u683C\u5F0F",
1323
+ "richEditor.heading1": "\u6807\u9898 1",
1324
+ "richEditor.heading2": "\u6807\u9898 2",
1325
+ "richEditor.heading3": "\u6807\u9898 3",
1326
+ "richEditor.quote": "\u5F15\u7528",
1327
+ "richEditor.codeBlock": "\u4EE3\u7801\u5757",
1328
+ "richEditor.fontSize": "\u5B57\u53F7",
1329
+ "richEditor.defaultSize": "\u9ED8\u8BA4",
1330
+ "richEditor.inlineCode": "\u884C\u5185\u4EE3\u7801",
1331
+ "richEditor.textColor": "\u6587\u5B57\u989C\u8272",
1332
+ "richEditor.bgColor": "\u80CC\u666F\u8272",
1333
+ "richEditor.alignLeft": "\u5DE6\u5BF9\u9F50",
1334
+ "richEditor.alignCenter": "\u5C45\u4E2D",
1335
+ "richEditor.alignRight": "\u53F3\u5BF9\u9F50",
1336
+ "richEditor.alignJustify": "\u4E24\u7AEF\u5BF9\u9F50",
1337
+ "richEditor.outdent": "\u51CF\u5C11\u7F29\u8FDB",
1338
+ "richEditor.indent": "\u589E\u52A0\u7F29\u8FDB",
1339
+ "richEditor.unlink": "\u53D6\u6D88\u94FE\u63A5",
1340
+ "richEditor.image": "\u63D2\u5165\u56FE\u7247",
1341
+ "richEditor.singleImage": "\u63D2\u5165\u5355\u56FE",
1342
+ "richEditor.multiImage": "\u63D2\u5165\u591A\u56FE",
1343
+ "richEditor.mediaLibrary": "\u4ECE\u7D20\u6750\u5E93\u9009\u62E9",
1344
+ "richEditor.hr": "\u5206\u5272\u7EBF",
1345
+ "richEditor.fullscreen": "\u5168\u5C4F",
1346
+ "richEditor.exitFullscreen": "\u9000\u51FA\u5168\u5C4F",
1347
+ "richEditor.wordCount": "\u5171 {count} \u5B57",
1348
+ "richEditor.uploading": "\u6B63\u5728\u4E0A\u4F20 {count} \u5F20\u56FE\u7247\u2026",
1349
+ "richEditor.uploadFailed": "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25",
1350
+ "richEditor.imageTypeError": "\u53EA\u80FD\u4E0A\u4F20\u56FE\u7247\u6587\u4EF6",
1351
+ "richEditor.imageSizeError": "\u56FE\u7247\u4E0D\u80FD\u8D85\u8FC7 {size}MB",
1352
+ "richEditor.imageUrlInvalid": "\u56FE\u7247\u5730\u5740\u4E0D\u5408\u6CD5\uFF0C\u5DF2\u62D2\u7EDD\u63D2\u5165",
1353
+ "richEditor.linkInvalid": "\u94FE\u63A5\u53EA\u652F\u6301 http(s)\u3001mailto\u3001tel \u6216\u7AD9\u5185\u76F8\u5BF9\u5730\u5740",
1310
1354
  "approval.start": "\u53D1\u8D77",
1311
1355
  "approval.cc": "\u6284\u9001",
1312
1356
  "approval.approved": "\u5DF2\u901A\u8FC7",
@@ -1515,7 +1559,6 @@ var en_US_default = {
1515
1559
  "richEditor.italic": "Italic",
1516
1560
  "richEditor.underline": "Underline",
1517
1561
  "richEditor.strike": "Strikethrough",
1518
- "richEditor.heading": "Heading 2",
1519
1562
  "richEditor.paragraph": "Paragraph",
1520
1563
  "richEditor.bulletList": "Bullet list",
1521
1564
  "richEditor.numberList": "Numbered list",
@@ -1526,6 +1569,39 @@ var en_US_default = {
1526
1569
  "richEditor.redo": "Redo",
1527
1570
  "richEditor.source": "Source",
1528
1571
  "richEditor.visual": "Visual",
1572
+ "richEditor.toolbar": "Formatting toolbar",
1573
+ "richEditor.block": "Paragraph style",
1574
+ "richEditor.heading1": "Heading 1",
1575
+ "richEditor.heading2": "Heading 2",
1576
+ "richEditor.heading3": "Heading 3",
1577
+ "richEditor.quote": "Quote",
1578
+ "richEditor.codeBlock": "Code block",
1579
+ "richEditor.fontSize": "Font size",
1580
+ "richEditor.defaultSize": "Default",
1581
+ "richEditor.inlineCode": "Inline code",
1582
+ "richEditor.textColor": "Text color",
1583
+ "richEditor.bgColor": "Background color",
1584
+ "richEditor.alignLeft": "Align left",
1585
+ "richEditor.alignCenter": "Align center",
1586
+ "richEditor.alignRight": "Align right",
1587
+ "richEditor.alignJustify": "Justify",
1588
+ "richEditor.outdent": "Decrease indent",
1589
+ "richEditor.indent": "Increase indent",
1590
+ "richEditor.unlink": "Remove link",
1591
+ "richEditor.image": "Insert image",
1592
+ "richEditor.singleImage": "Insert single image",
1593
+ "richEditor.multiImage": "Insert multiple images",
1594
+ "richEditor.mediaLibrary": "Choose from media library",
1595
+ "richEditor.hr": "Divider",
1596
+ "richEditor.fullscreen": "Fullscreen",
1597
+ "richEditor.exitFullscreen": "Exit fullscreen",
1598
+ "richEditor.wordCount": "{count} characters",
1599
+ "richEditor.uploading": "Uploading {count} image(s)\u2026",
1600
+ "richEditor.uploadFailed": "Image upload failed",
1601
+ "richEditor.imageTypeError": "Only image files are allowed",
1602
+ "richEditor.imageSizeError": "Image must be smaller than {size}MB",
1603
+ "richEditor.imageUrlInvalid": "Invalid image URL, insertion rejected",
1604
+ "richEditor.linkInvalid": "Only http(s), mailto, tel or relative URLs are allowed",
1529
1605
  "approval.start": "Started",
1530
1606
  "approval.cc": "CC",
1531
1607
  "approval.approved": "Approved",
@@ -9344,7 +9420,199 @@ var tree_select_default = {
9344
9420
  };
9345
9421
 
9346
9422
  // src/components/rich-editor/index.js
9347
- import { ref as ref27, watch as watch19, onMounted as onMounted15, onBeforeUnmount as onBeforeUnmount23 } from "vue";
9423
+ import { ref as ref28, computed as computed17, watch as watch20, onMounted as onMounted15, onBeforeUnmount as onBeforeUnmount23 } from "vue";
9424
+
9425
+ // src/components/media-picker/index.js
9426
+ import { computed as computed16, ref as ref27, watch as watch19 } from "vue";
9427
+ var media_picker_default = {
9428
+ name: "DjmMediaPicker",
9429
+ components: { DjmModal: modal_default, DjmPagination: pagination_default, DjmIcon: icon_default },
9430
+ props: {
9431
+ modelValue: { type: [Array, String, Number], default: "" },
9432
+ items: { type: Array, default: () => [] },
9433
+ multiple: { type: Boolean, default: false },
9434
+ // 最多选几个,0 表示不限
9435
+ limit: { type: Number, default: 0 },
9436
+ // image 只显示图片,file 只显示非图片,空串不过滤
9437
+ accept: { type: String, default: "" },
9438
+ title: { type: String, default: "" },
9439
+ triggerText: { type: String, default: "" },
9440
+ pageSize: { type: Number, default: 12 },
9441
+ disabled: { type: Boolean, default: false }
9442
+ },
9443
+ emits: ["update:modelValue", "change"],
9444
+ setup(props, { emit }) {
9445
+ const open = ref27(false);
9446
+ const keyword = ref27("");
9447
+ const page = ref27(1);
9448
+ const temp = ref27([]);
9449
+ const selectedIds = computed16(() => {
9450
+ if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
9451
+ if (props.modelValue === "" || props.modelValue === null || props.modelValue === void 0) return [];
9452
+ return [props.modelValue];
9453
+ });
9454
+ function isImage(item) {
9455
+ if (item.type) return item.type === "image";
9456
+ return /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(item.url || "");
9457
+ }
9458
+ const filtered = computed16(() => {
9459
+ const key = keyword.value.trim().toLowerCase();
9460
+ return props.items.filter((item) => {
9461
+ if (props.accept === "image" && !isImage(item)) return false;
9462
+ if (props.accept === "file" && isImage(item)) return false;
9463
+ if (key && !String(item.name || "").toLowerCase().includes(key)) return false;
9464
+ return true;
9465
+ });
9466
+ });
9467
+ const paged = computed16(() => {
9468
+ const start = (page.value - 1) * props.pageSize;
9469
+ return filtered.value.slice(start, start + props.pageSize);
9470
+ });
9471
+ watch19(keyword, () => {
9472
+ page.value = 1;
9473
+ });
9474
+ watch19(
9475
+ () => filtered.value.length,
9476
+ () => {
9477
+ const max = Math.max(1, Math.ceil(filtered.value.length / props.pageSize));
9478
+ if (page.value > max) page.value = max;
9479
+ }
9480
+ );
9481
+ function isChecked(id) {
9482
+ return temp.value.includes(id);
9483
+ }
9484
+ function atLimit() {
9485
+ return props.limit > 0 && temp.value.length >= props.limit;
9486
+ }
9487
+ function toggle(item) {
9488
+ if (props.multiple) {
9489
+ if (isChecked(item.id)) {
9490
+ temp.value = temp.value.filter((id) => id !== item.id);
9491
+ return;
9492
+ }
9493
+ if (atLimit()) return;
9494
+ temp.value = [...temp.value, item.id];
9495
+ return;
9496
+ }
9497
+ temp.value = [item.id];
9498
+ }
9499
+ function openPanel() {
9500
+ if (props.disabled) return;
9501
+ temp.value = [...selectedIds.value];
9502
+ keyword.value = "";
9503
+ page.value = 1;
9504
+ open.value = true;
9505
+ }
9506
+ function commit() {
9507
+ const next = props.multiple ? [...temp.value] : temp.value[0] !== void 0 ? temp.value[0] : "";
9508
+ emit("update:modelValue", next);
9509
+ emit("change", next);
9510
+ open.value = false;
9511
+ }
9512
+ function cancel() {
9513
+ temp.value = [...selectedIds.value];
9514
+ open.value = false;
9515
+ }
9516
+ return {
9517
+ open,
9518
+ keyword,
9519
+ page,
9520
+ temp,
9521
+ selectedIds,
9522
+ filtered,
9523
+ paged,
9524
+ isImage,
9525
+ isChecked,
9526
+ atLimit,
9527
+ toggle,
9528
+ openPanel,
9529
+ commit,
9530
+ cancel,
9531
+ t
9532
+ };
9533
+ },
9534
+ template: `
9535
+ <span class="djm-media-picker">
9536
+ <slot name="trigger" :open="openPanel" :count="selectedIds.length">
9537
+ <button
9538
+ class="djm-btn"
9539
+ type="button"
9540
+ :disabled="disabled"
9541
+ @click="openPanel"
9542
+ >{{ triggerText || t('mediaPicker.select') }}<span v-if="selectedIds.length" class="djm-media-picker-count">{{ selectedIds.length }}</span></button>
9543
+ </slot>
9544
+
9545
+ <DjmModal
9546
+ :model-value="open"
9547
+ :title="title || t('mediaPicker.title')"
9548
+ width="760px"
9549
+ @update:model-value="cancel"
9550
+ >
9551
+ <div class="djm-media-toolbar">
9552
+ <label class="djm-media-search">
9553
+ <DjmIcon name="search" :size="13" />
9554
+ <input
9555
+ v-model="keyword"
9556
+ class="djm-media-search-input"
9557
+ type="text"
9558
+ :placeholder="t('mediaPicker.search')"
9559
+ :aria-label="t('mediaPicker.search')"
9560
+ />
9561
+ </label>
9562
+ <span class="djm-media-toolbar-slot">
9563
+ <slot name="toolbar"></slot>
9564
+ </span>
9565
+ </div>
9566
+
9567
+ <div class="djm-media-grid" role="listbox" :aria-multiselectable="multiple ? 'true' : 'false'">
9568
+ <div
9569
+ v-for="item in paged"
9570
+ :key="item.id"
9571
+ class="djm-media-item"
9572
+ :class="{
9573
+ 'djm-is-checked': isChecked(item.id),
9574
+ 'djm-is-disabled': multiple && atLimit() && !isChecked(item.id)
9575
+ }"
9576
+ role="option"
9577
+ tabindex="0"
9578
+ :aria-selected="String(isChecked(item.id))"
9579
+ @click="toggle(item)"
9580
+ @keydown.enter.prevent="toggle(item)"
9581
+ @keydown.space.prevent="toggle(item)"
9582
+ >
9583
+ <div class="djm-media-thumb">
9584
+ <img v-if="isImage(item)" :src="item.url" :alt="item.name" loading="lazy" />
9585
+ <DjmIcon v-else name="folder" :size="26" />
9586
+ <span v-if="isChecked(item.id)" class="djm-media-check">
9587
+ <DjmIcon name="check" :size="11" />
9588
+ </span>
9589
+ </div>
9590
+ <div class="djm-media-name" :title="item.name">{{ item.name }}</div>
9591
+ </div>
9592
+ <div v-if="!paged.length" class="djm-media-empty">{{ t('mediaPicker.empty') }}</div>
9593
+ </div>
9594
+
9595
+ <div class="djm-media-foot">
9596
+ <span class="djm-media-total">
9597
+ {{ t('mediaPicker.total', { total: filtered.length }) }}
9598
+ <em v-if="multiple && limit > 0">{{ t('mediaPicker.limit', { limit }) }}</em>
9599
+ </span>
9600
+ <DjmPagination
9601
+ v-if="filtered.length > pageSize"
9602
+ v-model:page="page"
9603
+ :page-size="pageSize"
9604
+ :total="filtered.length"
9605
+ />
9606
+ </div>
9607
+
9608
+ <template #footer>
9609
+ <button class="djm-btn" type="button" @click="cancel">{{ t('common.cancel') }}</button>
9610
+ <button class="djm-btn djm-btn-primary" type="button" @click="commit">{{ t('common.confirm') }}</button>
9611
+ </template>
9612
+ </DjmModal>
9613
+ </span>
9614
+ `
9615
+ };
9348
9616
 
9349
9617
  // src/utils/sanitize-html.js
9350
9618
  var TAGS = /* @__PURE__ */ new Set([
@@ -9384,7 +9652,18 @@ var TAGS = /* @__PURE__ */ new Set([
9384
9652
  "th",
9385
9653
  "td"
9386
9654
  ]);
9387
- var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "title"]);
9655
+ var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "title", "style"]);
9656
+ var STYLE_PROPS = /* @__PURE__ */ new Set([
9657
+ "color",
9658
+ "background-color",
9659
+ "font-size",
9660
+ "font-weight",
9661
+ "font-style",
9662
+ "text-decoration",
9663
+ "text-align",
9664
+ "text-indent",
9665
+ "line-height"
9666
+ ]);
9388
9667
  var TAG_ATTRS = {
9389
9668
  a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
9390
9669
  img: /* @__PURE__ */ new Set(["src", "alt", "width", "height"]),
@@ -9392,11 +9671,29 @@ var TAG_ATTRS = {
9392
9671
  th: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope"]),
9393
9672
  td: /* @__PURE__ */ new Set(["colspan", "rowspan"])
9394
9673
  };
9674
+ function cleanStyle(value) {
9675
+ return String(value || "").split(";").map((item) => {
9676
+ const index = item.indexOf(":");
9677
+ if (index === -1) return "";
9678
+ const name = item.slice(0, index).trim().toLowerCase();
9679
+ const val = item.slice(index + 1).trim();
9680
+ if (!STYLE_PROPS.has(name)) return "";
9681
+ if (/url\s*\(|expression\s*\(|javascript:/i.test(val)) return "";
9682
+ return name + ": " + val;
9683
+ }).filter(Boolean).join("; ");
9684
+ }
9685
+ var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
9395
9686
  function safeUrl(value, tag) {
9396
- const url = String(value || "").replace(/[\u0000-\u0020]+/g, "").toLowerCase();
9397
- if (url.startsWith("javascript:") || url.startsWith("vbscript:")) return false;
9398
- if (url.startsWith("data:")) return tag === "img" && url.startsWith("data:image/");
9399
- return true;
9687
+ const url = String(value || "").replace(/[\u0000-\u0020]+/g, "");
9688
+ const scheme = url.match(/^([a-z][a-z0-9+.-]*):/i);
9689
+ if (!scheme) return true;
9690
+ const name = scheme[1].toLowerCase();
9691
+ if (SAFE_SCHEMES.has(name)) return true;
9692
+ if (tag === "img") {
9693
+ if (name === "blob") return true;
9694
+ if (name === "data" && /^data:image\//i.test(url)) return true;
9695
+ }
9696
+ return false;
9400
9697
  }
9401
9698
  function cleanNode(node) {
9402
9699
  const children = [...node.childNodes];
@@ -9419,6 +9716,12 @@ function cleanNode(node) {
9419
9716
  child.removeAttribute(attr.name);
9420
9717
  return;
9421
9718
  }
9719
+ if (name === "style") {
9720
+ const style = cleanStyle(attr.value);
9721
+ if (style) child.setAttribute("style", style);
9722
+ else child.removeAttribute("style");
9723
+ return;
9724
+ }
9422
9725
  if ((name === "href" || name === "src") && !safeUrl(attr.value, tag)) {
9423
9726
  child.removeAttribute(attr.name);
9424
9727
  }
@@ -9439,79 +9742,475 @@ function sanitizeHtml(html) {
9439
9742
  }
9440
9743
 
9441
9744
  // src/components/rich-editor/index.js
9745
+ var BLOCKS = [
9746
+ { value: "p", label: "paragraph" },
9747
+ { value: "h1", label: "heading1" },
9748
+ { value: "h2", label: "heading2" },
9749
+ { value: "h3", label: "heading3" },
9750
+ { value: "blockquote", label: "quote" },
9751
+ { value: "pre", label: "codeBlock" }
9752
+ ];
9753
+ var SIZES = [
9754
+ { value: "", label: "defaultSize" },
9755
+ { value: "12px", label: "12px" },
9756
+ { value: "14px", label: "14px" },
9757
+ { value: "16px", label: "16px" },
9758
+ { value: "18px", label: "18px" },
9759
+ { value: "24px", label: "24px" }
9760
+ ];
9761
+ var COLORS = ["#1f2d3d", "#5a6b7d", "#2b86d9", "#2fa36b", "#d97706", "#dc2626", "#7c3aed", "#ffffff"];
9762
+ var TOOLBAR_PRESETS = {
9763
+ simple: ["history", "inline", "list", "insert"],
9764
+ basic: ["history", "format", "inline", "align", "list", "insert", "view"],
9765
+ full: ["history", "format", "inline", "color", "align", "list", "insert", "view"]
9766
+ };
9767
+ function isSafeImageUrl(url) {
9768
+ const value = String(url || "").replace(/[\u0000-\u0020]+/g, "");
9769
+ if (!value) return false;
9770
+ const scheme = value.match(/^([a-z][a-z0-9+.-]*):/i);
9771
+ if (!scheme) return true;
9772
+ const name = scheme[1].toLowerCase();
9773
+ if (name === "http" || name === "https" || name === "blob") return true;
9774
+ return name === "data" && /^data:image\//i.test(value);
9775
+ }
9776
+ function isSafeLink(url) {
9777
+ const value = String(url || "").trim();
9778
+ if (!value) return false;
9779
+ const scheme = value.replace(/[\u0000-\u0020]+/g, "").match(/^([a-z][a-z0-9+.-]*):/i);
9780
+ if (!scheme) return true;
9781
+ return ["http", "https", "mailto", "tel"].indexOf(scheme[1].toLowerCase()) > -1;
9782
+ }
9442
9783
  var rich_editor_default = {
9443
9784
  name: "DjmRichEditor",
9444
- components: { Icon: icon_default },
9785
+ components: { Icon: icon_default, MediaPicker: media_picker_default },
9445
9786
  props: {
9446
9787
  modelValue: { type: String, default: "" },
9447
9788
  placeholder: { type: String, default: "" },
9448
9789
  minHeight: { type: Number, default: 200 },
9449
- // 开启后对外输出的 HTML 会走白名单清洗
9450
- sanitize: { type: Boolean, default: false },
9451
- disabled: { type: Boolean, default: false }
9790
+ // 默认开启:对外输出的 HTML 走白名单清洗,内容里不会带出脚本
9791
+ sanitize: { type: Boolean, default: true },
9792
+ disabled: { type: Boolean, default: false },
9793
+ // 工具条:simple / basic / full,或传分组数组(history / format / inline / color / align / list / insert / view)
9794
+ toolbar: { type: [String, Array], default: "full" },
9795
+ // 自定义上传:接收 File,返回图片地址(或 { url }),不传则转成 dataURL 本地预览
9796
+ uploadImage: { type: Function, default: null },
9797
+ // 已上传图片,提供后工具栏出现「从素材库选择」:字符串或 { url, name }
9798
+ media: { type: Array, default: () => [] },
9799
+ accept: { type: String, default: "image/*" },
9800
+ // 单张图片大小上限(MB)
9801
+ maxImageSize: { type: Number, default: 5 },
9802
+ // 是否允许一次选多张
9803
+ multiple: { type: Boolean, default: true },
9804
+ showCount: { type: Boolean, default: true }
9452
9805
  },
9453
- emits: ["update:modelValue"],
9806
+ emits: ["update:modelValue", "upload-error"],
9454
9807
  setup(props, { emit }) {
9455
- const editor = ref27(null);
9456
- const sourceMode = ref27(false);
9457
- const source = ref27(props.modelValue);
9458
- const formats = ref27({ bold: false, italic: false, underline: false, strike: false });
9808
+ const editor = ref28(null);
9809
+ const fileInput = ref28(null);
9810
+ const sourceMode = ref28(false);
9811
+ const source = ref28(props.modelValue);
9812
+ const fullscreen = ref28(false);
9813
+ const panel = ref28("");
9814
+ const uploadCount = ref28(0);
9815
+ const textColor = ref28(COLORS[0]);
9816
+ const bgColor = ref28("#fdf3c4");
9817
+ const fontSize = ref28("");
9818
+ const count2 = ref28(0);
9819
+ const formats = ref28({
9820
+ bold: false,
9821
+ italic: false,
9822
+ underline: false,
9823
+ strike: false,
9824
+ code: false,
9825
+ block: "p",
9826
+ align: "",
9827
+ list: ""
9828
+ });
9829
+ let savedRange = null;
9830
+ const ready = computed17(() => !props.disabled && !sourceMode.value);
9831
+ const toolbarGroups = computed17(() => {
9832
+ const list = Array.isArray(props.toolbar) ? props.toolbar : TOOLBAR_PRESETS[props.toolbar] || TOOLBAR_PRESETS.full;
9833
+ return new Set(list);
9834
+ });
9835
+ function has(group) {
9836
+ return toolbarGroups.value.has(group);
9837
+ }
9838
+ const mediaLibrary = computed17(
9839
+ () => (props.media || []).map((item, index) => {
9840
+ if (typeof item === "string") {
9841
+ return { id: "media-" + index, url: item, name: "", type: "image" };
9842
+ }
9843
+ return {
9844
+ id: item && item.id !== void 0 ? String(item.id) : "media-" + index,
9845
+ url: item && (item.url || item.src) || "",
9846
+ name: item && (item.name || item.alt) || "",
9847
+ type: "image"
9848
+ };
9849
+ }).filter((item) => !!item.url)
9850
+ );
9459
9851
  function clean(html) {
9460
9852
  return props.sanitize ? sanitizeHtml(html) : html;
9461
9853
  }
9854
+ function plainText(html) {
9855
+ return String(html || "").replace(/<[^>]*>/g, "").replace(/&nbsp;/g, " ").replace(/&[a-z]+;/gi, " ");
9856
+ }
9462
9857
  function syncFromDom() {
9463
9858
  if (!editor.value) return;
9464
- emit("update:modelValue", clean(editor.value.innerHTML));
9859
+ const html = clean(editor.value.innerHTML);
9860
+ count2.value = plainText(html).trim().length;
9861
+ emit("update:modelValue", html);
9465
9862
  }
9466
- function onPaste(event) {
9467
- const data = event.clipboardData;
9468
- const html = data && data.getData("text/html");
9469
- if (!html) return;
9470
- event.preventDefault();
9471
- document.execCommand("insertHTML", false, sanitizeHtml(html));
9863
+ function currentRange() {
9864
+ const selection = window.getSelection();
9865
+ if (!selection || !selection.rangeCount || !editor.value) return null;
9866
+ const range = selection.getRangeAt(0);
9867
+ return editor.value.contains(range.commonAncestorContainer) ? range : null;
9868
+ }
9869
+ function rememberRange() {
9870
+ const range = currentRange();
9871
+ savedRange = range ? range.cloneRange() : null;
9872
+ }
9873
+ function focusAt(range) {
9874
+ if (!editor.value) return;
9875
+ editor.value.focus();
9876
+ const selection = window.getSelection();
9877
+ selection.removeAllRanges();
9878
+ if (range) {
9879
+ selection.addRange(range);
9880
+ return;
9881
+ }
9882
+ const end = document.createRange();
9883
+ end.selectNodeContents(editor.value);
9884
+ end.collapse(false);
9885
+ selection.addRange(end);
9886
+ }
9887
+ function insertNodeAt(range, node) {
9888
+ if (!editor.value) return;
9889
+ focusAt(range);
9890
+ const selection = window.getSelection();
9891
+ if (selection.rangeCount) selection.getRangeAt(0).insertNode(node);
9892
+ else editor.value.appendChild(node);
9472
9893
  syncFromDom();
9894
+ syncFormats();
9895
+ }
9896
+ function ensureRange() {
9897
+ const range = currentRange();
9898
+ if (range) return range;
9899
+ if (!savedRange) return null;
9900
+ focusAt(savedRange);
9901
+ return currentRange();
9473
9902
  }
9474
9903
  function exec(command, value = null) {
9475
- if (props.disabled || sourceMode.value) return;
9904
+ if (!ready.value) return;
9905
+ if (typeof document.execCommand !== "function") return;
9906
+ ensureRange();
9907
+ document.execCommand("styleWithCSS", false, true);
9476
9908
  document.execCommand(command, false, value);
9477
9909
  if (editor.value) editor.value.focus();
9478
9910
  syncFromDom();
9479
9911
  syncFormats();
9480
9912
  }
9481
- function formatBlock(tag) {
9482
- exec("formatBlock", tag);
9913
+ function setBlock(value) {
9914
+ panel.value = "";
9915
+ exec("formatBlock", "<" + value + ">");
9916
+ }
9917
+ function setFontSize(size) {
9918
+ panel.value = "";
9919
+ if (!ready.value || !editor.value) return;
9920
+ fontSize.value = size;
9921
+ const range = ensureRange();
9922
+ if (!range || range.collapsed) return;
9923
+ if (!size) {
9924
+ const walker = document.createTreeWalker(editor.value, NodeFilter.SHOW_ELEMENT);
9925
+ const targets = [];
9926
+ while (walker.nextNode()) {
9927
+ const el = walker.currentNode;
9928
+ if (el.style && el.style.fontSize && range.intersectsNode(el)) targets.push(el);
9929
+ }
9930
+ targets.forEach((el) => {
9931
+ el.style.fontSize = "";
9932
+ });
9933
+ syncFromDom();
9934
+ return;
9935
+ }
9936
+ const span = document.createElement("span");
9937
+ span.style.fontSize = size;
9938
+ span.appendChild(range.extractContents());
9939
+ range.insertNode(span);
9940
+ const selection = window.getSelection();
9941
+ const after = document.createRange();
9942
+ after.setStartAfter(span);
9943
+ after.collapse(true);
9944
+ selection.removeAllRanges();
9945
+ selection.addRange(after);
9946
+ syncFromDom();
9947
+ syncFormats();
9948
+ }
9949
+ function applyColor(kind, color) {
9950
+ panel.value = "";
9951
+ if (kind === "text") textColor.value = color;
9952
+ else bgColor.value = color;
9953
+ if (!ready.value) return;
9954
+ if (typeof document.execCommand !== "function") return;
9955
+ document.execCommand("styleWithCSS", false, true);
9956
+ const command = kind === "text" ? "foreColor" : "hiliteColor";
9957
+ if (!document.execCommand(command, false, color) && kind === "bg") {
9958
+ document.execCommand("backColor", false, color);
9959
+ }
9960
+ if (editor.value) editor.value.focus();
9961
+ syncFromDom();
9962
+ syncFormats();
9963
+ }
9964
+ function togglePanel(name) {
9965
+ if (!ready.value) return;
9966
+ panel.value = panel.value === name ? "" : name;
9967
+ }
9968
+ function toggleInlineCode() {
9969
+ if (!ready.value || !editor.value) return;
9970
+ const range = currentRange();
9971
+ if (!range) return;
9972
+ const anchor = range.commonAncestorContainer;
9973
+ const node = anchor.nodeType === 1 ? anchor : anchor.parentElement;
9974
+ const code = node && node.closest ? node.closest("code") : null;
9975
+ if (code && editor.value.contains(code)) {
9976
+ const parent = code.parentNode;
9977
+ while (code.firstChild) parent.insertBefore(code.firstChild, code);
9978
+ parent.removeChild(code);
9979
+ syncFromDom();
9980
+ return;
9981
+ }
9982
+ if (range.collapsed) return;
9983
+ const wrapper = document.createElement("code");
9984
+ wrapper.appendChild(range.extractContents());
9985
+ range.insertNode(wrapper);
9986
+ const selection = window.getSelection();
9987
+ const after = document.createRange();
9988
+ after.setStartAfter(wrapper);
9989
+ after.collapse(true);
9990
+ selection.removeAllRanges();
9991
+ selection.addRange(after);
9992
+ syncFromDom();
9993
+ syncFormats();
9483
9994
  }
9484
9995
  function addLink() {
9996
+ if (!ready.value) return;
9485
9997
  const url = window.prompt(t("richEditor.linkPrompt"), "https://");
9486
- if (url) exec("createLink", url);
9998
+ if (!url) return;
9999
+ if (!isSafeLink(url)) {
10000
+ window.alert(t("richEditor.linkInvalid"));
10001
+ return;
10002
+ }
10003
+ const range = currentRange();
10004
+ if (range && range.collapsed) {
10005
+ const link = document.createElement("a");
10006
+ link.href = url;
10007
+ link.target = "_blank";
10008
+ link.rel = "noopener noreferrer";
10009
+ link.textContent = url;
10010
+ insertNodeAt(range, link);
10011
+ return;
10012
+ }
10013
+ exec("createLink", url);
10014
+ if (editor.value) {
10015
+ editor.value.querySelectorAll("a[href]").forEach((link) => {
10016
+ if (!link.getAttribute("target")) link.setAttribute("target", "_blank");
10017
+ link.setAttribute("rel", "noopener noreferrer");
10018
+ });
10019
+ }
10020
+ syncFromDom();
10021
+ }
10022
+ function readAsDataUrl(file) {
10023
+ return new Promise((resolve, reject) => {
10024
+ const reader = new FileReader();
10025
+ reader.onload = () => resolve(reader.result);
10026
+ reader.onerror = () => reject(reader.error || new Error("read failed"));
10027
+ reader.readAsDataURL(file);
10028
+ });
10029
+ }
10030
+ async function uploadOne(file) {
10031
+ if (props.uploadImage) {
10032
+ const result = await props.uploadImage(file);
10033
+ const url = typeof result === "string" ? result : result && result.url;
10034
+ if (!url) throw new Error(t("richEditor.imageUrlInvalid"));
10035
+ if (!isSafeImageUrl(url)) throw new Error(t("richEditor.imageUrlInvalid"));
10036
+ return String(url).trim();
10037
+ }
10038
+ return readAsDataUrl(file);
10039
+ }
10040
+ function isImage(file) {
10041
+ return file && /^image\//.test(file.type);
10042
+ }
10043
+ function pickImage(allowMultiple) {
10044
+ if (!ready.value || !fileInput.value) return;
10045
+ rememberRange();
10046
+ fileInput.value.multiple = !!allowMultiple && props.multiple;
10047
+ fileInput.value.click();
10048
+ }
10049
+ function insertMedia(ids) {
10050
+ if (!ready.value) return;
10051
+ const picked = (Array.isArray(ids) ? ids : [ids]).map((id) => mediaLibrary.value.find((item) => item.id === String(id))).filter(Boolean);
10052
+ const safe = picked.filter((item) => {
10053
+ if (isSafeImageUrl(item.url)) return true;
10054
+ emit("upload-error", { file: null, message: t("richEditor.imageUrlInvalid") });
10055
+ return false;
10056
+ });
10057
+ if (!safe.length) return;
10058
+ focusAt(savedRange);
10059
+ const fragment = document.createDocumentFragment();
10060
+ safe.forEach((item) => {
10061
+ const img = document.createElement("img");
10062
+ img.src = item.url;
10063
+ img.alt = item.name || "image";
10064
+ img.className = "djm-rich-editor-img";
10065
+ fragment.appendChild(img);
10066
+ });
10067
+ const selection = window.getSelection();
10068
+ if (selection.rangeCount) selection.getRangeAt(0).insertNode(fragment);
10069
+ else editor.value.appendChild(fragment);
10070
+ syncFromDom();
10071
+ syncFormats();
10072
+ }
10073
+ function insertImages(files) {
10074
+ if (!files.length || !editor.value) return;
10075
+ uploadCount.value = files.length;
10076
+ const holder = document.createElement("span");
10077
+ holder.className = "djm-rich-editor-uploading";
10078
+ holder.textContent = t("richEditor.uploading", { count: files.length });
10079
+ const anchor = savedRange && editor.value.contains(savedRange.commonAncestorContainer) ? savedRange : null;
10080
+ focusAt(anchor);
10081
+ const selection = window.getSelection();
10082
+ if (selection.rangeCount) selection.getRangeAt(0).insertNode(holder);
10083
+ else editor.value.appendChild(holder);
10084
+ Promise.all(files.map(async (file) => {
10085
+ if (!isImage(file)) return { file, error: t("richEditor.imageTypeError") };
10086
+ if (props.maxImageSize > 0 && file.size > props.maxImageSize * 1024 * 1024) {
10087
+ return { file, error: t("richEditor.imageSizeError", { size: props.maxImageSize }) };
10088
+ }
10089
+ try {
10090
+ return { file, url: await uploadOne(file) };
10091
+ } catch (error) {
10092
+ return { file, error: error && error.message || t("richEditor.uploadFailed") };
10093
+ }
10094
+ })).then((results) => {
10095
+ uploadCount.value = 0;
10096
+ const done = results.filter((item) => item.url);
10097
+ const failed = results.filter((item) => item.error);
10098
+ const alive = editor.value && editor.value.contains(holder);
10099
+ if (done.length && editor.value) {
10100
+ done.forEach((item) => {
10101
+ const img = document.createElement("img");
10102
+ img.src = item.url;
10103
+ img.alt = item.file.name || "image";
10104
+ img.className = "djm-rich-editor-img";
10105
+ if (alive) holder.parentNode.insertBefore(img, holder);
10106
+ else editor.value.appendChild(img);
10107
+ });
10108
+ }
10109
+ if (alive) holder.remove();
10110
+ failed.forEach((item) => emit("upload-error", { file: item.file, message: item.error }));
10111
+ syncFromDom();
10112
+ });
10113
+ }
10114
+ function onFilesChange(event) {
10115
+ const files = Array.from(event.target.files || []);
10116
+ event.target.value = "";
10117
+ insertImages(files);
10118
+ }
10119
+ function onPaste(event) {
10120
+ const data = event.clipboardData;
10121
+ if (!data) return;
10122
+ const images = Array.from(data.files || []).filter(isImage);
10123
+ if (images.length) {
10124
+ event.preventDefault();
10125
+ insertImages(images);
10126
+ return;
10127
+ }
10128
+ const html = data.getData("text/html");
10129
+ if (!html) return;
10130
+ event.preventDefault();
10131
+ document.execCommand("insertHTML", false, sanitizeHtml(html));
10132
+ syncFromDom();
10133
+ }
10134
+ function onDragOver(event) {
10135
+ if (!ready.value) return;
10136
+ if (event.dataTransfer && Array.from(event.dataTransfer.types || []).includes("Files")) {
10137
+ event.preventDefault();
10138
+ }
10139
+ }
10140
+ function onDrop(event) {
10141
+ if (!ready.value) return;
10142
+ const files = Array.from(event.dataTransfer && event.dataTransfer.files || []).filter(isImage);
10143
+ if (!files.length) return;
10144
+ event.preventDefault();
10145
+ rememberRange();
10146
+ insertImages(files);
10147
+ }
10148
+ function closestTag(node, tag) {
10149
+ const el = node && (node.nodeType === 1 ? node : node.parentElement);
10150
+ return el && el.closest ? el.closest(tag) : null;
10151
+ }
10152
+ function detectBlock() {
10153
+ const selection = window.getSelection();
10154
+ if (!selection || !selection.anchorNode) return "p";
10155
+ let node = selection.anchorNode;
10156
+ if (node.nodeType !== 1) node = node.parentElement;
10157
+ while (node && node !== editor.value) {
10158
+ const tag = node.tagName.toLowerCase();
10159
+ if (["p", "h1", "h2", "h3", "blockquote", "pre"].includes(tag)) return tag;
10160
+ node = node.parentElement;
10161
+ }
10162
+ return "p";
9487
10163
  }
9488
10164
  function syncFormats() {
9489
- if (props.disabled || sourceMode.value) return;
9490
- if (!editor.value || !editor.value.contains(document.activeElement)) return;
10165
+ if (props.disabled || sourceMode.value || !editor.value) return;
10166
+ if (!editor.value.contains(document.activeElement)) return;
10167
+ const selection = window.getSelection();
9491
10168
  formats.value = {
9492
10169
  bold: document.queryCommandState("bold"),
9493
10170
  italic: document.queryCommandState("italic"),
9494
10171
  underline: document.queryCommandState("underline"),
9495
- strike: document.queryCommandState("strikeThrough")
10172
+ strike: document.queryCommandState("strikeThrough"),
10173
+ code: !!closestTag(selection.anchorNode, "code"),
10174
+ block: detectBlock(),
10175
+ align: document.queryCommandState("justifyCenter") ? "center" : document.queryCommandState("justifyRight") ? "right" : document.queryCommandState("justifyFull") ? "justify" : "left",
10176
+ list: document.queryCommandState("insertOrderedList") ? "ol" : document.queryCommandState("insertUnorderedList") ? "ul" : ""
9496
10177
  };
9497
10178
  }
9498
10179
  function onSourceInput() {
10180
+ count2.value = plainText(source.value).trim().length;
9499
10181
  emit("update:modelValue", clean(source.value));
9500
10182
  }
9501
10183
  function toggleSource() {
9502
10184
  if (props.disabled) return;
10185
+ panel.value = "";
9503
10186
  if (sourceMode.value) {
9504
10187
  emit("update:modelValue", clean(source.value));
9505
10188
  sourceMode.value = false;
9506
10189
  requestAnimationFrame(() => {
9507
10190
  if (editor.value) editor.value.innerHTML = clean(source.value);
10191
+ syncFromDom();
9508
10192
  });
9509
10193
  } else {
9510
10194
  source.value = editor.value ? editor.value.innerHTML : props.modelValue;
9511
10195
  sourceMode.value = true;
9512
10196
  }
9513
10197
  }
9514
- watch19(
10198
+ function toggleFullscreen() {
10199
+ if (props.disabled) return;
10200
+ fullscreen.value = !fullscreen.value;
10201
+ panel.value = "";
10202
+ }
10203
+ function onDocumentClick(event) {
10204
+ if (!panel.value) return;
10205
+ if (!(event.target.closest && event.target.closest(".djm-rich-editor-color"))) panel.value = "";
10206
+ }
10207
+ function onKeydown(event) {
10208
+ if (event.key === "Escape" && fullscreen.value) {
10209
+ fullscreen.value = false;
10210
+ panel.value = "";
10211
+ }
10212
+ }
10213
+ watch20(
9515
10214
  () => props.modelValue,
9516
10215
  (value) => {
9517
10216
  if (sourceMode.value) {
@@ -9520,40 +10219,117 @@ var rich_editor_default = {
9520
10219
  }
9521
10220
  const next = clean(value);
9522
10221
  if (editor.value && editor.value.innerHTML !== next) editor.value.innerHTML = next;
10222
+ count2.value = plainText(next).trim().length;
9523
10223
  }
9524
10224
  );
9525
10225
  onMounted15(() => {
9526
10226
  if (editor.value) editor.value.innerHTML = clean(props.modelValue);
10227
+ count2.value = plainText(props.modelValue).trim().length;
9527
10228
  document.addEventListener("selectionchange", syncFormats);
10229
+ document.addEventListener("click", onDocumentClick);
10230
+ document.addEventListener("keydown", onKeydown);
10231
+ });
10232
+ onBeforeUnmount23(() => {
10233
+ document.removeEventListener("selectionchange", syncFormats);
10234
+ document.removeEventListener("click", onDocumentClick);
10235
+ document.removeEventListener("keydown", onKeydown);
9528
10236
  });
9529
- onBeforeUnmount23(() => document.removeEventListener("selectionchange", syncFormats));
9530
10237
  return {
9531
10238
  editor,
10239
+ fileInput,
9532
10240
  sourceMode,
9533
10241
  source,
10242
+ fullscreen,
10243
+ panel,
10244
+ uploadCount,
10245
+ textColor,
10246
+ bgColor,
10247
+ fontSize,
10248
+ count: count2,
9534
10249
  formats,
10250
+ blocks: BLOCKS,
10251
+ sizes: SIZES,
10252
+ colors: COLORS,
10253
+ mediaLibrary,
10254
+ has,
9535
10255
  exec,
9536
- formatBlock,
10256
+ setBlock,
10257
+ setFontSize,
10258
+ applyColor,
10259
+ togglePanel,
10260
+ toggleInlineCode,
9537
10261
  addLink,
9538
- onSourceInput,
10262
+ pickImage,
10263
+ insertMedia,
10264
+ onFilesChange,
9539
10265
  onPaste,
10266
+ onDragOver,
10267
+ onDrop,
10268
+ rememberRange,
10269
+ onSourceInput,
9540
10270
  toggleSource,
10271
+ toggleFullscreen,
9541
10272
  syncFromDom,
9542
10273
  syncFormats,
9543
10274
  t
9544
10275
  };
9545
10276
  },
9546
10277
  template: `
9547
- <div class="djm-rich-editor" :class="{ 'djm-is-disabled': disabled }">
9548
- <div class="djm-rich-editor-toolbar">
9549
- <div class="djm-rich-editor-group">
10278
+ <div
10279
+ class="djm-rich-editor"
10280
+ :class="{ 'djm-is-disabled': disabled, 'djm-is-fullscreen': fullscreen }"
10281
+ >
10282
+ <div class="djm-rich-editor-toolbar" role="toolbar" :aria-label="t('richEditor.toolbar')" @mousedown="rememberRange">
10283
+ <div class="djm-rich-editor-group" v-if="has('history')">
10284
+ <button
10285
+ class="djm-rich-editor-btn"
10286
+ type="button"
10287
+ :title="t('richEditor.undo')"
10288
+ :aria-label="t('richEditor.undo')"
10289
+ :disabled="disabled || sourceMode"
10290
+ @mousedown.prevent
10291
+ @click="exec('undo')"
10292
+ ><Icon name="undo" :size="13" /></button>
10293
+ <button
10294
+ class="djm-rich-editor-btn"
10295
+ type="button"
10296
+ :title="t('richEditor.redo')"
10297
+ :aria-label="t('richEditor.redo')"
10298
+ :disabled="disabled || sourceMode"
10299
+ @mousedown.prevent
10300
+ @click="exec('redo')"
10301
+ ><Icon name="redo" :size="13" /></button>
10302
+ </div>
10303
+
10304
+ <div class="djm-rich-editor-group" v-if="has('format')">
10305
+ <select
10306
+ class="djm-rich-editor-select"
10307
+ :value="formats.block"
10308
+ :aria-label="t('richEditor.block')"
10309
+ :disabled="disabled || sourceMode"
10310
+ @change="setBlock($event.target.value)"
10311
+ >
10312
+ <option v-for="item in blocks" :key="item.value" :value="item.value">{{ t('richEditor.' + item.label) }}</option>
10313
+ </select>
10314
+ <select
10315
+ class="djm-rich-editor-select djm-rich-editor-select-size"
10316
+ :value="fontSize"
10317
+ :aria-label="t('richEditor.fontSize')"
10318
+ :disabled="disabled || sourceMode"
10319
+ @change="setFontSize($event.target.value)"
10320
+ >
10321
+ <option v-for="item in sizes" :key="item.value" :value="item.value">{{ t('richEditor.' + item.label) }}</option>
10322
+ </select>
10323
+ </div>
10324
+
10325
+ <div class="djm-rich-editor-group" v-if="has('inline')">
9550
10326
  <button
9551
10327
  class="djm-rich-editor-btn djm-rich-editor-btn-bold"
9552
10328
  type="button"
9553
10329
  :title="t('richEditor.bold')"
9554
10330
  :aria-label="t('richEditor.bold')"
9555
10331
  :aria-pressed="String(formats.bold)"
9556
- :disabled="disabled"
10332
+ :disabled="disabled || sourceMode"
9557
10333
  @mousedown.prevent
9558
10334
  @click="exec('bold')"
9559
10335
  >B</button>
@@ -9563,7 +10339,7 @@ var rich_editor_default = {
9563
10339
  :title="t('richEditor.italic')"
9564
10340
  :aria-label="t('richEditor.italic')"
9565
10341
  :aria-pressed="String(formats.italic)"
9566
- :disabled="disabled"
10342
+ :disabled="disabled || sourceMode"
9567
10343
  @mousedown.prevent
9568
10344
  @click="exec('italic')"
9569
10345
  >I</button>
@@ -9573,7 +10349,7 @@ var rich_editor_default = {
9573
10349
  :title="t('richEditor.underline')"
9574
10350
  :aria-label="t('richEditor.underline')"
9575
10351
  :aria-pressed="String(formats.underline)"
9576
- :disabled="disabled"
10352
+ :disabled="disabled || sourceMode"
9577
10353
  @mousedown.prevent
9578
10354
  @click="exec('underline')"
9579
10355
  >U</button>
@@ -9583,46 +10359,130 @@ var rich_editor_default = {
9583
10359
  :title="t('richEditor.strike')"
9584
10360
  :aria-label="t('richEditor.strike')"
9585
10361
  :aria-pressed="String(formats.strike)"
9586
- :disabled="disabled"
10362
+ :disabled="disabled || sourceMode"
9587
10363
  @mousedown.prevent
9588
10364
  @click="exec('strikeThrough')"
9589
10365
  >S</button>
10366
+ <button
10367
+ class="djm-rich-editor-btn"
10368
+ type="button"
10369
+ :title="t('richEditor.inlineCode')"
10370
+ :aria-label="t('richEditor.inlineCode')"
10371
+ :aria-pressed="String(formats.code)"
10372
+ :disabled="disabled || sourceMode"
10373
+ @mousedown.prevent
10374
+ @click="toggleInlineCode"
10375
+ ><Icon name="code" :size="13" /></button>
9590
10376
  </div>
9591
10377
 
9592
- <div class="djm-rich-editor-group">
9593
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.heading')" :disabled="disabled" @mousedown.prevent @click="formatBlock('h2')">H2</button>
9594
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.paragraph')" :disabled="disabled" @mousedown.prevent @click="formatBlock('p')">{{ t('richEditor.paragraph') }}</button>
9595
- </div>
9596
-
9597
- <div class="djm-rich-editor-group">
9598
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.bulletList')" :disabled="disabled" @mousedown.prevent @click="exec('insertUnorderedList')">
9599
- <span class="djm-rich-editor-glyph">\u2022 \u2261</span>
9600
- </button>
9601
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.numberList')" :disabled="disabled" @mousedown.prevent @click="exec('insertOrderedList')">
9602
- <span class="djm-rich-editor-glyph">1. \u2261</span>
10378
+ <div class="djm-rich-editor-group djm-rich-editor-color" v-if="has('color')">
10379
+ <button
10380
+ class="djm-rich-editor-btn"
10381
+ type="button"
10382
+ :title="t('richEditor.textColor')"
10383
+ :aria-label="t('richEditor.textColor')"
10384
+ :aria-expanded="String(panel === 'text')"
10385
+ :disabled="disabled || sourceMode"
10386
+ @mousedown.prevent
10387
+ @click.stop="togglePanel('text')"
10388
+ >
10389
+ <span class="djm-rich-editor-letter" :style="{ borderBottomColor: textColor }">A</span>
9603
10390
  </button>
9604
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.link')" :aria-label="t('richEditor.link')" :disabled="disabled" @mousedown.prevent @click="addLink">
9605
- <Icon name="link" :size="13" />
10391
+ <button
10392
+ class="djm-rich-editor-btn"
10393
+ type="button"
10394
+ :title="t('richEditor.bgColor')"
10395
+ :aria-label="t('richEditor.bgColor')"
10396
+ :aria-expanded="String(panel === 'bg')"
10397
+ :disabled="disabled || sourceMode"
10398
+ @mousedown.prevent
10399
+ @click.stop="togglePanel('bg')"
10400
+ >
10401
+ <span class="djm-rich-editor-letter djm-rich-editor-letter-bg" :style="{ background: bgColor }">A</span>
9606
10402
  </button>
10403
+ <div v-if="panel === 'text' || panel === 'bg'" class="djm-rich-editor-panel" role="group" :aria-label="t('richEditor.textColor')">
10404
+ <button
10405
+ v-for="color in colors"
10406
+ :key="color"
10407
+ class="djm-rich-editor-swatch"
10408
+ type="button"
10409
+ :title="color"
10410
+ :aria-label="color"
10411
+ :style="{ background: color }"
10412
+ @mousedown.prevent
10413
+ @click="applyColor(panel, color)"
10414
+ ></button>
10415
+ </div>
9607
10416
  </div>
9608
10417
 
9609
- <div class="djm-rich-editor-group">
9610
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.clearFormat')" :disabled="disabled" @mousedown.prevent @click="exec('removeFormat')">{{ t('richEditor.clearFormat') }}</button>
9611
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.undo')" :aria-label="t('richEditor.undo')" :disabled="disabled" @mousedown.prevent @click="exec('undo')">\u21B6</button>
9612
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.redo')" :aria-label="t('richEditor.redo')" :disabled="disabled" @mousedown.prevent @click="exec('redo')">\u21B7</button>
10418
+ <div class="djm-rich-editor-group" v-if="has('align')">
10419
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.alignLeft')" :aria-label="t('richEditor.alignLeft')" :aria-pressed="String(formats.align === 'left')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('justifyLeft')"><Icon name="align-left" :size="13" /></button>
10420
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.alignCenter')" :aria-label="t('richEditor.alignCenter')" :aria-pressed="String(formats.align === 'center')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('justifyCenter')"><Icon name="align-center" :size="13" /></button>
10421
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.alignRight')" :aria-label="t('richEditor.alignRight')" :aria-pressed="String(formats.align === 'right')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('justifyRight')"><Icon name="align-right" :size="13" /></button>
10422
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.alignJustify')" :aria-label="t('richEditor.alignJustify')" :aria-pressed="String(formats.align === 'justify')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('justifyFull')"><Icon name="align-justify" :size="13" /></button>
9613
10423
  </div>
9614
10424
 
9615
- <div class="djm-rich-editor-spacer"></div>
9616
- <button
9617
- class="djm-rich-editor-btn"
9618
- :class="{ 'djm-is-active': sourceMode }"
9619
- type="button"
9620
- :aria-pressed="String(sourceMode)"
9621
- :disabled="disabled"
9622
- @click="toggleSource"
9623
- >
9624
- <Icon name="code" :size="13" />{{ sourceMode ? t('richEditor.visual') : t('richEditor.source') }}
9625
- </button>
10425
+ <div class="djm-rich-editor-group" v-if="has('list')">
10426
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.bulletList')" :aria-label="t('richEditor.bulletList')" :aria-pressed="String(formats.list === 'ul')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('insertUnorderedList')"><Icon name="list" :size="13" /></button>
10427
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.numberList')" :aria-label="t('richEditor.numberList')" :aria-pressed="String(formats.list === 'ol')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('insertOrderedList')"><Icon name="list-ordered" :size="13" /></button>
10428
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.outdent')" :aria-label="t('richEditor.outdent')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('outdent')"><Icon name="outdent" :size="13" /></button>
10429
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.indent')" :aria-label="t('richEditor.indent')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('indent')"><Icon name="indent" :size="13" /></button>
10430
+ </div>
10431
+
10432
+ <div class="djm-rich-editor-group" v-if="has('insert')">
10433
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.link')" :aria-label="t('richEditor.link')" :disabled="disabled || sourceMode" @mousedown.prevent @click="addLink"><Icon name="link" :size="13" /></button>
10434
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.unlink')" :aria-label="t('richEditor.unlink')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('unlink')"><Icon name="unlink" :size="13" /></button>
10435
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.singleImage')" :aria-label="t('richEditor.singleImage')" :disabled="disabled || sourceMode" @mousedown.prevent @click="pickImage(false)"><Icon name="image" :size="13" /></button>
10436
+ <button v-if="multiple" class="djm-rich-editor-btn" type="button" :title="t('richEditor.multiImage')" :aria-label="t('richEditor.multiImage')" :disabled="disabled || sourceMode" @mousedown.prevent @click="pickImage(true)"><Icon name="images" :size="13" /></button>
10437
+ <MediaPicker
10438
+ v-if="mediaLibrary.length"
10439
+ class="djm-rich-editor-picker"
10440
+ multiple
10441
+ accept="image"
10442
+ :items="mediaLibrary"
10443
+ :title="t('richEditor.mediaLibrary')"
10444
+ :disabled="disabled || sourceMode"
10445
+ @change="insertMedia"
10446
+ >
10447
+ <template #trigger="{ open }">
10448
+ <button
10449
+ class="djm-rich-editor-btn"
10450
+ type="button"
10451
+ :title="t('richEditor.mediaLibrary')"
10452
+ :aria-label="t('richEditor.mediaLibrary')"
10453
+ :disabled="disabled || sourceMode"
10454
+ @mousedown.prevent="rememberRange"
10455
+ @click="open"
10456
+ ><Icon name="grid" :size="13" /></button>
10457
+ </template>
10458
+ </MediaPicker>
10459
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.hr')" :aria-label="t('richEditor.hr')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('insertHorizontalRule')">\u2014</button>
10460
+ <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.clearFormat')" :aria-label="t('richEditor.clearFormat')" :disabled="disabled || sourceMode" @mousedown.prevent @click="exec('removeFormat')"><span class="djm-rich-editor-glyph">T\xD7</span></button>
10461
+ </div>
10462
+
10463
+ <div class="djm-rich-editor-spacer" v-if="has('view')"></div>
10464
+ <div class="djm-rich-editor-group djm-rich-editor-group-end" v-if="has('view')">
10465
+ <button
10466
+ class="djm-rich-editor-btn"
10467
+ :class="{ 'djm-is-active': sourceMode }"
10468
+ type="button"
10469
+ :title="sourceMode ? t('richEditor.visual') : t('richEditor.source')"
10470
+ :aria-label="sourceMode ? t('richEditor.visual') : t('richEditor.source')"
10471
+ :aria-pressed="String(sourceMode)"
10472
+ :disabled="disabled"
10473
+ @click="toggleSource"
10474
+ ><Icon name="code" :size="13" /></button>
10475
+ <button
10476
+ class="djm-rich-editor-btn"
10477
+ :class="{ 'djm-is-active': fullscreen }"
10478
+ type="button"
10479
+ :title="fullscreen ? t('richEditor.exitFullscreen') : t('richEditor.fullscreen')"
10480
+ :aria-label="fullscreen ? t('richEditor.exitFullscreen') : t('richEditor.fullscreen')"
10481
+ :aria-pressed="String(fullscreen)"
10482
+ :disabled="disabled"
10483
+ @click="toggleFullscreen"
10484
+ ><Icon :name="fullscreen ? 'minimize' : 'maximize'" :size="13" /></button>
10485
+ </div>
9626
10486
  </div>
9627
10487
 
9628
10488
  <div
@@ -9638,6 +10498,8 @@ var rich_editor_default = {
9638
10498
  :style="{ minHeight: minHeight + 'px' }"
9639
10499
  @input="syncFromDom"
9640
10500
  @paste="onPaste"
10501
+ @dragover="onDragOver"
10502
+ @drop="onDrop"
9641
10503
  ></div>
9642
10504
 
9643
10505
  <textarea
@@ -9650,12 +10512,27 @@ var rich_editor_default = {
9650
10512
  spellcheck="false"
9651
10513
  @input="onSourceInput"
9652
10514
  ></textarea>
10515
+
10516
+ <div v-if="showCount" class="djm-rich-editor-foot">
10517
+ <span v-if="uploadCount" class="djm-rich-editor-foot-item">{{ t('richEditor.uploading', { count: uploadCount }) }}</span>
10518
+ <span class="djm-rich-editor-foot-item">{{ t('richEditor.wordCount', { count }) }}</span>
10519
+ </div>
10520
+
10521
+ <input
10522
+ ref="fileInput"
10523
+ class="djm-rich-editor-file"
10524
+ type="file"
10525
+ :accept="accept"
10526
+ tabindex="-1"
10527
+ aria-hidden="true"
10528
+ @change="onFilesChange"
10529
+ />
9653
10530
  </div>
9654
10531
  `
9655
10532
  };
9656
10533
 
9657
10534
  // src/components/floating-window/index.js
9658
- import { ref as ref28, onBeforeUnmount as onBeforeUnmount24 } from "vue";
10535
+ import { ref as ref29, onBeforeUnmount as onBeforeUnmount24 } from "vue";
9659
10536
  function clamp(value, min, max) {
9660
10537
  return Math.min(Math.max(value, min), Math.max(min, max));
9661
10538
  }
@@ -9673,12 +10550,12 @@ var floating_window_default = {
9673
10550
  },
9674
10551
  emits: ["close", "focus"],
9675
10552
  setup(props, { emit }) {
9676
- const root = ref28(null);
9677
- const pos = ref28({ x: props.initialX, y: props.initialY });
9678
- const size = ref28({ w: props.width, h: props.height });
9679
- const minimized = ref28(false);
9680
- const maximized = ref28(false);
9681
- const saved = ref28(null);
10553
+ const root = ref29(null);
10554
+ const pos = ref29({ x: props.initialX, y: props.initialY });
10555
+ const size = ref29({ w: props.width, h: props.height });
10556
+ const minimized = ref29(false);
10557
+ const maximized = ref29(false);
10558
+ const saved = ref29(null);
9682
10559
  let drag = null;
9683
10560
  let resize = null;
9684
10561
  function stage() {
@@ -9814,7 +10691,7 @@ var floating_window_default = {
9814
10691
  };
9815
10692
 
9816
10693
  // src/components/upload/index.js
9817
- import { reactive as reactive4, ref as ref29, watch as watch20, onBeforeUnmount as onBeforeUnmount25 } from "vue";
10694
+ import { reactive as reactive4, ref as ref30, watch as watch21, onBeforeUnmount as onBeforeUnmount25 } from "vue";
9818
10695
  var uidSeed6 = 0;
9819
10696
  function extension(name) {
9820
10697
  const index = name.lastIndexOf(".");
@@ -9864,11 +10741,11 @@ var upload_default = {
9864
10741
  },
9865
10742
  emits: ["update:modelValue", "change", "success", "error", "remove", "exceed"],
9866
10743
  setup(props, { emit }) {
9867
- const files = ref29([]);
9868
- const dragActive = ref29(false);
9869
- const inputRef = ref29(null);
10744
+ const files = ref30([]);
10745
+ const dragActive = ref30(false);
10746
+ const inputRef = ref30(null);
9870
10747
  const tasks = /* @__PURE__ */ new Map();
9871
- watch20(
10748
+ watch21(
9872
10749
  () => props.modelValue,
9873
10750
  (list) => {
9874
10751
  const next = list || [];
@@ -10170,7 +11047,7 @@ var upload_default = {
10170
11047
  };
10171
11048
 
10172
11049
  // src/components/tree/index.js
10173
- import { computed as computed16, ref as ref30, watch as watch21 } from "vue";
11050
+ import { computed as computed18, ref as ref31, watch as watch22 } from "vue";
10174
11051
  var tree_default = {
10175
11052
  name: "DjmTree",
10176
11053
  components: { Icon: icon_default },
@@ -10202,17 +11079,17 @@ var tree_default = {
10202
11079
  },
10203
11080
  emits: ["update:checked", "update:expanded", "node-click", "check", "node-drop", "load-error"],
10204
11081
  setup(props, { emit }) {
10205
- const lazyChildren = ref30({});
10206
- const loadingKeys = ref30([]);
10207
- const innerExpanded = ref30([...props.expanded]);
10208
- const innerChecked = ref30([...props.checked]);
10209
- watch21(
11082
+ const lazyChildren = ref31({});
11083
+ const loadingKeys = ref31([]);
11084
+ const innerExpanded = ref31([...props.expanded]);
11085
+ const innerChecked = ref31([...props.checked]);
11086
+ watch22(
10210
11087
  () => props.expanded,
10211
11088
  (value) => {
10212
11089
  if (Array.isArray(value)) innerExpanded.value = value;
10213
11090
  }
10214
11091
  );
10215
- watch21(
11092
+ watch22(
10216
11093
  () => props.checked,
10217
11094
  (value) => {
10218
11095
  if (Array.isArray(value)) innerChecked.value = value;
@@ -10250,7 +11127,7 @@ var tree_default = {
10250
11127
  });
10251
11128
  return out;
10252
11129
  }
10253
- watch21(
11130
+ watch22(
10254
11131
  () => props.data,
10255
11132
  () => {
10256
11133
  if (props.defaultExpandAll) innerExpanded.value = collectExpandable(props.data);
@@ -10267,7 +11144,7 @@ var tree_default = {
10267
11144
  const children = childrenOf(node);
10268
11145
  return Boolean(children && children.some((child) => matchSubtree(child)));
10269
11146
  }
10270
- const rows = computed16(() => {
11147
+ const rows = computed18(() => {
10271
11148
  const result = [];
10272
11149
  const walk = (nodes, level) => {
10273
11150
  nodes.forEach((node) => {
@@ -10338,8 +11215,8 @@ var tree_default = {
10338
11215
  function isActive(node) {
10339
11216
  return props.activeKey !== "" && node[props.nodeKey] === props.activeKey;
10340
11217
  }
10341
- const rootEl = ref30(null);
10342
- const focusIndex = ref30(-1);
11218
+ const rootEl = ref31(null);
11219
+ const focusIndex = ref31(-1);
10343
11220
  function tabIndexOf(index) {
10344
11221
  return (focusIndex.value === -1 ? 0 : focusIndex.value) === index ? 0 : -1;
10345
11222
  }
@@ -10396,9 +11273,9 @@ var tree_default = {
10396
11273
  function rowClassOf(node, level) {
10397
11274
  return typeof props.rowClass === "function" ? props.rowClass(node, level) : props.rowClass;
10398
11275
  }
10399
- const dragKey = ref30("");
10400
- const dropKey = ref30("");
10401
- const dropPosition = ref30("");
11276
+ const dragKey = ref31("");
11277
+ const dropKey = ref31("");
11278
+ const dropPosition = ref31("");
10402
11279
  function findNodeByKey(key, nodes = props.data) {
10403
11280
  for (const node of nodes) {
10404
11281
  if (node[props.nodeKey] === key) return node;
@@ -10510,7 +11387,7 @@ var tree_default = {
10510
11387
  onDrop,
10511
11388
  onDragEnd,
10512
11389
  labelOf,
10513
- emptyLabel: computed16(() => props.emptyText || t("tree.empty")),
11390
+ emptyLabel: computed18(() => props.emptyText || t("tree.empty")),
10514
11391
  t
10515
11392
  };
10516
11393
  },
@@ -10579,7 +11456,7 @@ var tree_default = {
10579
11456
  };
10580
11457
 
10581
11458
  // src/components/editable-table/index.js
10582
- import { computed as computed17, nextTick as nextTick14, reactive as reactive5, ref as ref31, useSlots, watch as watch22 } from "vue";
11459
+ import { computed as computed19, nextTick as nextTick14, reactive as reactive5, ref as ref32, useSlots, watch as watch23 } from "vue";
10583
11460
  var ACTIONS_KEY = "__editable_actions";
10584
11461
  function isEmpty(value) {
10585
11462
  if (value === "" || value === null || value === void 0) return true;
@@ -10603,12 +11480,12 @@ var editable_table_default = {
10603
11480
  emits: ["edit", "save", "cancel"],
10604
11481
  setup(props, { emit }) {
10605
11482
  const slots = useSlots();
10606
- const editingKey = ref31("");
11483
+ const editingKey = ref32("");
10607
11484
  const draft = reactive5({});
10608
11485
  const errors = reactive5({});
10609
- const tableRef = ref31(null);
10610
- const editableColumns = computed17(() => props.columns.filter((column) => column.editor));
10611
- const renderColumns = computed17(() => {
11486
+ const tableRef = ref32(null);
11487
+ const editableColumns = computed19(() => props.columns.filter((column) => column.editor));
11488
+ const renderColumns = computed19(() => {
10612
11489
  if (!props.showActions) return props.columns;
10613
11490
  return props.columns.concat({
10614
11491
  key: ACTIONS_KEY,
@@ -10620,7 +11497,7 @@ var editable_table_default = {
10620
11497
  exportable: false
10621
11498
  });
10622
11499
  });
10623
- const forwardedSlotNames = computed17(() => {
11500
+ const forwardedSlotNames = computed19(() => {
10624
11501
  const taken = new Set(editableColumns.value.map((column) => "cell-" + column.key));
10625
11502
  taken.add("cell-" + ACTIONS_KEY);
10626
11503
  return Object.keys(slots).filter((name) => !taken.has(name));
@@ -10715,7 +11592,7 @@ var editable_table_default = {
10715
11592
  clearErrors();
10716
11593
  return true;
10717
11594
  }
10718
- watch22(
11595
+ watch23(
10719
11596
  () => props.rows,
10720
11597
  () => {
10721
11598
  if (editingKey.value && !findRow(editingKey.value)) {
@@ -10897,7 +11774,7 @@ var message_default = {
10897
11774
  };
10898
11775
 
10899
11776
  // src/components/org-picker/index.js
10900
- import { computed as computed18, onBeforeUnmount as onBeforeUnmount26, ref as ref32, watch as watch23 } from "vue";
11777
+ import { computed as computed20, onBeforeUnmount as onBeforeUnmount26, ref as ref33, watch as watch24 } from "vue";
10901
11778
  var org_picker_default = {
10902
11779
  name: "DjmOrgPicker",
10903
11780
  components: { DjmIcon: icon_default },
@@ -10916,13 +11793,13 @@ var org_picker_default = {
10916
11793
  },
10917
11794
  emits: ["update:modelValue", "change", "visible-change"],
10918
11795
  setup(props, { emit }) {
10919
- const open = ref32(false);
10920
- const root = ref32(null);
10921
- const searchRef = ref32(null);
10922
- const keyword = ref32("");
10923
- const activeDept = ref32("");
10924
- const temp = ref32([]);
10925
- const expandedIds = ref32(/* @__PURE__ */ new Set());
11796
+ const open = ref33(false);
11797
+ const root = ref33(null);
11798
+ const searchRef = ref33(null);
11799
+ const keyword = ref33("");
11800
+ const activeDept = ref33("");
11801
+ const temp = ref33([]);
11802
+ const expandedIds = ref33(/* @__PURE__ */ new Set());
10926
11803
  let detachPanel = null;
10927
11804
  function collectDeptIds(nodes, out = []) {
10928
11805
  nodes.forEach((node) => {
@@ -10952,22 +11829,22 @@ var org_picker_default = {
10952
11829
  walk([root2]);
10953
11830
  return ids;
10954
11831
  }
10955
- const staffMap = computed18(() => {
11832
+ const staffMap = computed20(() => {
10956
11833
  const map = /* @__PURE__ */ new Map();
10957
11834
  props.staff.forEach((item) => map.set(item.id, item));
10958
11835
  return map;
10959
11836
  });
10960
- const appliedStaff = computed18(
11837
+ const appliedStaff = computed20(
10961
11838
  () => props.modelValue.map((id) => staffMap.value.get(id)).filter(Boolean)
10962
11839
  );
10963
- const visibleTags = computed18(() => {
11840
+ const visibleTags = computed20(() => {
10964
11841
  if (props.maxTagCount <= 0) return appliedStaff.value;
10965
11842
  return appliedStaff.value.slice(0, props.maxTagCount);
10966
11843
  });
10967
- const restTagCount = computed18(
11844
+ const restTagCount = computed20(
10968
11845
  () => Math.max(0, appliedStaff.value.length - visibleTags.value.length)
10969
11846
  );
10970
- const visibleStaff = computed18(() => {
11847
+ const visibleStaff = computed20(() => {
10971
11848
  const key = keyword.value.trim().toLowerCase();
10972
11849
  if (key) {
10973
11850
  return props.staff.filter(
@@ -10978,7 +11855,7 @@ var org_picker_default = {
10978
11855
  const scope = deptScope(activeDept.value);
10979
11856
  return props.staff.filter((item) => scope.has(item.deptId));
10980
11857
  });
10981
- const deptRows = computed18(() => {
11858
+ const deptRows = computed20(() => {
10982
11859
  const rows = [];
10983
11860
  const walk = (nodes, depth) => {
10984
11861
  nodes.forEach((node) => {
@@ -10990,11 +11867,11 @@ var org_picker_default = {
10990
11867
  walk(props.departments, 0);
10991
11868
  return rows;
10992
11869
  });
10993
- const selectedCount = computed18(() => temp.value.length);
10994
- const placeholderText = computed18(() => props.placeholder || t("orgPicker.placeholder"));
10995
- const emptyLabel = computed18(() => props.emptyText || t("orgPicker.empty"));
10996
- const panel = ref32(null);
10997
- const panelStyle = ref32({ ...PANEL_HIDDEN_STYLE });
11870
+ const selectedCount = computed20(() => temp.value.length);
11871
+ const placeholderText = computed20(() => props.placeholder || t("orgPicker.placeholder"));
11872
+ const emptyLabel = computed20(() => props.emptyText || t("orgPicker.empty"));
11873
+ const panel = ref33(null);
11874
+ const panelStyle = ref33({ ...PANEL_HIDDEN_STYLE });
10998
11875
  const PANEL_HEIGHT = 342;
10999
11876
  function updatePosition() {
11000
11877
  if (!open.value || !root.value) return;
@@ -11011,7 +11888,7 @@ var org_picker_default = {
11011
11888
  expandedIds.value = new Set(collectDeptIds(props.departments));
11012
11889
  }
11013
11890
  expandAll();
11014
- watch23(() => props.departments, () => {
11891
+ watch24(() => props.departments, () => {
11015
11892
  expandedIds.value = /* @__PURE__ */ new Set([...expandedIds.value, ...collectDeptIds(props.departments)]);
11016
11893
  });
11017
11894
  function initialOf(item) {
@@ -11286,7 +12163,7 @@ var org_picker_default = {
11286
12163
  };
11287
12164
 
11288
12165
  // src/components/schema-form/index.js
11289
- import { computed as computed19, reactive as reactive7, ref as ref33, watch as watch24 } from "vue";
12166
+ import { computed as computed21, reactive as reactive7, ref as ref34, watch as watch25 } from "vue";
11290
12167
  var CONTROL_MAP = {
11291
12168
  input: input_default,
11292
12169
  number: input_number_default,
@@ -11322,26 +12199,26 @@ var schema_form_default = {
11322
12199
  },
11323
12200
  emits: ["update:modelValue", "change"],
11324
12201
  setup(props, { emit }) {
11325
- const innerForm = ref33(null);
12202
+ const innerForm = ref34(null);
11326
12203
  const model = reactive7({ ...props.modelValue });
11327
- watch24(() => props.fields, (fields) => {
12204
+ watch25(() => props.fields, (fields) => {
11328
12205
  fields.forEach((field) => {
11329
12206
  if (model[field.key] === void 0 && field.default !== void 0) {
11330
12207
  model[field.key] = typeof field.default === "function" ? field.default() : field.default;
11331
12208
  }
11332
12209
  });
11333
12210
  }, { immediate: true, deep: true });
11334
- watch24(() => props.modelValue, (value) => {
12211
+ watch25(() => props.modelValue, (value) => {
11335
12212
  if (!value) return;
11336
12213
  Object.keys(value).forEach((key) => {
11337
12214
  if (model[key] !== value[key]) model[key] = value[key];
11338
12215
  });
11339
12216
  }, { deep: true });
11340
- watch24(model, () => {
12217
+ watch25(model, () => {
11341
12218
  emit("update:modelValue", { ...model });
11342
12219
  emit("change", { ...model });
11343
12220
  }, { deep: true });
11344
- const formRules = computed19(() => {
12221
+ const formRules = computed21(() => {
11345
12222
  const merged = { ...props.rules };
11346
12223
  props.fields.forEach((field) => {
11347
12224
  if (!field.required && !field.rules) return;
@@ -11354,7 +12231,7 @@ var schema_form_default = {
11354
12231
  });
11355
12232
  return merged;
11356
12233
  });
11357
- const visibleFields = computed19(
12234
+ const visibleFields = computed21(
11358
12235
  () => props.fields.filter((field) => {
11359
12236
  if (typeof field.visibleWhen !== "function") return true;
11360
12237
  return Boolean(field.visibleWhen(model));
@@ -11548,7 +12425,7 @@ var approval_flow_default = {
11548
12425
  };
11549
12426
 
11550
12427
  // src/components/query-filter/index.js
11551
- import { computed as computed20, reactive as reactive8, ref as ref34, watch as watch25 } from "vue";
12428
+ import { computed as computed22, reactive as reactive8, ref as ref35, watch as watch26 } from "vue";
11552
12429
  var CONTROL_MAP2 = {
11553
12430
  input: input_default,
11554
12431
  number: input_number_default,
@@ -11573,26 +12450,26 @@ var query_filter_default = {
11573
12450
  },
11574
12451
  emits: ["update:modelValue", "search", "reset"],
11575
12452
  setup(props, { emit }) {
11576
- const expanded = ref34(false);
12453
+ const expanded = ref35(false);
11577
12454
  const model = reactive8({ ...props.modelValue });
11578
- watch25(() => props.fields, (fields) => {
12455
+ watch26(() => props.fields, (fields) => {
11579
12456
  fields.forEach((field) => {
11580
12457
  if (model[field.key] === void 0 && field.default !== void 0) {
11581
12458
  model[field.key] = typeof field.default === "function" ? field.default() : field.default;
11582
12459
  }
11583
12460
  });
11584
12461
  }, { immediate: true, deep: true });
11585
- watch25(() => props.modelValue, (value) => {
12462
+ watch26(() => props.modelValue, (value) => {
11586
12463
  if (!value) return;
11587
12464
  Object.keys(value).forEach((key) => {
11588
12465
  if (model[key] !== value[key]) model[key] = value[key];
11589
12466
  });
11590
12467
  }, { deep: true });
11591
- watch25(model, () => {
12468
+ watch26(model, () => {
11592
12469
  emit("update:modelValue", { ...model });
11593
12470
  }, { deep: true });
11594
- const collapsible = computed20(() => props.fields.length > props.visibleCount);
11595
- const visibleFields = computed20(() => {
12471
+ const collapsible = computed22(() => props.fields.length > props.visibleCount);
12472
+ const visibleFields = computed22(() => {
11596
12473
  if (!collapsible.value || expanded.value) return props.fields;
11597
12474
  return props.fields.slice(0, props.visibleCount);
11598
12475
  });
@@ -11721,198 +12598,6 @@ var filter_tags_default = {
11721
12598
  `
11722
12599
  };
11723
12600
 
11724
- // src/components/media-picker/index.js
11725
- import { computed as computed21, ref as ref35, watch as watch26 } from "vue";
11726
- var media_picker_default = {
11727
- name: "DjmMediaPicker",
11728
- components: { DjmModal: modal_default, DjmPagination: pagination_default, DjmIcon: icon_default },
11729
- props: {
11730
- modelValue: { type: [Array, String, Number], default: "" },
11731
- items: { type: Array, default: () => [] },
11732
- multiple: { type: Boolean, default: false },
11733
- // 最多选几个,0 表示不限
11734
- limit: { type: Number, default: 0 },
11735
- // image 只显示图片,file 只显示非图片,空串不过滤
11736
- accept: { type: String, default: "" },
11737
- title: { type: String, default: "" },
11738
- triggerText: { type: String, default: "" },
11739
- pageSize: { type: Number, default: 12 },
11740
- disabled: { type: Boolean, default: false }
11741
- },
11742
- emits: ["update:modelValue", "change"],
11743
- setup(props, { emit }) {
11744
- const open = ref35(false);
11745
- const keyword = ref35("");
11746
- const page = ref35(1);
11747
- const temp = ref35([]);
11748
- const selectedIds = computed21(() => {
11749
- if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
11750
- if (props.modelValue === "" || props.modelValue === null || props.modelValue === void 0) return [];
11751
- return [props.modelValue];
11752
- });
11753
- function isImage(item) {
11754
- if (item.type) return item.type === "image";
11755
- return /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(item.url || "");
11756
- }
11757
- const filtered = computed21(() => {
11758
- const key = keyword.value.trim().toLowerCase();
11759
- return props.items.filter((item) => {
11760
- if (props.accept === "image" && !isImage(item)) return false;
11761
- if (props.accept === "file" && isImage(item)) return false;
11762
- if (key && !String(item.name || "").toLowerCase().includes(key)) return false;
11763
- return true;
11764
- });
11765
- });
11766
- const paged = computed21(() => {
11767
- const start = (page.value - 1) * props.pageSize;
11768
- return filtered.value.slice(start, start + props.pageSize);
11769
- });
11770
- watch26(keyword, () => {
11771
- page.value = 1;
11772
- });
11773
- watch26(
11774
- () => filtered.value.length,
11775
- () => {
11776
- const max = Math.max(1, Math.ceil(filtered.value.length / props.pageSize));
11777
- if (page.value > max) page.value = max;
11778
- }
11779
- );
11780
- function isChecked(id) {
11781
- return temp.value.includes(id);
11782
- }
11783
- function atLimit() {
11784
- return props.limit > 0 && temp.value.length >= props.limit;
11785
- }
11786
- function toggle(item) {
11787
- if (props.multiple) {
11788
- if (isChecked(item.id)) {
11789
- temp.value = temp.value.filter((id) => id !== item.id);
11790
- return;
11791
- }
11792
- if (atLimit()) return;
11793
- temp.value = [...temp.value, item.id];
11794
- return;
11795
- }
11796
- temp.value = [item.id];
11797
- }
11798
- function openPanel() {
11799
- if (props.disabled) return;
11800
- temp.value = [...selectedIds.value];
11801
- keyword.value = "";
11802
- page.value = 1;
11803
- open.value = true;
11804
- }
11805
- function commit() {
11806
- const next = props.multiple ? [...temp.value] : temp.value[0] !== void 0 ? temp.value[0] : "";
11807
- emit("update:modelValue", next);
11808
- emit("change", next);
11809
- open.value = false;
11810
- }
11811
- function cancel() {
11812
- temp.value = [...selectedIds.value];
11813
- open.value = false;
11814
- }
11815
- return {
11816
- open,
11817
- keyword,
11818
- page,
11819
- temp,
11820
- selectedIds,
11821
- filtered,
11822
- paged,
11823
- isImage,
11824
- isChecked,
11825
- atLimit,
11826
- toggle,
11827
- openPanel,
11828
- commit,
11829
- cancel,
11830
- t
11831
- };
11832
- },
11833
- template: `
11834
- <span class="djm-media-picker">
11835
- <slot name="trigger" :open="openPanel" :count="selectedIds.length">
11836
- <button
11837
- class="djm-btn"
11838
- type="button"
11839
- :disabled="disabled"
11840
- @click="openPanel"
11841
- >{{ triggerText || t('mediaPicker.select') }}<span v-if="selectedIds.length" class="djm-media-picker-count">{{ selectedIds.length }}</span></button>
11842
- </slot>
11843
-
11844
- <DjmModal
11845
- :model-value="open"
11846
- :title="title || t('mediaPicker.title')"
11847
- width="760px"
11848
- @update:model-value="cancel"
11849
- >
11850
- <div class="djm-media-toolbar">
11851
- <label class="djm-media-search">
11852
- <DjmIcon name="search" :size="13" />
11853
- <input
11854
- v-model="keyword"
11855
- class="djm-media-search-input"
11856
- type="text"
11857
- :placeholder="t('mediaPicker.search')"
11858
- :aria-label="t('mediaPicker.search')"
11859
- />
11860
- </label>
11861
- <span class="djm-media-toolbar-slot">
11862
- <slot name="toolbar"></slot>
11863
- </span>
11864
- </div>
11865
-
11866
- <div class="djm-media-grid" role="listbox" :aria-multiselectable="multiple ? 'true' : 'false'">
11867
- <div
11868
- v-for="item in paged"
11869
- :key="item.id"
11870
- class="djm-media-item"
11871
- :class="{
11872
- 'djm-is-checked': isChecked(item.id),
11873
- 'djm-is-disabled': multiple && atLimit() && !isChecked(item.id)
11874
- }"
11875
- role="option"
11876
- tabindex="0"
11877
- :aria-selected="String(isChecked(item.id))"
11878
- @click="toggle(item)"
11879
- @keydown.enter.prevent="toggle(item)"
11880
- @keydown.space.prevent="toggle(item)"
11881
- >
11882
- <div class="djm-media-thumb">
11883
- <img v-if="isImage(item)" :src="item.url" :alt="item.name" loading="lazy" />
11884
- <DjmIcon v-else name="folder" :size="26" />
11885
- <span v-if="isChecked(item.id)" class="djm-media-check">
11886
- <DjmIcon name="check" :size="11" />
11887
- </span>
11888
- </div>
11889
- <div class="djm-media-name" :title="item.name">{{ item.name }}</div>
11890
- </div>
11891
- <div v-if="!paged.length" class="djm-media-empty">{{ t('mediaPicker.empty') }}</div>
11892
- </div>
11893
-
11894
- <div class="djm-media-foot">
11895
- <span class="djm-media-total">
11896
- {{ t('mediaPicker.total', { total: filtered.length }) }}
11897
- <em v-if="multiple && limit > 0">{{ t('mediaPicker.limit', { limit }) }}</em>
11898
- </span>
11899
- <DjmPagination
11900
- v-if="filtered.length > pageSize"
11901
- v-model:page="page"
11902
- :page-size="pageSize"
11903
- :total="filtered.length"
11904
- />
11905
- </div>
11906
-
11907
- <template #footer>
11908
- <button class="djm-btn" type="button" @click="cancel">{{ t('common.cancel') }}</button>
11909
- <button class="djm-btn djm-btn-primary" type="button" @click="commit">{{ t('common.confirm') }}</button>
11910
- </template>
11911
- </DjmModal>
11912
- </span>
11913
- `
11914
- };
11915
-
11916
12601
  // src/components/kanban/index.js
11917
12602
  import { ref as ref36, watch as watch27 } from "vue";
11918
12603
  function cloneColumns(list) {
@@ -12102,7 +12787,7 @@ var kanban_default = {
12102
12787
  };
12103
12788
 
12104
12789
  // src/components/attachment-list/index.js
12105
- import { computed as computed22 } from "vue";
12790
+ import { computed as computed23 } from "vue";
12106
12791
  var IMAGE_EXTS = ["png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"];
12107
12792
  function extOf(file) {
12108
12793
  if (file.ext) return String(file.ext).toLowerCase();
@@ -12126,14 +12811,14 @@ var attachment_list_default = {
12126
12811
  },
12127
12812
  emits: ["preview", "download", "remove"],
12128
12813
  setup(props, { emit }) {
12129
- const list = computed22(
12814
+ const list = computed23(
12130
12815
  () => props.files.map((file, index) => {
12131
12816
  const ext = extOf(file);
12132
12817
  const isImage = file.type === "image" || IMAGE_EXTS.includes(ext);
12133
12818
  return { ...file, _key: file.uid !== void 0 ? file.uid : index, _ext: ext, _isImage: isImage };
12134
12819
  })
12135
12820
  );
12136
- const emptyLabel = computed22(() => props.emptyText || t("attachment.empty"));
12821
+ const emptyLabel = computed23(() => props.emptyText || t("attachment.empty"));
12137
12822
  function onPreview(file) {
12138
12823
  emit("preview", file);
12139
12824
  }
@@ -12239,7 +12924,7 @@ var attachment_list_default = {
12239
12924
  };
12240
12925
 
12241
12926
  // src/components/notification/index.js
12242
- import { computed as computed23, onBeforeUnmount as onBeforeUnmount27, ref as ref37 } from "vue";
12927
+ import { computed as computed24, onBeforeUnmount as onBeforeUnmount27, ref as ref37 } from "vue";
12243
12928
  var TYPE_MAP = {
12244
12929
  info: { icon: "info", cls: "djm-is-info" },
12245
12930
  success: { icon: "check", cls: "djm-is-success" },
@@ -12259,8 +12944,8 @@ var notification_default = {
12259
12944
  },
12260
12945
  emits: ["click", "read", "read-all", "remove", "clear"],
12261
12946
  setup(props, { emit }) {
12262
- const unreadCount = computed23(() => props.items.filter((item) => !item.read).length);
12263
- const emptyLabel = computed23(() => props.emptyText || t("notification.empty"));
12947
+ const unreadCount = computed24(() => props.items.filter((item) => !item.read).length);
12948
+ const emptyLabel = computed24(() => props.emptyText || t("notification.empty"));
12264
12949
  function typeOf(item) {
12265
12950
  return TYPE_MAP[item.type] || TYPE_MAP.info;
12266
12951
  }
@@ -12392,7 +13077,7 @@ var config_provider_default = {
12392
13077
  };
12393
13078
 
12394
13079
  // src/components/date-time-picker/index.js
12395
- import { ref as ref38, computed as computed24, watch as watch29, onBeforeUnmount as onBeforeUnmount28 } from "vue";
13080
+ import { ref as ref38, computed as computed25, watch as watch29, onBeforeUnmount as onBeforeUnmount28 } from "vue";
12396
13081
  var uidSeed7 = 0;
12397
13082
  var pad4 = (value) => String(value).padStart(2, "0");
12398
13083
  function toDateKey(year, month, day) {
@@ -12434,22 +13119,22 @@ var date_time_picker_default = {
12434
13119
  let detachPanel = null;
12435
13120
  const today = /* @__PURE__ */ new Date();
12436
13121
  const todayKey = toDateKey(today.getFullYear(), today.getMonth(), today.getDate());
12437
- const hasSeconds = computed24(() => props.format.indexOf("ss") > -1);
13122
+ const hasSeconds = computed25(() => props.format.indexOf("ss") > -1);
12438
13123
  const draft = ref38({ date: "", h: "00", m: "00", s: "00" });
12439
13124
  const initial = parseDate(parseValue(props.modelValue).date);
12440
13125
  const viewYear = ref38(initial ? initial.year : today.getFullYear());
12441
13126
  const viewMonth = ref38(initial ? initial.month : today.getMonth());
12442
- const displayText = computed24(() => props.modelValue || "");
12443
- const placeholderText = computed24(() => props.placeholder || t("dateTimePicker.placeholder"));
12444
- const weeks = computed24(() => t("datePicker.weeks").split(","));
12445
- const hours = computed24(() => Array.from({ length: 24 }, (_, index) => pad4(index)));
12446
- const minutes = computed24(() => {
13127
+ const displayText = computed25(() => props.modelValue || "");
13128
+ const placeholderText = computed25(() => props.placeholder || t("dateTimePicker.placeholder"));
13129
+ const weeks = computed25(() => t("datePicker.weeks").split(","));
13130
+ const hours = computed25(() => Array.from({ length: 24 }, (_, index) => pad4(index)));
13131
+ const minutes = computed25(() => {
12447
13132
  const list = [];
12448
13133
  for (let value = 0; value < 60; value += Math.max(1, props.minuteStep)) list.push(pad4(value));
12449
13134
  return list;
12450
13135
  });
12451
- const seconds = computed24(() => Array.from({ length: 60 }, (_, index) => pad4(index)));
12452
- const timeCols = computed24(() => {
13136
+ const seconds = computed25(() => Array.from({ length: 60 }, (_, index) => pad4(index)));
13137
+ const timeCols = computed25(() => {
12453
13138
  const cols = [
12454
13139
  { key: "h", list: hours.value },
12455
13140
  { key: "m", list: minutes.value }
@@ -12457,8 +13142,8 @@ var date_time_picker_default = {
12457
13142
  if (hasSeconds.value) cols.push({ key: "s", list: seconds.value });
12458
13143
  return cols;
12459
13144
  });
12460
- const monthLabel = computed24(() => t("datePicker.title", { year: viewYear.value, month: viewMonth.value + 1 }));
12461
- const cells = computed24(() => {
13145
+ const monthLabel = computed25(() => t("datePicker.title", { year: viewYear.value, month: viewMonth.value + 1 }));
13146
+ const cells = computed25(() => {
12462
13147
  const first = new Date(viewYear.value, viewMonth.value, 1);
12463
13148
  const offset = (first.getDay() + 6) % 7;
12464
13149
  const start = new Date(viewYear.value, viewMonth.value, 1 - offset);
@@ -12719,7 +13404,7 @@ var date_time_picker_default = {
12719
13404
  };
12720
13405
 
12721
13406
  // src/components/virtual-list/index.js
12722
- import { ref as ref39, computed as computed25, watch as watch30, onMounted as onMounted16, onBeforeUnmount as onBeforeUnmount29 } from "vue";
13407
+ import { ref as ref39, computed as computed26, watch as watch30, onMounted as onMounted16, onBeforeUnmount as onBeforeUnmount29 } from "vue";
12723
13408
  var virtual_list_default = {
12724
13409
  name: "DjmVirtualList",
12725
13410
  props: {
@@ -12743,7 +13428,7 @@ var virtual_list_default = {
12743
13428
  function rowHeight(item, index) {
12744
13429
  return typeof props.itemHeight === "function" ? Math.max(1, Number(props.itemHeight(item, index)) || 1) : Math.max(1, props.itemHeight);
12745
13430
  }
12746
- const offsets = computed25(() => {
13431
+ const offsets = computed26(() => {
12747
13432
  const list = [0];
12748
13433
  let sum = 0;
12749
13434
  props.items.forEach((item, index) => {
@@ -12752,7 +13437,7 @@ var virtual_list_default = {
12752
13437
  });
12753
13438
  return list;
12754
13439
  });
12755
- const totalHeight = computed25(() => offsets.value[offsets.value.length - 1]);
13440
+ const totalHeight = computed26(() => offsets.value[offsets.value.length - 1]);
12756
13441
  function findStart(top) {
12757
13442
  const list = offsets.value;
12758
13443
  let low = 0;
@@ -12764,7 +13449,7 @@ var virtual_list_default = {
12764
13449
  }
12765
13450
  return Math.max(0, low - 1);
12766
13451
  }
12767
- const range = computed25(() => {
13452
+ const range = computed26(() => {
12768
13453
  const total = props.items.length;
12769
13454
  if (!total) return { start: 0, end: 0 };
12770
13455
  const start = Math.max(0, findStart(scrollTop.value) - props.overscan);
@@ -12773,7 +13458,7 @@ var virtual_list_default = {
12773
13458
  while (end < total && offsets.value[end] < limit) end += 1;
12774
13459
  return { start, end: Math.min(total, end + props.overscan) };
12775
13460
  });
12776
- const visibleRows = computed25(() => {
13461
+ const visibleRows = computed26(() => {
12777
13462
  const { start, end } = range.value;
12778
13463
  return props.items.slice(start, end).map((item, index) => {
12779
13464
  const realIndex = start + index;
@@ -12870,7 +13555,7 @@ var virtual_list_default = {
12870
13555
  };
12871
13556
 
12872
13557
  // src/components/batch-bar/index.js
12873
- import { computed as computed26 } from "vue";
13558
+ import { computed as computed27 } from "vue";
12874
13559
  var batch_bar_default = {
12875
13560
  name: "DjmBatchBar",
12876
13561
  props: {
@@ -12885,8 +13570,8 @@ var batch_bar_default = {
12885
13570
  },
12886
13571
  emits: ["clear", "select-all"],
12887
13572
  setup(props, { emit }) {
12888
- const text = computed26(() => props.label || t("batchBar.selected", { count: props.count }));
12889
- const canSelectAll = computed26(() => props.total > props.count);
13573
+ const text = computed27(() => props.label || t("batchBar.selected", { count: props.count }));
13574
+ const canSelectAll = computed27(() => props.total > props.count);
12890
13575
  return {
12891
13576
  text,
12892
13577
  canSelectAll,
@@ -12914,7 +13599,7 @@ var batch_bar_default = {
12914
13599
  };
12915
13600
 
12916
13601
  // src/components/import-wizard/index.js
12917
- import { ref as ref40, computed as computed27, watch as watch31 } from "vue";
13602
+ import { ref as ref40, computed as computed28, watch as watch31 } from "vue";
12918
13603
  function matchAccept2(name, accept) {
12919
13604
  if (!accept) return true;
12920
13605
  const ext = "." + String(name).split(".").pop().toLowerCase();
@@ -12964,7 +13649,7 @@ var import_wizard_default = {
12964
13649
  innerStep.value = next;
12965
13650
  emit("update:step", next);
12966
13651
  }
12967
- const steps = computed27(() => [
13652
+ const steps = computed28(() => [
12968
13653
  { index: 1, title: t("importWizard.stepFile") },
12969
13654
  { index: 2, title: t("importWizard.stepMapping") },
12970
13655
  { index: 3, title: t("importWizard.stepResult") }
@@ -13006,7 +13691,7 @@ var import_wizard_default = {
13006
13691
  mappingError.value = "";
13007
13692
  emit("update:mapping", next);
13008
13693
  }
13009
- const missingRequired = computed27(
13694
+ const missingRequired = computed28(
13010
13695
  () => props.columns.filter((col) => col.required && !innerMapping.value[col.key])
13011
13696
  );
13012
13697
  function submit() {
@@ -13039,7 +13724,7 @@ var import_wizard_default = {
13039
13724
  emit("retry");
13040
13725
  goStep(2);
13041
13726
  }
13042
- const stats = computed27(() => {
13727
+ const stats = computed28(() => {
13043
13728
  const data = props.result || {};
13044
13729
  return {
13045
13730
  total: data.total || 0,
@@ -13185,7 +13870,7 @@ var import_wizard_default = {
13185
13870
  };
13186
13871
 
13187
13872
  // src/components/diff/index.js
13188
- import { ref as ref41, computed as computed28 } from "vue";
13873
+ import { ref as ref41, computed as computed29 } from "vue";
13189
13874
 
13190
13875
  // src/utils/text-diff.js
13191
13876
  var MAX_CELLS = 1e6;
@@ -13249,8 +13934,8 @@ var diff_default = {
13249
13934
  afterLabel: { type: String, default: "" }
13250
13935
  },
13251
13936
  setup(props) {
13252
- const rows = computed28(() => diffLines(props.before, props.after));
13253
- const stats = computed28(() => {
13937
+ const rows = computed29(() => diffLines(props.before, props.after));
13938
+ const stats = computed29(() => {
13254
13939
  let add = 0;
13255
13940
  let remove = 0;
13256
13941
  rows.value.forEach((row) => {
@@ -13263,7 +13948,7 @@ var diff_default = {
13263
13948
  function expand(index) {
13264
13949
  expandedBlocks.value = expandedBlocks.value.concat(index);
13265
13950
  }
13266
- const displayRows = computed28(() => {
13951
+ const displayRows = computed29(() => {
13267
13952
  const ctx = Math.max(0, props.context);
13268
13953
  const list = rows.value;
13269
13954
  if (!ctx) return list;
@@ -13294,7 +13979,7 @@ var diff_default = {
13294
13979
  }
13295
13980
  return out;
13296
13981
  });
13297
- const splitRows = computed28(() => {
13982
+ const splitRows = computed29(() => {
13298
13983
  const list = displayRows.value;
13299
13984
  const out = [];
13300
13985
  let i = 0;
@@ -13319,8 +14004,8 @@ var diff_default = {
13319
14004
  }
13320
14005
  return out;
13321
14006
  });
13322
- const beforeLabelText = computed28(() => props.beforeLabel || t("diff.before"));
13323
- const afterLabelText = computed28(() => props.afterLabel || t("diff.after"));
14007
+ const beforeLabelText = computed29(() => props.beforeLabel || t("diff.before"));
14008
+ const afterLabelText = computed29(() => props.afterLabel || t("diff.after"));
13324
14009
  return {
13325
14010
  stats,
13326
14011
  displayRows,
@@ -13386,7 +14071,7 @@ var diff_default = {
13386
14071
  };
13387
14072
 
13388
14073
  // src/components/tree-table/index.js
13389
- import { ref as ref42, computed as computed29, watch as watch32 } from "vue";
14074
+ import { ref as ref42, computed as computed30, watch as watch32 } from "vue";
13390
14075
  function keyOf(row, rowKey) {
13391
14076
  return row && row[rowKey] !== void 0 ? row[rowKey] : row && row.id;
13392
14077
  }
@@ -13441,7 +14126,7 @@ var tree_table_default = {
13441
14126
  if (value) innerChecked.value = value.slice();
13442
14127
  }
13443
14128
  );
13444
- const rows = computed29(() => {
14129
+ const rows = computed30(() => {
13445
14130
  const out = [];
13446
14131
  function walk(list, level, parentKey) {
13447
14132
  ;
@@ -13456,7 +14141,7 @@ var tree_table_default = {
13456
14141
  walk(props.data, 1, null);
13457
14142
  return out;
13458
14143
  });
13459
- const descendantMap = computed29(() => {
14144
+ const descendantMap = computed30(() => {
13460
14145
  const map = {};
13461
14146
  function collect(row) {
13462
14147
  const key = keyOf(row, props.rowKey);
@@ -13471,16 +14156,16 @@ var tree_table_default = {
13471
14156
  (props.data || []).forEach(collect);
13472
14157
  return map;
13473
14158
  });
13474
- const leafKeys = computed29(() => {
14159
+ const leafKeys = computed30(() => {
13475
14160
  const keys = [];
13476
14161
  eachNode(props.data, (row) => {
13477
14162
  if (!row.children || !row.children.length) keys.push(keyOf(row, props.rowKey));
13478
14163
  });
13479
14164
  return keys;
13480
14165
  });
13481
- const checkedSet = computed29(() => new Set(innerChecked.value));
13482
- const allChecked = computed29(() => leafKeys.value.length > 0 && leafKeys.value.every((key) => checkedSet.value.has(key)));
13483
- const someChecked = computed29(() => !allChecked.value && leafKeys.value.some((key) => checkedSet.value.has(key)));
14166
+ const checkedSet = computed30(() => new Set(innerChecked.value));
14167
+ const allChecked = computed30(() => leafKeys.value.length > 0 && leafKeys.value.every((key) => checkedSet.value.has(key)));
14168
+ const someChecked = computed30(() => !allChecked.value && leafKeys.value.some((key) => checkedSet.value.has(key)));
13484
14169
  function isIndeterminate(row) {
13485
14170
  if (!props.selectable || props.checkStrictly) return false;
13486
14171
  const desc = descendantMap.value[keyOf(row, props.rowKey)] || [];
@@ -13514,7 +14199,7 @@ var tree_table_default = {
13514
14199
  }
13515
14200
  emitChecked(Array.from(set));
13516
14201
  }
13517
- const nodeMap = computed29(() => {
14202
+ const nodeMap = computed30(() => {
13518
14203
  const map = {};
13519
14204
  eachNode(props.data, (row, level, parentKey) => {
13520
14205
  map[keyOf(row, props.rowKey)] = { row, parentKey };
@@ -13561,11 +14246,11 @@ var tree_table_default = {
13561
14246
  function onRowClick(item) {
13562
14247
  emit("row-click", item.row, item);
13563
14248
  }
13564
- const scrollStyle = computed29(() => {
14249
+ const scrollStyle = computed30(() => {
13565
14250
  if (!props.maxHeight) return null;
13566
14251
  return { maxHeight: typeof props.maxHeight === "number" ? props.maxHeight + "px" : props.maxHeight };
13567
14252
  });
13568
- const emptyLabel = computed29(() => props.emptyText || t("treeTable.empty"));
14253
+ const emptyLabel = computed30(() => props.emptyText || t("treeTable.empty"));
13569
14254
  return {
13570
14255
  rows,
13571
14256
  innerExpanded,
@@ -13667,7 +14352,7 @@ var tree_table_default = {
13667
14352
  };
13668
14353
 
13669
14354
  // src/components/schedule/index.js
13670
- import { ref as ref43, computed as computed30, watch as watch33, onBeforeUnmount as onBeforeUnmount30 } from "vue";
14355
+ import { ref as ref43, computed as computed31, watch as watch33, onBeforeUnmount as onBeforeUnmount30 } from "vue";
13671
14356
  var pad5 = (value) => String(value).padStart(2, "0");
13672
14357
  function toKey3(date) {
13673
14358
  return `${date.getFullYear()}-${pad5(date.getMonth() + 1)}-${pad5(date.getDate())}`;
@@ -13717,8 +14402,8 @@ var schedule_default = {
13717
14402
  const offset = (day + 6) % 7;
13718
14403
  return new Date(date.getFullYear(), date.getMonth(), date.getDate() - offset);
13719
14404
  }
13720
- const weekStartDate = computed30(() => startOfWeek(anchor.value));
13721
- const weekdays = computed30(() => t("schedule.weekdays").split(","));
14405
+ const weekStartDate = computed31(() => startOfWeek(anchor.value));
14406
+ const weekdays = computed31(() => t("schedule.weekdays").split(","));
13722
14407
  function layoutDay(list) {
13723
14408
  const sorted = list.map((item) => {
13724
14409
  const startMin = toMinutes(item.start, props.startHour * 60);
@@ -13751,7 +14436,7 @@ var schedule_default = {
13751
14436
  };
13752
14437
  });
13753
14438
  }
13754
- const days = computed30(() => {
14439
+ const days = computed31(() => {
13755
14440
  const start = weekStartDate.value;
13756
14441
  const list = Array.from({ length: 7 }, (_, index) => {
13757
14442
  const date = new Date(start.getFullYear(), start.getMonth(), start.getDate() + index);
@@ -13769,20 +14454,20 @@ var schedule_default = {
13769
14454
  });
13770
14455
  return props.showWeekend ? list : list.slice(0, 5);
13771
14456
  });
13772
- const hours = computed30(() => {
14457
+ const hours = computed31(() => {
13773
14458
  const list = [];
13774
14459
  for (let hour = props.startHour; hour < props.endHour; hour++) list.push(hour);
13775
14460
  return list;
13776
14461
  });
13777
- const bodyHeight = computed30(() => (props.endHour - props.startHour) * props.hourHeight);
13778
- const gridStyle = computed30(() => ({
14462
+ const bodyHeight = computed31(() => (props.endHour - props.startHour) * props.hourHeight);
14463
+ const gridStyle = computed31(() => ({
13779
14464
  gridTemplateColumns: "56px repeat(" + Math.max(1, days.value.length) + ", minmax(0, 1fr))"
13780
14465
  }));
13781
- const nowTop = computed30(() => (nowMinutes.value - props.startHour * 60) / 60 * props.hourHeight);
13782
- const showNowLine = computed30(
14466
+ const nowTop = computed31(() => (nowMinutes.value - props.startHour * 60) / 60 * props.hourHeight);
14467
+ const showNowLine = computed31(
13783
14468
  () => nowMinutes.value >= props.startHour * 60 && nowMinutes.value <= props.endHour * 60 && days.value.some((day) => day.today)
13784
14469
  );
13785
- const rangeLabel = computed30(() => {
14470
+ const rangeLabel = computed31(() => {
13786
14471
  const list = days.value;
13787
14472
  if (!list.length) return "";
13788
14473
  const first = list[0];
@@ -13912,7 +14597,7 @@ var schedule_default = {
13912
14597
  };
13913
14598
 
13914
14599
  // src/components/comment/index.js
13915
- import { ref as ref44, computed as computed31 } from "vue";
14600
+ import { ref as ref44, computed as computed32 } from "vue";
13916
14601
  var comment_default = {
13917
14602
  name: "DjmComment",
13918
14603
  components: { DjmAvatar: avatar_default, DjmIcon: icon_default },
@@ -13931,14 +14616,14 @@ var comment_default = {
13931
14616
  setup(props, { emit }) {
13932
14617
  const content = ref44("");
13933
14618
  const replyTarget = ref44(null);
13934
- const placeholderText = computed31(() => {
14619
+ const placeholderText = computed32(() => {
13935
14620
  if (replyTarget.value) return t("comment.replyPlaceholder", { name: replyTarget.value.author });
13936
14621
  return props.placeholder || t("comment.placeholder");
13937
14622
  });
13938
- const submitLabel = computed31(() => props.submitText || t("comment.submit"));
13939
- const emptyLabel = computed31(() => props.emptyText || t("comment.empty"));
13940
- const overLimit = computed31(() => content.value.length > props.maxLength);
13941
- const canSubmit = computed31(() => content.value.trim().length > 0 && !overLimit.value && !props.disabled && !props.loading);
14623
+ const submitLabel = computed32(() => props.submitText || t("comment.submit"));
14624
+ const emptyLabel = computed32(() => props.emptyText || t("comment.empty"));
14625
+ const overLimit = computed32(() => content.value.length > props.maxLength);
14626
+ const canSubmit = computed32(() => content.value.trim().length > 0 && !overLimit.value && !props.disabled && !props.loading);
13942
14627
  function onInput(event) {
13943
14628
  content.value = event.target.value;
13944
14629
  }
@@ -14253,7 +14938,7 @@ var signature_default = {
14253
14938
  };
14254
14939
 
14255
14940
  // src/components/print/index.js
14256
- import { ref as ref46, computed as computed32 } from "vue";
14941
+ import { ref as ref46, computed as computed33 } from "vue";
14257
14942
  function escapeHtml(text) {
14258
14943
  return String(text).replace(/[&<>"]/g, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[ch]);
14259
14944
  }
@@ -14270,7 +14955,7 @@ var print_default = {
14270
14955
  emits: ["before-print", "after-print"],
14271
14956
  setup(props, { emit }) {
14272
14957
  const content = ref46(null);
14273
- const label = computed32(() => props.buttonText || t("print.button"));
14958
+ const label = computed33(() => props.buttonText || t("print.button"));
14274
14959
  function collectStyles() {
14275
14960
  return Array.from(document.querySelectorAll('link[rel="stylesheet"], style')).map((el) => el.outerHTML).join("");
14276
14961
  }
@@ -14319,7 +15004,7 @@ var print_default = {
14319
15004
  };
14320
15005
 
14321
15006
  // src/components/gantt/index.js
14322
- import { computed as computed33 } from "vue";
15007
+ import { computed as computed34 } from "vue";
14323
15008
  var DAY = 864e5;
14324
15009
  var pad6 = (value) => String(value).padStart(2, "0");
14325
15010
  function parseDate3(value) {
@@ -14355,7 +15040,7 @@ var gantt_default = {
14355
15040
  emits: ["task-click"],
14356
15041
  setup(props, { emit }) {
14357
15042
  const todayKey = toKey4(/* @__PURE__ */ new Date());
14358
- const range = computed33(() => {
15043
+ const range = computed34(() => {
14359
15044
  const starts = props.tasks.map((task) => task.start).filter(Boolean).sort();
14360
15045
  const ends = props.tasks.map((task) => task.end || task.start).filter(Boolean).sort();
14361
15046
  let start = props.startDate || starts[0] || todayKey;
@@ -14368,7 +15053,7 @@ var gantt_default = {
14368
15053
  }
14369
15054
  return { start, end };
14370
15055
  });
14371
- const days = computed33(() => {
15056
+ const days = computed34(() => {
14372
15057
  const total = dayDiff(range.value.end, range.value.start) + 1;
14373
15058
  const base = parseDate3(range.value.start);
14374
15059
  return Array.from({ length: total }, (_, index) => {
@@ -14385,9 +15070,9 @@ var gantt_default = {
14385
15070
  };
14386
15071
  });
14387
15072
  });
14388
- const trackWidth = computed33(() => days.value.length * props.dayWidth);
14389
- const innerWidth = computed33(() => props.nameWidth + trackWidth.value);
14390
- const bars = computed33(
15073
+ const trackWidth = computed34(() => days.value.length * props.dayWidth);
15074
+ const innerWidth = computed34(() => props.nameWidth + trackWidth.value);
15075
+ const bars = computed34(
14391
15076
  () => props.tasks.map((task, index) => {
14392
15077
  const start = task.start || range.value.start;
14393
15078
  const end = task.end || start;
@@ -14403,12 +15088,12 @@ var gantt_default = {
14403
15088
  };
14404
15089
  })
14405
15090
  );
14406
- const todayLeft = computed33(() => {
15091
+ const todayLeft = computed34(() => {
14407
15092
  const index = days.value.findIndex((day) => day.today);
14408
15093
  if (index === -1) return null;
14409
15094
  return props.nameWidth + index * props.dayWidth + props.dayWidth / 2;
14410
15095
  });
14411
- const emptyLabel = computed33(() => props.emptyText || t("gantt.empty"));
15096
+ const emptyLabel = computed34(() => props.emptyText || t("gantt.empty"));
14412
15097
  function onClick(bar) {
14413
15098
  emit("task-click", bar.task);
14414
15099
  }
@@ -14490,7 +15175,7 @@ var gantt_default = {
14490
15175
  };
14491
15176
 
14492
15177
  // src/components/field-list/index.js
14493
- import { ref as ref47, computed as computed34, watch as watch35 } from "vue";
15178
+ import { ref as ref47, computed as computed35, watch as watch35 } from "vue";
14494
15179
  var uidSeed8 = 0;
14495
15180
  var field_list_default = {
14496
15181
  name: "DjmFieldList",
@@ -14521,10 +15206,10 @@ var field_list_default = {
14521
15206
  syncUids();
14522
15207
  watch35(() => props.modelValue.length, syncUids);
14523
15208
  const errors = ref47({});
14524
- const addLabel = computed34(() => props.addText || t("fieldList.add"));
14525
- const emptyLabel = computed34(() => props.emptyText || t("fieldList.empty"));
14526
- const canAdd = computed34(() => !props.disabled && (!props.max || props.modelValue.length < props.max));
14527
- const canRemove = computed34(() => !props.disabled && props.modelValue.length > props.min);
15209
+ const addLabel = computed35(() => props.addText || t("fieldList.add"));
15210
+ const emptyLabel = computed35(() => props.emptyText || t("fieldList.empty"));
15211
+ const canAdd = computed35(() => !props.disabled && (!props.max || props.modelValue.length < props.max));
15212
+ const canRemove = computed35(() => !props.disabled && props.modelValue.length > props.min);
14528
15213
  function commit(list) {
14529
15214
  emit("update:modelValue", list);
14530
15215
  emit("change", list);
@@ -14686,7 +15371,7 @@ var field_list_default = {
14686
15371
  };
14687
15372
 
14688
15373
  // src/components/chat/index.js
14689
- import { ref as ref48, computed as computed35, watch as watch36, nextTick as nextTick15 } from "vue";
15374
+ import { ref as ref48, computed as computed36, watch as watch36, nextTick as nextTick15 } from "vue";
14690
15375
  var chat_default = {
14691
15376
  name: "DjmChat",
14692
15377
  components: { DjmAvatar: avatar_default },
@@ -14705,10 +15390,10 @@ var chat_default = {
14705
15390
  setup(props, { emit }) {
14706
15391
  const content = ref48("");
14707
15392
  const listRef = ref48(null);
14708
- const placeholderText = computed35(() => props.placeholder || t("chat.placeholder"));
14709
- const sendLabel = computed35(() => props.sendText || t("chat.send"));
14710
- const emptyLabel = computed35(() => props.emptyText || t("chat.empty"));
14711
- const canSend = computed35(() => content.value.trim().length > 0 && !props.disabled && !props.loading);
15393
+ const placeholderText = computed36(() => props.placeholder || t("chat.placeholder"));
15394
+ const sendLabel = computed36(() => props.sendText || t("chat.send"));
15395
+ const emptyLabel = computed36(() => props.emptyText || t("chat.empty"));
15396
+ const canSend = computed36(() => content.value.trim().length > 0 && !props.disabled && !props.loading);
14712
15397
  function scrollToBottom() {
14713
15398
  const el = listRef.value;
14714
15399
  if (el) el.scrollTop = el.scrollHeight;
@@ -14826,7 +15511,7 @@ var chat_default = {
14826
15511
  };
14827
15512
 
14828
15513
  // src/components/org-chart/index.js
14829
- import { ref as ref49, computed as computed36, watch as watch37 } from "vue";
15514
+ import { ref as ref49, computed as computed37, watch as watch37 } from "vue";
14830
15515
  var org_chart_default = {
14831
15516
  name: "DjmOrgChart",
14832
15517
  components: { DjmAvatar: avatar_default, DjmIcon: icon_default },
@@ -14863,7 +15548,7 @@ var org_chart_default = {
14863
15548
  watch37(() => props.data, (data) => {
14864
15549
  collapsed.value = props.defaultExpandAll ? [] : collapsedByDefault(data);
14865
15550
  });
14866
- const layout = computed36(() => {
15551
+ const layout = computed37(() => {
14867
15552
  if (!props.data) return { nodes: [], links: [], width: 0, height: 0 };
14868
15553
  const rowH = props.nodeHeight + props.verticalGap;
14869
15554
  let cursor = 0;
@@ -14928,7 +15613,7 @@ var org_chart_default = {
14928
15613
  function onClick(item) {
14929
15614
  emit("node-click", item.node);
14930
15615
  }
14931
- const emptyLabel = computed36(() => props.emptyText || t("orgChart.empty"));
15616
+ const emptyLabel = computed37(() => props.emptyText || t("orgChart.empty"));
14932
15617
  return {
14933
15618
  layout,
14934
15619
  toggle,
@@ -14989,7 +15674,7 @@ var org_chart_default = {
14989
15674
  };
14990
15675
 
14991
15676
  // src/components/page-tabs/index.js
14992
- import { computed as computed37 } from "vue";
15677
+ import { computed as computed38 } from "vue";
14993
15678
  var page_tabs_default = {
14994
15679
  name: "DjmPageTabs",
14995
15680
  components: { DjmIcon: icon_default },
@@ -15000,7 +15685,7 @@ var page_tabs_default = {
15000
15685
  },
15001
15686
  emits: ["update:modelValue", "change", "close"],
15002
15687
  setup(props, { emit }) {
15003
- const label = computed37(() => t("pageTabs.label"));
15688
+ const label = computed38(() => t("pageTabs.label"));
15004
15689
  function canClose(tab) {
15005
15690
  return tab.closable === void 0 ? props.closable : tab.closable;
15006
15691
  }
@@ -15253,7 +15938,7 @@ var components = [
15253
15938
  org_chart_default,
15254
15939
  page_tabs_default
15255
15940
  ];
15256
- var version = "0.67.4";
15941
+ var version = "0.72.0";
15257
15942
  function install(app, options = {}) {
15258
15943
  if (options.locale) setLocale(options.locale);
15259
15944
  components.forEach((component) => app.component(component.name, component));