djm-ui 0.67.3 → 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 (40) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +38 -2
  3. package/dist/djm-ui.cjs +1051 -356
  4. package/dist/djm-ui.cjs.map +4 -4
  5. package/dist/djm-ui.css +752 -17
  6. package/dist/djm-ui.esm.js +1127 -432
  7. package/dist/djm-ui.esm.js.map +4 -4
  8. package/dist/djm-ui.esm.min.js +375 -229
  9. package/dist/djm-ui.min.cjs +375 -229
  10. package/dist/djm-ui.min.css +2 -2
  11. package/dist/djm-ui.umd.js +967 -272
  12. package/dist/djm-ui.umd.min.js +375 -229
  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/input/index.js +17 -6
  18. package/src/components/rich-editor/index.js +724 -61
  19. package/src/index.js +1 -1
  20. package/src/locale/en-US.js +33 -1
  21. package/src/locale/zh-CN.js +33 -1
  22. package/src/styles/base.css +2 -3
  23. package/src/styles/components/button.css +2 -1
  24. package/src/styles/components/console.css +5 -0
  25. package/src/styles/components/content.css +517 -0
  26. package/src/styles/components/form.css +5 -0
  27. package/src/styles/components/layout.css +327 -1
  28. package/src/styles/components/media-picker.css +3 -3
  29. package/src/styles/components/menu.css +2 -1
  30. package/src/styles/components/org-picker.css +1 -1
  31. package/src/styles/components/responsive.css +86 -0
  32. package/src/styles/components/rich-editor.css +206 -7
  33. package/src/styles/components/skin.css +54 -0
  34. package/src/styles/components/template.css +5 -0
  35. package/src/styles/index.css +1 -0
  36. package/src/styles/theme-dark.css +2 -0
  37. package/src/styles/tokens.css +8 -0
  38. package/src/theme/index.js +40 -0
  39. package/src/utils/sanitize-html.js +44 -6
  40. package/types/index.d.ts +8 -0
@@ -1,4 +1,4 @@
1
- /*! DJM UI v0.67.3 · 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",
@@ -1099,19 +1111,29 @@ var input_default = {
1099
1111
  this.focus();
1100
1112
  },
1101
1113
  focus() {
1114
+ this.clearStaleValue();
1102
1115
  if (this.$refs.input) this.$refs.input.focus();
1103
1116
  },
1117
+ onFocus(event) {
1118
+ this.clearStaleValue();
1119
+ this.$emit("focus", event);
1120
+ },
1104
1121
  blur() {
1105
1122
  if (this.$refs.input) this.$refs.input.blur();
1106
1123
  }
1107
1124
  },
1108
1125
  mounted() {
1109
- this.clearStaleValue();
1110
- this.staleTimer = setTimeout(() => this.clearStaleValue(), 300);
1126
+ this.staleTimers = [null, 200, 600, 1200, 2400].map((delay) => {
1127
+ if (delay === null) {
1128
+ this.clearStaleValue();
1129
+ return null;
1130
+ }
1131
+ return setTimeout(() => this.clearStaleValue(), delay);
1132
+ });
1111
1133
  window.addEventListener("pageshow", this.clearStaleValue);
1112
1134
  },
1113
1135
  beforeUnmount() {
1114
- clearTimeout(this.staleTimer);
1136
+ this.staleTimers.forEach((timer) => clearTimeout(timer));
1115
1137
  window.removeEventListener("pageshow", this.clearStaleValue);
1116
1138
  },
1117
1139
  template: `
@@ -1131,7 +1153,7 @@ var input_default = {
1131
1153
  @input="onInput"
1132
1154
  @change="$emit('change', $event)"
1133
1155
  @keyup.enter="$emit('enter', $event)"
1134
- @focus="$emit('focus', $event)"
1156
+ @focus="onFocus"
1135
1157
  @blur="$emit('blur', $event)"
1136
1158
  />
1137
1159
  <button
@@ -1161,7 +1183,7 @@ var input_default = {
1161
1183
  @input="onInput"
1162
1184
  @change="$emit('change', $event)"
1163
1185
  @keyup.enter="$emit('enter', $event)"
1164
- @focus="$emit('focus', $event)"
1186
+ @focus="onFocus"
1165
1187
  @blur="$emit('blur', $event)"
1166
1188
  />
1167
1189
  `
@@ -1286,7 +1308,6 @@ var zh_CN_default = {
1286
1308
  "richEditor.italic": "\u659C\u4F53",
1287
1309
  "richEditor.underline": "\u4E0B\u5212\u7EBF",
1288
1310
  "richEditor.strike": "\u5220\u9664\u7EBF",
1289
- "richEditor.heading": "\u4E8C\u7EA7\u6807\u9898",
1290
1311
  "richEditor.paragraph": "\u6B63\u6587",
1291
1312
  "richEditor.bulletList": "\u65E0\u5E8F\u5217\u8868",
1292
1313
  "richEditor.numberList": "\u6709\u5E8F\u5217\u8868",
@@ -1297,6 +1318,39 @@ var zh_CN_default = {
1297
1318
  "richEditor.redo": "\u91CD\u505A",
1298
1319
  "richEditor.source": "\u6E90\u7801",
1299
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",
1300
1354
  "approval.start": "\u53D1\u8D77",
1301
1355
  "approval.cc": "\u6284\u9001",
1302
1356
  "approval.approved": "\u5DF2\u901A\u8FC7",
@@ -1505,7 +1559,6 @@ var en_US_default = {
1505
1559
  "richEditor.italic": "Italic",
1506
1560
  "richEditor.underline": "Underline",
1507
1561
  "richEditor.strike": "Strikethrough",
1508
- "richEditor.heading": "Heading 2",
1509
1562
  "richEditor.paragraph": "Paragraph",
1510
1563
  "richEditor.bulletList": "Bullet list",
1511
1564
  "richEditor.numberList": "Numbered list",
@@ -1516,6 +1569,39 @@ var en_US_default = {
1516
1569
  "richEditor.redo": "Redo",
1517
1570
  "richEditor.source": "Source",
1518
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",
1519
1605
  "approval.start": "Started",
1520
1606
  "approval.cc": "CC",
1521
1607
  "approval.approved": "Approved",
@@ -9334,7 +9420,199 @@ var tree_select_default = {
9334
9420
  };
9335
9421
 
9336
9422
  // src/components/rich-editor/index.js
9337
- 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
+ };
9338
9616
 
9339
9617
  // src/utils/sanitize-html.js
9340
9618
  var TAGS = /* @__PURE__ */ new Set([
@@ -9374,7 +9652,18 @@ var TAGS = /* @__PURE__ */ new Set([
9374
9652
  "th",
9375
9653
  "td"
9376
9654
  ]);
9377
- 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
+ ]);
9378
9667
  var TAG_ATTRS = {
9379
9668
  a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
9380
9669
  img: /* @__PURE__ */ new Set(["src", "alt", "width", "height"]),
@@ -9382,11 +9671,29 @@ var TAG_ATTRS = {
9382
9671
  th: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope"]),
9383
9672
  td: /* @__PURE__ */ new Set(["colspan", "rowspan"])
9384
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"]);
9385
9686
  function safeUrl(value, tag) {
9386
- const url = String(value || "").replace(/[\u0000-\u0020]+/g, "").toLowerCase();
9387
- if (url.startsWith("javascript:") || url.startsWith("vbscript:")) return false;
9388
- if (url.startsWith("data:")) return tag === "img" && url.startsWith("data:image/");
9389
- 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;
9390
9697
  }
9391
9698
  function cleanNode(node) {
9392
9699
  const children = [...node.childNodes];
@@ -9409,6 +9716,12 @@ function cleanNode(node) {
9409
9716
  child.removeAttribute(attr.name);
9410
9717
  return;
9411
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
+ }
9412
9725
  if ((name === "href" || name === "src") && !safeUrl(attr.value, tag)) {
9413
9726
  child.removeAttribute(attr.name);
9414
9727
  }
@@ -9429,79 +9742,475 @@ function sanitizeHtml(html) {
9429
9742
  }
9430
9743
 
9431
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
+ }
9432
9783
  var rich_editor_default = {
9433
9784
  name: "DjmRichEditor",
9434
- components: { Icon: icon_default },
9785
+ components: { Icon: icon_default, MediaPicker: media_picker_default },
9435
9786
  props: {
9436
9787
  modelValue: { type: String, default: "" },
9437
9788
  placeholder: { type: String, default: "" },
9438
9789
  minHeight: { type: Number, default: 200 },
9439
- // 开启后对外输出的 HTML 会走白名单清洗
9440
- sanitize: { type: Boolean, default: false },
9441
- 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 }
9442
9805
  },
9443
- emits: ["update:modelValue"],
9806
+ emits: ["update:modelValue", "upload-error"],
9444
9807
  setup(props, { emit }) {
9445
- const editor = ref27(null);
9446
- const sourceMode = ref27(false);
9447
- const source = ref27(props.modelValue);
9448
- 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
+ );
9449
9851
  function clean(html) {
9450
9852
  return props.sanitize ? sanitizeHtml(html) : html;
9451
9853
  }
9854
+ function plainText(html) {
9855
+ return String(html || "").replace(/<[^>]*>/g, "").replace(/&nbsp;/g, " ").replace(/&[a-z]+;/gi, " ");
9856
+ }
9452
9857
  function syncFromDom() {
9453
9858
  if (!editor.value) return;
9454
- 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);
9455
9862
  }
9456
- function onPaste(event) {
9457
- const data = event.clipboardData;
9458
- const html = data && data.getData("text/html");
9459
- if (!html) return;
9460
- event.preventDefault();
9461
- 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);
9462
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();
9463
9902
  }
