djm-ui 0.67.4 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/README.md +38 -2
- package/dist/djm-ui.cjs +1032 -347
- package/dist/djm-ui.cjs.map +4 -4
- package/dist/djm-ui.css +752 -17
- package/dist/djm-ui.esm.js +1105 -420
- package/dist/djm-ui.esm.js.map +4 -4
- package/dist/djm-ui.esm.min.js +373 -227
- package/dist/djm-ui.min.cjs +373 -227
- package/dist/djm-ui.min.css +2 -2
- package/dist/djm-ui.umd.js +944 -259
- package/dist/djm-ui.umd.min.js +373 -227
- package/dist/mcp/manifest.json +12195 -0
- package/dist/mcp/server.js +90 -0
- package/package.json +12 -4
- package/src/components/icon/index.js +13 -1
- package/src/components/rich-editor/index.js +724 -61
- package/src/index.js +1 -1
- package/src/locale/en-US.js +33 -1
- package/src/locale/zh-CN.js +33 -1
- package/src/styles/base.css +2 -3
- package/src/styles/components/button.css +2 -1
- package/src/styles/components/console.css +5 -0
- package/src/styles/components/content.css +517 -0
- package/src/styles/components/form.css +5 -0
- package/src/styles/components/layout.css +326 -0
- package/src/styles/components/media-picker.css +3 -3
- package/src/styles/components/menu.css +2 -1
- package/src/styles/components/org-picker.css +1 -1
- package/src/styles/components/responsive.css +86 -0
- package/src/styles/components/rich-editor.css +206 -7
- package/src/styles/components/skin.css +54 -0
- package/src/styles/components/template.css +5 -0
- package/src/styles/index.css +1 -0
- package/src/styles/theme-dark.css +2 -0
- package/src/styles/tokens.css +8 -0
- package/src/theme/index.js +40 -0
- package/src/utils/sanitize-html.js +44 -6
- package/types/index.d.ts +8 -0
package/dist/djm-ui.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! DJM UI v0.
|
|
1
|
+
/*! DJM UI v0.72.0 · MIT · 2026-10-26 */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -1119,7 +1119,19 @@ var PATHS = {
|
|
|
1119
1119
|
activity: "M22 12h-4l-3 9L9 3l-3 9H2",
|
|
1120
1120
|
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",
|
|
1121
1121
|
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",
|
|
1122
|
-
moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"
|
|
1122
|
+
moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z",
|
|
1123
|
+
undo: "M3 10h11a5 5 0 1 1 0 10H8M3 10l4-4M3 10l4 4",
|
|
1124
|
+
redo: "M21 10H10a5 5 0 1 0 0 10h6M21 10l-4-4M21 10l-4 4",
|
|
1125
|
+
"align-left": "M4 6h16M4 12h10M4 18h13",
|
|
1126
|
+
"align-center": "M4 6h16M7 12h10M6 18h12",
|
|
1127
|
+
"align-right": "M4 6h16M10 12h10M7 18h13",
|
|
1128
|
+
"align-justify": "M4 6h16M4 12h16M4 18h16",
|
|
1129
|
+
list: "M9 6h11M9 12h11M9 18h11M4.5 6h.01M4.5 12h.01M4.5 18h.01",
|
|
1130
|
+
"list-ordered": "M10 6h10M10 12h10M10 18h10M4 6h1v4M4 12h2M4 16h2v2H4",
|
|
1131
|
+
outdent: "M4 6h16M10 12h10M10 18h10M8 9l-3 3 3 3",
|
|
1132
|
+
indent: "M4 6h16M10 12h10M10 18h10M5 9l3 3-3 3",
|
|
1133
|
+
unlink: "M10 13a5 5 0 0 0 7.5.5M14 11a5 5 0 0 0-7.5-.5M2 2l20 20",
|
|
1134
|
+
images: "M8 3h12v12M4 7h12v12H4zM7 15l3-3 3 3"
|
|
1123
1135
|
};
|
|
1124
1136
|
var icon_default = {
|
|
1125
1137
|
name: "DjmIcon",
|
|
@@ -1446,7 +1458,6 @@ var zh_CN_default = {
|
|
|
1446
1458
|
"richEditor.italic": "\u659C\u4F53",
|
|
1447
1459
|
"richEditor.underline": "\u4E0B\u5212\u7EBF",
|
|
1448
1460
|
"richEditor.strike": "\u5220\u9664\u7EBF",
|
|
1449
|
-
"richEditor.heading": "\u4E8C\u7EA7\u6807\u9898",
|
|
1450
1461
|
"richEditor.paragraph": "\u6B63\u6587",
|
|
1451
1462
|
"richEditor.bulletList": "\u65E0\u5E8F\u5217\u8868",
|
|
1452
1463
|
"richEditor.numberList": "\u6709\u5E8F\u5217\u8868",
|
|
@@ -1457,6 +1468,39 @@ var zh_CN_default = {
|
|
|
1457
1468
|
"richEditor.redo": "\u91CD\u505A",
|
|
1458
1469
|
"richEditor.source": "\u6E90\u7801",
|
|
1459
1470
|
"richEditor.visual": "\u53EF\u89C6\u5316",
|
|
1471
|
+
"richEditor.toolbar": "\u7F16\u8F91\u5DE5\u5177\u680F",
|
|
1472
|
+
"richEditor.block": "\u6BB5\u843D\u683C\u5F0F",
|
|
1473
|
+
"richEditor.heading1": "\u6807\u9898 1",
|
|
1474
|
+
"richEditor.heading2": "\u6807\u9898 2",
|
|
1475
|
+
"richEditor.heading3": "\u6807\u9898 3",
|
|
1476
|
+
"richEditor.quote": "\u5F15\u7528",
|
|
1477
|
+
"richEditor.codeBlock": "\u4EE3\u7801\u5757",
|
|
1478
|
+
"richEditor.fontSize": "\u5B57\u53F7",
|
|
1479
|
+
"richEditor.defaultSize": "\u9ED8\u8BA4",
|
|
1480
|
+
"richEditor.inlineCode": "\u884C\u5185\u4EE3\u7801",
|
|
1481
|
+
"richEditor.textColor": "\u6587\u5B57\u989C\u8272",
|
|
1482
|
+
"richEditor.bgColor": "\u80CC\u666F\u8272",
|
|
1483
|
+
"richEditor.alignLeft": "\u5DE6\u5BF9\u9F50",
|
|
1484
|
+
"richEditor.alignCenter": "\u5C45\u4E2D",
|
|
1485
|
+
"richEditor.alignRight": "\u53F3\u5BF9\u9F50",
|
|
1486
|
+
"richEditor.alignJustify": "\u4E24\u7AEF\u5BF9\u9F50",
|
|
1487
|
+
"richEditor.outdent": "\u51CF\u5C11\u7F29\u8FDB",
|
|
1488
|
+
"richEditor.indent": "\u589E\u52A0\u7F29\u8FDB",
|
|
1489
|
+
"richEditor.unlink": "\u53D6\u6D88\u94FE\u63A5",
|
|
1490
|
+
"richEditor.image": "\u63D2\u5165\u56FE\u7247",
|
|
1491
|
+
"richEditor.singleImage": "\u63D2\u5165\u5355\u56FE",
|
|
1492
|
+
"richEditor.multiImage": "\u63D2\u5165\u591A\u56FE",
|
|
1493
|
+
"richEditor.mediaLibrary": "\u4ECE\u7D20\u6750\u5E93\u9009\u62E9",
|
|
1494
|
+
"richEditor.hr": "\u5206\u5272\u7EBF",
|
|
1495
|
+
"richEditor.fullscreen": "\u5168\u5C4F",
|
|
1496
|
+
"richEditor.exitFullscreen": "\u9000\u51FA\u5168\u5C4F",
|
|
1497
|
+
"richEditor.wordCount": "\u5171 {count} \u5B57",
|
|
1498
|
+
"richEditor.uploading": "\u6B63\u5728\u4E0A\u4F20 {count} \u5F20\u56FE\u7247\u2026",
|
|
1499
|
+
"richEditor.uploadFailed": "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25",
|
|
1500
|
+
"richEditor.imageTypeError": "\u53EA\u80FD\u4E0A\u4F20\u56FE\u7247\u6587\u4EF6",
|
|
1501
|
+
"richEditor.imageSizeError": "\u56FE\u7247\u4E0D\u80FD\u8D85\u8FC7 {size}MB",
|
|
1502
|
+
"richEditor.imageUrlInvalid": "\u56FE\u7247\u5730\u5740\u4E0D\u5408\u6CD5\uFF0C\u5DF2\u62D2\u7EDD\u63D2\u5165",
|
|
1503
|
+
"richEditor.linkInvalid": "\u94FE\u63A5\u53EA\u652F\u6301 http(s)\u3001mailto\u3001tel \u6216\u7AD9\u5185\u76F8\u5BF9\u5730\u5740",
|
|
1460
1504
|
"approval.start": "\u53D1\u8D77",
|
|
1461
1505
|
"approval.cc": "\u6284\u9001",
|
|
1462
1506
|
"approval.approved": "\u5DF2\u901A\u8FC7",
|
|
@@ -1665,7 +1709,6 @@ var en_US_default = {
|
|
|
1665
1709
|
"richEditor.italic": "Italic",
|
|
1666
1710
|
"richEditor.underline": "Underline",
|
|
1667
1711
|
"richEditor.strike": "Strikethrough",
|
|
1668
|
-
"richEditor.heading": "Heading 2",
|
|
1669
1712
|
"richEditor.paragraph": "Paragraph",
|
|
1670
1713
|
"richEditor.bulletList": "Bullet list",
|
|
1671
1714
|
"richEditor.numberList": "Numbered list",
|
|
@@ -1676,6 +1719,39 @@ var en_US_default = {
|
|
|
1676
1719
|
"richEditor.redo": "Redo",
|
|
1677
1720
|
"richEditor.source": "Source",
|
|
1678
1721
|
"richEditor.visual": "Visual",
|
|
1722
|
+
"richEditor.toolbar": "Formatting toolbar",
|
|
1723
|
+
"richEditor.block": "Paragraph style",
|
|
1724
|
+
"richEditor.heading1": "Heading 1",
|
|
1725
|
+
"richEditor.heading2": "Heading 2",
|
|
1726
|
+
"richEditor.heading3": "Heading 3",
|
|
1727
|
+
"richEditor.quote": "Quote",
|
|
1728
|
+
"richEditor.codeBlock": "Code block",
|
|
1729
|
+
"richEditor.fontSize": "Font size",
|
|
1730
|
+
"richEditor.defaultSize": "Default",
|
|
1731
|
+
"richEditor.inlineCode": "Inline code",
|
|
1732
|
+
"richEditor.textColor": "Text color",
|
|
1733
|
+
"richEditor.bgColor": "Background color",
|
|
1734
|
+
"richEditor.alignLeft": "Align left",
|
|
1735
|
+
"richEditor.alignCenter": "Align center",
|
|
1736
|
+
"richEditor.alignRight": "Align right",
|
|
1737
|
+
"richEditor.alignJustify": "Justify",
|
|
1738
|
+
"richEditor.outdent": "Decrease indent",
|
|
1739
|
+
"richEditor.indent": "Increase indent",
|
|
1740
|
+
"richEditor.unlink": "Remove link",
|
|
1741
|
+
"richEditor.image": "Insert image",
|
|
1742
|
+
"richEditor.singleImage": "Insert single image",
|
|
1743
|
+
"richEditor.multiImage": "Insert multiple images",
|
|
1744
|
+
"richEditor.mediaLibrary": "Choose from media library",
|
|
1745
|
+
"richEditor.hr": "Divider",
|
|
1746
|
+
"richEditor.fullscreen": "Fullscreen",
|
|
1747
|
+
"richEditor.exitFullscreen": "Exit fullscreen",
|
|
1748
|
+
"richEditor.wordCount": "{count} characters",
|
|
1749
|
+
"richEditor.uploading": "Uploading {count} image(s)\u2026",
|
|
1750
|
+
"richEditor.uploadFailed": "Image upload failed",
|
|
1751
|
+
"richEditor.imageTypeError": "Only image files are allowed",
|
|
1752
|
+
"richEditor.imageSizeError": "Image must be smaller than {size}MB",
|
|
1753
|
+
"richEditor.imageUrlInvalid": "Invalid image URL, insertion rejected",
|
|
1754
|
+
"richEditor.linkInvalid": "Only http(s), mailto, tel or relative URLs are allowed",
|
|
1679
1755
|
"approval.start": "Started",
|
|
1680
1756
|
"approval.cc": "CC",
|
|
1681
1757
|
"approval.approved": "Approved",
|
|
@@ -9494,7 +9570,199 @@ var tree_select_default = {
|
|
|
9494
9570
|
};
|
|
9495
9571
|
|
|
9496
9572
|
// src/components/rich-editor/index.js
|
|
9573
|
+
var import_vue33 = require("vue");
|
|
9574
|
+
|
|
9575
|
+
// src/components/media-picker/index.js
|
|
9497
9576
|
var import_vue32 = require("vue");
|
|
9577
|
+
var media_picker_default = {
|
|
9578
|
+
name: "DjmMediaPicker",
|
|
9579
|
+
components: { DjmModal: modal_default, DjmPagination: pagination_default, DjmIcon: icon_default },
|
|
9580
|
+
props: {
|
|
9581
|
+
modelValue: { type: [Array, String, Number], default: "" },
|
|
9582
|
+
items: { type: Array, default: () => [] },
|
|
9583
|
+
multiple: { type: Boolean, default: false },
|
|
9584
|
+
// 最多选几个,0 表示不限
|
|
9585
|
+
limit: { type: Number, default: 0 },
|
|
9586
|
+
// image 只显示图片,file 只显示非图片,空串不过滤
|
|
9587
|
+
accept: { type: String, default: "" },
|
|
9588
|
+
title: { type: String, default: "" },
|
|
9589
|
+
triggerText: { type: String, default: "" },
|
|
9590
|
+
pageSize: { type: Number, default: 12 },
|
|
9591
|
+
disabled: { type: Boolean, default: false }
|
|
9592
|
+
},
|
|
9593
|
+
emits: ["update:modelValue", "change"],
|
|
9594
|
+
setup(props, { emit }) {
|
|
9595
|
+
const open = (0, import_vue32.ref)(false);
|
|
9596
|
+
const keyword = (0, import_vue32.ref)("");
|
|
9597
|
+
const page = (0, import_vue32.ref)(1);
|
|
9598
|
+
const temp = (0, import_vue32.ref)([]);
|
|
9599
|
+
const selectedIds = (0, import_vue32.computed)(() => {
|
|
9600
|
+
if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
|
|
9601
|
+
if (props.modelValue === "" || props.modelValue === null || props.modelValue === void 0) return [];
|
|
9602
|
+
return [props.modelValue];
|
|
9603
|
+
});
|
|
9604
|
+
function isImage(item) {
|
|
9605
|
+
if (item.type) return item.type === "image";
|
|
9606
|
+
return /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(item.url || "");
|
|
9607
|
+
}
|
|
9608
|
+
const filtered = (0, import_vue32.computed)(() => {
|
|
9609
|
+
const key = keyword.value.trim().toLowerCase();
|
|
9610
|
+
return props.items.filter((item) => {
|
|
9611
|
+
if (props.accept === "image" && !isImage(item)) return false;
|
|
9612
|
+
if (props.accept === "file" && isImage(item)) return false;
|
|
9613
|
+
if (key && !String(item.name || "").toLowerCase().includes(key)) return false;
|
|
9614
|
+
return true;
|
|
9615
|
+
});
|
|
9616
|
+
});
|
|
9617
|
+
const paged = (0, import_vue32.computed)(() => {
|
|
9618
|
+
const start = (page.value - 1) * props.pageSize;
|
|
9619
|
+
return filtered.value.slice(start, start + props.pageSize);
|
|
9620
|
+
});
|
|
9621
|
+
(0, import_vue32.watch)(keyword, () => {
|
|
9622
|
+
page.value = 1;
|
|
9623
|
+
});
|
|
9624
|
+
(0, import_vue32.watch)(
|
|
9625
|
+
() => filtered.value.length,
|
|
9626
|
+
() => {
|
|
9627
|
+
const max = Math.max(1, Math.ceil(filtered.value.length / props.pageSize));
|
|
9628
|
+
if (page.value > max) page.value = max;
|
|
9629
|
+
}
|
|
9630
|
+
);
|
|
9631
|
+
function isChecked(id) {
|
|
9632
|
+
return temp.value.includes(id);
|
|
9633
|
+
}
|
|
9634
|
+
function atLimit() {
|
|
9635
|
+
return props.limit > 0 && temp.value.length >= props.limit;
|
|
9636
|
+
}
|
|
9637
|
+
function toggle(item) {
|
|
9638
|
+
if (props.multiple) {
|
|
9639
|
+
if (isChecked(item.id)) {
|
|
9640
|
+
temp.value = temp.value.filter((id) => id !== item.id);
|
|
9641
|
+
return;
|
|
9642
|
+
}
|
|
9643
|
+
if (atLimit()) return;
|
|
9644
|
+
temp.value = [...temp.value, item.id];
|
|
9645
|
+
return;
|
|
9646
|
+
}
|
|
9647
|
+
temp.value = [item.id];
|
|
9648
|
+
}
|
|
9649
|
+
function openPanel() {
|
|
9650
|
+
if (props.disabled) return;
|
|
9651
|
+
temp.value = [...selectedIds.value];
|
|
9652
|
+
keyword.value = "";
|
|
9653
|
+
page.value = 1;
|
|
9654
|
+
open.value = true;
|
|
9655
|
+
}
|
|
9656
|
+
function commit() {
|
|
9657
|
+
const next = props.multiple ? [...temp.value] : temp.value[0] !== void 0 ? temp.value[0] : "";
|
|
9658
|
+
emit("update:modelValue", next);
|
|
9659
|
+
emit("change", next);
|
|
9660
|
+
open.value = false;
|
|
9661
|
+
}
|
|
9662
|
+
function cancel() {
|
|
9663
|
+
temp.value = [...selectedIds.value];
|
|
9664
|
+
open.value = false;
|
|
9665
|
+
}
|
|
9666
|
+
return {
|
|
9667
|
+
open,
|
|
9668
|
+
keyword,
|
|
9669
|
+
page,
|
|
9670
|
+
temp,
|
|
9671
|
+
selectedIds,
|
|
9672
|
+
filtered,
|
|
9673
|
+
paged,
|
|
9674
|
+
isImage,
|
|
9675
|
+
isChecked,
|
|
9676
|
+
atLimit,
|
|
9677
|
+
toggle,
|
|
9678
|
+
openPanel,
|
|
9679
|
+
commit,
|
|
9680
|
+
cancel,
|
|
9681
|
+
t
|
|
9682
|
+
};
|
|
9683
|
+
},
|
|
9684
|
+
template: `
|
|
9685
|
+
<span class="djm-media-picker">
|
|
9686
|
+
<slot name="trigger" :open="openPanel" :count="selectedIds.length">
|
|
9687
|
+
<button
|
|
9688
|
+
class="djm-btn"
|
|
9689
|
+
type="button"
|
|
9690
|
+
:disabled="disabled"
|
|
9691
|
+
@click="openPanel"
|
|
9692
|
+
>{{ triggerText || t('mediaPicker.select') }}<span v-if="selectedIds.length" class="djm-media-picker-count">{{ selectedIds.length }}</span></button>
|
|
9693
|
+
</slot>
|
|
9694
|
+
|
|
9695
|
+
<DjmModal
|
|
9696
|
+
:model-value="open"
|
|
9697
|
+
:title="title || t('mediaPicker.title')"
|
|
9698
|
+
width="760px"
|
|
9699
|
+
@update:model-value="cancel"
|
|
9700
|
+
>
|
|
9701
|
+
<div class="djm-media-toolbar">
|
|
9702
|
+
<label class="djm-media-search">
|
|
9703
|
+
<DjmIcon name="search" :size="13" />
|
|
9704
|
+
<input
|
|
9705
|
+
v-model="keyword"
|
|
9706
|
+
class="djm-media-search-input"
|
|
9707
|
+
type="text"
|
|
9708
|
+
:placeholder="t('mediaPicker.search')"
|
|
9709
|
+
:aria-label="t('mediaPicker.search')"
|
|
9710
|
+
/>
|
|
9711
|
+
</label>
|
|
9712
|
+
<span class="djm-media-toolbar-slot">
|
|
9713
|
+
<slot name="toolbar"></slot>
|
|
9714
|
+
</span>
|
|
9715
|
+
</div>
|
|
9716
|
+
|
|
9717
|
+
<div class="djm-media-grid" role="listbox" :aria-multiselectable="multiple ? 'true' : 'false'">
|
|
9718
|
+
<div
|
|
9719
|
+
v-for="item in paged"
|
|
9720
|
+
:key="item.id"
|
|
9721
|
+
class="djm-media-item"
|
|
9722
|
+
:class="{
|
|
9723
|
+
'djm-is-checked': isChecked(item.id),
|
|
9724
|
+
'djm-is-disabled': multiple && atLimit() && !isChecked(item.id)
|
|
9725
|
+
}"
|
|
9726
|
+
role="option"
|
|
9727
|
+
tabindex="0"
|
|
9728
|
+
:aria-selected="String(isChecked(item.id))"
|
|
9729
|
+
@click="toggle(item)"
|
|
9730
|
+
@keydown.enter.prevent="toggle(item)"
|
|
9731
|
+
@keydown.space.prevent="toggle(item)"
|
|
9732
|
+
>
|
|
9733
|
+
<div class="djm-media-thumb">
|
|
9734
|
+
<img v-if="isImage(item)" :src="item.url" :alt="item.name" loading="lazy" />
|
|
9735
|
+
<DjmIcon v-else name="folder" :size="26" />
|
|
9736
|
+
<span v-if="isChecked(item.id)" class="djm-media-check">
|
|
9737
|
+
<DjmIcon name="check" :size="11" />
|
|
9738
|
+
</span>
|
|
9739
|
+
</div>
|
|
9740
|
+
<div class="djm-media-name" :title="item.name">{{ item.name }}</div>
|
|
9741
|
+
</div>
|
|
9742
|
+
<div v-if="!paged.length" class="djm-media-empty">{{ t('mediaPicker.empty') }}</div>
|
|
9743
|
+
</div>
|
|
9744
|
+
|
|
9745
|
+
<div class="djm-media-foot">
|
|
9746
|
+
<span class="djm-media-total">
|
|
9747
|
+
{{ t('mediaPicker.total', { total: filtered.length }) }}
|
|
9748
|
+
<em v-if="multiple && limit > 0">{{ t('mediaPicker.limit', { limit }) }}</em>
|
|
9749
|
+
</span>
|
|
9750
|
+
<DjmPagination
|
|
9751
|
+
v-if="filtered.length > pageSize"
|
|
9752
|
+
v-model:page="page"
|
|
9753
|
+
:page-size="pageSize"
|
|
9754
|
+
:total="filtered.length"
|
|
9755
|
+
/>
|
|
9756
|
+
</div>
|
|
9757
|
+
|
|
9758
|
+
<template #footer>
|
|
9759
|
+
<button class="djm-btn" type="button" @click="cancel">{{ t('common.cancel') }}</button>
|
|
9760
|
+
<button class="djm-btn djm-btn-primary" type="button" @click="commit">{{ t('common.confirm') }}</button>
|
|
9761
|
+
</template>
|
|
9762
|
+
</DjmModal>
|
|
9763
|
+
</span>
|
|
9764
|
+
`
|
|
9765
|
+
};
|
|
9498
9766
|
|
|
9499
9767
|
// src/utils/sanitize-html.js
|
|
9500
9768
|
var TAGS = /* @__PURE__ */ new Set([
|
|
@@ -9534,7 +9802,18 @@ var TAGS = /* @__PURE__ */ new Set([
|
|
|
9534
9802
|
"th",
|
|
9535
9803
|
"td"
|
|
9536
9804
|
]);
|
|
9537
|
-
var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "title"]);
|
|
9805
|
+
var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "title", "style"]);
|
|
9806
|
+
var STYLE_PROPS = /* @__PURE__ */ new Set([
|
|
9807
|
+
"color",
|
|
9808
|
+
"background-color",
|
|
9809
|
+
"font-size",
|
|
9810
|
+
"font-weight",
|
|
9811
|
+
"font-style",
|
|
9812
|
+
"text-decoration",
|
|
9813
|
+
"text-align",
|
|
9814
|
+
"text-indent",
|
|
9815
|
+
"line-height"
|
|
9816
|
+
]);
|
|
9538
9817
|
var TAG_ATTRS = {
|
|
9539
9818
|
a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
|
|
9540
9819
|
img: /* @__PURE__ */ new Set(["src", "alt", "width", "height"]),
|
|
@@ -9542,11 +9821,29 @@ var TAG_ATTRS = {
|
|
|
9542
9821
|
th: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope"]),
|
|
9543
9822
|
td: /* @__PURE__ */ new Set(["colspan", "rowspan"])
|
|
9544
9823
|
};
|
|
9824
|
+
function cleanStyle(value) {
|
|
9825
|
+
return String(value || "").split(";").map((item) => {
|
|
9826
|
+
const index = item.indexOf(":");
|
|
9827
|
+
if (index === -1) return "";
|
|
9828
|
+
const name = item.slice(0, index).trim().toLowerCase();
|
|
9829
|
+
const val = item.slice(index + 1).trim();
|
|
9830
|
+
if (!STYLE_PROPS.has(name)) return "";
|
|
9831
|
+
if (/url\s*\(|expression\s*\(|javascript:/i.test(val)) return "";
|
|
9832
|
+
return name + ": " + val;
|
|
9833
|
+
}).filter(Boolean).join("; ");
|
|
9834
|
+
}
|
|
9835
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
9545
9836
|
function safeUrl(value, tag) {
|
|
9546
|
-
const url = String(value || "").replace(/[\u0000-\u0020]+/g, "")
|
|
9547
|
-
|
|
9548
|
-
if (
|
|
9549
|
-
|
|
9837
|
+
const url = String(value || "").replace(/[\u0000-\u0020]+/g, "");
|
|
9838
|
+
const scheme = url.match(/^([a-z][a-z0-9+.-]*):/i);
|
|
9839
|
+
if (!scheme) return true;
|
|
9840
|
+
const name = scheme[1].toLowerCase();
|
|
9841
|
+
if (SAFE_SCHEMES.has(name)) return true;
|
|
9842
|
+
if (tag === "img") {
|
|
9843
|
+
if (name === "blob") return true;
|
|
9844
|
+
if (name === "data" && /^data:image\//i.test(url)) return true;
|
|
9845
|
+
}
|
|
9846
|
+
return false;
|
|
9550
9847
|
}
|
|
9551
9848
|
function cleanNode(node) {
|
|
9552
9849
|
const children = [...node.childNodes];
|
|
@@ -9569,6 +9866,12 @@ function cleanNode(node) {
|
|
|
9569
9866
|
child.removeAttribute(attr.name);
|
|
9570
9867
|
return;
|
|
9571
9868
|
}
|
|
9869
|
+
if (name === "style") {
|
|
9870
|
+
const style = cleanStyle(attr.value);
|
|
9871
|
+
if (style) child.setAttribute("style", style);
|
|
9872
|
+
else child.removeAttribute("style");
|
|
9873
|
+
return;
|
|
9874
|
+
}
|
|
9572
9875
|
if ((name === "href" || name === "src") && !safeUrl(attr.value, tag)) {
|
|
9573
9876
|
child.removeAttribute(attr.name);
|
|
9574
9877
|
}
|
|
@@ -9589,79 +9892,475 @@ function sanitizeHtml(html) {
|
|
|
9589
9892
|
}
|
|
9590
9893
|
|
|
9591
9894
|
// src/components/rich-editor/index.js
|
|
9895
|
+
var BLOCKS = [
|
|
9896
|
+
{ value: "p", label: "paragraph" },
|
|
9897
|
+
{ value: "h1", label: "heading1" },
|
|
9898
|
+
{ value: "h2", label: "heading2" },
|
|
9899
|
+
{ value: "h3", label: "heading3" },
|
|
9900
|
+
{ value: "blockquote", label: "quote" },
|
|
9901
|
+
{ value: "pre", label: "codeBlock" }
|
|
9902
|
+
];
|
|
9903
|
+
var SIZES = [
|
|
9904
|
+
{ value: "", label: "defaultSize" },
|
|
9905
|
+
{ value: "12px", label: "12px" },
|
|
9906
|
+
{ value: "14px", label: "14px" },
|
|
9907
|
+
{ value: "16px", label: "16px" },
|
|
9908
|
+
{ value: "18px", label: "18px" },
|
|
9909
|
+
{ value: "24px", label: "24px" }
|
|
9910
|
+
];
|
|
9911
|
+
var COLORS = ["#1f2d3d", "#5a6b7d", "#2b86d9", "#2fa36b", "#d97706", "#dc2626", "#7c3aed", "#ffffff"];
|
|
9912
|
+
var TOOLBAR_PRESETS = {
|
|
9913
|
+
simple: ["history", "inline", "list", "insert"],
|
|
9914
|
+
basic: ["history", "format", "inline", "align", "list", "insert", "view"],
|
|
9915
|
+
full: ["history", "format", "inline", "color", "align", "list", "insert", "view"]
|
|
9916
|
+
};
|
|
9917
|
+
function isSafeImageUrl(url) {
|
|
9918
|
+
const value = String(url || "").replace(/[\u0000-\u0020]+/g, "");
|
|
9919
|
+
if (!value) return false;
|
|
9920
|
+
const scheme = value.match(/^([a-z][a-z0-9+.-]*):/i);
|
|
9921
|
+
if (!scheme) return true;
|
|
9922
|
+
const name = scheme[1].toLowerCase();
|
|
9923
|
+
if (name === "http" || name === "https" || name === "blob") return true;
|
|
9924
|
+
return name === "data" && /^data:image\//i.test(value);
|
|
9925
|
+
}
|
|
9926
|
+
function isSafeLink(url) {
|
|
9927
|
+
const value = String(url || "").trim();
|
|
9928
|
+
if (!value) return false;
|
|
9929
|
+
const scheme = value.replace(/[\u0000-\u0020]+/g, "").match(/^([a-z][a-z0-9+.-]*):/i);
|
|
9930
|
+
if (!scheme) return true;
|
|
9931
|
+
return ["http", "https", "mailto", "tel"].indexOf(scheme[1].toLowerCase()) > -1;
|
|
9932
|
+
}
|
|
9592
9933
|
var rich_editor_default = {
|
|
9593
9934
|
name: "DjmRichEditor",
|
|
9594
|
-
components: { Icon: icon_default },
|
|
9935
|
+
components: { Icon: icon_default, MediaPicker: media_picker_default },
|
|
9595
9936
|
props: {
|
|
9596
9937
|
modelValue: { type: String, default: "" },
|
|
9597
9938
|
placeholder: { type: String, default: "" },
|
|
9598
9939
|
minHeight: { type: Number, default: 200 },
|
|
9599
|
-
//
|
|
9600
|
-
sanitize: { type: Boolean, default:
|
|
9601
|
-
disabled: { type: Boolean, default: false }
|
|
9940
|
+
// 默认开启:对外输出的 HTML 走白名单清洗,内容里不会带出脚本
|
|
9941
|
+
sanitize: { type: Boolean, default: true },
|
|
9942
|
+
disabled: { type: Boolean, default: false },
|
|
9943
|
+
// 工具条:simple / basic / full,或传分组数组(history / format / inline / color / align / list / insert / view)
|
|
9944
|
+
toolbar: { type: [String, Array], default: "full" },
|
|
9945
|
+
// 自定义上传:接收 File,返回图片地址(或 { url }),不传则转成 dataURL 本地预览
|
|
9946
|
+
uploadImage: { type: Function, default: null },
|
|
9947
|
+
// 已上传图片,提供后工具栏出现「从素材库选择」:字符串或 { url, name }
|
|
9948
|
+
media: { type: Array, default: () => [] },
|
|
9949
|
+
accept: { type: String, default: "image/*" },
|
|
9950
|
+
// 单张图片大小上限(MB)
|
|
9951
|
+
maxImageSize: { type: Number, default: 5 },
|
|
9952
|
+
// 是否允许一次选多张
|
|
9953
|
+
multiple: { type: Boolean, default: true },
|
|
9954
|
+
showCount: { type: Boolean, default: true }
|
|
9602
9955
|
},
|
|
9603
|
-
emits: ["update:modelValue"],
|
|
9956
|
+
emits: ["update:modelValue", "upload-error"],
|
|
9604
9957
|
setup(props, { emit }) {
|
|
9605
|
-
const editor = (0,
|
|
9606
|
-
const
|
|
9607
|
-
const
|
|
9608
|
-
const
|
|
9958
|
+
const editor = (0, import_vue33.ref)(null);
|
|
9959
|
+
const fileInput = (0, import_vue33.ref)(null);
|
|
9960
|
+
const sourceMode = (0, import_vue33.ref)(false);
|
|
9961
|
+
const source = (0, import_vue33.ref)(props.modelValue);
|
|
9962
|
+
const fullscreen = (0, import_vue33.ref)(false);
|
|
9963
|
+
const panel = (0, import_vue33.ref)("");
|
|
9964
|
+
const uploadCount = (0, import_vue33.ref)(0);
|
|
9965
|
+
const textColor = (0, import_vue33.ref)(COLORS[0]);
|
|
9966
|
+
const bgColor = (0, import_vue33.ref)("#fdf3c4");
|
|
9967
|
+
const fontSize = (0, import_vue33.ref)("");
|
|
9968
|
+
const count2 = (0, import_vue33.ref)(0);
|
|
9969
|
+
const formats = (0, import_vue33.ref)({
|
|
9970
|
+
bold: false,
|
|
9971
|
+
italic: false,
|
|
9972
|
+
underline: false,
|
|
9973
|
+
strike: false,
|
|
9974
|
+
code: false,
|
|
9975
|
+
block: "p",
|
|
9976
|
+
align: "",
|
|
9977
|
+
list: ""
|
|
9978
|
+
});
|
|
9979
|
+
let savedRange = null;
|
|
9980
|
+
const ready = (0, import_vue33.computed)(() => !props.disabled && !sourceMode.value);
|
|
9981
|
+
const toolbarGroups = (0, import_vue33.computed)(() => {
|
|
9982
|
+
const list = Array.isArray(props.toolbar) ? props.toolbar : TOOLBAR_PRESETS[props.toolbar] || TOOLBAR_PRESETS.full;
|
|
9983
|
+
return new Set(list);
|
|
9984
|
+
});
|
|
9985
|
+
function has(group) {
|
|
9986
|
+
return toolbarGroups.value.has(group);
|
|
9987
|
+
}
|
|
9988
|
+
const mediaLibrary = (0, import_vue33.computed)(
|
|
9989
|
+
() => (props.media || []).map((item, index) => {
|
|
9990
|
+
if (typeof item === "string") {
|
|
9991
|
+
return { id: "media-" + index, url: item, name: "", type: "image" };
|
|
9992
|
+
}
|
|
9993
|
+
return {
|
|
9994
|
+
id: item && item.id !== void 0 ? String(item.id) : "media-" + index,
|
|
9995
|
+
url: item && (item.url || item.src) || "",
|
|
9996
|
+
name: item && (item.name || item.alt) || "",
|
|
9997
|
+
type: "image"
|
|
9998
|
+
};
|
|
9999
|
+
}).filter((item) => !!item.url)
|
|
10000
|
+
);
|
|
9609
10001
|
function clean(html) {
|
|
9610
10002
|
return props.sanitize ? sanitizeHtml(html) : html;
|
|
9611
10003
|
}
|
|
10004
|
+
function plainText(html) {
|
|
10005
|
+
return String(html || "").replace(/<[^>]*>/g, "").replace(/ /g, " ").replace(/&[a-z]+;/gi, " ");
|
|
10006
|
+
}
|
|
9612
10007
|
function syncFromDom() {
|
|
9613
10008
|
if (!editor.value) return;
|
|
9614
|
-
|
|
10009
|
+
const html = clean(editor.value.innerHTML);
|
|
10010
|
+
count2.value = plainText(html).trim().length;
|
|
10011
|
+
emit("update:modelValue", html);
|
|
9615
10012
|
}
|
|
9616
|
-
function
|
|
9617
|
-
const
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
10013
|
+
function currentRange() {
|
|
10014
|
+
const selection = window.getSelection();
|
|
10015
|
+
if (!selection || !selection.rangeCount || !editor.value) return null;
|
|
10016
|
+
const range = selection.getRangeAt(0);
|
|
10017
|
+
return editor.value.contains(range.commonAncestorContainer) ? range : null;
|
|
10018
|
+
}
|
|
10019
|
+
function rememberRange() {
|
|
10020
|
+
const range = currentRange();
|
|
10021
|
+
savedRange = range ? range.cloneRange() : null;
|
|
10022
|
+
}
|
|
10023
|
+
function focusAt(range) {
|
|
10024
|
+
if (!editor.value) return;
|
|
10025
|
+
editor.value.focus();
|
|
10026
|
+
const selection = window.getSelection();
|
|
10027
|
+
selection.removeAllRanges();
|
|
10028
|
+
if (range) {
|
|
10029
|
+
selection.addRange(range);
|
|
10030
|
+
return;
|
|
10031
|
+
}
|
|
10032
|
+
const end = document.createRange();
|
|
10033
|
+
end.selectNodeContents(editor.value);
|
|
10034
|
+
end.collapse(false);
|
|
10035
|
+
selection.addRange(end);
|
|
10036
|
+
}
|
|
10037
|
+
function insertNodeAt(range, node) {
|
|
10038
|
+
if (!editor.value) return;
|
|
10039
|
+
focusAt(range);
|
|
10040
|
+
const selection = window.getSelection();
|
|
10041
|
+
if (selection.rangeCount) selection.getRangeAt(0).insertNode(node);
|
|
10042
|
+
else editor.value.appendChild(node);
|
|
9622
10043
|
syncFromDom();
|
|
10044
|
+
syncFormats();
|
|
10045
|
+
}
|
|
10046
|
+
function ensureRange() {
|
|
10047
|
+
const range = currentRange();
|
|
10048
|
+
if (range) return range;
|
|
10049
|
+
if (!savedRange) return null;
|
|
10050
|
+
focusAt(savedRange);
|
|
10051
|
+
return currentRange();
|
|
9623
10052
|
}
|
|
9624
10053
|
function exec(command, value = null) {
|
|
9625
|
-
if (
|
|
10054
|
+
if (!ready.value) return;
|
|
10055
|
+
if (typeof document.execCommand !== "function") return;
|
|
10056
|
+
ensureRange();
|
|
10057
|
+
document.execCommand("styleWithCSS", false, true);
|
|
9626
10058
|
document.execCommand(command, false, value);
|
|
9627
10059
|
if (editor.value) editor.value.focus();
|
|
9628
10060
|
syncFromDom();
|
|
9629
10061
|
syncFormats();
|
|
9630
10062
|
}
|
|
9631
|
-
function
|
|
9632
|
-
|
|
10063
|
+
function setBlock(value) {
|
|
10064
|
+
panel.value = "";
|
|
10065
|
+
exec("formatBlock", "<" + value + ">");
|
|
10066
|
+
}
|
|
10067
|
+
function setFontSize(size) {
|
|
10068
|
+
panel.value = "";
|
|
10069
|
+
if (!ready.value || !editor.value) return;
|
|
10070
|
+
fontSize.value = size;
|
|
10071
|
+
const range = ensureRange();
|
|
10072
|
+
if (!range || range.collapsed) return;
|
|
10073
|
+
if (!size) {
|
|
10074
|
+
const walker = document.createTreeWalker(editor.value, NodeFilter.SHOW_ELEMENT);
|
|
10075
|
+
const targets = [];
|
|
10076
|
+
while (walker.nextNode()) {
|
|
10077
|
+
const el = walker.currentNode;
|
|
10078
|
+
if (el.style && el.style.fontSize && range.intersectsNode(el)) targets.push(el);
|
|
10079
|
+
}
|
|
10080
|
+
targets.forEach((el) => {
|
|
10081
|
+
el.style.fontSize = "";
|
|
10082
|
+
});
|
|
10083
|
+
syncFromDom();
|
|
10084
|
+
return;
|
|
10085
|
+
}
|
|
10086
|
+
const span = document.createElement("span");
|
|
10087
|
+
span.style.fontSize = size;
|
|
10088
|
+
span.appendChild(range.extractContents());
|
|
10089
|
+
range.insertNode(span);
|
|
10090
|
+
const selection = window.getSelection();
|
|
10091
|
+
const after = document.createRange();
|
|
10092
|
+
after.setStartAfter(span);
|
|
10093
|
+
after.collapse(true);
|
|
10094
|
+
selection.removeAllRanges();
|
|
10095
|
+
selection.addRange(after);
|
|
10096
|
+
syncFromDom();
|
|
10097
|
+
syncFormats();
|
|
10098
|
+
}
|
|
10099
|
+
function applyColor(kind, color) {
|
|
10100
|
+
panel.value = "";
|
|
10101
|
+
if (kind === "text") textColor.value = color;
|
|
10102
|
+
else bgColor.value = color;
|
|
10103
|
+
if (!ready.value) return;
|
|
10104
|
+
if (typeof document.execCommand !== "function") return;
|
|
10105
|
+
document.execCommand("styleWithCSS", false, true);
|
|
10106
|
+
const command = kind === "text" ? "foreColor" : "hiliteColor";
|
|
10107
|
+
if (!document.execCommand(command, false, color) && kind === "bg") {
|
|
10108
|
+
document.execCommand("backColor", false, color);
|
|
10109
|
+
}
|
|
10110
|
+
if (editor.value) editor.value.focus();
|
|
10111
|
+
syncFromDom();
|
|
10112
|
+
syncFormats();
|
|
10113
|
+
}
|
|
10114
|
+
function togglePanel(name) {
|
|
10115
|
+
if (!ready.value) return;
|
|
10116
|
+
panel.value = panel.value === name ? "" : name;
|
|
10117
|
+
}
|
|
10118
|
+
function toggleInlineCode() {
|
|
10119
|
+
if (!ready.value || !editor.value) return;
|
|
10120
|
+
const range = currentRange();
|
|
10121
|
+
if (!range) return;
|
|
10122
|
+
const anchor = range.commonAncestorContainer;
|
|
10123
|
+
const node = anchor.nodeType === 1 ? anchor : anchor.parentElement;
|
|
10124
|
+
const code = node && node.closest ? node.closest("code") : null;
|
|
10125
|
+
if (code && editor.value.contains(code)) {
|
|
10126
|
+
const parent = code.parentNode;
|
|
10127
|
+
while (code.firstChild) parent.insertBefore(code.firstChild, code);
|
|
10128
|
+
parent.removeChild(code);
|
|
10129
|
+
syncFromDom();
|
|
10130
|
+
return;
|
|
10131
|
+
}
|
|
10132
|
+
if (range.collapsed) return;
|
|
10133
|
+
const wrapper = document.createElement("code");
|
|
10134
|
+
wrapper.appendChild(range.extractContents());
|
|
10135
|
+
range.insertNode(wrapper);
|
|
10136
|
+
const selection = window.getSelection();
|
|
10137
|
+
const after = document.createRange();
|
|
10138
|
+
after.setStartAfter(wrapper);
|
|
10139
|
+
after.collapse(true);
|
|
10140
|
+
selection.removeAllRanges();
|
|
10141
|
+
selection.addRange(after);
|
|
10142
|
+
syncFromDom();
|
|
10143
|
+
syncFormats();
|
|
9633
10144
|
}
|
|
9634
10145
|
function addLink() {
|
|
10146
|
+
if (!ready.value) return;
|
|
9635
10147
|
const url = window.prompt(t("richEditor.linkPrompt"), "https://");
|
|
9636
|
-
if (url)
|
|
10148
|
+
if (!url) return;
|
|
10149
|
+
if (!isSafeLink(url)) {
|
|
10150
|
+
window.alert(t("richEditor.linkInvalid"));
|
|
10151
|
+
return;
|
|
10152
|
+
}
|
|
10153
|
+
const range = currentRange();
|
|
10154
|
+
if (range && range.collapsed) {
|
|
10155
|
+
const link = document.createElement("a");
|
|
10156
|
+
link.href = url;
|
|
10157
|
+
link.target = "_blank";
|
|
10158
|
+
link.rel = "noopener noreferrer";
|
|
10159
|
+
link.textContent = url;
|
|
10160
|
+
insertNodeAt(range, link);
|
|
10161
|
+
return;
|
|
10162
|
+
}
|
|
10163
|
+
exec("createLink", url);
|
|
10164
|
+
if (editor.value) {
|
|
10165
|
+
editor.value.querySelectorAll("a[href]").forEach((link) => {
|
|
10166
|
+
if (!link.getAttribute("target")) link.setAttribute("target", "_blank");
|
|
10167
|
+
link.setAttribute("rel", "noopener noreferrer");
|
|
10168
|
+
});
|
|
10169
|
+
}
|
|
10170
|
+
syncFromDom();
|
|
10171
|
+
}
|
|
10172
|
+
function readAsDataUrl(file) {
|
|
10173
|
+
return new Promise((resolve, reject) => {
|
|
10174
|
+
const reader = new FileReader();
|
|
10175
|
+
reader.onload = () => resolve(reader.result);
|
|
10176
|
+
reader.onerror = () => reject(reader.error || new Error("read failed"));
|
|
10177
|
+
reader.readAsDataURL(file);
|
|
10178
|
+
});
|
|
10179
|
+
}
|
|
10180
|
+
async function uploadOne(file) {
|
|
10181
|
+
if (props.uploadImage) {
|
|
10182
|
+
const result = await props.uploadImage(file);
|
|
10183
|
+
const url = typeof result === "string" ? result : result && result.url;
|
|
10184
|
+
if (!url) throw new Error(t("richEditor.imageUrlInvalid"));
|
|
10185
|
+
if (!isSafeImageUrl(url)) throw new Error(t("richEditor.imageUrlInvalid"));
|
|
10186
|
+
return String(url).trim();
|
|
10187
|
+
}
|
|
10188
|
+
return readAsDataUrl(file);
|
|
10189
|
+
}
|
|
10190
|
+
function isImage(file) {
|
|
10191
|
+
return file && /^image\//.test(file.type);
|
|
10192
|
+
}
|
|
10193
|
+
function pickImage(allowMultiple) {
|
|
10194
|
+
if (!ready.value || !fileInput.value) return;
|
|
10195
|
+
rememberRange();
|
|
10196
|
+
fileInput.value.multiple = !!allowMultiple && props.multiple;
|
|
10197
|
+
fileInput.value.click();
|
|
10198
|
+
}
|
|
10199
|
+
function insertMedia(ids) {
|
|
10200
|
+
if (!ready.value) return;
|
|
10201
|
+
const picked = (Array.isArray(ids) ? ids : [ids]).map((id) => mediaLibrary.value.find((item) => item.id === String(id))).filter(Boolean);
|
|
10202
|
+
const safe = picked.filter((item) => {
|
|
10203
|
+
if (isSafeImageUrl(item.url)) return true;
|
|
10204
|
+
emit("upload-error", { file: null, message: t("richEditor.imageUrlInvalid") });
|
|
10205
|
+
return false;
|
|
10206
|
+
});
|
|
10207
|
+
if (!safe.length) return;
|
|
10208
|
+
focusAt(savedRange);
|
|
10209
|
+
const fragment = document.createDocumentFragment();
|
|
10210
|
+
safe.forEach((item) => {
|
|
10211
|
+
const img = document.createElement("img");
|
|
10212
|
+
img.src = item.url;
|
|
10213
|
+
img.alt = item.name || "image";
|
|
10214
|
+
img.className = "djm-rich-editor-img";
|
|
10215
|
+
fragment.appendChild(img);
|
|
10216
|
+
});
|
|
10217
|
+
const selection = window.getSelection();
|
|
10218
|
+
if (selection.rangeCount) selection.getRangeAt(0).insertNode(fragment);
|
|
10219
|
+
else editor.value.appendChild(fragment);
|
|
10220
|
+
syncFromDom();
|
|
10221
|
+
syncFormats();
|
|
10222
|
+
}
|
|
10223
|
+
function insertImages(files) {
|
|
10224
|
+
if (!files.length || !editor.value) return;
|
|
10225
|
+
uploadCount.value = files.length;
|
|
10226
|
+
const holder = document.createElement("span");
|
|
10227
|
+
holder.className = "djm-rich-editor-uploading";
|
|
10228
|
+
holder.textContent = t("richEditor.uploading", { count: files.length });
|
|
10229
|
+
const anchor = savedRange && editor.value.contains(savedRange.commonAncestorContainer) ? savedRange : null;
|
|
10230
|
+
focusAt(anchor);
|
|
10231
|
+
const selection = window.getSelection();
|
|
10232
|
+
if (selection.rangeCount) selection.getRangeAt(0).insertNode(holder);
|
|
10233
|
+
else editor.value.appendChild(holder);
|
|
10234
|
+
Promise.all(files.map(async (file) => {
|
|
10235
|
+
if (!isImage(file)) return { file, error: t("richEditor.imageTypeError") };
|
|
10236
|
+
if (props.maxImageSize > 0 && file.size > props.maxImageSize * 1024 * 1024) {
|
|
10237
|
+
return { file, error: t("richEditor.imageSizeError", { size: props.maxImageSize }) };
|
|
10238
|
+
}
|
|
10239
|
+
try {
|
|
10240
|
+
return { file, url: await uploadOne(file) };
|
|
10241
|
+
} catch (error) {
|
|
10242
|
+
return { file, error: error && error.message || t("richEditor.uploadFailed") };
|
|
10243
|
+
}
|
|
10244
|
+
})).then((results) => {
|
|
10245
|
+
uploadCount.value = 0;
|
|
10246
|
+
const done = results.filter((item) => item.url);
|
|
10247
|
+
const failed = results.filter((item) => item.error);
|
|
10248
|
+
const alive = editor.value && editor.value.contains(holder);
|
|
10249
|
+
if (done.length && editor.value) {
|
|
10250
|
+
done.forEach((item) => {
|
|
10251
|
+
const img = document.createElement("img");
|
|
10252
|
+
img.src = item.url;
|
|
10253
|
+
img.alt = item.file.name || "image";
|
|
10254
|
+
img.className = "djm-rich-editor-img";
|
|
10255
|
+
if (alive) holder.parentNode.insertBefore(img, holder);
|
|
10256
|
+
else editor.value.appendChild(img);
|
|
10257
|
+
});
|
|
10258
|
+
}
|
|
10259
|
+
if (alive) holder.remove();
|
|
10260
|
+
failed.forEach((item) => emit("upload-error", { file: item.file, message: item.error }));
|
|
10261
|
+
syncFromDom();
|
|
10262
|
+
});
|
|
10263
|
+
}
|
|
10264
|
+
function onFilesChange(event) {
|
|
10265
|
+
const files = Array.from(event.target.files || []);
|
|
10266
|
+
event.target.value = "";
|
|
10267
|
+
insertImages(files);
|
|
10268
|
+
}
|
|
10269
|
+
function onPaste(event) {
|
|
10270
|
+
const data = event.clipboardData;
|
|
10271
|
+
if (!data) return;
|
|
10272
|
+
const images = Array.from(data.files || []).filter(isImage);
|
|
10273
|
+
if (images.length) {
|
|
10274
|
+
event.preventDefault();
|
|
10275
|
+
insertImages(images);
|
|
10276
|
+
return;
|
|
10277
|
+
}
|
|
10278
|
+
const html = data.getData("text/html");
|
|
10279
|
+
if (!html) return;
|
|
10280
|
+
event.preventDefault();
|
|
10281
|
+
document.execCommand("insertHTML", false, sanitizeHtml(html));
|
|
10282
|
+
syncFromDom();
|
|
10283
|
+
}
|
|
10284
|
+
function onDragOver(event) {
|
|
10285
|
+
if (!ready.value) return;
|
|
10286
|
+
if (event.dataTransfer && Array.from(event.dataTransfer.types || []).includes("Files")) {
|
|
10287
|
+
event.preventDefault();
|
|
10288
|
+
}
|
|
10289
|
+
}
|
|
10290
|
+
function onDrop(event) {
|
|
10291
|
+
if (!ready.value) return;
|
|
10292
|
+
const files = Array.from(event.dataTransfer && event.dataTransfer.files || []).filter(isImage);
|
|
10293
|
+
if (!files.length) return;
|
|
10294
|
+
event.preventDefault();
|
|
10295
|
+
rememberRange();
|
|
10296
|
+
insertImages(files);
|
|
10297
|
+
}
|
|
10298
|
+
function closestTag(node, tag) {
|
|
10299
|
+
const el = node && (node.nodeType === 1 ? node : node.parentElement);
|
|
10300
|
+
return el && el.closest ? el.closest(tag) : null;
|
|
10301
|
+
}
|
|
10302
|
+
function detectBlock() {
|
|
10303
|
+
const selection = window.getSelection();
|
|
10304
|
+
if (!selection || !selection.anchorNode) return "p";
|
|
10305
|
+
let node = selection.anchorNode;
|
|
10306
|
+
if (node.nodeType !== 1) node = node.parentElement;
|
|
10307
|
+
while (node && node !== editor.value) {
|
|
10308
|
+
const tag = node.tagName.toLowerCase();
|
|
10309
|
+
if (["p", "h1", "h2", "h3", "blockquote", "pre"].includes(tag)) return tag;
|
|
10310
|
+
node = node.parentElement;
|
|
10311
|
+
}
|
|
10312
|
+
return "p";
|
|
9637
10313
|
}
|
|
9638
10314
|
function syncFormats() {
|
|
9639
|
-
if (props.disabled || sourceMode.value) return;
|
|
9640
|
-
if (!editor.value
|
|
10315
|
+
if (props.disabled || sourceMode.value || !editor.value) return;
|
|
10316
|
+
if (!editor.value.contains(document.activeElement)) return;
|
|
10317
|
+
const selection = window.getSelection();
|
|
9641
10318
|
formats.value = {
|
|
9642
10319
|
bold: document.queryCommandState("bold"),
|
|
9643
10320
|
italic: document.queryCommandState("italic"),
|
|
9644
10321
|
underline: document.queryCommandState("underline"),
|
|
9645
|
-
strike: document.queryCommandState("strikeThrough")
|
|
10322
|
+
strike: document.queryCommandState("strikeThrough"),
|
|
10323
|
+
code: !!closestTag(selection.anchorNode, "code"),
|
|
10324
|
+
block: detectBlock(),
|
|
10325
|
+
align: document.queryCommandState("justifyCenter") ? "center" : document.queryCommandState("justifyRight") ? "right" : document.queryCommandState("justifyFull") ? "justify" : "left",
|
|
10326
|
+
list: document.queryCommandState("insertOrderedList") ? "ol" : document.queryCommandState("insertUnorderedList") ? "ul" : ""
|
|
9646
10327
|
};
|
|
9647
10328
|
}
|
|
9648
10329
|
function onSourceInput() {
|
|
10330
|
+
count2.value = plainText(source.value).trim().length;
|
|
9649
10331
|
emit("update:modelValue", clean(source.value));
|
|
9650
10332
|
}
|
|
9651
10333
|
function toggleSource() {
|
|
9652
10334
|
if (props.disabled) return;
|
|
10335
|
+
panel.value = "";
|
|
9653
10336
|
if (sourceMode.value) {
|
|
9654
10337
|
emit("update:modelValue", clean(source.value));
|
|
9655
10338
|
sourceMode.value = false;
|
|
9656
10339
|
requestAnimationFrame(() => {
|
|
9657
10340
|
if (editor.value) editor.value.innerHTML = clean(source.value);
|
|
10341
|
+
syncFromDom();
|
|
9658
10342
|
});
|
|
9659
10343
|
} else {
|
|
9660
10344
|
source.value = editor.value ? editor.value.innerHTML : props.modelValue;
|
|
9661
10345
|
sourceMode.value = true;
|
|
9662
10346
|
}
|
|
9663
10347
|
}
|
|
9664
|
-
(
|
|
10348
|
+
function toggleFullscreen() {
|
|
10349
|
+
if (props.disabled) return;
|
|
10350
|
+
fullscreen.value = !fullscreen.value;
|
|
10351
|
+
panel.value = "";
|
|
10352
|
+
}
|
|
10353
|
+
function onDocumentClick(event) {
|
|
10354
|
+
if (!panel.value) return;
|
|
10355
|
+
if (!(event.target.closest && event.target.closest(".djm-rich-editor-color"))) panel.value = "";
|
|
10356
|
+
}
|
|
10357
|
+
function onKeydown(event) {
|
|
10358
|
+
if (event.key === "Escape" && fullscreen.value) {
|
|
10359
|
+
fullscreen.value = false;
|
|
10360
|
+
panel.value = "";
|
|
10361
|
+
}
|
|
10362
|
+
}
|
|
10363
|
+
(0, import_vue33.watch)(
|
|
9665
10364
|
() => props.modelValue,
|
|
9666
10365
|
(value) => {
|
|
9667
10366
|
if (sourceMode.value) {
|
|
@@ -9670,40 +10369,117 @@ var rich_editor_default = {
|
|
|
9670
10369
|
}
|
|
9671
10370
|
const next = clean(value);
|
|
9672
10371
|
if (editor.value && editor.value.innerHTML !== next) editor.value.innerHTML = next;
|
|
10372
|
+
count2.value = plainText(next).trim().length;
|
|
9673
10373
|
}
|
|
9674
10374
|
);
|
|
9675
|
-
(0,
|
|
10375
|
+
(0, import_vue33.onMounted)(() => {
|
|
9676
10376
|
if (editor.value) editor.value.innerHTML = clean(props.modelValue);
|
|
10377
|
+
count2.value = plainText(props.modelValue).trim().length;
|
|
9677
10378
|
document.addEventListener("selectionchange", syncFormats);
|
|
10379
|
+
document.addEventListener("click", onDocumentClick);
|
|
10380
|
+
document.addEventListener("keydown", onKeydown);
|
|
10381
|
+
});
|
|
10382
|
+
(0, import_vue33.onBeforeUnmount)(() => {
|
|
10383
|
+
document.removeEventListener("selectionchange", syncFormats);
|
|
10384
|
+
document.removeEventListener("click", onDocumentClick);
|
|
10385
|
+
document.removeEventListener("keydown", onKeydown);
|
|
9678
10386
|
});
|
|
9679
|
-
(0, import_vue32.onBeforeUnmount)(() => document.removeEventListener("selectionchange", syncFormats));
|
|
9680
10387
|
return {
|
|
9681
10388
|
editor,
|
|
10389
|
+
fileInput,
|
|
9682
10390
|
sourceMode,
|
|
9683
10391
|
source,
|
|
10392
|
+
fullscreen,
|
|
10393
|
+
panel,
|
|
10394
|
+
uploadCount,
|
|
10395
|
+
textColor,
|
|
10396
|
+
bgColor,
|
|
10397
|
+
fontSize,
|
|
10398
|
+
count: count2,
|
|
9684
10399
|
formats,
|
|
10400
|
+
blocks: BLOCKS,
|
|
10401
|
+
sizes: SIZES,
|
|
10402
|
+
colors: COLORS,
|
|
10403
|
+
mediaLibrary,
|
|
10404
|
+
has,
|
|
9685
10405
|
exec,
|
|
9686
|
-
|
|
10406
|
+
setBlock,
|
|
10407
|
+
setFontSize,
|
|
10408
|
+
applyColor,
|
|
10409
|
+
togglePanel,
|
|
10410
|
+
toggleInlineCode,
|
|
9687
10411
|
addLink,
|
|
9688
|
-
|
|
10412
|
+
pickImage,
|
|
10413
|
+
insertMedia,
|
|
10414
|
+
onFilesChange,
|
|
9689
10415
|
onPaste,
|
|
10416
|
+
onDragOver,
|
|
10417
|
+
onDrop,
|
|
10418
|
+
rememberRange,
|
|
10419
|
+
onSourceInput,
|
|
9690
10420
|
toggleSource,
|
|
10421
|
+
toggleFullscreen,
|
|
9691
10422
|
syncFromDom,
|
|
9692
10423
|
syncFormats,
|
|
9693
10424
|
t
|
|
9694
10425
|
};
|
|
9695
10426
|
},
|
|
9696
10427
|
template: `
|
|
9697
|
-
<div
|
|
9698
|
-
|
|
9699
|
-
|
|
10428
|
+
<div
|
|
10429
|
+
class="djm-rich-editor"
|
|
10430
|
+
:class="{ 'djm-is-disabled': disabled, 'djm-is-fullscreen': fullscreen }"
|
|
10431
|
+
>
|
|
10432
|
+
<div class="djm-rich-editor-toolbar" role="toolbar" :aria-label="t('richEditor.toolbar')" @mousedown="rememberRange">
|
|
10433
|
+
<div class="djm-rich-editor-group" v-if="has('history')">
|
|
10434
|
+
<button
|
|
10435
|
+
class="djm-rich-editor-btn"
|
|
10436
|
+
type="button"
|
|
10437
|
+
:title="t('richEditor.undo')"
|
|
10438
|
+
:aria-label="t('richEditor.undo')"
|
|
10439
|
+
:disabled="disabled || sourceMode"
|
|
10440
|
+
@mousedown.prevent
|
|
10441
|
+
@click="exec('undo')"
|
|
10442
|
+
><Icon name="undo" :size="13" /></button>
|
|
10443
|
+
<button
|
|
10444
|
+
class="djm-rich-editor-btn"
|
|
10445
|
+
type="button"
|
|
10446
|
+
:title="t('richEditor.redo')"
|
|
10447
|
+
:aria-label="t('richEditor.redo')"
|
|
10448
|
+
:disabled="disabled || sourceMode"
|
|
10449
|
+
@mousedown.prevent
|
|
10450
|
+
@click="exec('redo')"
|
|
10451
|
+
><Icon name="redo" :size="13" /></button>
|
|
10452
|
+
</div>
|
|
10453
|
+
|
|
10454
|
+
<div class="djm-rich-editor-group" v-if="has('format')">
|
|
10455
|
+
<select
|
|
10456
|
+
class="djm-rich-editor-select"
|
|
10457
|
+
:value="formats.block"
|
|
10458
|
+
:aria-label="t('richEditor.block')"
|
|
10459
|
+
:disabled="disabled || sourceMode"
|
|
10460
|
+
@change="setBlock($event.target.value)"
|
|
10461
|
+
>
|
|
10462
|
+
<option v-for="item in blocks" :key="item.value" :value="item.value">{{ t('richEditor.' + item.label) }}</option>
|
|
10463
|
+
</select>
|
|
10464
|
+
<select
|
|
10465
|
+
class="djm-rich-editor-select djm-rich-editor-select-size"
|
|
10466
|
+
:value="fontSize"
|
|
10467
|
+
:aria-label="t('richEditor.fontSize')"
|
|
10468
|
+
:disabled="disabled || sourceMode"
|
|
10469
|
+
@change="setFontSize($event.target.value)"
|
|
10470
|
+
>
|
|
10471
|
+
<option v-for="item in sizes" :key="item.value" :value="item.value">{{ t('richEditor.' + item.label) }}</option>
|
|
10472
|
+
</select>
|
|
10473
|
+
</div>
|
|
10474
|
+
|
|
10475
|
+
<div class="djm-rich-editor-group" v-if="has('inline')">
|
|
9700
10476
|
<button
|
|
9701
10477
|
class="djm-rich-editor-btn djm-rich-editor-btn-bold"
|
|
9702
10478
|
type="button"
|
|
9703
10479
|
:title="t('richEditor.bold')"
|
|
9704
10480
|
:aria-label="t('richEditor.bold')"
|
|
9705
10481
|
:aria-pressed="String(formats.bold)"
|
|
9706
|
-
:disabled="disabled"
|
|
10482
|
+
:disabled="disabled || sourceMode"
|
|
9707
10483
|
@mousedown.prevent
|
|
9708
10484
|
@click="exec('bold')"
|
|
9709
10485
|
>B</button>
|
|
@@ -9713,7 +10489,7 @@ var rich_editor_default = {
|
|
|
9713
10489
|
:title="t('richEditor.italic')"
|
|
9714
10490
|
:aria-label="t('richEditor.italic')"
|
|
9715
10491
|
:aria-pressed="String(formats.italic)"
|
|
9716
|
-
:disabled="disabled"
|
|
10492
|
+
:disabled="disabled || sourceMode"
|
|
9717
10493
|
@mousedown.prevent
|
|
9718
10494
|
@click="exec('italic')"
|
|
9719
10495
|
>I</button>
|
|
@@ -9723,7 +10499,7 @@ var rich_editor_default = {
|
|
|
9723
10499
|
:title="t('richEditor.underline')"
|
|
9724
10500
|
:aria-label="t('richEditor.underline')"
|
|
9725
10501
|
:aria-pressed="String(formats.underline)"
|
|
9726
|
-
:disabled="disabled"
|
|
10502
|
+
:disabled="disabled || sourceMode"
|
|
9727
10503
|
@mousedown.prevent
|
|
9728
10504
|
@click="exec('underline')"
|
|
9729
10505
|
>U</button>
|
|
@@ -9733,46 +10509,130 @@ var rich_editor_default = {
|
|
|
9733
10509
|
:title="t('richEditor.strike')"
|
|
9734
10510
|
:aria-label="t('richEditor.strike')"
|
|
9735
10511
|
:aria-pressed="String(formats.strike)"
|
|
9736
|
-
:disabled="disabled"
|
|
10512
|
+
:disabled="disabled || sourceMode"
|
|
10513
|
+
@mousedown.prevent
|
|
10514
|
+
@click="exec('strikeThrough')"
|
|
10515
|
+
>S</button>
|
|
10516
|
+
<button
|
|
10517
|
+
class="djm-rich-editor-btn"
|
|
10518
|
+
type="button"
|
|
10519
|
+
:title="t('richEditor.inlineCode')"
|
|
10520
|
+
:aria-label="t('richEditor.inlineCode')"
|
|
10521
|
+
:aria-pressed="String(formats.code)"
|
|
10522
|
+
:disabled="disabled || sourceMode"
|
|
10523
|
+
@mousedown.prevent
|
|
10524
|
+
@click="toggleInlineCode"
|
|
10525
|
+
><Icon name="code" :size="13" /></button>
|
|
10526
|
+
</div>
|
|
10527
|
+
|
|
10528
|
+
<div class="djm-rich-editor-group djm-rich-editor-color" v-if="has('color')">
|
|
10529
|
+
<button
|
|
10530
|
+
class="djm-rich-editor-btn"
|
|
10531
|
+
type="button"
|
|
10532
|
+
:title="t('richEditor.textColor')"
|
|
10533
|
+
:aria-label="t('richEditor.textColor')"
|
|
10534
|
+
:aria-expanded="String(panel === 'text')"
|
|
10535
|
+
:disabled="disabled || sourceMode"
|
|
10536
|
+
@mousedown.prevent
|
|
10537
|
+
@click.stop="togglePanel('text')"
|
|
10538
|
+
>
|
|
10539
|
+
<span class="djm-rich-editor-letter" :style="{ borderBottomColor: textColor }">A</span>
|
|
10540
|
+
</button>
|
|
10541
|
+
<button
|
|
10542
|
+
class="djm-rich-editor-btn"
|
|
10543
|
+
type="button"
|
|
10544
|
+
:title="t('richEditor.bgColor')"
|
|
10545
|
+
:aria-label="t('richEditor.bgColor')"
|
|
10546
|
+
:aria-expanded="String(panel === 'bg')"
|
|
10547
|
+
:disabled="disabled || sourceMode"
|
|
9737
10548
|
@mousedown.prevent
|
|
9738
|
-
@click="
|
|
9739
|
-
>
|
|
10549
|
+
@click.stop="togglePanel('bg')"
|
|
10550
|
+
>
|
|
10551
|
+
<span class="djm-rich-editor-letter djm-rich-editor-letter-bg" :style="{ background: bgColor }">A</span>
|
|
10552
|
+
</button>
|
|
10553
|
+
<div v-if="panel === 'text' || panel === 'bg'" class="djm-rich-editor-panel" role="group" :aria-label="t('richEditor.textColor')">
|
|
10554
|
+
<button
|
|
10555
|
+
v-for="color in colors"
|
|
10556
|
+
:key="color"
|
|
10557
|
+
class="djm-rich-editor-swatch"
|
|
10558
|
+
type="button"
|
|
10559
|
+
:title="color"
|
|
10560
|
+
:aria-label="color"
|
|
10561
|
+
:style="{ background: color }"
|
|
10562
|
+
@mousedown.prevent
|
|
10563
|
+
@click="applyColor(panel, color)"
|
|
10564
|
+
></button>
|
|
10565
|
+
</div>
|
|
9740
10566
|
</div>
|
|
9741
10567
|
|
|
9742
|
-
<div class="djm-rich-editor-group">
|
|
9743
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
9744
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
10568
|
+
<div class="djm-rich-editor-group" v-if="has('align')">
|
|
10569
|
+
<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>
|
|
10570
|
+
<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>
|
|
10571
|
+
<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>
|
|
10572
|
+
<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>
|
|
9745
10573
|
</div>
|
|
9746
10574
|
|
|
9747
|
-
<div class="djm-rich-editor-group">
|
|
9748
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.bulletList')" :disabled="disabled" @mousedown.prevent @click="exec('insertUnorderedList')">
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
9752
|
-
<span class="djm-rich-editor-glyph">1. \u2261</span>
|
|
9753
|
-
</button>
|
|
9754
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.link')" :aria-label="t('richEditor.link')" :disabled="disabled" @mousedown.prevent @click="addLink">
|
|
9755
|
-
<Icon name="link" :size="13" />
|
|
9756
|
-
</button>
|
|
10575
|
+
<div class="djm-rich-editor-group" v-if="has('list')">
|
|
10576
|
+
<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>
|
|
10577
|
+
<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>
|
|
10578
|
+
<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>
|
|
10579
|
+
<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>
|
|
9757
10580
|
</div>
|
|
9758
10581
|
|
|
9759
|
-
<div class="djm-rich-editor-group">
|
|
9760
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
9761
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
9762
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
10582
|
+
<div class="djm-rich-editor-group" v-if="has('insert')">
|
|
10583
|
+
<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>
|
|
10584
|
+
<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>
|
|
10585
|
+
<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>
|
|
10586
|
+
<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>
|
|
10587
|
+
<MediaPicker
|
|
10588
|
+
v-if="mediaLibrary.length"
|
|
10589
|
+
class="djm-rich-editor-picker"
|
|
10590
|
+
multiple
|
|
10591
|
+
accept="image"
|
|
10592
|
+
:items="mediaLibrary"
|
|
10593
|
+
:title="t('richEditor.mediaLibrary')"
|
|
10594
|
+
:disabled="disabled || sourceMode"
|
|
10595
|
+
@change="insertMedia"
|
|
10596
|
+
>
|
|
10597
|
+
<template #trigger="{ open }">
|
|
10598
|
+
<button
|
|
10599
|
+
class="djm-rich-editor-btn"
|
|
10600
|
+
type="button"
|
|
10601
|
+
:title="t('richEditor.mediaLibrary')"
|
|
10602
|
+
:aria-label="t('richEditor.mediaLibrary')"
|
|
10603
|
+
:disabled="disabled || sourceMode"
|
|
10604
|
+
@mousedown.prevent="rememberRange"
|
|
10605
|
+
@click="open"
|
|
10606
|
+
><Icon name="grid" :size="13" /></button>
|
|
10607
|
+
</template>
|
|
10608
|
+
</MediaPicker>
|
|
10609
|
+
<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>
|
|
10610
|
+
<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>
|
|
9763
10611
|
</div>
|
|
9764
10612
|
|
|
9765
|
-
<div class="djm-rich-editor-spacer"></div>
|
|
9766
|
-
<
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
10613
|
+
<div class="djm-rich-editor-spacer" v-if="has('view')"></div>
|
|
10614
|
+
<div class="djm-rich-editor-group djm-rich-editor-group-end" v-if="has('view')">
|
|
10615
|
+
<button
|
|
10616
|
+
class="djm-rich-editor-btn"
|
|
10617
|
+
:class="{ 'djm-is-active': sourceMode }"
|
|
10618
|
+
type="button"
|
|
10619
|
+
:title="sourceMode ? t('richEditor.visual') : t('richEditor.source')"
|
|
10620
|
+
:aria-label="sourceMode ? t('richEditor.visual') : t('richEditor.source')"
|
|
10621
|
+
:aria-pressed="String(sourceMode)"
|
|
10622
|
+
:disabled="disabled"
|
|
10623
|
+
@click="toggleSource"
|
|
10624
|
+
><Icon name="code" :size="13" /></button>
|
|
10625
|
+
<button
|
|
10626
|
+
class="djm-rich-editor-btn"
|
|
10627
|
+
:class="{ 'djm-is-active': fullscreen }"
|
|
10628
|
+
type="button"
|
|
10629
|
+
:title="fullscreen ? t('richEditor.exitFullscreen') : t('richEditor.fullscreen')"
|
|
10630
|
+
:aria-label="fullscreen ? t('richEditor.exitFullscreen') : t('richEditor.fullscreen')"
|
|
10631
|
+
:aria-pressed="String(fullscreen)"
|
|
10632
|
+
:disabled="disabled"
|
|
10633
|
+
@click="toggleFullscreen"
|
|
10634
|
+
><Icon :name="fullscreen ? 'minimize' : 'maximize'" :size="13" /></button>
|
|
10635
|
+
</div>
|
|
9776
10636
|
</div>
|
|
9777
10637
|
|
|
9778
10638
|
<div
|
|
@@ -9788,6 +10648,8 @@ var rich_editor_default = {
|
|
|
9788
10648
|
:style="{ minHeight: minHeight + 'px' }"
|
|
9789
10649
|
@input="syncFromDom"
|
|
9790
10650
|
@paste="onPaste"
|
|
10651
|
+
@dragover="onDragOver"
|
|
10652
|
+
@drop="onDrop"
|
|
9791
10653
|
></div>
|
|
9792
10654
|
|
|
9793
10655
|
<textarea
|
|
@@ -9800,12 +10662,27 @@ var rich_editor_default = {
|
|
|
9800
10662
|
spellcheck="false"
|
|
9801
10663
|
@input="onSourceInput"
|
|
9802
10664
|
></textarea>
|
|
10665
|
+
|
|
10666
|
+
<div v-if="showCount" class="djm-rich-editor-foot">
|
|
10667
|
+
<span v-if="uploadCount" class="djm-rich-editor-foot-item">{{ t('richEditor.uploading', { count: uploadCount }) }}</span>
|
|
10668
|
+
<span class="djm-rich-editor-foot-item">{{ t('richEditor.wordCount', { count }) }}</span>
|
|
10669
|
+
</div>
|
|
10670
|
+
|
|
10671
|
+
<input
|
|
10672
|
+
ref="fileInput"
|
|
10673
|
+
class="djm-rich-editor-file"
|
|
10674
|
+
type="file"
|
|
10675
|
+
:accept="accept"
|
|
10676
|
+
tabindex="-1"
|
|
10677
|
+
aria-hidden="true"
|
|
10678
|
+
@change="onFilesChange"
|
|
10679
|
+
/>
|
|
9803
10680
|
</div>
|
|
9804
10681
|
`
|
|
9805
10682
|
};
|
|
9806
10683
|
|
|
9807
10684
|
// src/components/floating-window/index.js
|
|
9808
|
-
var
|
|
10685
|
+
var import_vue34 = require("vue");
|
|
9809
10686
|
function clamp(value, min, max) {
|
|
9810
10687
|
return Math.min(Math.max(value, min), Math.max(min, max));
|
|
9811
10688
|
}
|
|
@@ -9823,12 +10700,12 @@ var floating_window_default = {
|
|
|
9823
10700
|
},
|
|
9824
10701
|
emits: ["close", "focus"],
|
|
9825
10702
|
setup(props, { emit }) {
|
|
9826
|
-
const root = (0,
|
|
9827
|
-
const pos = (0,
|
|
9828
|
-
const size = (0,
|
|
9829
|
-
const minimized = (0,
|
|
9830
|
-
const maximized = (0,
|
|
9831
|
-
const saved = (0,
|
|
10703
|
+
const root = (0, import_vue34.ref)(null);
|
|
10704
|
+
const pos = (0, import_vue34.ref)({ x: props.initialX, y: props.initialY });
|
|
10705
|
+
const size = (0, import_vue34.ref)({ w: props.width, h: props.height });
|
|
10706
|
+
const minimized = (0, import_vue34.ref)(false);
|
|
10707
|
+
const maximized = (0, import_vue34.ref)(false);
|
|
10708
|
+
const saved = (0, import_vue34.ref)(null);
|
|
9832
10709
|
let drag = null;
|
|
9833
10710
|
let resize = null;
|
|
9834
10711
|
function stage() {
|
|
@@ -9916,7 +10793,7 @@ var floating_window_default = {
|
|
|
9916
10793
|
}
|
|
9917
10794
|
emit("focus");
|
|
9918
10795
|
}
|
|
9919
|
-
(0,
|
|
10796
|
+
(0, import_vue34.onBeforeUnmount)(() => {
|
|
9920
10797
|
window.removeEventListener("pointermove", onDrag);
|
|
9921
10798
|
window.removeEventListener("pointerup", endDrag);
|
|
9922
10799
|
window.removeEventListener("pointermove", onResize);
|
|
@@ -9964,7 +10841,7 @@ var floating_window_default = {
|
|
|
9964
10841
|
};
|
|
9965
10842
|
|
|
9966
10843
|
// src/components/upload/index.js
|
|
9967
|
-
var
|
|
10844
|
+
var import_vue35 = require("vue");
|
|
9968
10845
|
var uidSeed6 = 0;
|
|
9969
10846
|
function extension(name) {
|
|
9970
10847
|
const index = name.lastIndexOf(".");
|
|
@@ -10014,11 +10891,11 @@ var upload_default = {
|
|
|
10014
10891
|
},
|
|
10015
10892
|
emits: ["update:modelValue", "change", "success", "error", "remove", "exceed"],
|
|
10016
10893
|
setup(props, { emit }) {
|
|
10017
|
-
const files = (0,
|
|
10018
|
-
const dragActive = (0,
|
|
10019
|
-
const inputRef = (0,
|
|
10894
|
+
const files = (0, import_vue35.ref)([]);
|
|
10895
|
+
const dragActive = (0, import_vue35.ref)(false);
|
|
10896
|
+
const inputRef = (0, import_vue35.ref)(null);
|
|
10020
10897
|
const tasks = /* @__PURE__ */ new Map();
|
|
10021
|
-
(0,
|
|
10898
|
+
(0, import_vue35.watch)(
|
|
10022
10899
|
() => props.modelValue,
|
|
10023
10900
|
(list) => {
|
|
10024
10901
|
const next = list || [];
|
|
@@ -10112,7 +10989,7 @@ var upload_default = {
|
|
|
10112
10989
|
if (pass === false) continue;
|
|
10113
10990
|
}
|
|
10114
10991
|
uidSeed6 += 1;
|
|
10115
|
-
const item = (0,
|
|
10992
|
+
const item = (0, import_vue35.reactive)({
|
|
10116
10993
|
uid: uidSeed6,
|
|
10117
10994
|
name: raw.name,
|
|
10118
10995
|
size: raw.size,
|
|
@@ -10172,7 +11049,7 @@ var upload_default = {
|
|
|
10172
11049
|
files.value = [];
|
|
10173
11050
|
emitList();
|
|
10174
11051
|
}
|
|
10175
|
-
(0,
|
|
11052
|
+
(0, import_vue35.onBeforeUnmount)(() => {
|
|
10176
11053
|
files.value.forEach((item) => {
|
|
10177
11054
|
if (item.url) URL.revokeObjectURL(item.url);
|
|
10178
11055
|
});
|
|
@@ -10320,7 +11197,7 @@ var upload_default = {
|
|
|
10320
11197
|
};
|
|
10321
11198
|
|
|
10322
11199
|
// src/components/tree/index.js
|
|
10323
|
-
var
|
|
11200
|
+
var import_vue36 = require("vue");
|
|
10324
11201
|
var tree_default = {
|
|
10325
11202
|
name: "DjmTree",
|
|
10326
11203
|
components: { Icon: icon_default },
|
|
@@ -10352,17 +11229,17 @@ var tree_default = {
|
|
|
10352
11229
|
},
|
|
10353
11230
|
emits: ["update:checked", "update:expanded", "node-click", "check", "node-drop", "load-error"],
|
|
10354
11231
|
setup(props, { emit }) {
|
|
10355
|
-
const lazyChildren = (0,
|
|
10356
|
-
const loadingKeys = (0,
|
|
10357
|
-
const innerExpanded = (0,
|
|
10358
|
-
const innerChecked = (0,
|
|
10359
|
-
(0,
|
|
11232
|
+
const lazyChildren = (0, import_vue36.ref)({});
|
|
11233
|
+
const loadingKeys = (0, import_vue36.ref)([]);
|
|
11234
|
+
const innerExpanded = (0, import_vue36.ref)([...props.expanded]);
|
|
11235
|
+
const innerChecked = (0, import_vue36.ref)([...props.checked]);
|
|
11236
|
+
(0, import_vue36.watch)(
|
|
10360
11237
|
() => props.expanded,
|
|
10361
11238
|
(value) => {
|
|
10362
11239
|
if (Array.isArray(value)) innerExpanded.value = value;
|
|
10363
11240
|
}
|
|
10364
11241
|
);
|
|
10365
|
-
(0,
|
|
11242
|
+
(0, import_vue36.watch)(
|
|
10366
11243
|
() => props.checked,
|
|
10367
11244
|
(value) => {
|
|
10368
11245
|
if (Array.isArray(value)) innerChecked.value = value;
|
|
@@ -10400,7 +11277,7 @@ var tree_default = {
|
|
|
10400
11277
|
});
|
|
10401
11278
|
return out;
|
|
10402
11279
|
}
|
|
10403
|
-
(0,
|
|
11280
|
+
(0, import_vue36.watch)(
|
|
10404
11281
|
() => props.data,
|
|
10405
11282
|
() => {
|
|
10406
11283
|
if (props.defaultExpandAll) innerExpanded.value = collectExpandable(props.data);
|
|
@@ -10417,7 +11294,7 @@ var tree_default = {
|
|
|
10417
11294
|
const children = childrenOf(node);
|
|
10418
11295
|
return Boolean(children && children.some((child) => matchSubtree(child)));
|
|
10419
11296
|
}
|
|
10420
|
-
const rows = (0,
|
|
11297
|
+
const rows = (0, import_vue36.computed)(() => {
|
|
10421
11298
|
const result = [];
|
|
10422
11299
|
const walk = (nodes, level) => {
|
|
10423
11300
|
nodes.forEach((node) => {
|
|
@@ -10488,8 +11365,8 @@ var tree_default = {
|
|
|
10488
11365
|
function isActive(node) {
|
|
10489
11366
|
return props.activeKey !== "" && node[props.nodeKey] === props.activeKey;
|
|
10490
11367
|
}
|
|
10491
|
-
const rootEl = (0,
|
|
10492
|
-
const focusIndex = (0,
|
|
11368
|
+
const rootEl = (0, import_vue36.ref)(null);
|
|
11369
|
+
const focusIndex = (0, import_vue36.ref)(-1);
|
|
10493
11370
|
function tabIndexOf(index) {
|
|
10494
11371
|
return (focusIndex.value === -1 ? 0 : focusIndex.value) === index ? 0 : -1;
|
|
10495
11372
|
}
|
|
@@ -10546,9 +11423,9 @@ var tree_default = {
|
|
|
10546
11423
|
function rowClassOf(node, level) {
|
|
10547
11424
|
return typeof props.rowClass === "function" ? props.rowClass(node, level) : props.rowClass;
|
|
10548
11425
|
}
|
|
10549
|
-
const dragKey = (0,
|
|
10550
|
-
const dropKey = (0,
|
|
10551
|
-
const dropPosition = (0,
|
|
11426
|
+
const dragKey = (0, import_vue36.ref)("");
|
|
11427
|
+
const dropKey = (0, import_vue36.ref)("");
|
|
11428
|
+
const dropPosition = (0, import_vue36.ref)("");
|
|
10552
11429
|
function findNodeByKey(key, nodes = props.data) {
|
|
10553
11430
|
for (const node of nodes) {
|
|
10554
11431
|
if (node[props.nodeKey] === key) return node;
|
|
@@ -10660,7 +11537,7 @@ var tree_default = {
|
|
|
10660
11537
|
onDrop,
|
|
10661
11538
|
onDragEnd,
|
|
10662
11539
|
labelOf,
|
|
10663
|
-
emptyLabel: (0,
|
|
11540
|
+
emptyLabel: (0, import_vue36.computed)(() => props.emptyText || t("tree.empty")),
|
|
10664
11541
|
t
|
|
10665
11542
|
};
|
|
10666
11543
|
},
|
|
@@ -10729,7 +11606,7 @@ var tree_default = {
|
|
|
10729
11606
|
};
|
|
10730
11607
|
|
|
10731
11608
|
// src/components/editable-table/index.js
|
|
10732
|
-
var
|
|
11609
|
+
var import_vue37 = require("vue");
|
|
10733
11610
|
var ACTIONS_KEY = "__editable_actions";
|
|
10734
11611
|
function isEmpty(value) {
|
|
10735
11612
|
if (value === "" || value === null || value === void 0) return true;
|
|
@@ -10752,13 +11629,13 @@ var editable_table_default = {
|
|
|
10752
11629
|
},
|
|
10753
11630
|
emits: ["edit", "save", "cancel"],
|
|
10754
11631
|
setup(props, { emit }) {
|
|
10755
|
-
const slots = (0,
|
|
10756
|
-
const editingKey = (0,
|
|
10757
|
-
const draft = (0,
|
|
10758
|
-
const errors = (0,
|
|
10759
|
-
const tableRef = (0,
|
|
10760
|
-
const editableColumns = (0,
|
|
10761
|
-
const renderColumns = (0,
|
|
11632
|
+
const slots = (0, import_vue37.useSlots)();
|
|
11633
|
+
const editingKey = (0, import_vue37.ref)("");
|
|
11634
|
+
const draft = (0, import_vue37.reactive)({});
|
|
11635
|
+
const errors = (0, import_vue37.reactive)({});
|
|
11636
|
+
const tableRef = (0, import_vue37.ref)(null);
|
|
11637
|
+
const editableColumns = (0, import_vue37.computed)(() => props.columns.filter((column) => column.editor));
|
|
11638
|
+
const renderColumns = (0, import_vue37.computed)(() => {
|
|
10762
11639
|
if (!props.showActions) return props.columns;
|
|
10763
11640
|
return props.columns.concat({
|
|
10764
11641
|
key: ACTIONS_KEY,
|
|
@@ -10770,7 +11647,7 @@ var editable_table_default = {
|
|
|
10770
11647
|
exportable: false
|
|
10771
11648
|
});
|
|
10772
11649
|
});
|
|
10773
|
-
const forwardedSlotNames = (0,
|
|
11650
|
+
const forwardedSlotNames = (0, import_vue37.computed)(() => {
|
|
10774
11651
|
const taken = new Set(editableColumns.value.map((column) => "cell-" + column.key));
|
|
10775
11652
|
taken.add("cell-" + ACTIONS_KEY);
|
|
10776
11653
|
return Object.keys(slots).filter((name) => !taken.has(name));
|
|
@@ -10845,7 +11722,7 @@ var editable_table_default = {
|
|
|
10845
11722
|
return false;
|
|
10846
11723
|
}
|
|
10847
11724
|
if (!validate()) {
|
|
10848
|
-
(0,
|
|
11725
|
+
(0, import_vue37.nextTick)(() => {
|
|
10849
11726
|
const el = tableRef.value && tableRef.value.$el ? tableRef.value.$el.querySelector(".djm-cell-error") : null;
|
|
10850
11727
|
if (el) el.scrollIntoView({ block: "nearest" });
|
|
10851
11728
|
});
|
|
@@ -10865,7 +11742,7 @@ var editable_table_default = {
|
|
|
10865
11742
|
clearErrors();
|
|
10866
11743
|
return true;
|
|
10867
11744
|
}
|
|
10868
|
-
(0,
|
|
11745
|
+
(0, import_vue37.watch)(
|
|
10869
11746
|
() => props.rows,
|
|
10870
11747
|
() => {
|
|
10871
11748
|
if (editingKey.value && !findRow(editingKey.value)) {
|
|
@@ -11001,8 +11878,8 @@ var descriptions_default = {
|
|
|
11001
11878
|
};
|
|
11002
11879
|
|
|
11003
11880
|
// src/components/message/index.js
|
|
11004
|
-
var
|
|
11005
|
-
var djmMessages = (0,
|
|
11881
|
+
var import_vue38 = require("vue");
|
|
11882
|
+
var djmMessages = (0, import_vue38.reactive)([]);
|
|
11006
11883
|
var seq = 0;
|
|
11007
11884
|
function close(id) {
|
|
11008
11885
|
const index = djmMessages.findIndex((item) => item.id === id);
|
|
@@ -11047,7 +11924,7 @@ var message_default = {
|
|
|
11047
11924
|
};
|
|
11048
11925
|
|
|
11049
11926
|
// src/components/org-picker/index.js
|
|
11050
|
-
var
|
|
11927
|
+
var import_vue39 = require("vue");
|
|
11051
11928
|
var org_picker_default = {
|
|
11052
11929
|
name: "DjmOrgPicker",
|
|
11053
11930
|
components: { DjmIcon: icon_default },
|
|
@@ -11066,13 +11943,13 @@ var org_picker_default = {
|
|
|
11066
11943
|
},
|
|
11067
11944
|
emits: ["update:modelValue", "change", "visible-change"],
|
|
11068
11945
|
setup(props, { emit }) {
|
|
11069
|
-
const open = (0,
|
|
11070
|
-
const root = (0,
|
|
11071
|
-
const searchRef = (0,
|
|
11072
|
-
const keyword = (0,
|
|
11073
|
-
const activeDept = (0,
|
|
11074
|
-
const temp = (0,
|
|
11075
|
-
const expandedIds = (0,
|
|
11946
|
+
const open = (0, import_vue39.ref)(false);
|
|
11947
|
+
const root = (0, import_vue39.ref)(null);
|
|
11948
|
+
const searchRef = (0, import_vue39.ref)(null);
|
|
11949
|
+
const keyword = (0, import_vue39.ref)("");
|
|
11950
|
+
const activeDept = (0, import_vue39.ref)("");
|
|
11951
|
+
const temp = (0, import_vue39.ref)([]);
|
|
11952
|
+
const expandedIds = (0, import_vue39.ref)(/* @__PURE__ */ new Set());
|
|
11076
11953
|
let detachPanel = null;
|
|
11077
11954
|
function collectDeptIds(nodes, out = []) {
|
|
11078
11955
|
nodes.forEach((node) => {
|
|
@@ -11102,22 +11979,22 @@ var org_picker_default = {
|
|
|
11102
11979
|
walk([root2]);
|
|
11103
11980
|
return ids;
|
|
11104
11981
|
}
|
|
11105
|
-
const staffMap = (0,
|
|
11982
|
+
const staffMap = (0, import_vue39.computed)(() => {
|
|
11106
11983
|
const map = /* @__PURE__ */ new Map();
|
|
11107
11984
|
props.staff.forEach((item) => map.set(item.id, item));
|
|
11108
11985
|
return map;
|
|
11109
11986
|
});
|
|
11110
|
-
const appliedStaff = (0,
|
|
11987
|
+
const appliedStaff = (0, import_vue39.computed)(
|
|
11111
11988
|
() => props.modelValue.map((id) => staffMap.value.get(id)).filter(Boolean)
|
|
11112
11989
|
);
|
|
11113
|
-
const visibleTags = (0,
|
|
11990
|
+
const visibleTags = (0, import_vue39.computed)(() => {
|
|
11114
11991
|
if (props.maxTagCount <= 0) return appliedStaff.value;
|
|
11115
11992
|
return appliedStaff.value.slice(0, props.maxTagCount);
|
|
11116
11993
|
});
|
|
11117
|
-
const restTagCount = (0,
|
|
11994
|
+
const restTagCount = (0, import_vue39.computed)(
|
|
11118
11995
|
() => Math.max(0, appliedStaff.value.length - visibleTags.value.length)
|
|
11119
11996
|
);
|
|
11120
|
-
const visibleStaff = (0,
|
|
11997
|
+
const visibleStaff = (0, import_vue39.computed)(() => {
|
|
11121
11998
|
const key = keyword.value.trim().toLowerCase();
|
|
11122
11999
|
if (key) {
|
|
11123
12000
|
return props.staff.filter(
|
|
@@ -11128,7 +12005,7 @@ var org_picker_default = {
|
|
|
11128
12005
|
const scope = deptScope(activeDept.value);
|
|
11129
12006
|
return props.staff.filter((item) => scope.has(item.deptId));
|
|
11130
12007
|
});
|
|
11131
|
-
const deptRows = (0,
|
|
12008
|
+
const deptRows = (0, import_vue39.computed)(() => {
|
|
11132
12009
|
const rows = [];
|
|
11133
12010
|
const walk = (nodes, depth) => {
|
|
11134
12011
|
nodes.forEach((node) => {
|
|
@@ -11140,11 +12017,11 @@ var org_picker_default = {
|
|
|
11140
12017
|
walk(props.departments, 0);
|
|
11141
12018
|
return rows;
|
|
11142
12019
|
});
|
|
11143
|
-
const selectedCount = (0,
|
|
11144
|
-
const placeholderText = (0,
|
|
11145
|
-
const emptyLabel = (0,
|
|
11146
|
-
const panel = (0,
|
|
11147
|
-
const panelStyle = (0,
|
|
12020
|
+
const selectedCount = (0, import_vue39.computed)(() => temp.value.length);
|
|
12021
|
+
const placeholderText = (0, import_vue39.computed)(() => props.placeholder || t("orgPicker.placeholder"));
|
|
12022
|
+
const emptyLabel = (0, import_vue39.computed)(() => props.emptyText || t("orgPicker.empty"));
|
|
12023
|
+
const panel = (0, import_vue39.ref)(null);
|
|
12024
|
+
const panelStyle = (0, import_vue39.ref)({ ...PANEL_HIDDEN_STYLE });
|
|
11148
12025
|
const PANEL_HEIGHT = 342;
|
|
11149
12026
|
function updatePosition() {
|
|
11150
12027
|
if (!open.value || !root.value) return;
|
|
@@ -11161,7 +12038,7 @@ var org_picker_default = {
|
|
|
11161
12038
|
expandedIds.value = new Set(collectDeptIds(props.departments));
|
|
11162
12039
|
}
|
|
11163
12040
|
expandAll();
|
|
11164
|
-
(0,
|
|
12041
|
+
(0, import_vue39.watch)(() => props.departments, () => {
|
|
11165
12042
|
expandedIds.value = /* @__PURE__ */ new Set([...expandedIds.value, ...collectDeptIds(props.departments)]);
|
|
11166
12043
|
});
|
|
11167
12044
|
function initialOf(item) {
|
|
@@ -11250,7 +12127,7 @@ var org_picker_default = {
|
|
|
11250
12127
|
function clearTemp() {
|
|
11251
12128
|
temp.value = [];
|
|
11252
12129
|
}
|
|
11253
|
-
(0,
|
|
12130
|
+
(0, import_vue39.onBeforeUnmount)(() => {
|
|
11254
12131
|
if (detachPanel) {
|
|
11255
12132
|
detachPanel();
|
|
11256
12133
|
detachPanel = null;
|
|
@@ -11436,7 +12313,7 @@ var org_picker_default = {
|
|
|
11436
12313
|
};
|
|
11437
12314
|
|
|
11438
12315
|
// src/components/schema-form/index.js
|
|
11439
|
-
var
|
|
12316
|
+
var import_vue40 = require("vue");
|
|
11440
12317
|
var CONTROL_MAP = {
|
|
11441
12318
|
input: input_default,
|
|
11442
12319
|
number: input_number_default,
|
|
@@ -11472,26 +12349,26 @@ var schema_form_default = {
|
|
|
11472
12349
|
},
|
|
11473
12350
|
emits: ["update:modelValue", "change"],
|
|
11474
12351
|
setup(props, { emit }) {
|
|
11475
|
-
const innerForm = (0,
|
|
11476
|
-
const model = (0,
|
|
11477
|
-
(0,
|
|
12352
|
+
const innerForm = (0, import_vue40.ref)(null);
|
|
12353
|
+
const model = (0, import_vue40.reactive)({ ...props.modelValue });
|
|
12354
|
+
(0, import_vue40.watch)(() => props.fields, (fields) => {
|
|
11478
12355
|
fields.forEach((field) => {
|
|
11479
12356
|
if (model[field.key] === void 0 && field.default !== void 0) {
|
|
11480
12357
|
model[field.key] = typeof field.default === "function" ? field.default() : field.default;
|
|
11481
12358
|
}
|
|
11482
12359
|
});
|
|
11483
12360
|
}, { immediate: true, deep: true });
|
|
11484
|
-
(0,
|
|
12361
|
+
(0, import_vue40.watch)(() => props.modelValue, (value) => {
|
|
11485
12362
|
if (!value) return;
|
|
11486
12363
|
Object.keys(value).forEach((key) => {
|
|
11487
12364
|
if (model[key] !== value[key]) model[key] = value[key];
|
|
11488
12365
|
});
|
|
11489
12366
|
}, { deep: true });
|
|
11490
|
-
(0,
|
|
12367
|
+
(0, import_vue40.watch)(model, () => {
|
|
11491
12368
|
emit("update:modelValue", { ...model });
|
|
11492
12369
|
emit("change", { ...model });
|
|
11493
12370
|
}, { deep: true });
|
|
11494
|
-
const formRules = (0,
|
|
12371
|
+
const formRules = (0, import_vue40.computed)(() => {
|
|
11495
12372
|
const merged = { ...props.rules };
|
|
11496
12373
|
props.fields.forEach((field) => {
|
|
11497
12374
|
if (!field.required && !field.rules) return;
|
|
@@ -11504,7 +12381,7 @@ var schema_form_default = {
|
|
|
11504
12381
|
});
|
|
11505
12382
|
return merged;
|
|
11506
12383
|
});
|
|
11507
|
-
const visibleFields = (0,
|
|
12384
|
+
const visibleFields = (0, import_vue40.computed)(
|
|
11508
12385
|
() => props.fields.filter((field) => {
|
|
11509
12386
|
if (typeof field.visibleWhen !== "function") return true;
|
|
11510
12387
|
return Boolean(field.visibleWhen(model));
|
|
@@ -11698,7 +12575,7 @@ var approval_flow_default = {
|
|
|
11698
12575
|
};
|
|
11699
12576
|
|
|
11700
12577
|
// src/components/query-filter/index.js
|
|
11701
|
-
var
|
|
12578
|
+
var import_vue41 = require("vue");
|
|
11702
12579
|
var CONTROL_MAP2 = {
|
|
11703
12580
|
input: input_default,
|
|
11704
12581
|
number: input_number_default,
|
|
@@ -11723,26 +12600,26 @@ var query_filter_default = {
|
|
|
11723
12600
|
},
|
|
11724
12601
|
emits: ["update:modelValue", "search", "reset"],
|
|
11725
12602
|
setup(props, { emit }) {
|
|
11726
|
-
const expanded = (0,
|
|
11727
|
-
const model = (0,
|
|
11728
|
-
(0,
|
|
12603
|
+
const expanded = (0, import_vue41.ref)(false);
|
|
12604
|
+
const model = (0, import_vue41.reactive)({ ...props.modelValue });
|
|
12605
|
+
(0, import_vue41.watch)(() => props.fields, (fields) => {
|
|
11729
12606
|
fields.forEach((field) => {
|
|
11730
12607
|
if (model[field.key] === void 0 && field.default !== void 0) {
|
|
11731
12608
|
model[field.key] = typeof field.default === "function" ? field.default() : field.default;
|
|
11732
12609
|
}
|
|
11733
12610
|
});
|
|
11734
12611
|
}, { immediate: true, deep: true });
|
|
11735
|
-
(0,
|
|
12612
|
+
(0, import_vue41.watch)(() => props.modelValue, (value) => {
|
|
11736
12613
|
if (!value) return;
|
|
11737
12614
|
Object.keys(value).forEach((key) => {
|
|
11738
12615
|
if (model[key] !== value[key]) model[key] = value[key];
|
|
11739
12616
|
});
|
|
11740
12617
|
}, { deep: true });
|
|
11741
|
-
(0,
|
|
12618
|
+
(0, import_vue41.watch)(model, () => {
|
|
11742
12619
|
emit("update:modelValue", { ...model });
|
|
11743
12620
|
}, { deep: true });
|
|
11744
|
-
const collapsible = (0,
|
|
11745
|
-
const visibleFields = (0,
|
|
12621
|
+
const collapsible = (0, import_vue41.computed)(() => props.fields.length > props.visibleCount);
|
|
12622
|
+
const visibleFields = (0, import_vue41.computed)(() => {
|
|
11746
12623
|
if (!collapsible.value || expanded.value) return props.fields;
|
|
11747
12624
|
return props.fields.slice(0, props.visibleCount);
|
|
11748
12625
|
});
|
|
@@ -11871,198 +12748,6 @@ var filter_tags_default = {
|
|
|
11871
12748
|
`
|
|
11872
12749
|
};
|
|
11873
12750
|
|
|
11874
|
-
// src/components/media-picker/index.js
|
|
11875
|
-
var import_vue41 = require("vue");
|
|
11876
|
-
var media_picker_default = {
|
|
11877
|
-
name: "DjmMediaPicker",
|
|
11878
|
-
components: { DjmModal: modal_default, DjmPagination: pagination_default, DjmIcon: icon_default },
|
|
11879
|
-
props: {
|
|
11880
|
-
modelValue: { type: [Array, String, Number], default: "" },
|
|
11881
|
-
items: { type: Array, default: () => [] },
|
|
11882
|
-
multiple: { type: Boolean, default: false },
|
|
11883
|
-
// 最多选几个,0 表示不限
|
|
11884
|
-
limit: { type: Number, default: 0 },
|
|
11885
|
-
// image 只显示图片,file 只显示非图片,空串不过滤
|
|
11886
|
-
accept: { type: String, default: "" },
|
|
11887
|
-
title: { type: String, default: "" },
|
|
11888
|
-
triggerText: { type: String, default: "" },
|
|
11889
|
-
pageSize: { type: Number, default: 12 },
|
|
11890
|
-
disabled: { type: Boolean, default: false }
|
|
11891
|
-
},
|
|
11892
|
-
emits: ["update:modelValue", "change"],
|
|
11893
|
-
setup(props, { emit }) {
|
|
11894
|
-
const open = (0, import_vue41.ref)(false);
|
|
11895
|
-
const keyword = (0, import_vue41.ref)("");
|
|
11896
|
-
const page = (0, import_vue41.ref)(1);
|
|
11897
|
-
const temp = (0, import_vue41.ref)([]);
|
|
11898
|
-
const selectedIds = (0, import_vue41.computed)(() => {
|
|
11899
|
-
if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
|
|
11900
|
-
if (props.modelValue === "" || props.modelValue === null || props.modelValue === void 0) return [];
|
|
11901
|
-
return [props.modelValue];
|
|
11902
|
-
});
|
|
11903
|
-
function isImage(item) {
|
|
11904
|
-
if (item.type) return item.type === "image";
|
|
11905
|
-
return /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(item.url || "");
|
|
11906
|
-
}
|
|
11907
|
-
const filtered = (0, import_vue41.computed)(() => {
|
|
11908
|
-
const key = keyword.value.trim().toLowerCase();
|
|
11909
|
-
return props.items.filter((item) => {
|
|
11910
|
-
if (props.accept === "image" && !isImage(item)) return false;
|
|
11911
|
-
if (props.accept === "file" && isImage(item)) return false;
|
|
11912
|
-
if (key && !String(item.name || "").toLowerCase().includes(key)) return false;
|
|
11913
|
-
return true;
|
|
11914
|
-
});
|
|
11915
|
-
});
|
|
11916
|
-
const paged = (0, import_vue41.computed)(() => {
|
|
11917
|
-
const start = (page.value - 1) * props.pageSize;
|
|
11918
|
-
return filtered.value.slice(start, start + props.pageSize);
|
|
11919
|
-
});
|
|
11920
|
-
(0, import_vue41.watch)(keyword, () => {
|
|
11921
|
-
page.value = 1;
|
|
11922
|
-
});
|
|
11923
|
-
(0, import_vue41.watch)(
|
|
11924
|
-
() => filtered.value.length,
|
|
11925
|
-
() => {
|
|
11926
|
-
const max = Math.max(1, Math.ceil(filtered.value.length / props.pageSize));
|
|
11927
|
-
if (page.value > max) page.value = max;
|
|
11928
|
-
}
|
|
11929
|
-
);
|
|
11930
|
-
function isChecked(id) {
|
|
11931
|
-
return temp.value.includes(id);
|
|
11932
|
-
}
|
|
11933
|
-
function atLimit() {
|
|
11934
|
-
return props.limit > 0 && temp.value.length >= props.limit;
|
|
11935
|
-
}
|
|
11936
|
-
function toggle(item) {
|
|
11937
|
-
if (props.multiple) {
|
|
11938
|
-
if (isChecked(item.id)) {
|
|
11939
|
-
temp.value = temp.value.filter((id) => id !== item.id);
|
|
11940
|
-
return;
|
|
11941
|
-
}
|
|
11942
|
-
if (atLimit()) return;
|
|
11943
|
-
temp.value = [...temp.value, item.id];
|
|
11944
|
-
return;
|
|
11945
|
-
}
|
|
11946
|
-
temp.value = [item.id];
|
|
11947
|
-
}
|
|
11948
|
-
function openPanel() {
|
|
11949
|
-
if (props.disabled) return;
|
|
11950
|
-
temp.value = [...selectedIds.value];
|
|
11951
|
-
keyword.value = "";
|
|
11952
|
-
page.value = 1;
|
|
11953
|
-
open.value = true;
|
|
11954
|
-
}
|
|
11955
|
-
function commit() {
|
|
11956
|
-
const next = props.multiple ? [...temp.value] : temp.value[0] !== void 0 ? temp.value[0] : "";
|
|
11957
|
-
emit("update:modelValue", next);
|
|
11958
|
-
emit("change", next);
|
|
11959
|
-
open.value = false;
|
|
11960
|
-
}
|
|
11961
|
-
function cancel() {
|
|
11962
|
-
temp.value = [...selectedIds.value];
|
|
11963
|
-
open.value = false;
|
|
11964
|
-
}
|
|
11965
|
-
return {
|
|
11966
|
-
open,
|
|
11967
|
-
keyword,
|
|
11968
|
-
page,
|
|
11969
|
-
temp,
|
|
11970
|
-
selectedIds,
|
|
11971
|
-
filtered,
|
|
11972
|
-
paged,
|
|
11973
|
-
isImage,
|
|
11974
|
-
isChecked,
|
|
11975
|
-
atLimit,
|
|
11976
|
-
toggle,
|
|
11977
|
-
openPanel,
|
|
11978
|
-
commit,
|
|
11979
|
-
cancel,
|
|
11980
|
-
t
|
|
11981
|
-
};
|
|
11982
|
-
},
|
|
11983
|
-
template: `
|
|
11984
|
-
<span class="djm-media-picker">
|
|
11985
|
-
<slot name="trigger" :open="openPanel" :count="selectedIds.length">
|
|
11986
|
-
<button
|
|
11987
|
-
class="djm-btn"
|
|
11988
|
-
type="button"
|
|
11989
|
-
:disabled="disabled"
|
|
11990
|
-
@click="openPanel"
|
|
11991
|
-
>{{ triggerText || t('mediaPicker.select') }}<span v-if="selectedIds.length" class="djm-media-picker-count">{{ selectedIds.length }}</span></button>
|
|
11992
|
-
</slot>
|
|
11993
|
-
|
|
11994
|
-
<DjmModal
|
|
11995
|
-
:model-value="open"
|
|
11996
|
-
:title="title || t('mediaPicker.title')"
|
|
11997
|
-
width="760px"
|
|
11998
|
-
@update:model-value="cancel"
|
|
11999
|
-
>
|
|
12000
|
-
<div class="djm-media-toolbar">
|
|
12001
|
-
<label class="djm-media-search">
|
|
12002
|
-
<DjmIcon name="search" :size="13" />
|
|
12003
|
-
<input
|
|
12004
|
-
v-model="keyword"
|
|
12005
|
-
class="djm-media-search-input"
|
|
12006
|
-
type="text"
|
|
12007
|
-
:placeholder="t('mediaPicker.search')"
|
|
12008
|
-
:aria-label="t('mediaPicker.search')"
|
|
12009
|
-
/>
|
|
12010
|
-
</label>
|
|
12011
|
-
<span class="djm-media-toolbar-slot">
|
|
12012
|
-
<slot name="toolbar"></slot>
|
|
12013
|
-
</span>
|
|
12014
|
-
</div>
|
|
12015
|
-
|
|
12016
|
-
<div class="djm-media-grid" role="listbox" :aria-multiselectable="multiple ? 'true' : 'false'">
|
|
12017
|
-
<div
|
|
12018
|
-
v-for="item in paged"
|
|
12019
|
-
:key="item.id"
|
|
12020
|
-
class="djm-media-item"
|
|
12021
|
-
:class="{
|
|
12022
|
-
'djm-is-checked': isChecked(item.id),
|
|
12023
|
-
'djm-is-disabled': multiple && atLimit() && !isChecked(item.id)
|
|
12024
|
-
}"
|
|
12025
|
-
role="option"
|
|
12026
|
-
tabindex="0"
|
|
12027
|
-
:aria-selected="String(isChecked(item.id))"
|
|
12028
|
-
@click="toggle(item)"
|
|
12029
|
-
@keydown.enter.prevent="toggle(item)"
|
|
12030
|
-
@keydown.space.prevent="toggle(item)"
|
|
12031
|
-
>
|
|
12032
|
-
<div class="djm-media-thumb">
|
|
12033
|
-
<img v-if="isImage(item)" :src="item.url" :alt="item.name" loading="lazy" />
|
|
12034
|
-
<DjmIcon v-else name="folder" :size="26" />
|
|
12035
|
-
<span v-if="isChecked(item.id)" class="djm-media-check">
|
|
12036
|
-
<DjmIcon name="check" :size="11" />
|
|
12037
|
-
</span>
|
|
12038
|
-
</div>
|
|
12039
|
-
<div class="djm-media-name" :title="item.name">{{ item.name }}</div>
|
|
12040
|
-
</div>
|
|
12041
|
-
<div v-if="!paged.length" class="djm-media-empty">{{ t('mediaPicker.empty') }}</div>
|
|
12042
|
-
</div>
|
|
12043
|
-
|
|
12044
|
-
<div class="djm-media-foot">
|
|
12045
|
-
<span class="djm-media-total">
|
|
12046
|
-
{{ t('mediaPicker.total', { total: filtered.length }) }}
|
|
12047
|
-
<em v-if="multiple && limit > 0">{{ t('mediaPicker.limit', { limit }) }}</em>
|
|
12048
|
-
</span>
|
|
12049
|
-
<DjmPagination
|
|
12050
|
-
v-if="filtered.length > pageSize"
|
|
12051
|
-
v-model:page="page"
|
|
12052
|
-
:page-size="pageSize"
|
|
12053
|
-
:total="filtered.length"
|
|
12054
|
-
/>
|
|
12055
|
-
</div>
|
|
12056
|
-
|
|
12057
|
-
<template #footer>
|
|
12058
|
-
<button class="djm-btn" type="button" @click="cancel">{{ t('common.cancel') }}</button>
|
|
12059
|
-
<button class="djm-btn djm-btn-primary" type="button" @click="commit">{{ t('common.confirm') }}</button>
|
|
12060
|
-
</template>
|
|
12061
|
-
</DjmModal>
|
|
12062
|
-
</span>
|
|
12063
|
-
`
|
|
12064
|
-
};
|
|
12065
|
-
|
|
12066
12751
|
// src/components/kanban/index.js
|
|
12067
12752
|
var import_vue42 = require("vue");
|
|
12068
12753
|
function cloneColumns(list) {
|
|
@@ -15403,7 +16088,7 @@ var components = [
|
|
|
15403
16088
|
org_chart_default,
|
|
15404
16089
|
page_tabs_default
|
|
15405
16090
|
];
|
|
15406
|
-
var version = "0.
|
|
16091
|
+
var version = "0.72.0";
|
|
15407
16092
|
function install(app, options = {}) {
|
|
15408
16093
|
if (options.locale) setLocale(options.locale);
|
|
15409
16094
|
components.forEach((component) => app.component(component.name, component));
|