9464
9903
  function exec(command, value = null) {
9465
- 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);
9466
9908
  document.execCommand(command, false, value);
9467
9909
  if (editor.value) editor.value.focus();
9468
9910
  syncFromDom();
9469
9911
  syncFormats();
9470
9912
  }
9471
- function formatBlock(tag) {
9472
- 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();
9473
9994
  }
9474
9995
  function addLink() {
9996
+ if (!ready.value) return;
9475
9997
  const url = window.prompt(t("richEditor.linkPrompt"), "https://");
9476
- 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();
9477
10021
  }
9478
- function syncFormats() {
9479
- if (props.disabled || sourceMode.value) return;
9480
- if (!editor.value || !editor.value.contains(document.activeElement)) return;
9481
- formats.value = {
9482
- bold: document.queryCommandState("bold"),
9483
- italic: document.queryCommandState("italic"),
9484
- underline: document.queryCommandState("underline"),
9485
- strike: document.queryCommandState("strikeThrough")
9486
- };
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
+ });
9487
10029
  }
9488
- function onSourceInput() {
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";
10163
+ }
10164
+ function syncFormats() {
10165
+ if (props.disabled || sourceMode.value || !editor.value) return;
10166
+ if (!editor.value.contains(document.activeElement)) return;
10167
+ const selection = window.getSelection();
10168
+ formats.value = {
10169
+ bold: document.queryCommandState("bold"),
10170
+ italic: document.queryCommandState("italic"),
10171
+ underline: document.queryCommandState("underline"),
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" : ""
10177
+ };
10178
+ }
10179
+ function onSourceInput() {
10180
+ count2.value = plainText(source.value).trim().length;
9489
10181
  emit("update:modelValue", clean(source.value));
9490
10182
  }
9491
10183
  function toggleSource() {
9492
10184
  if (props.disabled) return;
10185
+ panel.value = "";
9493
10186
  if (sourceMode.value) {
9494
10187
  emit("update:modelValue", clean(source.value));
9495
10188
  sourceMode.value = false;
9496
10189
  requestAnimationFrame(() => {
9497
10190
  if (editor.value) editor.value.innerHTML = clean(source.value);
10191
+ syncFromDom();
9498
10192
  });
9499
10193
  } else {
9500
10194
  source.value = editor.value ? editor.value.innerHTML : props.modelValue;
9501
10195
  sourceMode.value = true;
9502
10196
  }
9503
10197
  }
9504
- 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(
9505
10214
  () => props.modelValue,
9506
10215
  (value) => {
9507
10216
  if (sourceMode.value) {
@@ -9510,40 +10219,117 @@ var rich_editor_default = {
9510
10219
  }
9511
10220
  const next = clean(value);
9512
10221
  if (editor.value && editor.value.innerHTML !== next) editor.value.innerHTML = next;
10222
+ count2.value = plainText(next).trim().length;
9513
10223
  }
9514
10224
  );
9515
10225
  onMounted15(() => {
9516
10226
  if (editor.value) editor.value.innerHTML = clean(props.modelValue);
10227
+ count2.value = plainText(props.modelValue).trim().length;
9517
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);
9518
10236
  });
9519
- onBeforeUnmount23(() => document.removeEventListener("selectionchange", syncFormats));
9520
10237
  return {
9521
10238
  editor,
10239
+ fileInput,
9522
10240
  sourceMode,
9523
10241
  source,
10242
+ fullscreen,
10243
+ panel,
10244
+ uploadCount,
10245
+ textColor,
10246
+ bgColor,
10247
+ fontSize,
10248
+ count: count2,
9524
10249
  formats,
10250
+ blocks: BLOCKS,
10251
+ sizes: SIZES,
10252
+ colors: COLORS,
10253
+ mediaLibrary,
10254
+ has,
9525
10255
  exec,
9526
- formatBlock,
10256
+ setBlock,
10257
+ setFontSize,
10258
+ applyColor,
10259
+ togglePanel,
10260
+ toggleInlineCode,
9527
10261
  addLink,
9528
- onSourceInput,
10262
+ pickImage,
10263
+ insertMedia,
10264
+ onFilesChange,
9529
10265
  onPaste,
10266
+ onDragOver,
10267
+ onDrop,
10268
+ rememberRange,
10269
+ onSourceInput,
9530
10270
  toggleSource,
10271
+ toggleFullscreen,
9531
10272
  syncFromDom,
9532
10273
  syncFormats,
9533
10274
  t
9534
10275
  };
9535
10276
  },
9536
10277
  template: `
9537
- <div class="djm-rich-editor" :class="{ 'djm-is-disabled': disabled }">
9538
- <div class="djm-rich-editor-toolbar">
9539
- <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')">
9540
10326
  <button
9541
10327
  class="djm-rich-editor-btn djm-rich-editor-btn-bold"
9542
10328
  type="button"
9543
10329
  :title="t('richEditor.bold')"
9544
10330
  :aria-label="t('richEditor.bold')"
9545
10331
  :aria-pressed="String(formats.bold)"
9546
- :disabled="disabled"
10332
+ :disabled="disabled || sourceMode"
9547
10333
  @mousedown.prevent
9548
10334
  @click="exec('bold')"
9549
10335
  >B</button>
@@ -9553,7 +10339,7 @@ var rich_editor_default = {
9553
10339
  :title="t('richEditor.italic')"
9554
10340
  :aria-label="t('richEditor.italic')"
9555
10341
  :aria-pressed="String(formats.italic)"
9556
- :disabled="disabled"
10342
+ :disabled="disabled || sourceMode"
9557
10343
  @mousedown.prevent
9558
10344
  @click="exec('italic')"
9559
10345
  >I</button>
@@ -9563,7 +10349,7 @@ var rich_editor_default = {
9563
10349
  :title="t('richEditor.underline')"
9564
10350
  :aria-label="t('richEditor.underline')"
9565
10351
  :aria-pressed="String(formats.underline)"
9566
- :disabled="disabled"
10352
+ :disabled="disabled || sourceMode"
9567
10353
  @mousedown.prevent
9568
10354
  @click="exec('underline')"
9569
10355
  >U</button>
@@ -9573,46 +10359,130 @@ var rich_editor_default = {
9573
10359
  :title="t('richEditor.strike')"
9574
10360
  :aria-label="t('richEditor.strike')"
9575
10361
  :aria-pressed="String(formats.strike)"
9576
- :disabled="disabled"
10362
+ :disabled="disabled || sourceMode"
9577
10363
  @mousedown.prevent
9578
10364
  @click="exec('strikeThrough')"
9579
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>
9580
10376
  </div>
9581
10377
 
9582
- <div class="djm-rich-editor-group">
9583
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.heading')" :disabled="disabled" @mousedown.prevent @click="formatBlock('h2')">H2</button>
9584
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.paragraph')" :disabled="disabled" @mousedown.prevent @click="formatBlock('p')">{{ t('richEditor.paragraph') }}</button>
9585
- </div>
9586
-
9587
- <div class="djm-rich-editor-group">
9588
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.bulletList')" :disabled="disabled" @mousedown.prevent @click="exec('insertUnorderedList')">
9589
- <span class="djm-rich-editor-glyph">\u2022 \u2261</span>
9590
- </button>
9591
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.numberList')" :disabled="disabled" @mousedown.prevent @click="exec('insertOrderedList')">
9592
- <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>
9593
10390
  </button>
9594
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.link')" :aria-label="t('richEditor.link')" :disabled="disabled" @mousedown.prevent @click="addLink">
9595
- <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>
9596
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>
9597
10416
  </div>
9598
10417
 
9599
- <div class="djm-rich-editor-group">
9600
- <button class="djm-rich-editor-btn" type="button" :title="t('richEditor.clearFormat')" :disabled="disabled" @mousedown.prevent @click="exec('removeFormat')">{{ t('richEditor.clearFormat') }}</button>
9601
- <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>
9602
- <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>
9603
10423
  </div>
9604
10424
 
9605
- <div class="djm-rich-editor-spacer"></div>
9606
- <button
9607
- class="djm-rich-editor-btn"
9608
- :class="{ 'djm-is-active': sourceMode }"
9609
- type="button"
9610
- :aria-pressed="String(sourceMode)"
9611
- :disabled="disabled"
9612
- @click="toggleSource"
9613
- >
9614
- <Icon name="code" :size="13" />{{ sourceMode ? t('richEditor.visual') : t('richEditor.source') }}
9615
- </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>
9616
10486
  </div>
9617
10487
 
9618
10488
  <div
@@ -9628,6 +10498,8 @@ var rich_editor_default = {
9628
10498
  :style="{ minHeight: minHeight + 'px' }"
9629
10499
  @input="syncFromDom"
9630
10500
  @paste="onPaste"
10501
+ @dragover="onDragOver"
10502
+ @drop="onDrop"
9631
10503
  ></div>
9632
10504
 
9633
10505
  <textarea
@@ -9640,12 +10512,27 @@ var rich_editor_default = {
9640
10512
  spellcheck="false"
9641
10513
  @input="onSourceInput"
9642
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
+ />
9643
10530
  </div>
9644
10531
  `
9645
10532
  };
9646
10533
 
9647
10534
  // src/components/floating-window/index.js
9648
- import { ref as ref28, onBeforeUnmount as onBeforeUnmount24 } from "vue";
10535
+ import { ref as ref29, onBeforeUnmount as onBeforeUnmount24 } from "vue";
9649
10536
  function clamp(value, min, max) {
9650
10537
  return Math.min(Math.max(value, min), Math.max(min, max));
9651
10538
  }
@@ -9663,12 +10550,12 @@ var floating_window_default = {
9663
10550
  },
9664
10551
  emits: ["close", "focus"],
9665
10552
  setup(props, { emit }) {
9666
- const root = ref28(null);
9667
- const pos = ref28({ x: props.initialX, y: props.initialY });
9668
- const size = ref28({ w: props.width, h: props.height });
9669
- const minimized = ref28(false);
9670
- const maximized = ref28(false);
9671
- 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);
9672
10559
  let drag = null;
9673
10560
  let resize = null;
9674
10561
  function stage() {
@@ -9804,7 +10691,7 @@ var floating_window_default = {
9804
10691
  };
9805
10692
 
9806
10693
  // src/components/upload/index.js
9807
- 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";
9808
10695
  var uidSeed6 = 0;
9809
10696
  function extension(name) {
9810
10697
  const index = name.lastIndexOf(".");
@@ -9854,11 +10741,11 @@ var upload_default = {
9854
10741
  },
9855
10742
  emits: ["update:modelValue", "change", "success", "error", "remove", "exceed"],
9856
10743
  setup(props, { emit }) {
9857
- const files = ref29([]);
9858
- const dragActive = ref29(false);
9859
- const inputRef = ref29(null);
10744
+ const files = ref30([]);
10745
+ const dragActive = ref30(false);
10746
+ const inputRef = ref30(null);
9860
10747
  const tasks = /* @__PURE__ */ new Map();
9861
- watch20(
10748
+ watch21(
9862
10749
  () => props.modelValue,
9863
10750
  (list) => {
9864
10751
  const next = list || [];
@@ -10160,7 +11047,7 @@ var upload_default = {
10160
11047
  };
10161
11048
 
10162
11049
  // src/components/tree/index.js
10163
- 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";
10164
11051
  var tree_default = {
10165
11052
  name: "DjmTree",
10166
11053
  components: { Icon: icon_default },
@@ -10192,17 +11079,17 @@ var tree_default = {
10192
11079
  },
10193
11080
  emits: ["update:checked", "update:expanded", "node-click", "check", "node-drop", "load-error"],
10194
11081
  setup(props, { emit }) {
10195
- const lazyChildren = ref30({});
10196
- const loadingKeys = ref30([]);
10197
- const innerExpanded = ref30([...props.expanded]);
10198
- const innerChecked = ref30([...props.checked]);
10199
- watch21(
11082
+ const lazyChildren = ref31({});
11083
+ const loadingKeys = ref31([]);
11084
+ const innerExpanded = ref31([...props.expanded]);
11085
+ const innerChecked = ref31([...props.checked]);
11086
+ watch22(
10200
11087
  () => props.expanded,
10201
11088
  (value) => {
10202
11089
  if (Array.isArray(value)) innerExpanded.value = value;
10203
11090
  }
10204
11091
  );
10205
- watch21(
11092
+ watch22(
10206
11093
  () => props.checked,
10207
11094
  (value) => {
10208
11095
  if (Array.isArray(value)) innerChecked.value = value;
@@ -10240,7 +11127,7 @@ var tree_default = {
10240
11127
  });
10241
11128
  return out;
10242
11129
  }
10243
- watch21(
11130
+ watch22(
10244
11131
  () => props.data,
10245
11132
  () => {
10246
11133
  if (props.defaultExpandAll) innerExpanded.value = collectExpandable(props.data);
@@ -10257,7 +11144,7 @@ var tree_default = {
10257
11144
  const children = childrenOf(node);
10258
11145
  return Boolean(children && children.some((child) => matchSubtree(child)));
10259
11146
  }
10260
- const rows = computed16(() => {
11147
+ const rows = computed18(() => {
10261
11148
  const result = [];
10262
11149
  const walk = (nodes, level) => {
10263
11150
  nodes.forEach((node) => {
@@ -10328,8 +11215,8 @@ var tree_default = {
10328
11215
  function isActive(node) {
10329
11216
  return props.activeKey !== "" && node[props.nodeKey] === props.activeKey;
10330
11217
  }
10331
- const rootEl = ref30(null);
10332
- const focusIndex = ref30(-1);
11218
+ const rootEl = ref31(null);
11219
+ const focusIndex = ref31(-1);
10333
11220
  function tabIndexOf(index) {
10334
11221
  return (focusIndex.value === -1 ? 0 : focusIndex.value) === index ? 0 : -1;
10335
11222
  }
@@ -10386,9 +11273,9 @@ var tree_default = {
10386
11273
  function rowClassOf(node, level) {
10387
11274
  return typeof props.rowClass === "function" ? props.rowClass(node, level) : props.rowClass;
10388
11275
  }
10389
- const dragKey = ref30("");
10390
- const dropKey = ref30("");
10391
- const dropPosition = ref30("");
11276
+ const dragKey = ref31("");
11277
+ const dropKey = ref31("");
11278
+ const dropPosition = ref31("");
10392
11279
  function findNodeByKey(key, nodes = props.data) {
10393
11280
  for (const node of nodes) {
10394
11281
  if (node[props.nodeKey] === key) return node;
@@ -10500,7 +11387,7 @@ var tree_default = {
10500
11387
  onDrop,
10501
11388
  onDragEnd,
10502
11389
  labelOf,
10503
- emptyLabel: computed16(() => props.emptyText || t("tree.empty")),
11390
+ emptyLabel: computed18(() => props.emptyText || t("tree.empty")),
10504
11391
  t
10505
11392
  };
10506
11393
  },
@@ -10569,7 +11456,7 @@ var tree_default = {
10569
11456
  };
10570
11457
 
10571
11458
  // src/components/editable-table/index.js
10572
- 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";
10573
11460
  var ACTIONS_KEY = "__editable_actions";
10574
11461
  function isEmpty(value) {
10575
11462
  if (value === "" || value === null || value === void 0) return true;
@@ -10593,12 +11480,12 @@ var editable_table_default = {
10593
11480
  emits: ["edit", "save", "cancel"],
10594
11481
  setup(props, { emit }) {
10595
11482
  const slots = useSlots();
10596
- const editingKey = ref31("");
11483
+ const editingKey = ref32("");
10597
11484
  const draft = reactive5({});
10598
11485
  const errors = reactive5({});
10599
- const tableRef = ref31(null);
10600
- const editableColumns = computed17(() => props.columns.filter((column) => column.editor));
10601
- const renderColumns = computed17(() => {
11486
+ const tableRef = ref32(null);
11487
+ const editableColumns = computed19(() => props.columns.filter((column) => column.editor));
11488
+ const renderColumns = computed19(() => {
10602
11489
  if (!props.showActions) return props.columns;
10603
11490
  return props.columns.concat({
10604
11491
  key: ACTIONS_KEY,
@@ -10610,7 +11497,7 @@ var editable_table_default = {
10610
11497
  exportable: false
10611
11498
  });
10612
11499
  });
10613
- const forwardedSlotNames = computed17(() => {
11500
+ const forwardedSlotNames = computed19(() => {
10614
11501
  const taken = new Set(editableColumns.value.map((column) => "cell-" + column.key));
10615
11502
  taken.add("cell-" + ACTIONS_KEY);
10616
11503
  return Object.keys(slots).filter((name) => !taken.has(name));
@@ -10705,7 +11592,7 @@ var editable_table_default = {
10705
11592
  clearErrors();
10706
11593
  return true;
10707
11594
  }
10708
- watch22(
11595
+ watch23(
10709
11596
  () => props.rows,
10710
11597
  () => {
10711
11598
  if (editingKey.value && !findRow(editingKey.value)) {
@@ -10887,7 +11774,7 @@ var message_default = {
10887
11774
  };
10888
11775
 
10889
11776
  // src/components/org-picker/index.js
10890
- 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";
10891
11778
  var org_picker_default = {
10892
11779
  name: "DjmOrgPicker",
10893
11780
  components: { DjmIcon: icon_default },
@@ -10906,13 +11793,13 @@ var org_picker_default = {
10906
11793
  },
10907
11794
  emits: ["update:modelValue", "change", "visible-change"],
10908
11795
  setup(props, { emit }) {
10909
- const open = ref32(false);
10910
- const root = ref32(null);
10911
- const searchRef = ref32(null);
10912
- const keyword = ref32("");
10913
- const activeDept = ref32("");
10914
- const temp = ref32([]);
10915
- 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());
10916
11803
  let detachPanel = null;
10917
11804
  function collectDeptIds(nodes, out = []) {
10918
11805
  nodes.forEach((node) => {
@@ -10942,22 +11829,22 @@ var org_picker_default = {
10942
11829
  walk([root2]);
10943
11830
  return ids;
10944
11831
  }
10945
- const staffMap = computed18(() => {
11832
+ const staffMap = computed20(() => {
10946
11833
  const map = /* @__PURE__ */ new Map();
10947
11834
  props.staff.forEach((item) => map.set(item.id, item));
10948
11835
  return map;
10949
11836
  });
10950
- const appliedStaff = computed18(
11837
+ const appliedStaff = computed20(
10951
11838
  () => props.modelValue.map((id) => staffMap.value.get(id)).filter(Boolean)
10952
11839
  );
10953
- const visibleTags = computed18(() => {
11840
+ const visibleTags = computed20(() => {
10954
11841
  if (props.maxTagCount <= 0) return appliedStaff.value;
10955
11842
  return appliedStaff.value.slice(0, props.maxTagCount);
10956
11843
  });
10957
- const restTagCount = computed18(
11844
+ const restTagCount = computed20(
10958
11845
  () => Math.max(0, appliedStaff.value.length - visibleTags.value.length)
10959
11846
  );
10960
- const visibleStaff = computed18(() => {
11847
+ const visibleStaff = computed20(() => {
10961
11848
  const key = keyword.value.trim().toLowerCase();
10962
11849
  if (key) {
10963
11850
  return props.staff.filter(
@@ -10968,7 +11855,7 @@ var org_picker_default = {
10968
11855
  const scope = deptScope(activeDept.value);
10969
11856
  return props.staff.filter((item) => scope.has(item.deptId));
10970
11857
  });
10971
- const deptRows = computed18(() => {
11858
+ const deptRows = computed20(() => {
10972
11859
  const rows = [];
10973
11860
  const walk = (nodes, depth) => {
10974
11861
  nodes.forEach((node) => {
@@ -10980,11 +11867,11 @@ var org_picker_default = {
10980
11867
  walk(props.departments, 0);
10981
11868
  return rows;
10982
11869
  });
10983
- const selectedCount = computed18(() => temp.value.length);
10984
- const placeholderText = computed18(() => props.placeholder || t("orgPicker.placeholder"));
10985
- const emptyLabel = computed18(() => props.emptyText || t("orgPicker.empty"));
10986
- const panel = ref32(null);
10987
- 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 });
10988
11875
  const PANEL_HEIGHT = 342;
10989
11876
  function updatePosition() {
10990
11877
  if (!open.value || !root.value) return;
@@ -11001,7 +11888,7 @@ var org_picker_default = {
11001
11888
  expandedIds.value = new Set(collectDeptIds(props.departments));
11002
11889
  }
11003
11890
  expandAll();
11004
- watch23(() => props.departments, () => {
11891
+ watch24(() => props.departments, () => {
11005
11892
  expandedIds.value = /* @__PURE__ */ new Set([...expandedIds.value, ...collectDeptIds(props.departments)]);
11006
11893
  });
11007
11894
  function initialOf(item) {
@@ -11276,7 +12163,7 @@ var org_picker_default = {
11276
12163
  };
11277
12164
 
11278
12165
  // src/components/schema-form/index.js
11279
- 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";
11280
12167
  var CONTROL_MAP = {
11281
12168
  input: input_default,
11282
12169
  number: input_number_default,
@@ -11312,26 +12199,26 @@ var schema_form_default = {
11312
12199
  },
11313
12200
  emits: ["update:modelValue", "change"],
11314
12201
  setup(props, { emit }) {
11315
- const innerForm = ref33(null);
12202
+ const innerForm = ref34(null);
11316
12203
  const model = reactive7({ ...props.modelValue });
11317
- watch24(() => props.fields, (fields) => {
12204
+ watch25(() => props.fields, (fields) => {
11318
12205
  fields.forEach((field) => {
11319
12206
  if (model[field.key] === void 0 && field.default !== void 0) {
11320
12207
  model[field.key] = typeof field.default === "function" ? field.default() : field.default;
11321
12208
  }
11322
12209
  });
11323
12210
  }, { immediate: true, deep: true });
11324
- watch24(() => props.modelValue, (value) => {
12211
+ watch25(() => props.modelValue, (value) => {
11325
12212
  if (!value) return;
11326
12213
  Object.keys(value).forEach((key) => {
11327
12214
  if (model[key] !== value[key]) model[key] = value[key];
11328
12215
  });
11329
12216
  }, { deep: true });
11330
- watch24(model, () => {
12217
+ watch25(model, () => {
11331
12218
  emit("update:modelValue", { ...model });
11332
12219
  emit("change", { ...model });
11333
12220
  }, { deep: true });
11334
- const formRules = computed19(() => {
12221
+ const formRules = computed21(() => {
11335
12222
  const merged = { ...props.rules };
11336
12223
  props.fields.forEach((field) => {
11337
12224
  if (!field.required && !field.rules) return;
@@ -11344,7 +12231,7 @@ var schema_form_default = {
11344
12231
  });
11345
12232
  return merged;
11346
12233
  });
11347
- const visibleFields = computed19(
12234
+ const visibleFields = computed21(
11348
12235
  () => props.fields.filter((field) => {
11349
12236
  if (typeof field.visibleWhen !== "function") return true;
11350
12237
  return Boolean(field.visibleWhen(model));
@@ -11538,7 +12425,7 @@ var approval_flow_default = {
11538
12425
  };
11539
12426
 
11540
12427
  // src/components/query-filter/index.js
11541
- 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";
11542
12429
  var CONTROL_MAP2 = {
11543
12430
  input: input_default,
11544
12431
  number: input_number_default,
@@ -11563,26 +12450,26 @@ var query_filter_default = {
11563
12450
  },
11564
12451
  emits: ["update:modelValue", "search", "reset"],
11565
12452
  setup(props, { emit }) {
11566
- const expanded = ref34(false);
12453
+ const expanded = ref35(false);
11567
12454
  const model = reactive8({ ...props.modelValue });
11568
- watch25(() => props.fields, (fields) => {
12455
+ watch26(() => props.fields, (fields) => {
11569
12456
  fields.forEach((field) => {
11570
12457
  if (model[field.key] === void 0 && field.default !== void 0) {
11571
12458
  model[field.key] = typeof field.default === "function" ? field.default() : field.default;
11572
12459
  }
11573
12460
  });
11574
12461
  }, { immediate: true, deep: true });
11575
- watch25(() => props.modelValue, (value) => {
12462
+ watch26(() => props.modelValue, (value) => {
11576
12463
  if (!value) return;
11577
12464
  Object.keys(value).forEach((key) => {
11578
12465
  if (model[key] !== value[key]) model[key] = value[key];
11579
12466
  });
11580
12467
  }, { deep: true });
11581
- watch25(model, () => {
12468
+ watch26(model, () => {
11582
12469
  emit("update:modelValue", { ...model });
11583
12470
  }, { deep: true });
11584
- const collapsible = computed20(() => props.fields.length > props.visibleCount);
11585
- const visibleFields = computed20(() => {
12471
+ const collapsible = computed22(() => props.fields.length > props.visibleCount);
12472
+ const visibleFields = computed22(() => {
11586
12473
  if (!collapsible.value || expanded.value) return props.fields;
11587
12474
  return props.fields.slice(0, props.visibleCount);
11588
12475
  });
@@ -11711,198 +12598,6 @@ var filter_tags_default = {
11711
12598
  `
11712
12599
  };
11713
12600
 
11714
- // src/components/media-picker/index.js
11715
- import { computed as computed21, ref as ref35, watch as watch26 } from "vue";
11716
- var media_picker_default = {
11717
- name: "DjmMediaPicker",
11718
- components: { DjmModal: modal_default, DjmPagination: pagination_default, DjmIcon: icon_default },
11719
- props: {
11720
- modelValue: { type: [Array, String, Number], default: "" },
11721
- items: { type: Array, default: () => [] },
11722
- multiple: { type: Boolean, default: false },
11723
- // 最多选几个,0 表示不限
11724
- limit: { type: Number, default: 0 },
11725
- // image 只显示图片,file 只显示非图片,空串不过滤
11726
- accept: { type: String, default: "" },
11727
- title: { type: String, default: "" },
11728
- triggerText: { type: String, default: "" },
11729
- pageSize: { type: Number, default: 12 },
11730
- disabled: { type: Boolean, default: false }
11731
- },
11732
- emits: ["update:modelValue", "change"],
11733
- setup(props, { emit }) {
11734
- const open = ref35(false);
11735
- const keyword = ref35("");
11736
- const page = ref35(1);
11737
- const temp = ref35([]);
11738
- const selectedIds = computed21(() => {
11739
- if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
11740
- if (props.modelValue === "" || props.modelValue === null || props.modelValue === void 0) return [];
11741
- return [props.modelValue];
11742
- });
11743
- function isImage(item) {
11744
- if (item.type) return item.type === "image";
11745
- return /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(item.url || "");
11746
- }
11747
- const filtered = computed21(() => {
11748
- const key = keyword.value.trim().toLowerCase();
11749
- return props.items.filter((item) => {
11750
- if (props.accept === "image" && !isImage(item)) return false;
11751
- if (props.accept === "file" && isImage(item)) return false;
11752
- if (key && !String(item.name || "").toLowerCase().includes(key)) return false;
11753
- return true;
11754
- });
11755
- });
11756
- const paged = computed21(() => {
11757
- const start = (page.value - 1) * props.pageSize;
11758
- return filtered.value.slice(start, start + props.pageSize);
11759
- });
11760
- watch26(keyword, () => {
11761
- page.value = 1;
11762
- });
11763
- watch26(
11764
- () => filtered.value.length,
11765
- () => {
11766
- const max = Math.max(1, Math.ceil(filtered.value.length / props.pageSize));
11767
- if (page.value > max) page.value = max;
11768
- }
11769
- );
11770
- function isChecked(id) {
11771
- return temp.value.includes(id);
11772
- }
11773
- function atLimit() {
11774
- return props.limit > 0 && temp.value.length >= props.limit;
11775
- }
11776
- function toggle(item) {
11777
- if (props.multiple) {
11778
- if (isChecked(item.id)) {
11779
- temp.value = temp.value.filter((id) => id !== item.id);
11780
- return;
11781
- }
11782
- if (atLimit()) return;
11783
- temp.value = [...temp.value, item.id];
11784
- return;
11785
- }
11786
- temp.value = [item.id];
11787
- }
11788
- function openPanel() {
11789
- if (props.disabled) return;
11790
- temp.value = [...selectedIds.value];
11791
- keyword.value = "";
11792
- page.value = 1;
11793
- open.value = true;
11794
- }
11795
- function commit() {
11796
- const next = props.multiple ? [...temp.value] : temp.value[0] !== void 0 ? temp.value[0] : "";
11797
- emit("update:modelValue", next);
11798
- emit("change", next);
11799
- open.value = false;
11800
- }
11801
- function cancel() {
11802
- temp.value = [...selectedIds.value];
11803
- open.value = false;
11804
- }
11805
- return {
11806
- open,
11807
- keyword,
11808
- page,
11809
- temp,
11810
- selectedIds,
11811
- filtered,
11812
- paged,
11813
- isImage,
11814
- isChecked,
11815
- atLimit,
11816
- toggle,
11817
- openPanel,
11818
- commit,
11819
- cancel,
11820
- t
11821
- };
11822
- },
11823
- template: `
11824
- <span class="djm-media-picker">
11825
- <slot name="trigger" :open="openPanel" :count="selectedIds.length">
11826
- <button
11827
- class="djm-btn"
11828
- type="button"
11829
- :disabled="disabled"
11830
- @click="openPanel"
11831
- >{{ triggerText || t('mediaPicker.select') }}<span v-if="selectedIds.length" class="djm-media-picker-count">{{ selectedIds.length }}</span></button>
11832
- </slot>
11833
-
11834
- <DjmModal
11835
- :model-value="open"
11836
- :title="title || t('mediaPicker.title')"
11837
- width="760px"
11838
- @update:model-value="cancel"
11839
- >
11840
- <div class="djm-media-toolbar">
11841
- <label class="djm-media-search">
11842
- <DjmIcon name="search" :size="13" />
11843
- <input
11844
- v-model="keyword"
11845
- class="djm-media-search-input"
11846
- type="text"
11847
- :placeholder="t('mediaPicker.search')"
11848
- :aria-label="t('mediaPicker.search')"
11849
- />
11850
- </label>
11851
- <span class="djm-media-toolbar-slot">
11852
- <slot name="toolbar"></slot>
11853
- </span>
11854
- </div>
11855
-
11856
- <div class="djm-media-grid" role="listbox" :aria-multiselectable="multiple ? 'true' : 'false'">
11857
- <div
11858
- v-for="item in paged"
11859
- :key="item.id"
11860
- class="djm-media-item"
11861
- :class="{
11862
- 'djm-is-checked': isChecked(item.id),
11863
- 'djm-is-disabled': multiple && atLimit() && !isChecked(item.id)
11864
- }"
11865
- role="option"
11866
- tabindex="0"
11867
- :aria-selected="String(isChecked(item.id))"
11868
- @click="toggle(item)"
11869
- @keydown.enter.prevent="toggle(item)"
11870
- @keydown.space.prevent="toggle(item)"
11871
- >
11872
- <div class="djm-media-thumb">
11873
- <img v-if="isImage(item)" :src="item.url" :alt="item.name" loading="lazy" />
11874
- <DjmIcon v-else name="folder" :size="26" />
11875
- <span v-if="isChecked(item.id)" class="djm-media-check">
11876
- <DjmIcon name="check" :size="11" />
11877
- </span>
11878
- </div>
11879
- <div class="djm-media-name" :title="item.name">{{ item.name }}</div>
11880
- </div>
11881
- <div v-if="!paged.length" class="djm-media-empty">{{ t('mediaPicker.empty') }}</div>
11882
- </div>
11883
-
11884
- <div class="djm-media-foot">
11885
- <span class="djm-media-total">
11886
- {{ t('mediaPicker.total', { total: filtered.length }) }}
11887
- <em v-if="multiple && limit > 0">{{ t('mediaPicker.limit', { limit }) }}</em>
11888
- </span>
11889
- <DjmPagination
11890
- v-if="filtered.length > pageSize"
11891
- v-model:page="page"
11892
- :page-size="pageSize"
11893
- :total="filtered.length"
11894
- />
11895
- </div>
11896
-
11897
- <template #footer>
11898
- <button class="djm-btn" type="button" @click="cancel">{{ t('common.cancel') }}</button>
11899
- <button class="djm-btn djm-btn-primary" type="button" @click="commit">{{ t('common.confirm') }}</button>
11900
- </template>
11901
- </DjmModal>
11902
- </span>
11903
- `
11904
- };
11905
-
11906
12601
  // src/components/kanban/index.js
11907
12602
  import { ref as ref36, watch as watch27 } from "vue";
11908
12603
  function cloneColumns(list) {
@@ -12092,7 +12787,7 @@ var kanban_default = {
12092
12787
  };
12093
12788
 
12094
12789
  // src/components/attachment-list/index.js
12095
- import { computed as computed22 } from "vue";
12790
+ import { computed as computed23 } from "vue";
12096
12791
  var IMAGE_EXTS = ["png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"];
12097
12792
  function extOf(file) {
12098
12793
  if (file.ext) return String(file.ext).toLowerCase();
@@ -12116,14 +12811,14 @@ var attachment_list_default = {
12116
12811
  },
12117
12812
  emits: ["preview", "download", "remove"],
12118
12813
  setup(props, { emit }) {
12119
- const list = computed22(
12814
+ const list = computed23(
12120
12815
  () => props.files.map((file, index) => {
12121
12816
  const ext = extOf(file);
12122
12817
  const isImage = file.type === "image" || IMAGE_EXTS.includes(ext);
12123
12818
  return { ...file, _key: file.uid !== void 0 ? file.uid : index, _ext: ext, _isImage: isImage };
12124
12819
  })
12125
12820
  );
12126
- const emptyLabel = computed22(() => props.emptyText || t("attachment.empty"));
12821
+ const emptyLabel = computed23(() => props.emptyText || t("attachment.empty"));
12127
12822
  function onPreview(file) {
12128
12823
  emit("preview", file);
12129
12824
  }
@@ -12229,7 +12924,7 @@ var attachment_list_default = {
12229
12924
  };
12230
12925
 
12231
12926
  // src/components/notification/index.js
12232
- 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";
12233
12928
  var TYPE_MAP = {
12234
12929
  info: { icon: "info", cls: "djm-is-info" },
12235
12930
  success: { icon: "check", cls: "djm-is-success" },
@@ -12249,8 +12944,8 @@ var notification_default = {
12249
12944
  },
12250
12945
  emits: ["click", "read", "read-all", "remove", "clear"],
12251
12946
  setup(props, { emit }) {
12252
- const unreadCount = computed23(() => props.items.filter((item) => !item.read).length);
12253
- 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"));
12254
12949
  function typeOf(item) {
12255
12950
  return TYPE_MAP[item.type] || TYPE_MAP.info;
12256
12951
  }
@@ -12382,7 +13077,7 @@ var config_provider_default = {
12382
13077
  };
12383
13078
 
12384
13079
  // src/components/date-time-picker/index.js
12385
- 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";
12386
13081
  var uidSeed7 = 0;
12387
13082
  var pad4 = (value) => String(value).padStart(2, "0");
12388
13083
  function toDateKey(year, month, day) {
@@ -12424,22 +13119,22 @@ var date_time_picker_default = {
12424
13119
  let detachPanel = null;
12425
13120
  const today = /* @__PURE__ */ new Date();
12426
13121
  const todayKey = toDateKey(today.getFullYear(), today.getMonth(), today.getDate());
12427
- const hasSeconds = computed24(() => props.format.indexOf("ss") > -1);
13122
+ const hasSeconds = computed25(() => props.format.indexOf("ss") > -1);
12428
13123
  const draft = ref38({ date: "", h: "00", m: "00", s: "00" });
12429
13124
  const initial = parseDate(parseValue(props.modelValue).date);
12430
13125
  const viewYear = ref38(initial ? initial.year : today.getFullYear());
12431
13126
  const viewMonth = ref38(initial ? initial.month : today.getMonth());
12432
- const displayText = computed24(() => props.modelValue || "");
12433
- const placeholderText = computed24(() => props.placeholder || t("dateTimePicker.placeholder"));
12434
- const weeks = computed24(() => t("datePicker.weeks").split(","));
12435
- const hours = computed24(() => Array.from({ length: 24 }, (_, index) => pad4(index)));
12436
- 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(() => {
12437
13132
  const list = [];
12438
13133
  for (let value = 0; value < 60; value += Math.max(1, props.minuteStep)) list.push(pad4(value));
12439
13134
  return list;
12440
13135
  });
12441
- const seconds = computed24(() => Array.from({ length: 60 }, (_, index) => pad4(index)));
12442
- const timeCols = computed24(() => {
13136
+ const seconds = computed25(() => Array.from({ length: 60 }, (_, index) => pad4(index)));
13137
+ const timeCols = computed25(() => {
12443
13138
  const cols = [
12444
13139
  { key: "h", list: hours.value },
12445
13140
  { key: "m", list: minutes.value }
@@ -12447,8 +13142,8 @@ var date_time_picker_default = {
12447
13142
  if (hasSeconds.value) cols.push({ key: "s", list: seconds.value });
12448
13143
  return cols;
12449
13144
  });
12450
- const monthLabel = computed24(() => t("datePicker.title", { year: viewYear.value, month: viewMonth.value + 1 }));
12451
- const cells = computed24(() => {
13145
+ const monthLabel = computed25(() => t("datePicker.title", { year: viewYear.value, month: viewMonth.value + 1 }));
13146
+ const cells = computed25(() => {
12452
13147
  const first = new Date(viewYear.value, viewMonth.value, 1);
12453
13148
  const offset = (first.getDay() + 6) % 7;
12454
13149
  const start = new Date(viewYear.value, viewMonth.value, 1 - offset);
@@ -12709,7 +13404,7 @@ var date_time_picker_default = {
12709
13404
  };
12710
13405
 
12711
13406
  // src/components/virtual-list/index.js
12712
- 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";
12713
13408
  var virtual_list_default = {
12714
13409
  name: "DjmVirtualList",
12715
13410
  props: {
@@ -12733,7 +13428,7 @@ var virtual_list_default = {
12733
13428
  function rowHeight(item, index) {
12734
13429
  return typeof props.itemHeight === "function" ? Math.max(1, Number(props.itemHeight(item, index)) || 1) : Math.max(1, props.itemHeight);
12735
13430
  }
12736
- const offsets = computed25(() => {
13431
+ const offsets = computed26(() => {
12737
13432
  const list = [0];
12738
13433
  let sum = 0;
12739
13434
  props.items.forEach((item, index) => {
@@ -12742,7 +13437,7 @@ var virtual_list_default = {
12742
13437
  });
12743
13438
  return list;
12744
13439
  });
12745
- const totalHeight = computed25(() => offsets.value[offsets.value.length - 1]);
13440
+ const totalHeight = computed26(() => offsets.value[offsets.value.length - 1]);
12746
13441
  function findStart(top) {
12747
13442
  const list = offsets.value;
12748
13443
  let low = 0;
@@ -12754,7 +13449,7 @@ var virtual_list_default = {
12754
13449
  }
12755
13450
  return Math.max(0, low - 1);
12756
13451
  }
12757
- const range = computed25(() => {
13452
+ const range = computed26(() => {
12758
13453
  const total = props.items.length;
12759
13454
  if (!total) return { start: 0, end: 0 };
12760
13455
  const start = Math.max(0, findStart(scrollTop.value) - props.overscan);
@@ -12763,7 +13458,7 @@ var virtual_list_default = {
12763
13458
  while (end < total && offsets.value[end] < limit) end += 1;
12764
13459
  return { start, end: Math.min(total, end + props.overscan) };
12765
13460
  });
12766
- const visibleRows = computed25(() => {
13461
+ const visibleRows = computed26(() => {
12767
13462
  const { start, end } = range.value;
12768
13463
  return props.items.slice(start, end).map((item, index) => {
12769
13464
  const realIndex = start + index;
@@ -12860,7 +13555,7 @@ var virtual_list_default = {
12860
13555
  };
12861
13556
 
12862
13557
  // src/components/batch-bar/index.js
12863
- import { computed as computed26 } from "vue";
13558
+ import { computed as computed27 } from "vue";
12864
13559
  var batch_bar_default = {
12865
13560
  name: "DjmBatchBar",
12866
13561
  props: {
@@ -12875,8 +13570,8 @@ var batch_bar_default = {
12875
13570
  },
12876
13571
  emits: ["clear", "select-all"],
12877
13572
  setup(props, { emit }) {
12878
- const text = computed26(() => props.label || t("batchBar.selected", { count: props.count }));
12879
- 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);
12880
13575
  return {
12881
13576
  text,
12882
13577
  canSelectAll,
@@ -12904,7 +13599,7 @@ var batch_bar_default = {
12904
13599
  };
12905
13600
 
12906
13601
  // src/components/import-wizard/index.js
12907
- 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";
12908
13603
  function matchAccept2(name, accept) {
12909
13604
  if (!accept) return true;
12910
13605
  const ext = "." + String(name).split(".").pop().toLowerCase();
@@ -12954,7 +13649,7 @@ var import_wizard_default = {
12954
13649
  innerStep.value = next;
12955
13650
  emit("update:step", next);
12956
13651
  }
12957
- const steps = computed27(() => [
13652
+ const steps = computed28(() => [
12958
13653
  { index: 1, title: t("importWizard.stepFile") },
12959
13654
  { index: 2, title: t("importWizard.stepMapping") },
12960
13655
  { index: 3, title: t("importWizard.stepResult") }
@@ -12996,7 +13691,7 @@ var import_wizard_default = {
12996
13691
  mappingError.value = "";
12997
13692
  emit("update:mapping", next);
12998
13693
  }
12999
- const missingRequired = computed27(
13694
+ const missingRequired = computed28(
13000
13695
  () => props.columns.filter((col) => col.required && !innerMapping.value[col.key])
13001
13696
  );
13002
13697
  function submit() {
@@ -13029,7 +13724,7 @@ var import_wizard_default = {
13029
13724
  emit("retry");
13030
13725
  goStep(2);
13031
13726
  }
13032
- const stats = computed27(() => {
13727
+ const stats = computed28(() => {
13033
13728
  const data = props.result || {};
13034
13729
  return {
13035
13730
  total: data.total || 0,
@@ -13175,7 +13870,7 @@ var import_wizard_default = {
13175
13870
  };
13176
13871
 
13177
13872
  // src/components/diff/index.js
13178
- import { ref as ref41, computed as computed28 } from "vue";
13873
+ import { ref as ref41, computed as computed29 } from "vue";
13179
13874
 
13180
13875
  // src/utils/text-diff.js
13181
13876
  var MAX_CELLS = 1e6;
@@ -13239,8 +13934,8 @@ var diff_default = {
13239
13934
  afterLabel: { type: String, default: "" }
13240
13935
  },
13241
13936
  setup(props) {
13242
- const rows = computed28(() => diffLines(props.before, props.after));
13243
- const stats = computed28(() => {
13937
+ const rows = computed29(() => diffLines(props.before, props.after));
13938
+ const stats = computed29(() => {
13244
13939
  let add = 0;
13245
13940
  let remove = 0;
13246
13941
  rows.value.forEach((row) => {
@@ -13253,7 +13948,7 @@ var diff_default = {
13253
13948
  function expand(index) {
13254
13949
  expandedBlocks.value = expandedBlocks.value.concat(index);
13255
13950
  }
13256
- const displayRows = computed28(() => {
13951
+ const displayRows = computed29(() => {
13257
13952
  const ctx = Math.max(0, props.context);
13258
13953
  const list = rows.value;
13259
13954
  if (!ctx) return list;
@@ -13284,7 +13979,7 @@ var diff_default = {
13284
13979
  }
13285
13980
  return out;
13286
13981
  });
13287
- const splitRows = computed28(() => {
13982
+ const splitRows = computed29(() => {
13288
13983
  const list = displayRows.value;
13289
13984
  const out = [];
13290
13985
  let i = 0;
@@ -13309,8 +14004,8 @@ var diff_default = {
13309
14004
  }
13310
14005
  return out;
13311
14006
  });
13312
- const beforeLabelText = computed28(() => props.beforeLabel || t("diff.before"));
13313
- 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"));
13314
14009
  return {
13315
14010
  stats,
13316
14011
  displayRows,
@@ -13376,7 +14071,7 @@ var diff_default = {
13376
14071
  };
13377
14072
 
13378
14073
  // src/components/tree-table/index.js
13379
- 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";
13380
14075
  function keyOf(row, rowKey) {
13381
14076
  return row && row[rowKey] !== void 0 ? row[rowKey] : row && row.id;
13382
14077
  }
@@ -13431,7 +14126,7 @@ var tree_table_default = {
13431
14126
  if (value) innerChecked.value = value.slice();
13432
14127
  }
13433
14128
  );
13434
- const rows = computed29(() => {
14129
+ const rows = computed30(() => {
13435
14130
  const out = [];
13436
14131
  function walk(list, level, parentKey) {
13437
14132
  ;
@@ -13446,7 +14141,7 @@ var tree_table_default = {
13446
14141
  walk(props.data, 1, null);
13447
14142
  return out;
13448
14143
  });
13449
- const descendantMap = computed29(() => {
14144
+ const descendantMap = computed30(() => {
13450
14145
  const map = {};
13451
14146
  function collect(row) {
13452
14147
  const key = keyOf(row, props.rowKey);
@@ -13461,16 +14156,16 @@ var tree_table_default = {
13461
14156
  (props.data || []).forEach(collect);
13462
14157
  return map;
13463
14158
  });
13464
- const leafKeys = computed29(() => {
14159
+ const leafKeys = computed30(() => {
13465
14160
  const keys = [];
13466
14161
  eachNode(props.data, (row) => {
13467
14162
  if (!row.children || !row.children.length) keys.push(keyOf(row, props.rowKey));
13468
14163
  });
13469
14164
  return keys;
13470
14165
  });
13471
- const checkedSet = computed29(() => new Set(innerChecked.value));
13472
- const allChecked = computed29(() => leafKeys.value.length > 0 && leafKeys.value.every((key) => checkedSet.value.has(key)));
13473
- 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)));
13474
14169
  function isIndeterminate(row) {
13475
14170
  if (!props.selectable || props.checkStrictly) return false;
13476
14171
  const desc = descendantMap.value[keyOf(row, props.rowKey)] || [];
@@ -13504,7 +14199,7 @@ var tree_table_default = {
13504
14199
  }
13505
14200
  emitChecked(Array.from(set));
13506
14201
  }
13507
- const nodeMap = computed29(() => {
14202
+ const nodeMap = computed30(() => {
13508
14203
  const map = {};
13509
14204
  eachNode(props.data, (row, level, parentKey) => {
13510
14205
  map[keyOf(row, props.rowKey)] = { row, parentKey };
@@ -13551,11 +14246,11 @@ var tree_table_default = {
13551
14246
  function onRowClick(item) {
13552
14247
  emit("row-click", item.row, item);
13553
14248
  }
13554
- const scrollStyle = computed29(() => {
14249
+ const scrollStyle = computed30(() => {
13555
14250
  if (!props.maxHeight) return null;
13556
14251
  return { maxHeight: typeof props.maxHeight === "number" ? props.maxHeight + "px" : props.maxHeight };
13557
14252
  });
13558
- const emptyLabel = computed29(() => props.emptyText || t("treeTable.empty"));
14253
+ const emptyLabel = computed30(() => props.emptyText || t("treeTable.empty"));
13559
14254
  return {
13560
14255
  rows,
13561
14256
  innerExpanded,
@@ -13657,7 +14352,7 @@ var tree_table_default = {
13657
14352
  };
13658
14353
 
13659
14354
  // src/components/schedule/index.js
13660
- 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";
13661
14356
  var pad5 = (value) => String(value).padStart(2, "0");
13662
14357
  function toKey3(date) {
13663
14358
  return `${date.getFullYear()}-${pad5(date.getMonth() + 1)}-${pad5(date.getDate())}`;
@@ -13707,8 +14402,8 @@ var schedule_default = {
13707
14402
  const offset = (day + 6) % 7;
13708
14403
  return new Date(date.getFullYear(), date.getMonth(), date.getDate() - offset);
13709
14404
  }
13710
- const weekStartDate = computed30(() => startOfWeek(anchor.value));
13711
- const weekdays = computed30(() => t("schedule.weekdays").split(","));
14405
+ const weekStartDate = computed31(() => startOfWeek(anchor.value));
14406
+ const weekdays = computed31(() => t("schedule.weekdays").split(","));
13712
14407
  function layoutDay(list) {
13713
14408
  const sorted = list.map((item) => {
13714
14409
  const startMin = toMinutes(item.start, props.startHour * 60);
@@ -13741,7 +14436,7 @@ var schedule_default = {
13741
14436
  };
13742
14437
  });
13743
14438
  }
13744
- const days = computed30(() => {
14439
+ const days = computed31(() => {
13745
14440
  const start = weekStartDate.value;
13746
14441
  const list = Array.from({ length: 7 }, (_, index) => {
13747
14442
  const date = new Date(start.getFullYear(), start.getMonth(), start.getDate() + index);
@@ -13759,20 +14454,20 @@ var schedule_default = {
13759
14454
  });
13760
14455
  return props.showWeekend ? list : list.slice(0, 5);
13761
14456
  });
13762
- const hours = computed30(() => {
14457
+ const hours = computed31(() => {
13763
14458
  const list = [];
13764
14459
  for (let hour = props.startHour; hour < props.endHour; hour++) list.push(hour);
13765
14460
  return list;
13766
14461
  });
13767
- const bodyHeight = computed30(() => (props.endHour - props.startHour) * props.hourHeight);
13768
- const gridStyle = computed30(() => ({
14462
+ const bodyHeight = computed31(() => (props.endHour - props.startHour) * props.hourHeight);
14463
+ const gridStyle = computed31(() => ({
13769
14464
  gridTemplateColumns: "56px repeat(" + Math.max(1, days.value.length) + ", minmax(0, 1fr))"
13770
14465
  }));
13771
- const nowTop = computed30(() => (nowMinutes.value - props.startHour * 60) / 60 * props.hourHeight);
13772
- const showNowLine = computed30(
14466
+ const nowTop = computed31(() => (nowMinutes.value - props.startHour * 60) / 60 * props.hourHeight);
14467
+ const showNowLine = computed31(
13773
14468
  () => nowMinutes.value >= props.startHour * 60 && nowMinutes.value <= props.endHour * 60 && days.value.some((day) => day.today)
13774
14469
  );
13775
- const rangeLabel = computed30(() => {
14470
+ const rangeLabel = computed31(() => {
13776
14471
  const list = days.value;
13777
14472
  if (!list.length) return "";
13778
14473
  const first = list[0];
@@ -13902,7 +14597,7 @@ var schedule_default = {
13902
14597
  };
13903
14598
 
13904
14599
  // src/components/comment/index.js
13905
- import { ref as ref44, computed as computed31 } from "vue";
14600
+ import { ref as ref44, computed as computed32 } from "vue";
13906
14601
  var comment_default = {
13907
14602
  name: "DjmComment",
13908
14603
  components: { DjmAvatar: avatar_default, DjmIcon: icon_default },
@@ -13921,14 +14616,14 @@ var comment_default = {
13921
14616
  setup(props, { emit }) {
13922
14617
  const content = ref44("");
13923
14618
  const replyTarget = ref44(null);
13924
- const placeholderText = computed31(() => {
14619
+ const placeholderText = computed32(() => {
13925
14620
  if (replyTarget.value) return t("comment.replyPlaceholder", { name: replyTarget.value.author });
13926
14621
  return props.placeholder || t("comment.placeholder");
13927
14622
  });
13928
- const submitLabel = computed31(() => props.submitText || t("comment.submit"));
13929
- const emptyLabel = computed31(() => props.emptyText || t("comment.empty"));
13930
- const overLimit = computed31(() => content.value.length > props.maxLength);
13931
- 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);
13932
14627
  function onInput(event) {
13933
14628
  content.value = event.target.value;
13934
14629
  }
@@ -14243,7 +14938,7 @@ var signature_default = {
14243
14938
  };
14244
14939
 
14245
14940
  // src/components/print/index.js
14246
- import { ref as ref46, computed as computed32 } from "vue";
14941
+ import { ref as ref46, computed as computed33 } from "vue";
14247
14942
  function escapeHtml(text) {
14248
14943
  return String(text).replace(/[&<>"]/g, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[ch]);
14249
14944
  }
@@ -14260,7 +14955,7 @@ var print_default = {
14260
14955
  emits: ["before-print", "after-print"],
14261
14956
  setup(props, { emit }) {
14262
14957
  const content = ref46(null);
14263
- const label = computed32(() => props.buttonText || t("print.button"));
14958
+ const label = computed33(() => props.buttonText || t("print.button"));
14264
14959
  function collectStyles() {
14265
14960
  return Array.from(document.querySelectorAll('link[rel="stylesheet"], style')).map((el) => el.outerHTML).join("");
14266
14961
  }
@@ -14309,7 +15004,7 @@ var print_default = {
14309
15004
  };
14310
15005
 
14311
15006
  // src/components/gantt/index.js
14312
- import { computed as computed33 } from "vue";
15007
+ import { computed as computed34 } from "vue";
14313
15008
  var DAY = 864e5;
14314
15009
  var pad6 = (value) => String(value).padStart(2, "0");
14315
15010
  function parseDate3(value) {
@@ -14345,7 +15040,7 @@ var gantt_default = {
14345
15040
  emits: ["task-click"],
14346
15041
  setup(props, { emit }) {
14347
15042
  const todayKey = toKey4(/* @__PURE__ */ new Date());
14348
- const range = computed33(() => {
15043
+ const range = computed34(() => {
14349
15044
  const starts = props.tasks.map((task) => task.start).filter(Boolean).sort();
14350
15045
  const ends = props.tasks.map((task) => task.end || task.start).filter(Boolean).sort();
14351
15046
  let start = props.startDate || starts[0] || todayKey;
@@ -14358,7 +15053,7 @@ var gantt_default = {
14358
15053
  }
14359
15054
  return { start, end };
14360
15055
  });
14361
- const days = computed33(() => {
15056
+ const days = computed34(() => {
14362
15057
  const total = dayDiff(range.value.end, range.value.start) + 1;
14363
15058
  const base = parseDate3(range.value.start);
14364
15059
  return Array.from({ length: total }, (_, index) => {
@@ -14375,9 +15070,9 @@ var gantt_default = {
14375
15070
  };
14376
15071
  });
14377
15072
  });
14378
- const trackWidth = computed33(() => days.value.length * props.dayWidth);
14379
- const innerWidth = computed33(() => props.nameWidth + trackWidth.value);
14380
- const bars = computed33(
15073
+ const trackWidth = computed34(() => days.value.length * props.dayWidth);
15074
+ const innerWidth = computed34(() => props.nameWidth + trackWidth.value);
15075
+ const bars = computed34(
14381
15076
  () => props.tasks.map((task, index) => {
14382
15077
  const start = task.start || range.value.start;
14383
15078
  const end = task.end || start;
@@ -14393,12 +15088,12 @@ var gantt_default = {
14393
15088
  };
14394
15089
  })
14395
15090
  );
14396
- const todayLeft = computed33(() => {
15091
+ const todayLeft = computed34(() => {
14397
15092
  const index = days.value.findIndex((day) => day.today);
14398
15093
  if (index === -1) return null;
14399
15094
  return props.nameWidth + index * props.dayWidth + props.dayWidth / 2;
14400
15095
  });
14401
- const emptyLabel = computed33(() => props.emptyText || t("gantt.empty"));
15096
+ const emptyLabel = computed34(() => props.emptyText || t("gantt.empty"));
14402
15097
  function onClick(bar) {
14403
15098
  emit("task-click", bar.task);
14404
15099
  }
@@ -14480,7 +15175,7 @@ var gantt_default = {
14480
15175
  };
14481
15176
 
14482
15177
  // src/components/field-list/index.js
14483
- 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";
14484
15179
  var uidSeed8 = 0;
14485
15180
  var field_list_default = {
14486
15181
  name: "DjmFieldList",
@@ -14511,10 +15206,10 @@ var field_list_default = {
14511
15206
  syncUids();
14512
15207
  watch35(() => props.modelValue.length, syncUids);
14513
15208
  const errors = ref47({});
14514
- const addLabel = computed34(() => props.addText || t("fieldList.add"));
14515
- const emptyLabel = computed34(() => props.emptyText || t("fieldList.empty"));
14516
- const canAdd = computed34(() => !props.disabled && (!props.max || props.modelValue.length < props.max));
14517
- 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);
14518
15213
  function commit(list) {
14519
15214
  emit("update:modelValue", list);
14520
15215
  emit("change", list);
@@ -14676,7 +15371,7 @@ var field_list_default = {
14676
15371
  };
14677
15372
 
14678
15373
  // src/components/chat/index.js
14679
- 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";
14680
15375
  var chat_default = {
14681
15376
  name: "DjmChat",
14682
15377
  components: { DjmAvatar: avatar_default },
@@ -14695,10 +15390,10 @@ var chat_default = {
14695
15390
  setup(props, { emit }) {
14696
15391
  const content = ref48("");
14697
15392
  const listRef = ref48(null);
14698
- const placeholderText = computed35(() => props.placeholder || t("chat.placeholder"));
14699
- const sendLabel = computed35(() => props.sendText || t("chat.send"));
14700
- const emptyLabel = computed35(() => props.emptyText || t("chat.empty"));
14701
- 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);
14702
15397
  function scrollToBottom() {
14703
15398
  const el = listRef.value;
14704
15399
  if (el) el.scrollTop = el.scrollHeight;
@@ -14816,7 +15511,7 @@ var chat_default = {
14816
15511
  };
14817
15512
 
14818
15513
  // src/components/org-chart/index.js
14819
- 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";
14820
15515
  var org_chart_default = {
14821
15516
  name: "DjmOrgChart",
14822
15517
  components: { DjmAvatar: avatar_default, DjmIcon: icon_default },
@@ -14853,7 +15548,7 @@ var org_chart_default = {
14853
15548
  watch37(() => props.data, (data) => {
14854
15549
  collapsed.value = props.defaultExpandAll ? [] : collapsedByDefault(data);
14855
15550
  });
14856
- const layout = computed36(() => {
15551
+ const layout = computed37(() => {
14857
15552
  if (!props.data) return { nodes: [], links: [], width: 0, height: 0 };
14858
15553
  const rowH = props.nodeHeight + props.verticalGap;
14859
15554
  let cursor = 0;
@@ -14918,7 +15613,7 @@ var org_chart_default = {
14918
15613
  function onClick(item) {
14919
15614
  emit("node-click", item.node);
14920
15615
  }
14921
- const emptyLabel = computed36(() => props.emptyText || t("orgChart.empty"));
15616
+ const emptyLabel = computed37(() => props.emptyText || t("orgChart.empty"));
14922
15617
  return {
14923
15618
  layout,
14924
15619
  toggle,
@@ -14979,7 +15674,7 @@ var org_chart_default = {
14979
15674
  };
14980
15675
 
14981
15676
  // src/components/page-tabs/index.js
14982
- import { computed as computed37 } from "vue";
15677
+ import { computed as computed38 } from "vue";
14983
15678
  var page_tabs_default = {
14984
15679
  name: "DjmPageTabs",
14985
15680
  components: { DjmIcon: icon_default },
@@ -14990,7 +15685,7 @@ var page_tabs_default = {
14990
15685
  },
14991
15686
  emits: ["update:modelValue", "change", "close"],
14992
15687
  setup(props, { emit }) {
14993
- const label = computed37(() => t("pageTabs.label"));
15688
+ const label = computed38(() => t("pageTabs.label"));
14994
15689
  function canClose(tab) {
14995
15690
  return tab.closable === void 0 ? props.closable : tab.closable;
14996
15691
  }
@@ -15243,7 +15938,7 @@ var components = [
15243
15938
  org_chart_default,
15244
15939
  page_tabs_default
15245
15940
  ];
15246
- var version = "0.67.3";
15941
+ var version = "0.72.0";
15247
15942
  function install(app, options = {}) {
15248
15943
  if (options.locale) setLocale(options.locale);
15249
15944
  components.forEach((component) => app.component(component.name, component));