djm-ui 0.67.4 → 0.73.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 +116 -0
- package/README.md +38 -2
- package/dist/djm-ui.cjs +1625 -803
- package/dist/djm-ui.cjs.map +4 -4
- package/dist/djm-ui.css +1043 -41
- package/dist/djm-ui.esm.js +1581 -761
- package/dist/djm-ui.esm.js.map +4 -4
- package/dist/djm-ui.esm.min.js +439 -247
- package/dist/djm-ui.min.cjs +439 -247
- package/dist/djm-ui.min.css +2 -2
- package/dist/djm-ui.umd.js +1104 -285
- package/dist/djm-ui.umd.min.js +439 -247
- package/dist/mcp/manifest.json +12300 -0
- package/dist/mcp/server.js +90 -0
- package/package.json +12 -4
- package/src/components/drawer/index.js +4 -2
- package/src/components/icon/index.js +13 -1
- package/src/components/modal/index.js +4 -2
- package/src/components/progress/index.js +72 -3
- package/src/components/rich-editor/index.js +724 -61
- package/src/components/spin/index.js +69 -22
- package/src/index.js +2 -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/feedback.css +46 -0
- package/src/styles/components/form.css +5 -0
- package/src/styles/components/glass.css +58 -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/spin.css +156 -4
- package/src/styles/components/template.css +5 -0
- package/src/styles/index.css +2 -0
- package/src/styles/theme-dark.css +5 -0
- package/src/styles/tokens.css +13 -0
- package/src/theme/index.js +62 -0
- package/src/utils/sanitize-html.js +44 -6
- package/types/index.d.ts +17 -0
package/dist/djm-ui.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! DJM UI v0.
|
|
1
|
+
/*! DJM UI v0.73.0 · MIT · 2026-10-26 */
|
|
2
2
|
"use strict";
|
|
3
3
|
var DjmUI = (() => {
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1040,6 +1040,7 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
1040
1040
|
djmMessage: () => djmMessage,
|
|
1041
1041
|
djmMessages: () => djmMessages,
|
|
1042
1042
|
getLocale: () => getLocale,
|
|
1043
|
+
glass: () => glass,
|
|
1043
1044
|
hasPermission: () => hasPermission,
|
|
1044
1045
|
initTheme: () => initTheme,
|
|
1045
1046
|
install: () => install,
|
|
@@ -1049,6 +1050,7 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
1049
1050
|
primaryColor: () => primaryColor,
|
|
1050
1051
|
resetPrimaryColor: () => resetPrimaryColor,
|
|
1051
1052
|
sanitizeHtml: () => sanitizeHtml,
|
|
1053
|
+
setGlass: () => setGlass,
|
|
1052
1054
|
setLocale: () => setLocale,
|
|
1053
1055
|
setPermissions: () => setPermissions,
|
|
1054
1056
|
setPrimaryColor: () => setPrimaryColor,
|
|
@@ -1119,7 +1121,19 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
1119
1121
|
activity: "M22 12h-4l-3 9L9 3l-3 9H2",
|
|
1120
1122
|
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
1123
|
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"
|
|
1124
|
+
moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z",
|
|
1125
|
+
undo: "M3 10h11a5 5 0 1 1 0 10H8M3 10l4-4M3 10l4 4",
|
|
1126
|
+
redo: "M21 10H10a5 5 0 1 0 0 10h6M21 10l-4-4M21 10l-4 4",
|
|
1127
|
+
"align-left": "M4 6h16M4 12h10M4 18h13",
|
|
1128
|
+
"align-center": "M4 6h16M7 12h10M6 18h12",
|
|
1129
|
+
"align-right": "M4 6h16M10 12h10M7 18h13",
|
|
1130
|
+
"align-justify": "M4 6h16M4 12h16M4 18h16",
|
|
1131
|
+
list: "M9 6h11M9 12h11M9 18h11M4.5 6h.01M4.5 12h.01M4.5 18h.01",
|
|
1132
|
+
"list-ordered": "M10 6h10M10 12h10M10 18h10M4 6h1v4M4 12h2M4 16h2v2H4",
|
|
1133
|
+
outdent: "M4 6h16M10 12h10M10 18h10M8 9l-3 3 3 3",
|
|
1134
|
+
indent: "M4 6h16M10 12h10M10 18h10M5 9l3 3-3 3",
|
|
1135
|
+
unlink: "M10 13a5 5 0 0 0 7.5.5M14 11a5 5 0 0 0-7.5-.5M2 2l20 20",
|
|
1136
|
+
images: "M8 3h12v12M4 7h12v12H4zM7 15l3-3 3 3"
|
|
1123
1137
|
};
|
|
1124
1138
|
var icon_default = {
|
|
1125
1139
|
name: "DjmIcon",
|
|
@@ -1458,7 +1472,6 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
1458
1472
|
"richEditor.italic": "\u659C\u4F53",
|
|
1459
1473
|
"richEditor.underline": "\u4E0B\u5212\u7EBF",
|
|
1460
1474
|
"richEditor.strike": "\u5220\u9664\u7EBF",
|
|
1461
|
-
"richEditor.heading": "\u4E8C\u7EA7\u6807\u9898",
|
|
1462
1475
|
"richEditor.paragraph": "\u6B63\u6587",
|
|
1463
1476
|
"richEditor.bulletList": "\u65E0\u5E8F\u5217\u8868",
|
|
1464
1477
|
"richEditor.numberList": "\u6709\u5E8F\u5217\u8868",
|
|
@@ -1469,6 +1482,39 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
1469
1482
|
"richEditor.redo": "\u91CD\u505A",
|
|
1470
1483
|
"richEditor.source": "\u6E90\u7801",
|
|
1471
1484
|
"richEditor.visual": "\u53EF\u89C6\u5316",
|
|
1485
|
+
"richEditor.toolbar": "\u7F16\u8F91\u5DE5\u5177\u680F",
|
|
1486
|
+
"richEditor.block": "\u6BB5\u843D\u683C\u5F0F",
|
|
1487
|
+
"richEditor.heading1": "\u6807\u9898 1",
|
|
1488
|
+
"richEditor.heading2": "\u6807\u9898 2",
|
|
1489
|
+
"richEditor.heading3": "\u6807\u9898 3",
|
|
1490
|
+
"richEditor.quote": "\u5F15\u7528",
|
|
1491
|
+
"richEditor.codeBlock": "\u4EE3\u7801\u5757",
|
|
1492
|
+
"richEditor.fontSize": "\u5B57\u53F7",
|
|
1493
|
+
"richEditor.defaultSize": "\u9ED8\u8BA4",
|
|
1494
|
+
"richEditor.inlineCode": "\u884C\u5185\u4EE3\u7801",
|
|
1495
|
+
"richEditor.textColor": "\u6587\u5B57\u989C\u8272",
|
|
1496
|
+
"richEditor.bgColor": "\u80CC\u666F\u8272",
|
|
1497
|
+
"richEditor.alignLeft": "\u5DE6\u5BF9\u9F50",
|
|
1498
|
+
"richEditor.alignCenter": "\u5C45\u4E2D",
|
|
1499
|
+
"richEditor.alignRight": "\u53F3\u5BF9\u9F50",
|
|
1500
|
+
"richEditor.alignJustify": "\u4E24\u7AEF\u5BF9\u9F50",
|
|
1501
|
+
"richEditor.outdent": "\u51CF\u5C11\u7F29\u8FDB",
|
|
1502
|
+
"richEditor.indent": "\u589E\u52A0\u7F29\u8FDB",
|
|
1503
|
+
"richEditor.unlink": "\u53D6\u6D88\u94FE\u63A5",
|
|
1504
|
+
"richEditor.image": "\u63D2\u5165\u56FE\u7247",
|
|
1505
|
+
"richEditor.singleImage": "\u63D2\u5165\u5355\u56FE",
|
|
1506
|
+
"richEditor.multiImage": "\u63D2\u5165\u591A\u56FE",
|
|
1507
|
+
"richEditor.mediaLibrary": "\u4ECE\u7D20\u6750\u5E93\u9009\u62E9",
|
|
1508
|
+
"richEditor.hr": "\u5206\u5272\u7EBF",
|
|
1509
|
+
"richEditor.fullscreen": "\u5168\u5C4F",
|
|
1510
|
+
"richEditor.exitFullscreen": "\u9000\u51FA\u5168\u5C4F",
|
|
1511
|
+
"richEditor.wordCount": "\u5171 {count} \u5B57",
|
|
1512
|
+
"richEditor.uploading": "\u6B63\u5728\u4E0A\u4F20 {count} \u5F20\u56FE\u7247\u2026",
|
|
1513
|
+
"richEditor.uploadFailed": "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25",
|
|
1514
|
+
"richEditor.imageTypeError": "\u53EA\u80FD\u4E0A\u4F20\u56FE\u7247\u6587\u4EF6",
|
|
1515
|
+
"richEditor.imageSizeError": "\u56FE\u7247\u4E0D\u80FD\u8D85\u8FC7 {size}MB",
|
|
1516
|
+
"richEditor.imageUrlInvalid": "\u56FE\u7247\u5730\u5740\u4E0D\u5408\u6CD5\uFF0C\u5DF2\u62D2\u7EDD\u63D2\u5165",
|
|
1517
|
+
"richEditor.linkInvalid": "\u94FE\u63A5\u53EA\u652F\u6301 http(s)\u3001mailto\u3001tel \u6216\u7AD9\u5185\u76F8\u5BF9\u5730\u5740",
|
|
1472
1518
|
"approval.start": "\u53D1\u8D77",
|
|
1473
1519
|
"approval.cc": "\u6284\u9001",
|
|
1474
1520
|
"approval.approved": "\u5DF2\u901A\u8FC7",
|
|
@@ -1677,7 +1723,6 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
1677
1723
|
"richEditor.italic": "Italic",
|
|
1678
1724
|
"richEditor.underline": "Underline",
|
|
1679
1725
|
"richEditor.strike": "Strikethrough",
|
|
1680
|
-
"richEditor.heading": "Heading 2",
|
|
1681
1726
|
"richEditor.paragraph": "Paragraph",
|
|
1682
1727
|
"richEditor.bulletList": "Bullet list",
|
|
1683
1728
|
"richEditor.numberList": "Numbered list",
|
|
@@ -1688,6 +1733,39 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
1688
1733
|
"richEditor.redo": "Redo",
|
|
1689
1734
|
"richEditor.source": "Source",
|
|
1690
1735
|
"richEditor.visual": "Visual",
|
|
1736
|
+
"richEditor.toolbar": "Formatting toolbar",
|
|
1737
|
+
"richEditor.block": "Paragraph style",
|
|
1738
|
+
"richEditor.heading1": "Heading 1",
|
|
1739
|
+
"richEditor.heading2": "Heading 2",
|
|
1740
|
+
"richEditor.heading3": "Heading 3",
|
|
1741
|
+
"richEditor.quote": "Quote",
|
|
1742
|
+
"richEditor.codeBlock": "Code block",
|
|
1743
|
+
"richEditor.fontSize": "Font size",
|
|
1744
|
+
"richEditor.defaultSize": "Default",
|
|
1745
|
+
"richEditor.inlineCode": "Inline code",
|
|
1746
|
+
"richEditor.textColor": "Text color",
|
|
1747
|
+
"richEditor.bgColor": "Background color",
|
|
1748
|
+
"richEditor.alignLeft": "Align left",
|
|
1749
|
+
"richEditor.alignCenter": "Align center",
|
|
1750
|
+
"richEditor.alignRight": "Align right",
|
|
1751
|
+
"richEditor.alignJustify": "Justify",
|
|
1752
|
+
"richEditor.outdent": "Decrease indent",
|
|
1753
|
+
"richEditor.indent": "Increase indent",
|
|
1754
|
+
"richEditor.unlink": "Remove link",
|
|
1755
|
+
"richEditor.image": "Insert image",
|
|
1756
|
+
"richEditor.singleImage": "Insert single image",
|
|
1757
|
+
"richEditor.multiImage": "Insert multiple images",
|
|
1758
|
+
"richEditor.mediaLibrary": "Choose from media library",
|
|
1759
|
+
"richEditor.hr": "Divider",
|
|
1760
|
+
"richEditor.fullscreen": "Fullscreen",
|
|
1761
|
+
"richEditor.exitFullscreen": "Exit fullscreen",
|
|
1762
|
+
"richEditor.wordCount": "{count} characters",
|
|
1763
|
+
"richEditor.uploading": "Uploading {count} image(s)\u2026",
|
|
1764
|
+
"richEditor.uploadFailed": "Image upload failed",
|
|
1765
|
+
"richEditor.imageTypeError": "Only image files are allowed",
|
|
1766
|
+
"richEditor.imageSizeError": "Image must be smaller than {size}MB",
|
|
1767
|
+
"richEditor.imageUrlInvalid": "Invalid image URL, insertion rejected",
|
|
1768
|
+
"richEditor.linkInvalid": "Only http(s), mailto, tel or relative URLs are allowed",
|
|
1691
1769
|
"approval.start": "Started",
|
|
1692
1770
|
"approval.cc": "CC",
|
|
1693
1771
|
"approval.approved": "Approved",
|
|
@@ -4177,12 +4255,18 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
4177
4255
|
name: "DjmProgress",
|
|
4178
4256
|
props: {
|
|
4179
4257
|
percent: { type: Number, default: 0 },
|
|
4258
|
+
// line 横向 / circle 环形
|
|
4259
|
+
type: { type: String, default: "line" },
|
|
4180
4260
|
// '' / success / error / warning
|
|
4181
4261
|
status: { type: String, default: "" },
|
|
4182
4262
|
showText: { type: Boolean, default: true },
|
|
4183
4263
|
strokeWidth: { type: Number, default: 6 },
|
|
4184
4264
|
// 自定义进度色,不传由状态决定
|
|
4185
|
-
color: { type: String, default: "" }
|
|
4265
|
+
color: { type: String, default: "" },
|
|
4266
|
+
// 环形直径(px)
|
|
4267
|
+
size: { type: Number, default: 96 },
|
|
4268
|
+
// 无障碍名称,默认「进度」
|
|
4269
|
+
label: { type: String, default: "" }
|
|
4186
4270
|
},
|
|
4187
4271
|
computed: {
|
|
4188
4272
|
safePercent() {
|
|
@@ -4195,10 +4279,72 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
4195
4279
|
},
|
|
4196
4280
|
trackStyle() {
|
|
4197
4281
|
return { height: this.strokeWidth + "px" };
|
|
4282
|
+
},
|
|
4283
|
+
circleSize() {
|
|
4284
|
+
return Math.max(48, Number(this.size) || 96);
|
|
4285
|
+
},
|
|
4286
|
+
circleRadius() {
|
|
4287
|
+
return (this.circleSize - this.strokeWidth) / 2;
|
|
4288
|
+
},
|
|
4289
|
+
circleLength() {
|
|
4290
|
+
return 2 * Math.PI * this.circleRadius;
|
|
4291
|
+
},
|
|
4292
|
+
circleOffset() {
|
|
4293
|
+
return this.circleLength * (1 - this.safePercent / 100);
|
|
4294
|
+
},
|
|
4295
|
+
circleStyle() {
|
|
4296
|
+
return { width: this.circleSize + "px", height: this.circleSize + "px" };
|
|
4297
|
+
},
|
|
4298
|
+
circleLabelStyle() {
|
|
4299
|
+
return { fontSize: Math.max(12, Math.round(this.circleSize / 5)) + "px" };
|
|
4198
4300
|
}
|
|
4199
4301
|
},
|
|
4200
4302
|
template: `
|
|
4201
|
-
<div
|
|
4303
|
+
<div
|
|
4304
|
+
v-if="type === 'circle'"
|
|
4305
|
+
class="djm-progress-circle"
|
|
4306
|
+
:class="status ? 'djm-is-' + status : ''"
|
|
4307
|
+
:style="circleStyle"
|
|
4308
|
+
role="progressbar"
|
|
4309
|
+
:aria-label="label || '\u8FDB\u5EA6'"
|
|
4310
|
+
:aria-valuenow="safePercent"
|
|
4311
|
+
aria-valuemin="0"
|
|
4312
|
+
aria-valuemax="100"
|
|
4313
|
+
>
|
|
4314
|
+
<svg :viewBox="'0 0 ' + circleSize + ' ' + circleSize" aria-hidden="true">
|
|
4315
|
+
<circle
|
|
4316
|
+
class="djm-progress-circle-track"
|
|
4317
|
+
:cx="circleSize / 2"
|
|
4318
|
+
:cy="circleSize / 2"
|
|
4319
|
+
:r="circleRadius"
|
|
4320
|
+
:stroke-width="strokeWidth"
|
|
4321
|
+
fill="none"
|
|
4322
|
+
/>
|
|
4323
|
+
<circle
|
|
4324
|
+
class="djm-progress-circle-bar"
|
|
4325
|
+
:cx="circleSize / 2"
|
|
4326
|
+
:cy="circleSize / 2"
|
|
4327
|
+
:r="circleRadius"
|
|
4328
|
+
:stroke-width="strokeWidth"
|
|
4329
|
+
fill="none"
|
|
4330
|
+
stroke-linecap="round"
|
|
4331
|
+
:stroke-dasharray="circleLength"
|
|
4332
|
+
:stroke-dashoffset="circleOffset"
|
|
4333
|
+
:style="color ? { stroke: color } : null"
|
|
4334
|
+
/>
|
|
4335
|
+
</svg>
|
|
4336
|
+
<span v-if="showText" class="djm-progress-circle-label" :style="circleLabelStyle">{{ safePercent }}%</span>
|
|
4337
|
+
</div>
|
|
4338
|
+
<div
|
|
4339
|
+
v-else
|
|
4340
|
+
class="djm-progress-line"
|
|
4341
|
+
:class="status ? 'djm-is-' + status : ''"
|
|
4342
|
+
role="progressbar"
|
|
4343
|
+
:aria-label="label || '\u8FDB\u5EA6'"
|
|
4344
|
+
:aria-valuenow="safePercent"
|
|
4345
|
+
aria-valuemin="0"
|
|
4346
|
+
aria-valuemax="100"
|
|
4347
|
+
>
|
|
4202
4348
|
<div class="djm-progress" :style="trackStyle">
|
|
4203
4349
|
<div class="djm-progress-bar" :style="barStyle"></div>
|
|
4204
4350
|
</div>
|
|
@@ -5404,39 +5550,81 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
5404
5550
|
name: "DjmSpin",
|
|
5405
5551
|
props: {
|
|
5406
5552
|
spinning: { type: Boolean, default: true },
|
|
5407
|
-
size: { type: String, default: "md" },
|
|
5408
5553
|
// sm | md | lg
|
|
5409
|
-
|
|
5554
|
+
size: { type: String, default: "md" },
|
|
5555
|
+
// ring 环形 / dots 点状 / bars 柱状 / pulse 脉冲
|
|
5556
|
+
type: { type: String, default: "ring" },
|
|
5557
|
+
tip: { type: String, default: "" },
|
|
5558
|
+
// 延迟多少毫秒后显示,0 表示立即
|
|
5559
|
+
delay: { type: Number, default: 0 },
|
|
5560
|
+
// 全屏加载:遮罩铺满视口
|
|
5561
|
+
fullscreen: { type: Boolean, default: false },
|
|
5562
|
+
// 自定义指示器颜色,默认跟随品牌色
|
|
5563
|
+
color: { type: String, default: "" }
|
|
5564
|
+
},
|
|
5565
|
+
setup(props) {
|
|
5566
|
+
const visible = ref(false);
|
|
5567
|
+
let timer = null;
|
|
5568
|
+
function sync() {
|
|
5569
|
+
window.clearTimeout(timer);
|
|
5570
|
+
timer = null;
|
|
5571
|
+
if (!props.spinning) {
|
|
5572
|
+
visible.value = false;
|
|
5573
|
+
return;
|
|
5574
|
+
}
|
|
5575
|
+
if (props.delay > 0) {
|
|
5576
|
+
timer = window.setTimeout(() => {
|
|
5577
|
+
visible.value = true;
|
|
5578
|
+
}, props.delay);
|
|
5579
|
+
} else {
|
|
5580
|
+
visible.value = true;
|
|
5581
|
+
}
|
|
5582
|
+
}
|
|
5583
|
+
watch(() => [props.spinning, props.delay], sync, { immediate: true });
|
|
5584
|
+
onBeforeUnmount(() => window.clearTimeout(timer));
|
|
5585
|
+
return { visible };
|
|
5410
5586
|
},
|
|
5411
5587
|
computed: {
|
|
5412
|
-
// 有默认插槽即为包裹模式
|
|
5413
5588
|
wrapped() {
|
|
5414
5589
|
return !!this.$slots.default;
|
|
5590
|
+
},
|
|
5591
|
+
rootClass() {
|
|
5592
|
+
return [
|
|
5593
|
+
"djm-spin",
|
|
5594
|
+
"djm-spin-" + this.size,
|
|
5595
|
+
{
|
|
5596
|
+
"djm-spin-wrap": this.wrapped && !this.fullscreen,
|
|
5597
|
+
"djm-spin-inline": !this.wrapped && !this.fullscreen,
|
|
5598
|
+
"djm-spin-fullscreen": this.fullscreen
|
|
5599
|
+
}
|
|
5600
|
+
];
|
|
5415
5601
|
}
|
|
5416
5602
|
},
|
|
5417
5603
|
template: `
|
|
5418
5604
|
<div
|
|
5419
|
-
v-if="wrapped"
|
|
5420
|
-
class="
|
|
5421
|
-
:
|
|
5422
|
-
:
|
|
5605
|
+
v-if="fullscreen || wrapped || visible"
|
|
5606
|
+
:class="rootClass"
|
|
5607
|
+
:aria-busy="String(visible)"
|
|
5608
|
+
:role="visible ? 'status' : undefined"
|
|
5609
|
+
:aria-label="!wrapped && !fullscreen && !tip && visible ? '\u52A0\u8F7D\u4E2D' : undefined"
|
|
5423
5610
|
>
|
|
5424
|
-
<slot />
|
|
5425
|
-
<div v-if="
|
|
5426
|
-
<span class="djm-
|
|
5611
|
+
<slot v-if="wrapped && !fullscreen" />
|
|
5612
|
+
<div v-if="visible" class="djm-spin-mask">
|
|
5613
|
+
<span class="djm-spin-indicator" :class="'djm-spin-' + type" :style="color ? { color } : null">
|
|
5614
|
+
<slot name="indicator">
|
|
5615
|
+
<i v-if="type === 'ring'" class="djm-spinner"></i>
|
|
5616
|
+
<template v-else-if="type === 'dots'">
|
|
5617
|
+
<i></i><i></i><i></i>
|
|
5618
|
+
</template>
|
|
5619
|
+
<template v-else-if="type === 'bars'">
|
|
5620
|
+
<i></i><i></i><i></i><i></i>
|
|
5621
|
+
</template>
|
|
5622
|
+
<i v-else class="djm-spin-core"></i>
|
|
5623
|
+
</slot>
|
|
5624
|
+
</span>
|
|
5427
5625
|
<span v-if="tip" class="djm-spin-tip">{{ tip }}</span>
|
|
5428
5626
|
</div>
|
|
5429
5627
|
</div>
|
|
5430
|
-
<span
|
|
5431
|
-
v-else
|
|
5432
|
-
class="djm-spin-inline"
|
|
5433
|
-
:class="'djm-spin-' + size"
|
|
5434
|
-
role="status"
|
|
5435
|
-
:aria-label="tip ? undefined : '\u52A0\u8F7D\u4E2D'"
|
|
5436
|
-
>
|
|
5437
|
-
<span class="djm-spinner"></span>
|
|
5438
|
-
<span v-if="tip" class="djm-spin-tip">{{ tip }}</span>
|
|
5439
|
-
</span>
|
|
5440
5628
|
`
|
|
5441
5629
|
};
|
|
5442
5630
|
|
|
@@ -6042,7 +6230,9 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
6042
6230
|
// src/theme/index.js
|
|
6043
6231
|
var KEY = "djm-theme";
|
|
6044
6232
|
var COLOR_KEY = "djm-primary";
|
|
6233
|
+
var GLASS_KEY = "djm-glass";
|
|
6045
6234
|
var theme = ref("light");
|
|
6235
|
+
var glass = ref(false);
|
|
6046
6236
|
var PRESET_COLORS = [
|
|
6047
6237
|
{ name: "\u9ED8\u8BA4\u84DD", value: "#1a6fc4" },
|
|
6048
6238
|
{ name: "\u79D1\u6280\u84DD", value: "#2563eb" },
|
|
@@ -6175,6 +6365,15 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
6175
6365
|
function toggleTheme() {
|
|
6176
6366
|
setTheme(theme.value === "dark" ? "light" : "dark");
|
|
6177
6367
|
}
|
|
6368
|
+
function setGlass(enabled) {
|
|
6369
|
+
glass.value = !!enabled;
|
|
6370
|
+
document.documentElement.dataset.djmGlass = glass.value ? "on" : "";
|
|
6371
|
+
try {
|
|
6372
|
+
if (glass.value) localStorage.setItem(GLASS_KEY, "1");
|
|
6373
|
+
else localStorage.removeItem(GLASS_KEY);
|
|
6374
|
+
} catch (error) {
|
|
6375
|
+
}
|
|
6376
|
+
}
|
|
6178
6377
|
function initTheme() {
|
|
6179
6378
|
let saved = "";
|
|
6180
6379
|
try {
|
|
@@ -6194,9 +6393,16 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
6194
6393
|
} catch (error) {
|
|
6195
6394
|
color = "";
|
|
6196
6395
|
}
|
|
6396
|
+
let glassSaved = false;
|
|
6397
|
+
try {
|
|
6398
|
+
glassSaved = localStorage.getItem(GLASS_KEY) === "1";
|
|
6399
|
+
} catch (error) {
|
|
6400
|
+
glassSaved = false;
|
|
6401
|
+
}
|
|
6197
6402
|
if (color) primaryColor.value = color;
|
|
6198
6403
|
const next = param === "dark" || param === "light" ? param : saved || "light";
|
|
6199
6404
|
setTheme(next);
|
|
6405
|
+
setGlass(glassSaved);
|
|
6200
6406
|
}
|
|
6201
6407
|
|
|
6202
6408
|
// src/components/watermark/index.js
|
|
@@ -8091,7 +8297,9 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
8091
8297
|
// 点击遮罩关闭
|
|
8092
8298
|
maskClosable: { type: Boolean, default: true },
|
|
8093
8299
|
showClose: { type: Boolean, default: true },
|
|
8094
|
-
closeOnEsc: { type: Boolean, default: true }
|
|
8300
|
+
closeOnEsc: { type: Boolean, default: true },
|
|
8301
|
+
// 毛玻璃底
|
|
8302
|
+
glass: { type: Boolean, default: false }
|
|
8095
8303
|
},
|
|
8096
8304
|
emits: ["update:modelValue", "open", "close"],
|
|
8097
8305
|
setup(props, { emit }) {
|
|
@@ -8154,7 +8362,7 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
8154
8362
|
<aside
|
|
8155
8363
|
ref="panelRef"
|
|
8156
8364
|
class="djm-drawer"
|
|
8157
|
-
:class="'djm-drawer-' + placement"
|
|
8365
|
+
:class="['djm-drawer-' + placement, { 'djm-is-glass': glass }]"
|
|
8158
8366
|
:style="panelStyle"
|
|
8159
8367
|
role="dialog"
|
|
8160
8368
|
aria-modal="true"
|
|
@@ -8203,7 +8411,9 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
8203
8411
|
// 是否允许 Esc 关闭
|
|
8204
8412
|
closeOnEsc: { type: Boolean, default: true },
|
|
8205
8413
|
// 全屏铺满,适合配置类大表单
|
|
8206
|
-
fullscreen: { type: Boolean, default: false }
|
|
8414
|
+
fullscreen: { type: Boolean, default: false },
|
|
8415
|
+
// 毛玻璃底:需要显示背后页面时使用
|
|
8416
|
+
glass: { type: Boolean, default: false }
|
|
8207
8417
|
},
|
|
8208
8418
|
emits: ["update:modelValue", "open", "close"],
|
|
8209
8419
|
setup(props, { emit }) {
|
|
@@ -8269,7 +8479,7 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
8269
8479
|
<div
|
|
8270
8480
|
ref="dialogRef"
|
|
8271
8481
|
class="djm-modal"
|
|
8272
|
-
:class="{ 'djm-is-fullscreen': fullscreen }"
|
|
8482
|
+
:class="{ 'djm-is-fullscreen': fullscreen, 'djm-is-glass': glass }"
|
|
8273
8483
|
:style="dialogStyle"
|
|
8274
8484
|
role="dialog"
|
|
8275
8485
|
aria-modal="true"
|
|
@@ -9470,6 +9680,197 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9470
9680
|
`
|
|
9471
9681
|
};
|
|
9472
9682
|
|
|
9683
|
+
// src/components/media-picker/index.js
|
|
9684
|
+
var media_picker_default = {
|
|
9685
|
+
name: "DjmMediaPicker",
|
|
9686
|
+
components: { DjmModal: modal_default, DjmPagination: pagination_default, DjmIcon: icon_default },
|
|
9687
|
+
props: {
|
|
9688
|
+
modelValue: { type: [Array, String, Number], default: "" },
|
|
9689
|
+
items: { type: Array, default: () => [] },
|
|
9690
|
+
multiple: { type: Boolean, default: false },
|
|
9691
|
+
// 最多选几个,0 表示不限
|
|
9692
|
+
limit: { type: Number, default: 0 },
|
|
9693
|
+
// image 只显示图片,file 只显示非图片,空串不过滤
|
|
9694
|
+
accept: { type: String, default: "" },
|
|
9695
|
+
title: { type: String, default: "" },
|
|
9696
|
+
triggerText: { type: String, default: "" },
|
|
9697
|
+
pageSize: { type: Number, default: 12 },
|
|
9698
|
+
disabled: { type: Boolean, default: false }
|
|
9699
|
+
},
|
|
9700
|
+
emits: ["update:modelValue", "change"],
|
|
9701
|
+
setup(props, { emit }) {
|
|
9702
|
+
const open = ref(false);
|
|
9703
|
+
const keyword = ref("");
|
|
9704
|
+
const page = ref(1);
|
|
9705
|
+
const temp = ref([]);
|
|
9706
|
+
const selectedIds = computed(() => {
|
|
9707
|
+
if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
|
|
9708
|
+
if (props.modelValue === "" || props.modelValue === null || props.modelValue === void 0) return [];
|
|
9709
|
+
return [props.modelValue];
|
|
9710
|
+
});
|
|
9711
|
+
function isImage(item) {
|
|
9712
|
+
if (item.type) return item.type === "image";
|
|
9713
|
+
return /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(item.url || "");
|
|
9714
|
+
}
|
|
9715
|
+
const filtered = computed(() => {
|
|
9716
|
+
const key = keyword.value.trim().toLowerCase();
|
|
9717
|
+
return props.items.filter((item) => {
|
|
9718
|
+
if (props.accept === "image" && !isImage(item)) return false;
|
|
9719
|
+
if (props.accept === "file" && isImage(item)) return false;
|
|
9720
|
+
if (key && !String(item.name || "").toLowerCase().includes(key)) return false;
|
|
9721
|
+
return true;
|
|
9722
|
+
});
|
|
9723
|
+
});
|
|
9724
|
+
const paged = computed(() => {
|
|
9725
|
+
const start = (page.value - 1) * props.pageSize;
|
|
9726
|
+
return filtered.value.slice(start, start + props.pageSize);
|
|
9727
|
+
});
|
|
9728
|
+
watch(keyword, () => {
|
|
9729
|
+
page.value = 1;
|
|
9730
|
+
});
|
|
9731
|
+
watch(
|
|
9732
|
+
() => filtered.value.length,
|
|
9733
|
+
() => {
|
|
9734
|
+
const max = Math.max(1, Math.ceil(filtered.value.length / props.pageSize));
|
|
9735
|
+
if (page.value > max) page.value = max;
|
|
9736
|
+
}
|
|
9737
|
+
);
|
|
9738
|
+
function isChecked(id) {
|
|
9739
|
+
return temp.value.includes(id);
|
|
9740
|
+
}
|
|
9741
|
+
function atLimit() {
|
|
9742
|
+
return props.limit > 0 && temp.value.length >= props.limit;
|
|
9743
|
+
}
|
|
9744
|
+
function toggle(item) {
|
|
9745
|
+
if (props.multiple) {
|
|
9746
|
+
if (isChecked(item.id)) {
|
|
9747
|
+
temp.value = temp.value.filter((id) => id !== item.id);
|
|
9748
|
+
return;
|
|
9749
|
+
}
|
|
9750
|
+
if (atLimit()) return;
|
|
9751
|
+
temp.value = [...temp.value, item.id];
|
|
9752
|
+
return;
|
|
9753
|
+
}
|
|
9754
|
+
temp.value = [item.id];
|
|
9755
|
+
}
|
|
9756
|
+
function openPanel() {
|
|
9757
|
+
if (props.disabled) return;
|
|
9758
|
+
temp.value = [...selectedIds.value];
|
|
9759
|
+
keyword.value = "";
|
|
9760
|
+
page.value = 1;
|
|
9761
|
+
open.value = true;
|
|
9762
|
+
}
|
|
9763
|
+
function commit() {
|
|
9764
|
+
const next = props.multiple ? [...temp.value] : temp.value[0] !== void 0 ? temp.value[0] : "";
|
|
9765
|
+
emit("update:modelValue", next);
|
|
9766
|
+
emit("change", next);
|
|
9767
|
+
open.value = false;
|
|
9768
|
+
}
|
|
9769
|
+
function cancel() {
|
|
9770
|
+
temp.value = [...selectedIds.value];
|
|
9771
|
+
open.value = false;
|
|
9772
|
+
}
|
|
9773
|
+
return {
|
|
9774
|
+
open,
|
|
9775
|
+
keyword,
|
|
9776
|
+
page,
|
|
9777
|
+
temp,
|
|
9778
|
+
selectedIds,
|
|
9779
|
+
filtered,
|
|
9780
|
+
paged,
|
|
9781
|
+
isImage,
|
|
9782
|
+
isChecked,
|
|
9783
|
+
atLimit,
|
|
9784
|
+
toggle,
|
|
9785
|
+
openPanel,
|
|
9786
|
+
commit,
|
|
9787
|
+
cancel,
|
|
9788
|
+
t
|
|
9789
|
+
};
|
|
9790
|
+
},
|
|
9791
|
+
template: `
|
|
9792
|
+
<span class="djm-media-picker">
|
|
9793
|
+
<slot name="trigger" :open="openPanel" :count="selectedIds.length">
|
|
9794
|
+
<button
|
|
9795
|
+
class="djm-btn"
|
|
9796
|
+
type="button"
|
|
9797
|
+
:disabled="disabled"
|
|
9798
|
+
@click="openPanel"
|
|
9799
|
+
>{{ triggerText || t('mediaPicker.select') }}<span v-if="selectedIds.length" class="djm-media-picker-count">{{ selectedIds.length }}</span></button>
|
|
9800
|
+
</slot>
|
|
9801
|
+
|
|
9802
|
+
<DjmModal
|
|
9803
|
+
:model-value="open"
|
|
9804
|
+
:title="title || t('mediaPicker.title')"
|
|
9805
|
+
width="760px"
|
|
9806
|
+
@update:model-value="cancel"
|
|
9807
|
+
>
|
|
9808
|
+
<div class="djm-media-toolbar">
|
|
9809
|
+
<label class="djm-media-search">
|
|
9810
|
+
<DjmIcon name="search" :size="13" />
|
|
9811
|
+
<input
|
|
9812
|
+
v-model="keyword"
|
|
9813
|
+
class="djm-media-search-input"
|
|
9814
|
+
type="text"
|
|
9815
|
+
:placeholder="t('mediaPicker.search')"
|
|
9816
|
+
:aria-label="t('mediaPicker.search')"
|
|
9817
|
+
/>
|
|
9818
|
+
</label>
|
|
9819
|
+
<span class="djm-media-toolbar-slot">
|
|
9820
|
+
<slot name="toolbar"></slot>
|
|
9821
|
+
</span>
|
|
9822
|
+
</div>
|
|
9823
|
+
|
|
9824
|
+
<div class="djm-media-grid" role="listbox" :aria-multiselectable="multiple ? 'true' : 'false'">
|
|
9825
|
+
<div
|
|
9826
|
+
v-for="item in paged"
|
|
9827
|
+
:key="item.id"
|
|
9828
|
+
class="djm-media-item"
|
|
9829
|
+
:class="{
|
|
9830
|
+
'djm-is-checked': isChecked(item.id),
|
|
9831
|
+
'djm-is-disabled': multiple && atLimit() && !isChecked(item.id)
|
|
9832
|
+
}"
|
|
9833
|
+
role="option"
|
|
9834
|
+
tabindex="0"
|
|
9835
|
+
:aria-selected="String(isChecked(item.id))"
|
|
9836
|
+
@click="toggle(item)"
|
|
9837
|
+
@keydown.enter.prevent="toggle(item)"
|
|
9838
|
+
@keydown.space.prevent="toggle(item)"
|
|
9839
|
+
>
|
|
9840
|
+
<div class="djm-media-thumb">
|
|
9841
|
+
<img v-if="isImage(item)" :src="item.url" :alt="item.name" loading="lazy" />
|
|
9842
|
+
<DjmIcon v-else name="folder" :size="26" />
|
|
9843
|
+
<span v-if="isChecked(item.id)" class="djm-media-check">
|
|
9844
|
+
<DjmIcon name="check" :size="11" />
|
|
9845
|
+
</span>
|
|
9846
|
+
</div>
|
|
9847
|
+
<div class="djm-media-name" :title="item.name">{{ item.name }}</div>
|
|
9848
|
+
</div>
|
|
9849
|
+
<div v-if="!paged.length" class="djm-media-empty">{{ t('mediaPicker.empty') }}</div>
|
|
9850
|
+
</div>
|
|
9851
|
+
|
|
9852
|
+
<div class="djm-media-foot">
|
|
9853
|
+
<span class="djm-media-total">
|
|
9854
|
+
{{ t('mediaPicker.total', { total: filtered.length }) }}
|
|
9855
|
+
<em v-if="multiple && limit > 0">{{ t('mediaPicker.limit', { limit }) }}</em>
|
|
9856
|
+
</span>
|
|
9857
|
+
<DjmPagination
|
|
9858
|
+
v-if="filtered.length > pageSize"
|
|
9859
|
+
v-model:page="page"
|
|
9860
|
+
:page-size="pageSize"
|
|
9861
|
+
:total="filtered.length"
|
|
9862
|
+
/>
|
|
9863
|
+
</div>
|
|
9864
|
+
|
|
9865
|
+
<template #footer>
|
|
9866
|
+
<button class="djm-btn" type="button" @click="cancel">{{ t('common.cancel') }}</button>
|
|
9867
|
+
<button class="djm-btn djm-btn-primary" type="button" @click="commit">{{ t('common.confirm') }}</button>
|
|
9868
|
+
</template>
|
|
9869
|
+
</DjmModal>
|
|
9870
|
+
</span>
|
|
9871
|
+
`
|
|
9872
|
+
};
|
|
9873
|
+
|
|
9473
9874
|
// src/utils/sanitize-html.js
|
|
9474
9875
|
var TAGS = /* @__PURE__ */ new Set([
|
|
9475
9876
|
"p",
|
|
@@ -9508,7 +9909,18 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9508
9909
|
"th",
|
|
9509
9910
|
"td"
|
|
9510
9911
|
]);
|
|
9511
|
-
var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "title"]);
|
|
9912
|
+
var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "title", "style"]);
|
|
9913
|
+
var STYLE_PROPS = /* @__PURE__ */ new Set([
|
|
9914
|
+
"color",
|
|
9915
|
+
"background-color",
|
|
9916
|
+
"font-size",
|
|
9917
|
+
"font-weight",
|
|
9918
|
+
"font-style",
|
|
9919
|
+
"text-decoration",
|
|
9920
|
+
"text-align",
|
|
9921
|
+
"text-indent",
|
|
9922
|
+
"line-height"
|
|
9923
|
+
]);
|
|
9512
9924
|
var TAG_ATTRS = {
|
|
9513
9925
|
a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
|
|
9514
9926
|
img: /* @__PURE__ */ new Set(["src", "alt", "width", "height"]),
|
|
@@ -9516,11 +9928,29 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9516
9928
|
th: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope"]),
|
|
9517
9929
|
td: /* @__PURE__ */ new Set(["colspan", "rowspan"])
|
|
9518
9930
|
};
|
|
9931
|
+
function cleanStyle(value) {
|
|
9932
|
+
return String(value || "").split(";").map((item) => {
|
|
9933
|
+
const index = item.indexOf(":");
|
|
9934
|
+
if (index === -1) return "";
|
|
9935
|
+
const name = item.slice(0, index).trim().toLowerCase();
|
|
9936
|
+
const val = item.slice(index + 1).trim();
|
|
9937
|
+
if (!STYLE_PROPS.has(name)) return "";
|
|
9938
|
+
if (/url\s*\(|expression\s*\(|javascript:/i.test(val)) return "";
|
|
9939
|
+
return name + ": " + val;
|
|
9940
|
+
}).filter(Boolean).join("; ");
|
|
9941
|
+
}
|
|
9942
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
9519
9943
|
function safeUrl(value, tag) {
|
|
9520
|
-
const url = String(value || "").replace(/[\u0000-\u0020]+/g, "")
|
|
9521
|
-
|
|
9522
|
-
if (
|
|
9523
|
-
|
|
9944
|
+
const url = String(value || "").replace(/[\u0000-\u0020]+/g, "");
|
|
9945
|
+
const scheme = url.match(/^([a-z][a-z0-9+.-]*):/i);
|
|
9946
|
+
if (!scheme) return true;
|
|
9947
|
+
const name = scheme[1].toLowerCase();
|
|
9948
|
+
if (SAFE_SCHEMES.has(name)) return true;
|
|
9949
|
+
if (tag === "img") {
|
|
9950
|
+
if (name === "blob") return true;
|
|
9951
|
+
if (name === "data" && /^data:image\//i.test(url)) return true;
|
|
9952
|
+
}
|
|
9953
|
+
return false;
|
|
9524
9954
|
}
|
|
9525
9955
|
function cleanNode(node) {
|
|
9526
9956
|
const children = [...node.childNodes];
|
|
@@ -9543,6 +9973,12 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9543
9973
|
child.removeAttribute(attr.name);
|
|
9544
9974
|
return;
|
|
9545
9975
|
}
|
|
9976
|
+
if (name === "style") {
|
|
9977
|
+
const style = cleanStyle(attr.value);
|
|
9978
|
+
if (style) child.setAttribute("style", style);
|
|
9979
|
+
else child.removeAttribute("style");
|
|
9980
|
+
return;
|
|
9981
|
+
}
|
|
9546
9982
|
if ((name === "href" || name === "src") && !safeUrl(attr.value, tag)) {
|
|
9547
9983
|
child.removeAttribute(attr.name);
|
|
9548
9984
|
}
|
|
@@ -9563,78 +9999,474 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9563
9999
|
}
|
|
9564
10000
|
|
|
9565
10001
|
// src/components/rich-editor/index.js
|
|
10002
|
+
var BLOCKS = [
|
|
10003
|
+
{ value: "p", label: "paragraph" },
|
|
10004
|
+
{ value: "h1", label: "heading1" },
|
|
10005
|
+
{ value: "h2", label: "heading2" },
|
|
10006
|
+
{ value: "h3", label: "heading3" },
|
|
10007
|
+
{ value: "blockquote", label: "quote" },
|
|
10008
|
+
{ value: "pre", label: "codeBlock" }
|
|
10009
|
+
];
|
|
10010
|
+
var SIZES = [
|
|
10011
|
+
{ value: "", label: "defaultSize" },
|
|
10012
|
+
{ value: "12px", label: "12px" },
|
|
10013
|
+
{ value: "14px", label: "14px" },
|
|
10014
|
+
{ value: "16px", label: "16px" },
|
|
10015
|
+
{ value: "18px", label: "18px" },
|
|
10016
|
+
{ value: "24px", label: "24px" }
|
|
10017
|
+
];
|
|
10018
|
+
var COLORS = ["#1f2d3d", "#5a6b7d", "#2b86d9", "#2fa36b", "#d97706", "#dc2626", "#7c3aed", "#ffffff"];
|
|
10019
|
+
var TOOLBAR_PRESETS = {
|
|
10020
|
+
simple: ["history", "inline", "list", "insert"],
|
|
10021
|
+
basic: ["history", "format", "inline", "align", "list", "insert", "view"],
|
|
10022
|
+
full: ["history", "format", "inline", "color", "align", "list", "insert", "view"]
|
|
10023
|
+
};
|
|
10024
|
+
function isSafeImageUrl(url) {
|
|
10025
|
+
const value = String(url || "").replace(/[\u0000-\u0020]+/g, "");
|
|
10026
|
+
if (!value) return false;
|
|
10027
|
+
const scheme = value.match(/^([a-z][a-z0-9+.-]*):/i);
|
|
10028
|
+
if (!scheme) return true;
|
|
10029
|
+
const name = scheme[1].toLowerCase();
|
|
10030
|
+
if (name === "http" || name === "https" || name === "blob") return true;
|
|
10031
|
+
return name === "data" && /^data:image\//i.test(value);
|
|
10032
|
+
}
|
|
10033
|
+
function isSafeLink(url) {
|
|
10034
|
+
const value = String(url || "").trim();
|
|
10035
|
+
if (!value) return false;
|
|
10036
|
+
const scheme = value.replace(/[\u0000-\u0020]+/g, "").match(/^([a-z][a-z0-9+.-]*):/i);
|
|
10037
|
+
if (!scheme) return true;
|
|
10038
|
+
return ["http", "https", "mailto", "tel"].indexOf(scheme[1].toLowerCase()) > -1;
|
|
10039
|
+
}
|
|
9566
10040
|
var rich_editor_default = {
|
|
9567
10041
|
name: "DjmRichEditor",
|
|
9568
|
-
components: { Icon: icon_default },
|
|
10042
|
+
components: { Icon: icon_default, MediaPicker: media_picker_default },
|
|
9569
10043
|
props: {
|
|
9570
10044
|
modelValue: { type: String, default: "" },
|
|
9571
10045
|
placeholder: { type: String, default: "" },
|
|
9572
10046
|
minHeight: { type: Number, default: 200 },
|
|
9573
|
-
//
|
|
9574
|
-
sanitize: { type: Boolean, default:
|
|
9575
|
-
disabled: { type: Boolean, default: false }
|
|
10047
|
+
// 默认开启:对外输出的 HTML 走白名单清洗,内容里不会带出脚本
|
|
10048
|
+
sanitize: { type: Boolean, default: true },
|
|
10049
|
+
disabled: { type: Boolean, default: false },
|
|
10050
|
+
// 工具条:simple / basic / full,或传分组数组(history / format / inline / color / align / list / insert / view)
|
|
10051
|
+
toolbar: { type: [String, Array], default: "full" },
|
|
10052
|
+
// 自定义上传:接收 File,返回图片地址(或 { url }),不传则转成 dataURL 本地预览
|
|
10053
|
+
uploadImage: { type: Function, default: null },
|
|
10054
|
+
// 已上传图片,提供后工具栏出现「从素材库选择」:字符串或 { url, name }
|
|
10055
|
+
media: { type: Array, default: () => [] },
|
|
10056
|
+
accept: { type: String, default: "image/*" },
|
|
10057
|
+
// 单张图片大小上限(MB)
|
|
10058
|
+
maxImageSize: { type: Number, default: 5 },
|
|
10059
|
+
// 是否允许一次选多张
|
|
10060
|
+
multiple: { type: Boolean, default: true },
|
|
10061
|
+
showCount: { type: Boolean, default: true }
|
|
9576
10062
|
},
|
|
9577
|
-
emits: ["update:modelValue"],
|
|
10063
|
+
emits: ["update:modelValue", "upload-error"],
|
|
9578
10064
|
setup(props, { emit }) {
|
|
9579
10065
|
const editor = ref(null);
|
|
10066
|
+
const fileInput = ref(null);
|
|
9580
10067
|
const sourceMode = ref(false);
|
|
9581
10068
|
const source = ref(props.modelValue);
|
|
9582
|
-
const
|
|
10069
|
+
const fullscreen = ref(false);
|
|
10070
|
+
const panel = ref("");
|
|
10071
|
+
const uploadCount = ref(0);
|
|
10072
|
+
const textColor = ref(COLORS[0]);
|
|
10073
|
+
const bgColor = ref("#fdf3c4");
|
|
10074
|
+
const fontSize = ref("");
|
|
10075
|
+
const count2 = ref(0);
|
|
10076
|
+
const formats = ref({
|
|
10077
|
+
bold: false,
|
|
10078
|
+
italic: false,
|
|
10079
|
+
underline: false,
|
|
10080
|
+
strike: false,
|
|
10081
|
+
code: false,
|
|
10082
|
+
block: "p",
|
|
10083
|
+
align: "",
|
|
10084
|
+
list: ""
|
|
10085
|
+
});
|
|
10086
|
+
let savedRange = null;
|
|
10087
|
+
const ready = computed(() => !props.disabled && !sourceMode.value);
|
|
10088
|
+
const toolbarGroups = computed(() => {
|
|
10089
|
+
const list = Array.isArray(props.toolbar) ? props.toolbar : TOOLBAR_PRESETS[props.toolbar] || TOOLBAR_PRESETS.full;
|
|
10090
|
+
return new Set(list);
|
|
10091
|
+
});
|
|
10092
|
+
function has(group) {
|
|
10093
|
+
return toolbarGroups.value.has(group);
|
|
10094
|
+
}
|
|
10095
|
+
const mediaLibrary = computed(
|
|
10096
|
+
() => (props.media || []).map((item, index) => {
|
|
10097
|
+
if (typeof item === "string") {
|
|
10098
|
+
return { id: "media-" + index, url: item, name: "", type: "image" };
|
|
10099
|
+
}
|
|
10100
|
+
return {
|
|
10101
|
+
id: item && item.id !== void 0 ? String(item.id) : "media-" + index,
|
|
10102
|
+
url: item && (item.url || item.src) || "",
|
|
10103
|
+
name: item && (item.name || item.alt) || "",
|
|
10104
|
+
type: "image"
|
|
10105
|
+
};
|
|
10106
|
+
}).filter((item) => !!item.url)
|
|
10107
|
+
);
|
|
9583
10108
|
function clean(html) {
|
|
9584
10109
|
return props.sanitize ? sanitizeHtml(html) : html;
|
|
9585
10110
|
}
|
|
10111
|
+
function plainText(html) {
|
|
10112
|
+
return String(html || "").replace(/<[^>]*>/g, "").replace(/ /g, " ").replace(/&[a-z]+;/gi, " ");
|
|
10113
|
+
}
|
|
9586
10114
|
function syncFromDom() {
|
|
9587
10115
|
if (!editor.value) return;
|
|
9588
|
-
|
|
10116
|
+
const html = clean(editor.value.innerHTML);
|
|
10117
|
+
count2.value = plainText(html).trim().length;
|
|
10118
|
+
emit("update:modelValue", html);
|
|
9589
10119
|
}
|
|
9590
|
-
function
|
|
9591
|
-
const
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
10120
|
+
function currentRange() {
|
|
10121
|
+
const selection = window.getSelection();
|
|
10122
|
+
if (!selection || !selection.rangeCount || !editor.value) return null;
|
|
10123
|
+
const range = selection.getRangeAt(0);
|
|
10124
|
+
return editor.value.contains(range.commonAncestorContainer) ? range : null;
|
|
10125
|
+
}
|
|
10126
|
+
function rememberRange() {
|
|
10127
|
+
const range = currentRange();
|
|
10128
|
+
savedRange = range ? range.cloneRange() : null;
|
|
10129
|
+
}
|
|
10130
|
+
function focusAt(range) {
|
|
10131
|
+
if (!editor.value) return;
|
|
10132
|
+
editor.value.focus();
|
|
10133
|
+
const selection = window.getSelection();
|
|
10134
|
+
selection.removeAllRanges();
|
|
10135
|
+
if (range) {
|
|
10136
|
+
selection.addRange(range);
|
|
10137
|
+
return;
|
|
10138
|
+
}
|
|
10139
|
+
const end = document.createRange();
|
|
10140
|
+
end.selectNodeContents(editor.value);
|
|
10141
|
+
end.collapse(false);
|
|
10142
|
+
selection.addRange(end);
|
|
10143
|
+
}
|
|
10144
|
+
function insertNodeAt(range, node) {
|
|
10145
|
+
if (!editor.value) return;
|
|
10146
|
+
focusAt(range);
|
|
10147
|
+
const selection = window.getSelection();
|
|
10148
|
+
if (selection.rangeCount) selection.getRangeAt(0).insertNode(node);
|
|
10149
|
+
else editor.value.appendChild(node);
|
|
9596
10150
|
syncFromDom();
|
|
10151
|
+
syncFormats();
|
|
10152
|
+
}
|
|
10153
|
+
function ensureRange() {
|
|
10154
|
+
const range = currentRange();
|
|
10155
|
+
if (range) return range;
|
|
10156
|
+
if (!savedRange) return null;
|
|
10157
|
+
focusAt(savedRange);
|
|
10158
|
+
return currentRange();
|
|
9597
10159
|
}
|
|
9598
10160
|
function exec(command, value = null) {
|
|
9599
|
-
if (
|
|
10161
|
+
if (!ready.value) return;
|
|
10162
|
+
if (typeof document.execCommand !== "function") return;
|
|
10163
|
+
ensureRange();
|
|
10164
|
+
document.execCommand("styleWithCSS", false, true);
|
|
9600
10165
|
document.execCommand(command, false, value);
|
|
9601
10166
|
if (editor.value) editor.value.focus();
|
|
9602
10167
|
syncFromDom();
|
|
9603
10168
|
syncFormats();
|
|
9604
10169
|
}
|
|
9605
|
-
function
|
|
9606
|
-
|
|
10170
|
+
function setBlock(value) {
|
|
10171
|
+
panel.value = "";
|
|
10172
|
+
exec("formatBlock", "<" + value + ">");
|
|
10173
|
+
}
|
|
10174
|
+
function setFontSize(size) {
|
|
10175
|
+
panel.value = "";
|
|
10176
|
+
if (!ready.value || !editor.value) return;
|
|
10177
|
+
fontSize.value = size;
|
|
10178
|
+
const range = ensureRange();
|
|
10179
|
+
if (!range || range.collapsed) return;
|
|
10180
|
+
if (!size) {
|
|
10181
|
+
const walker = document.createTreeWalker(editor.value, NodeFilter.SHOW_ELEMENT);
|
|
10182
|
+
const targets = [];
|
|
10183
|
+
while (walker.nextNode()) {
|
|
10184
|
+
const el = walker.currentNode;
|
|
10185
|
+
if (el.style && el.style.fontSize && range.intersectsNode(el)) targets.push(el);
|
|
10186
|
+
}
|
|
10187
|
+
targets.forEach((el) => {
|
|
10188
|
+
el.style.fontSize = "";
|
|
10189
|
+
});
|
|
10190
|
+
syncFromDom();
|
|
10191
|
+
return;
|
|
10192
|
+
}
|
|
10193
|
+
const span = document.createElement("span");
|
|
10194
|
+
span.style.fontSize = size;
|
|
10195
|
+
span.appendChild(range.extractContents());
|
|
10196
|
+
range.insertNode(span);
|
|
10197
|
+
const selection = window.getSelection();
|
|
10198
|
+
const after = document.createRange();
|
|
10199
|
+
after.setStartAfter(span);
|
|
10200
|
+
after.collapse(true);
|
|
10201
|
+
selection.removeAllRanges();
|
|
10202
|
+
selection.addRange(after);
|
|
10203
|
+
syncFromDom();
|
|
10204
|
+
syncFormats();
|
|
10205
|
+
}
|
|
10206
|
+
function applyColor(kind, color) {
|
|
10207
|
+
panel.value = "";
|
|
10208
|
+
if (kind === "text") textColor.value = color;
|
|
10209
|
+
else bgColor.value = color;
|
|
10210
|
+
if (!ready.value) return;
|
|
10211
|
+
if (typeof document.execCommand !== "function") return;
|
|
10212
|
+
document.execCommand("styleWithCSS", false, true);
|
|
10213
|
+
const command = kind === "text" ? "foreColor" : "hiliteColor";
|
|
10214
|
+
if (!document.execCommand(command, false, color) && kind === "bg") {
|
|
10215
|
+
document.execCommand("backColor", false, color);
|
|
10216
|
+
}
|
|
10217
|
+
if (editor.value) editor.value.focus();
|
|
10218
|
+
syncFromDom();
|
|
10219
|
+
syncFormats();
|
|
10220
|
+
}
|
|
10221
|
+
function togglePanel(name) {
|
|
10222
|
+
if (!ready.value) return;
|
|
10223
|
+
panel.value = panel.value === name ? "" : name;
|
|
10224
|
+
}
|
|
10225
|
+
function toggleInlineCode() {
|
|
10226
|
+
if (!ready.value || !editor.value) return;
|
|
10227
|
+
const range = currentRange();
|
|
10228
|
+
if (!range) return;
|
|
10229
|
+
const anchor = range.commonAncestorContainer;
|
|
10230
|
+
const node = anchor.nodeType === 1 ? anchor : anchor.parentElement;
|
|
10231
|
+
const code = node && node.closest ? node.closest("code") : null;
|
|
10232
|
+
if (code && editor.value.contains(code)) {
|
|
10233
|
+
const parent = code.parentNode;
|
|
10234
|
+
while (code.firstChild) parent.insertBefore(code.firstChild, code);
|
|
10235
|
+
parent.removeChild(code);
|
|
10236
|
+
syncFromDom();
|
|
10237
|
+
return;
|
|
10238
|
+
}
|
|
10239
|
+
if (range.collapsed) return;
|
|
10240
|
+
const wrapper = document.createElement("code");
|
|
10241
|
+
wrapper.appendChild(range.extractContents());
|
|
10242
|
+
range.insertNode(wrapper);
|
|
10243
|
+
const selection = window.getSelection();
|
|
10244
|
+
const after = document.createRange();
|
|
10245
|
+
after.setStartAfter(wrapper);
|
|
10246
|
+
after.collapse(true);
|
|
10247
|
+
selection.removeAllRanges();
|
|
10248
|
+
selection.addRange(after);
|
|
10249
|
+
syncFromDom();
|
|
10250
|
+
syncFormats();
|
|
9607
10251
|
}
|
|
9608
10252
|
function addLink() {
|
|
10253
|
+
if (!ready.value) return;
|
|
9609
10254
|
const url = window.prompt(t("richEditor.linkPrompt"), "https://");
|
|
9610
|
-
if (url)
|
|
10255
|
+
if (!url) return;
|
|
10256
|
+
if (!isSafeLink(url)) {
|
|
10257
|
+
window.alert(t("richEditor.linkInvalid"));
|
|
10258
|
+
return;
|
|
10259
|
+
}
|
|
10260
|
+
const range = currentRange();
|
|
10261
|
+
if (range && range.collapsed) {
|
|
10262
|
+
const link = document.createElement("a");
|
|
10263
|
+
link.href = url;
|
|
10264
|
+
link.target = "_blank";
|
|
10265
|
+
link.rel = "noopener noreferrer";
|
|
10266
|
+
link.textContent = url;
|
|
10267
|
+
insertNodeAt(range, link);
|
|
10268
|
+
return;
|
|
10269
|
+
}
|
|
10270
|
+
exec("createLink", url);
|
|
10271
|
+
if (editor.value) {
|
|
10272
|
+
editor.value.querySelectorAll("a[href]").forEach((link) => {
|
|
10273
|
+
if (!link.getAttribute("target")) link.setAttribute("target", "_blank");
|
|
10274
|
+
link.setAttribute("rel", "noopener noreferrer");
|
|
10275
|
+
});
|
|
10276
|
+
}
|
|
10277
|
+
syncFromDom();
|
|
10278
|
+
}
|
|
10279
|
+
function readAsDataUrl(file) {
|
|
10280
|
+
return new Promise((resolve, reject) => {
|
|
10281
|
+
const reader = new FileReader();
|
|
10282
|
+
reader.onload = () => resolve(reader.result);
|
|
10283
|
+
reader.onerror = () => reject(reader.error || new Error("read failed"));
|
|
10284
|
+
reader.readAsDataURL(file);
|
|
10285
|
+
});
|
|
10286
|
+
}
|
|
10287
|
+
async function uploadOne(file) {
|
|
10288
|
+
if (props.uploadImage) {
|
|
10289
|
+
const result = await props.uploadImage(file);
|
|
10290
|
+
const url = typeof result === "string" ? result : result && result.url;
|
|
10291
|
+
if (!url) throw new Error(t("richEditor.imageUrlInvalid"));
|
|
10292
|
+
if (!isSafeImageUrl(url)) throw new Error(t("richEditor.imageUrlInvalid"));
|
|
10293
|
+
return String(url).trim();
|
|
10294
|
+
}
|
|
10295
|
+
return readAsDataUrl(file);
|
|
10296
|
+
}
|
|
10297
|
+
function isImage(file) {
|
|
10298
|
+
return file && /^image\//.test(file.type);
|
|
10299
|
+
}
|
|
10300
|
+
function pickImage(allowMultiple) {
|
|
10301
|
+
if (!ready.value || !fileInput.value) return;
|
|
10302
|
+
rememberRange();
|
|
10303
|
+
fileInput.value.multiple = !!allowMultiple && props.multiple;
|
|
10304
|
+
fileInput.value.click();
|
|
10305
|
+
}
|
|
10306
|
+
function insertMedia(ids) {
|
|
10307
|
+
if (!ready.value) return;
|
|
10308
|
+
const picked = (Array.isArray(ids) ? ids : [ids]).map((id) => mediaLibrary.value.find((item) => item.id === String(id))).filter(Boolean);
|
|
10309
|
+
const safe = picked.filter((item) => {
|
|
10310
|
+
if (isSafeImageUrl(item.url)) return true;
|
|
10311
|
+
emit("upload-error", { file: null, message: t("richEditor.imageUrlInvalid") });
|
|
10312
|
+
return false;
|
|
10313
|
+
});
|
|
10314
|
+
if (!safe.length) return;
|
|
10315
|
+
focusAt(savedRange);
|
|
10316
|
+
const fragment = document.createDocumentFragment();
|
|
10317
|
+
safe.forEach((item) => {
|
|
10318
|
+
const img = document.createElement("img");
|
|
10319
|
+
img.src = item.url;
|
|
10320
|
+
img.alt = item.name || "image";
|
|
10321
|
+
img.className = "djm-rich-editor-img";
|
|
10322
|
+
fragment.appendChild(img);
|
|
10323
|
+
});
|
|
10324
|
+
const selection = window.getSelection();
|
|
10325
|
+
if (selection.rangeCount) selection.getRangeAt(0).insertNode(fragment);
|
|
10326
|
+
else editor.value.appendChild(fragment);
|
|
10327
|
+
syncFromDom();
|
|
10328
|
+
syncFormats();
|
|
10329
|
+
}
|
|
10330
|
+
function insertImages(files) {
|
|
10331
|
+
if (!files.length || !editor.value) return;
|
|
10332
|
+
uploadCount.value = files.length;
|
|
10333
|
+
const holder = document.createElement("span");
|
|
10334
|
+
holder.className = "djm-rich-editor-uploading";
|
|
10335
|
+
holder.textContent = t("richEditor.uploading", { count: files.length });
|
|
10336
|
+
const anchor = savedRange && editor.value.contains(savedRange.commonAncestorContainer) ? savedRange : null;
|
|
10337
|
+
focusAt(anchor);
|
|
10338
|
+
const selection = window.getSelection();
|
|
10339
|
+
if (selection.rangeCount) selection.getRangeAt(0).insertNode(holder);
|
|
10340
|
+
else editor.value.appendChild(holder);
|
|
10341
|
+
Promise.all(files.map(async (file) => {
|
|
10342
|
+
if (!isImage(file)) return { file, error: t("richEditor.imageTypeError") };
|
|
10343
|
+
if (props.maxImageSize > 0 && file.size > props.maxImageSize * 1024 * 1024) {
|
|
10344
|
+
return { file, error: t("richEditor.imageSizeError", { size: props.maxImageSize }) };
|
|
10345
|
+
}
|
|
10346
|
+
try {
|
|
10347
|
+
return { file, url: await uploadOne(file) };
|
|
10348
|
+
} catch (error) {
|
|
10349
|
+
return { file, error: error && error.message || t("richEditor.uploadFailed") };
|
|
10350
|
+
}
|
|
10351
|
+
})).then((results) => {
|
|
10352
|
+
uploadCount.value = 0;
|
|
10353
|
+
const done = results.filter((item) => item.url);
|
|
10354
|
+
const failed = results.filter((item) => item.error);
|
|
10355
|
+
const alive = editor.value && editor.value.contains(holder);
|
|
10356
|
+
if (done.length && editor.value) {
|
|
10357
|
+
done.forEach((item) => {
|
|
10358
|
+
const img = document.createElement("img");
|
|
10359
|
+
img.src = item.url;
|
|
10360
|
+
img.alt = item.file.name || "image";
|
|
10361
|
+
img.className = "djm-rich-editor-img";
|
|
10362
|
+
if (alive) holder.parentNode.insertBefore(img, holder);
|
|
10363
|
+
else editor.value.appendChild(img);
|
|
10364
|
+
});
|
|
10365
|
+
}
|
|
10366
|
+
if (alive) holder.remove();
|
|
10367
|
+
failed.forEach((item) => emit("upload-error", { file: item.file, message: item.error }));
|
|
10368
|
+
syncFromDom();
|
|
10369
|
+
});
|
|
10370
|
+
}
|
|
10371
|
+
function onFilesChange(event) {
|
|
10372
|
+
const files = Array.from(event.target.files || []);
|
|
10373
|
+
event.target.value = "";
|
|
10374
|
+
insertImages(files);
|
|
10375
|
+
}
|
|
10376
|
+
function onPaste(event) {
|
|
10377
|
+
const data = event.clipboardData;
|
|
10378
|
+
if (!data) return;
|
|
10379
|
+
const images = Array.from(data.files || []).filter(isImage);
|
|
10380
|
+
if (images.length) {
|
|
10381
|
+
event.preventDefault();
|
|
10382
|
+
insertImages(images);
|
|
10383
|
+
return;
|
|
10384
|
+
}
|
|
10385
|
+
const html = data.getData("text/html");
|
|
10386
|
+
if (!html) return;
|
|
10387
|
+
event.preventDefault();
|
|
10388
|
+
document.execCommand("insertHTML", false, sanitizeHtml(html));
|
|
10389
|
+
syncFromDom();
|
|
10390
|
+
}
|
|
10391
|
+
function onDragOver(event) {
|
|
10392
|
+
if (!ready.value) return;
|
|
10393
|
+
if (event.dataTransfer && Array.from(event.dataTransfer.types || []).includes("Files")) {
|
|
10394
|
+
event.preventDefault();
|
|
10395
|
+
}
|
|
10396
|
+
}
|
|
10397
|
+
function onDrop(event) {
|
|
10398
|
+
if (!ready.value) return;
|
|
10399
|
+
const files = Array.from(event.dataTransfer && event.dataTransfer.files || []).filter(isImage);
|
|
10400
|
+
if (!files.length) return;
|
|
10401
|
+
event.preventDefault();
|
|
10402
|
+
rememberRange();
|
|
10403
|
+
insertImages(files);
|
|
10404
|
+
}
|
|
10405
|
+
function closestTag(node, tag) {
|
|
10406
|
+
const el = node && (node.nodeType === 1 ? node : node.parentElement);
|
|
10407
|
+
return el && el.closest ? el.closest(tag) : null;
|
|
10408
|
+
}
|
|
10409
|
+
function detectBlock() {
|
|
10410
|
+
const selection = window.getSelection();
|
|
10411
|
+
if (!selection || !selection.anchorNode) return "p";
|
|
10412
|
+
let node = selection.anchorNode;
|
|
10413
|
+
if (node.nodeType !== 1) node = node.parentElement;
|
|
10414
|
+
while (node && node !== editor.value) {
|
|
10415
|
+
const tag = node.tagName.toLowerCase();
|
|
10416
|
+
if (["p", "h1", "h2", "h3", "blockquote", "pre"].includes(tag)) return tag;
|
|
10417
|
+
node = node.parentElement;
|
|
10418
|
+
}
|
|
10419
|
+
return "p";
|
|
9611
10420
|
}
|
|
9612
10421
|
function syncFormats() {
|
|
9613
|
-
if (props.disabled || sourceMode.value) return;
|
|
9614
|
-
if (!editor.value
|
|
10422
|
+
if (props.disabled || sourceMode.value || !editor.value) return;
|
|
10423
|
+
if (!editor.value.contains(document.activeElement)) return;
|
|
10424
|
+
const selection = window.getSelection();
|
|
9615
10425
|
formats.value = {
|
|
9616
10426
|
bold: document.queryCommandState("bold"),
|
|
9617
10427
|
italic: document.queryCommandState("italic"),
|
|
9618
10428
|
underline: document.queryCommandState("underline"),
|
|
9619
|
-
strike: document.queryCommandState("strikeThrough")
|
|
10429
|
+
strike: document.queryCommandState("strikeThrough"),
|
|
10430
|
+
code: !!closestTag(selection.anchorNode, "code"),
|
|
10431
|
+
block: detectBlock(),
|
|
10432
|
+
align: document.queryCommandState("justifyCenter") ? "center" : document.queryCommandState("justifyRight") ? "right" : document.queryCommandState("justifyFull") ? "justify" : "left",
|
|
10433
|
+
list: document.queryCommandState("insertOrderedList") ? "ol" : document.queryCommandState("insertUnorderedList") ? "ul" : ""
|
|
9620
10434
|
};
|
|
9621
10435
|
}
|
|
9622
10436
|
function onSourceInput() {
|
|
10437
|
+
count2.value = plainText(source.value).trim().length;
|
|
9623
10438
|
emit("update:modelValue", clean(source.value));
|
|
9624
10439
|
}
|
|
9625
10440
|
function toggleSource() {
|
|
9626
10441
|
if (props.disabled) return;
|
|
10442
|
+
panel.value = "";
|
|
9627
10443
|
if (sourceMode.value) {
|
|
9628
10444
|
emit("update:modelValue", clean(source.value));
|
|
9629
10445
|
sourceMode.value = false;
|
|
9630
10446
|
requestAnimationFrame(() => {
|
|
9631
10447
|
if (editor.value) editor.value.innerHTML = clean(source.value);
|
|
10448
|
+
syncFromDom();
|
|
9632
10449
|
});
|
|
9633
10450
|
} else {
|
|
9634
10451
|
source.value = editor.value ? editor.value.innerHTML : props.modelValue;
|
|
9635
10452
|
sourceMode.value = true;
|
|
9636
10453
|
}
|
|
9637
10454
|
}
|
|
10455
|
+
function toggleFullscreen() {
|
|
10456
|
+
if (props.disabled) return;
|
|
10457
|
+
fullscreen.value = !fullscreen.value;
|
|
10458
|
+
panel.value = "";
|
|
10459
|
+
}
|
|
10460
|
+
function onDocumentClick(event) {
|
|
10461
|
+
if (!panel.value) return;
|
|
10462
|
+
if (!(event.target.closest && event.target.closest(".djm-rich-editor-color"))) panel.value = "";
|
|
10463
|
+
}
|
|
10464
|
+
function onKeydown(event) {
|
|
10465
|
+
if (event.key === "Escape" && fullscreen.value) {
|
|
10466
|
+
fullscreen.value = false;
|
|
10467
|
+
panel.value = "";
|
|
10468
|
+
}
|
|
10469
|
+
}
|
|
9638
10470
|
watch(
|
|
9639
10471
|
() => props.modelValue,
|
|
9640
10472
|
(value) => {
|
|
@@ -9644,40 +10476,117 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9644
10476
|
}
|
|
9645
10477
|
const next = clean(value);
|
|
9646
10478
|
if (editor.value && editor.value.innerHTML !== next) editor.value.innerHTML = next;
|
|
10479
|
+
count2.value = plainText(next).trim().length;
|
|
9647
10480
|
}
|
|
9648
10481
|
);
|
|
9649
10482
|
onMounted(() => {
|
|
9650
10483
|
if (editor.value) editor.value.innerHTML = clean(props.modelValue);
|
|
10484
|
+
count2.value = plainText(props.modelValue).trim().length;
|
|
9651
10485
|
document.addEventListener("selectionchange", syncFormats);
|
|
10486
|
+
document.addEventListener("click", onDocumentClick);
|
|
10487
|
+
document.addEventListener("keydown", onKeydown);
|
|
10488
|
+
});
|
|
10489
|
+
onBeforeUnmount(() => {
|
|
10490
|
+
document.removeEventListener("selectionchange", syncFormats);
|
|
10491
|
+
document.removeEventListener("click", onDocumentClick);
|
|
10492
|
+
document.removeEventListener("keydown", onKeydown);
|
|
9652
10493
|
});
|
|
9653
|
-
onBeforeUnmount(() => document.removeEventListener("selectionchange", syncFormats));
|
|
9654
10494
|
return {
|
|
9655
10495
|
editor,
|
|
10496
|
+
fileInput,
|
|
9656
10497
|
sourceMode,
|
|
9657
10498
|
source,
|
|
10499
|
+
fullscreen,
|
|
10500
|
+
panel,
|
|
10501
|
+
uploadCount,
|
|
10502
|
+
textColor,
|
|
10503
|
+
bgColor,
|
|
10504
|
+
fontSize,
|
|
10505
|
+
count: count2,
|
|
9658
10506
|
formats,
|
|
10507
|
+
blocks: BLOCKS,
|
|
10508
|
+
sizes: SIZES,
|
|
10509
|
+
colors: COLORS,
|
|
10510
|
+
mediaLibrary,
|
|
10511
|
+
has,
|
|
9659
10512
|
exec,
|
|
9660
|
-
|
|
10513
|
+
setBlock,
|
|
10514
|
+
setFontSize,
|
|
10515
|
+
applyColor,
|
|
10516
|
+
togglePanel,
|
|
10517
|
+
toggleInlineCode,
|
|
9661
10518
|
addLink,
|
|
9662
|
-
|
|
10519
|
+
pickImage,
|
|
10520
|
+
insertMedia,
|
|
10521
|
+
onFilesChange,
|
|
9663
10522
|
onPaste,
|
|
10523
|
+
onDragOver,
|
|
10524
|
+
onDrop,
|
|
10525
|
+
rememberRange,
|
|
10526
|
+
onSourceInput,
|
|
9664
10527
|
toggleSource,
|
|
10528
|
+
toggleFullscreen,
|
|
9665
10529
|
syncFromDom,
|
|
9666
10530
|
syncFormats,
|
|
9667
10531
|
t
|
|
9668
10532
|
};
|
|
9669
10533
|
},
|
|
9670
10534
|
template: `
|
|
9671
|
-
<div
|
|
9672
|
-
|
|
9673
|
-
|
|
10535
|
+
<div
|
|
10536
|
+
class="djm-rich-editor"
|
|
10537
|
+
:class="{ 'djm-is-disabled': disabled, 'djm-is-fullscreen': fullscreen }"
|
|
10538
|
+
>
|
|
10539
|
+
<div class="djm-rich-editor-toolbar" role="toolbar" :aria-label="t('richEditor.toolbar')" @mousedown="rememberRange">
|
|
10540
|
+
<div class="djm-rich-editor-group" v-if="has('history')">
|
|
10541
|
+
<button
|
|
10542
|
+
class="djm-rich-editor-btn"
|
|
10543
|
+
type="button"
|
|
10544
|
+
:title="t('richEditor.undo')"
|
|
10545
|
+
:aria-label="t('richEditor.undo')"
|
|
10546
|
+
:disabled="disabled || sourceMode"
|
|
10547
|
+
@mousedown.prevent
|
|
10548
|
+
@click="exec('undo')"
|
|
10549
|
+
><Icon name="undo" :size="13" /></button>
|
|
10550
|
+
<button
|
|
10551
|
+
class="djm-rich-editor-btn"
|
|
10552
|
+
type="button"
|
|
10553
|
+
:title="t('richEditor.redo')"
|
|
10554
|
+
:aria-label="t('richEditor.redo')"
|
|
10555
|
+
:disabled="disabled || sourceMode"
|
|
10556
|
+
@mousedown.prevent
|
|
10557
|
+
@click="exec('redo')"
|
|
10558
|
+
><Icon name="redo" :size="13" /></button>
|
|
10559
|
+
</div>
|
|
10560
|
+
|
|
10561
|
+
<div class="djm-rich-editor-group" v-if="has('format')">
|
|
10562
|
+
<select
|
|
10563
|
+
class="djm-rich-editor-select"
|
|
10564
|
+
:value="formats.block"
|
|
10565
|
+
:aria-label="t('richEditor.block')"
|
|
10566
|
+
:disabled="disabled || sourceMode"
|
|
10567
|
+
@change="setBlock($event.target.value)"
|
|
10568
|
+
>
|
|
10569
|
+
<option v-for="item in blocks" :key="item.value" :value="item.value">{{ t('richEditor.' + item.label) }}</option>
|
|
10570
|
+
</select>
|
|
10571
|
+
<select
|
|
10572
|
+
class="djm-rich-editor-select djm-rich-editor-select-size"
|
|
10573
|
+
:value="fontSize"
|
|
10574
|
+
:aria-label="t('richEditor.fontSize')"
|
|
10575
|
+
:disabled="disabled || sourceMode"
|
|
10576
|
+
@change="setFontSize($event.target.value)"
|
|
10577
|
+
>
|
|
10578
|
+
<option v-for="item in sizes" :key="item.value" :value="item.value">{{ t('richEditor.' + item.label) }}</option>
|
|
10579
|
+
</select>
|
|
10580
|
+
</div>
|
|
10581
|
+
|
|
10582
|
+
<div class="djm-rich-editor-group" v-if="has('inline')">
|
|
9674
10583
|
<button
|
|
9675
10584
|
class="djm-rich-editor-btn djm-rich-editor-btn-bold"
|
|
9676
10585
|
type="button"
|
|
9677
10586
|
:title="t('richEditor.bold')"
|
|
9678
10587
|
:aria-label="t('richEditor.bold')"
|
|
9679
10588
|
:aria-pressed="String(formats.bold)"
|
|
9680
|
-
:disabled="disabled"
|
|
10589
|
+
:disabled="disabled || sourceMode"
|
|
9681
10590
|
@mousedown.prevent
|
|
9682
10591
|
@click="exec('bold')"
|
|
9683
10592
|
>B</button>
|
|
@@ -9687,7 +10596,7 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9687
10596
|
:title="t('richEditor.italic')"
|
|
9688
10597
|
:aria-label="t('richEditor.italic')"
|
|
9689
10598
|
:aria-pressed="String(formats.italic)"
|
|
9690
|
-
:disabled="disabled"
|
|
10599
|
+
:disabled="disabled || sourceMode"
|
|
9691
10600
|
@mousedown.prevent
|
|
9692
10601
|
@click="exec('italic')"
|
|
9693
10602
|
>I</button>
|
|
@@ -9697,7 +10606,7 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9697
10606
|
:title="t('richEditor.underline')"
|
|
9698
10607
|
:aria-label="t('richEditor.underline')"
|
|
9699
10608
|
:aria-pressed="String(formats.underline)"
|
|
9700
|
-
:disabled="disabled"
|
|
10609
|
+
:disabled="disabled || sourceMode"
|
|
9701
10610
|
@mousedown.prevent
|
|
9702
10611
|
@click="exec('underline')"
|
|
9703
10612
|
>U</button>
|
|
@@ -9707,46 +10616,130 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9707
10616
|
:title="t('richEditor.strike')"
|
|
9708
10617
|
:aria-label="t('richEditor.strike')"
|
|
9709
10618
|
:aria-pressed="String(formats.strike)"
|
|
9710
|
-
:disabled="disabled"
|
|
10619
|
+
:disabled="disabled || sourceMode"
|
|
9711
10620
|
@mousedown.prevent
|
|
9712
10621
|
@click="exec('strikeThrough')"
|
|
9713
10622
|
>S</button>
|
|
10623
|
+
<button
|
|
10624
|
+
class="djm-rich-editor-btn"
|
|
10625
|
+
type="button"
|
|
10626
|
+
:title="t('richEditor.inlineCode')"
|
|
10627
|
+
:aria-label="t('richEditor.inlineCode')"
|
|
10628
|
+
:aria-pressed="String(formats.code)"
|
|
10629
|
+
:disabled="disabled || sourceMode"
|
|
10630
|
+
@mousedown.prevent
|
|
10631
|
+
@click="toggleInlineCode"
|
|
10632
|
+
><Icon name="code" :size="13" /></button>
|
|
9714
10633
|
</div>
|
|
9715
10634
|
|
|
9716
|
-
<div class="djm-rich-editor-group">
|
|
9717
|
-
<button
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
10635
|
+
<div class="djm-rich-editor-group djm-rich-editor-color" v-if="has('color')">
|
|
10636
|
+
<button
|
|
10637
|
+
class="djm-rich-editor-btn"
|
|
10638
|
+
type="button"
|
|
10639
|
+
:title="t('richEditor.textColor')"
|
|
10640
|
+
:aria-label="t('richEditor.textColor')"
|
|
10641
|
+
:aria-expanded="String(panel === 'text')"
|
|
10642
|
+
:disabled="disabled || sourceMode"
|
|
10643
|
+
@mousedown.prevent
|
|
10644
|
+
@click.stop="togglePanel('text')"
|
|
10645
|
+
>
|
|
10646
|
+
<span class="djm-rich-editor-letter" :style="{ borderBottomColor: textColor }">A</span>
|
|
9727
10647
|
</button>
|
|
9728
|
-
<button
|
|
9729
|
-
|
|
10648
|
+
<button
|
|
10649
|
+
class="djm-rich-editor-btn"
|
|
10650
|
+
type="button"
|
|
10651
|
+
:title="t('richEditor.bgColor')"
|
|
10652
|
+
:aria-label="t('richEditor.bgColor')"
|
|
10653
|
+
:aria-expanded="String(panel === 'bg')"
|
|
10654
|
+
:disabled="disabled || sourceMode"
|
|
10655
|
+
@mousedown.prevent
|
|
10656
|
+
@click.stop="togglePanel('bg')"
|
|
10657
|
+
>
|
|
10658
|
+
<span class="djm-rich-editor-letter djm-rich-editor-letter-bg" :style="{ background: bgColor }">A</span>
|
|
9730
10659
|
</button>
|
|
10660
|
+
<div v-if="panel === 'text' || panel === 'bg'" class="djm-rich-editor-panel" role="group" :aria-label="t('richEditor.textColor')">
|
|
10661
|
+
<button
|
|
10662
|
+
v-for="color in colors"
|
|
10663
|
+
:key="color"
|
|
10664
|
+
class="djm-rich-editor-swatch"
|
|
10665
|
+
type="button"
|
|
10666
|
+
:title="color"
|
|
10667
|
+
:aria-label="color"
|
|
10668
|
+
:style="{ background: color }"
|
|
10669
|
+
@mousedown.prevent
|
|
10670
|
+
@click="applyColor(panel, color)"
|
|
10671
|
+
></button>
|
|
10672
|
+
</div>
|
|
9731
10673
|
</div>
|
|
9732
10674
|
|
|
9733
|
-
<div class="djm-rich-editor-group">
|
|
9734
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
9735
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
9736
|
-
<button class="djm-rich-editor-btn" type="button" :title="t('richEditor.
|
|
10675
|
+
<div class="djm-rich-editor-group" v-if="has('align')">
|
|
10676
|
+
<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>
|
|
10677
|
+
<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>
|
|
10678
|
+
<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>
|
|
10679
|
+
<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>
|
|
9737
10680
|
</div>
|
|
9738
10681
|
|
|
9739
|
-
<div class="djm-rich-editor-
|
|
9740
|
-
|
|
9741
|
-
class="djm-rich-editor-btn"
|
|
9742
|
-
|
|
9743
|
-
type="button"
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
<
|
|
9749
|
-
|
|
10682
|
+
<div class="djm-rich-editor-group" v-if="has('list')">
|
|
10683
|
+
<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>
|
|
10684
|
+
<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>
|
|
10685
|
+
<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>
|
|
10686
|
+
<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>
|
|
10687
|
+
</div>
|
|
10688
|
+
|
|
10689
|
+
<div class="djm-rich-editor-group" v-if="has('insert')">
|
|
10690
|
+
<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>
|
|
10691
|
+
<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>
|
|
10692
|
+
<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>
|
|
10693
|
+
<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>
|
|
10694
|
+
<MediaPicker
|
|
10695
|
+
v-if="mediaLibrary.length"
|
|
10696
|
+
class="djm-rich-editor-picker"
|
|
10697
|
+
multiple
|
|
10698
|
+
accept="image"
|
|
10699
|
+
:items="mediaLibrary"
|
|
10700
|
+
:title="t('richEditor.mediaLibrary')"
|
|
10701
|
+
:disabled="disabled || sourceMode"
|
|
10702
|
+
@change="insertMedia"
|
|
10703
|
+
>
|
|
10704
|
+
<template #trigger="{ open }">
|
|
10705
|
+
<button
|
|
10706
|
+
class="djm-rich-editor-btn"
|
|
10707
|
+
type="button"
|
|
10708
|
+
:title="t('richEditor.mediaLibrary')"
|
|
10709
|
+
:aria-label="t('richEditor.mediaLibrary')"
|
|
10710
|
+
:disabled="disabled || sourceMode"
|
|
10711
|
+
@mousedown.prevent="rememberRange"
|
|
10712
|
+
@click="open"
|
|
10713
|
+
><Icon name="grid" :size="13" /></button>
|
|
10714
|
+
</template>
|
|
10715
|
+
</MediaPicker>
|
|
10716
|
+
<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>
|
|
10717
|
+
<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>
|
|
10718
|
+
</div>
|
|
10719
|
+
|
|
10720
|
+
<div class="djm-rich-editor-spacer" v-if="has('view')"></div>
|
|
10721
|
+
<div class="djm-rich-editor-group djm-rich-editor-group-end" v-if="has('view')">
|
|
10722
|
+
<button
|
|
10723
|
+
class="djm-rich-editor-btn"
|
|
10724
|
+
:class="{ 'djm-is-active': sourceMode }"
|
|
10725
|
+
type="button"
|
|
10726
|
+
:title="sourceMode ? t('richEditor.visual') : t('richEditor.source')"
|
|
10727
|
+
:aria-label="sourceMode ? t('richEditor.visual') : t('richEditor.source')"
|
|
10728
|
+
:aria-pressed="String(sourceMode)"
|
|
10729
|
+
:disabled="disabled"
|
|
10730
|
+
@click="toggleSource"
|
|
10731
|
+
><Icon name="code" :size="13" /></button>
|
|
10732
|
+
<button
|
|
10733
|
+
class="djm-rich-editor-btn"
|
|
10734
|
+
:class="{ 'djm-is-active': fullscreen }"
|
|
10735
|
+
type="button"
|
|
10736
|
+
:title="fullscreen ? t('richEditor.exitFullscreen') : t('richEditor.fullscreen')"
|
|
10737
|
+
:aria-label="fullscreen ? t('richEditor.exitFullscreen') : t('richEditor.fullscreen')"
|
|
10738
|
+
:aria-pressed="String(fullscreen)"
|
|
10739
|
+
:disabled="disabled"
|
|
10740
|
+
@click="toggleFullscreen"
|
|
10741
|
+
><Icon :name="fullscreen ? 'minimize' : 'maximize'" :size="13" /></button>
|
|
10742
|
+
</div>
|
|
9750
10743
|
</div>
|
|
9751
10744
|
|
|
9752
10745
|
<div
|
|
@@ -9762,6 +10755,8 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9762
10755
|
:style="{ minHeight: minHeight + 'px' }"
|
|
9763
10756
|
@input="syncFromDom"
|
|
9764
10757
|
@paste="onPaste"
|
|
10758
|
+
@dragover="onDragOver"
|
|
10759
|
+
@drop="onDrop"
|
|
9765
10760
|
></div>
|
|
9766
10761
|
|
|
9767
10762
|
<textarea
|
|
@@ -9774,6 +10769,21 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
9774
10769
|
spellcheck="false"
|
|
9775
10770
|
@input="onSourceInput"
|
|
9776
10771
|
></textarea>
|
|
10772
|
+
|
|
10773
|
+
<div v-if="showCount" class="djm-rich-editor-foot">
|
|
10774
|
+
<span v-if="uploadCount" class="djm-rich-editor-foot-item">{{ t('richEditor.uploading', { count: uploadCount }) }}</span>
|
|
10775
|
+
<span class="djm-rich-editor-foot-item">{{ t('richEditor.wordCount', { count }) }}</span>
|
|
10776
|
+
</div>
|
|
10777
|
+
|
|
10778
|
+
<input
|
|
10779
|
+
ref="fileInput"
|
|
10780
|
+
class="djm-rich-editor-file"
|
|
10781
|
+
type="file"
|
|
10782
|
+
:accept="accept"
|
|
10783
|
+
tabindex="-1"
|
|
10784
|
+
aria-hidden="true"
|
|
10785
|
+
@change="onFilesChange"
|
|
10786
|
+
/>
|
|
9777
10787
|
</div>
|
|
9778
10788
|
`
|
|
9779
10789
|
};
|
|
@@ -11837,197 +12847,6 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
11837
12847
|
`
|
|
11838
12848
|
};
|
|
11839
12849
|
|
|
11840
|
-
// src/components/media-picker/index.js
|
|
11841
|
-
var media_picker_default = {
|
|
11842
|
-
name: "DjmMediaPicker",
|
|
11843
|
-
components: { DjmModal: modal_default, DjmPagination: pagination_default, DjmIcon: icon_default },
|
|
11844
|
-
props: {
|
|
11845
|
-
modelValue: { type: [Array, String, Number], default: "" },
|
|
11846
|
-
items: { type: Array, default: () => [] },
|
|
11847
|
-
multiple: { type: Boolean, default: false },
|
|
11848
|
-
// 最多选几个,0 表示不限
|
|
11849
|
-
limit: { type: Number, default: 0 },
|
|
11850
|
-
// image 只显示图片,file 只显示非图片,空串不过滤
|
|
11851
|
-
accept: { type: String, default: "" },
|
|
11852
|
-
title: { type: String, default: "" },
|
|
11853
|
-
triggerText: { type: String, default: "" },
|
|
11854
|
-
pageSize: { type: Number, default: 12 },
|
|
11855
|
-
disabled: { type: Boolean, default: false }
|
|
11856
|
-
},
|
|
11857
|
-
emits: ["update:modelValue", "change"],
|
|
11858
|
-
setup(props, { emit }) {
|
|
11859
|
-
const open = ref(false);
|
|
11860
|
-
const keyword = ref("");
|
|
11861
|
-
const page = ref(1);
|
|
11862
|
-
const temp = ref([]);
|
|
11863
|
-
const selectedIds = computed(() => {
|
|
11864
|
-
if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
|
|
11865
|
-
if (props.modelValue === "" || props.modelValue === null || props.modelValue === void 0) return [];
|
|
11866
|
-
return [props.modelValue];
|
|
11867
|
-
});
|
|
11868
|
-
function isImage(item) {
|
|
11869
|
-
if (item.type) return item.type === "image";
|
|
11870
|
-
return /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(item.url || "");
|
|
11871
|
-
}
|
|
11872
|
-
const filtered = computed(() => {
|
|
11873
|
-
const key = keyword.value.trim().toLowerCase();
|
|
11874
|
-
return props.items.filter((item) => {
|
|
11875
|
-
if (props.accept === "image" && !isImage(item)) return false;
|
|
11876
|
-
if (props.accept === "file" && isImage(item)) return false;
|
|
11877
|
-
if (key && !String(item.name || "").toLowerCase().includes(key)) return false;
|
|
11878
|
-
return true;
|
|
11879
|
-
});
|
|
11880
|
-
});
|
|
11881
|
-
const paged = computed(() => {
|
|
11882
|
-
const start = (page.value - 1) * props.pageSize;
|
|
11883
|
-
return filtered.value.slice(start, start + props.pageSize);
|
|
11884
|
-
});
|
|
11885
|
-
watch(keyword, () => {
|
|
11886
|
-
page.value = 1;
|
|
11887
|
-
});
|
|
11888
|
-
watch(
|
|
11889
|
-
() => filtered.value.length,
|
|
11890
|
-
() => {
|
|
11891
|
-
const max = Math.max(1, Math.ceil(filtered.value.length / props.pageSize));
|
|
11892
|
-
if (page.value > max) page.value = max;
|
|
11893
|
-
}
|
|
11894
|
-
);
|
|
11895
|
-
function isChecked(id) {
|
|
11896
|
-
return temp.value.includes(id);
|
|
11897
|
-
}
|
|
11898
|
-
function atLimit() {
|
|
11899
|
-
return props.limit > 0 && temp.value.length >= props.limit;
|
|
11900
|
-
}
|
|
11901
|
-
function toggle(item) {
|
|
11902
|
-
if (props.multiple) {
|
|
11903
|
-
if (isChecked(item.id)) {
|
|
11904
|
-
temp.value = temp.value.filter((id) => id !== item.id);
|
|
11905
|
-
return;
|
|
11906
|
-
}
|
|
11907
|
-
if (atLimit()) return;
|
|
11908
|
-
temp.value = [...temp.value, item.id];
|
|
11909
|
-
return;
|
|
11910
|
-
}
|
|
11911
|
-
temp.value = [item.id];
|
|
11912
|
-
}
|
|
11913
|
-
function openPanel() {
|
|
11914
|
-
if (props.disabled) return;
|
|
11915
|
-
temp.value = [...selectedIds.value];
|
|
11916
|
-
keyword.value = "";
|
|
11917
|
-
page.value = 1;
|
|
11918
|
-
open.value = true;
|
|
11919
|
-
}
|
|
11920
|
-
function commit() {
|
|
11921
|
-
const next = props.multiple ? [...temp.value] : temp.value[0] !== void 0 ? temp.value[0] : "";
|
|
11922
|
-
emit("update:modelValue", next);
|
|
11923
|
-
emit("change", next);
|
|
11924
|
-
open.value = false;
|
|
11925
|
-
}
|
|
11926
|
-
function cancel() {
|
|
11927
|
-
temp.value = [...selectedIds.value];
|
|
11928
|
-
open.value = false;
|
|
11929
|
-
}
|
|
11930
|
-
return {
|
|
11931
|
-
open,
|
|
11932
|
-
keyword,
|
|
11933
|
-
page,
|
|
11934
|
-
temp,
|
|
11935
|
-
selectedIds,
|
|
11936
|
-
filtered,
|
|
11937
|
-
paged,
|
|
11938
|
-
isImage,
|
|
11939
|
-
isChecked,
|
|
11940
|
-
atLimit,
|
|
11941
|
-
toggle,
|
|
11942
|
-
openPanel,
|
|
11943
|
-
commit,
|
|
11944
|
-
cancel,
|
|
11945
|
-
t
|
|
11946
|
-
};
|
|
11947
|
-
},
|
|
11948
|
-
template: `
|
|
11949
|
-
<span class="djm-media-picker">
|
|
11950
|
-
<slot name="trigger" :open="openPanel" :count="selectedIds.length">
|
|
11951
|
-
<button
|
|
11952
|
-
class="djm-btn"
|
|
11953
|
-
type="button"
|
|
11954
|
-
:disabled="disabled"
|
|
11955
|
-
@click="openPanel"
|
|
11956
|
-
>{{ triggerText || t('mediaPicker.select') }}<span v-if="selectedIds.length" class="djm-media-picker-count">{{ selectedIds.length }}</span></button>
|
|
11957
|
-
</slot>
|
|
11958
|
-
|
|
11959
|
-
<DjmModal
|
|
11960
|
-
:model-value="open"
|
|
11961
|
-
:title="title || t('mediaPicker.title')"
|
|
11962
|
-
width="760px"
|
|
11963
|
-
@update:model-value="cancel"
|
|
11964
|
-
>
|
|
11965
|
-
<div class="djm-media-toolbar">
|
|
11966
|
-
<label class="djm-media-search">
|
|
11967
|
-
<DjmIcon name="search" :size="13" />
|
|
11968
|
-
<input
|
|
11969
|
-
v-model="keyword"
|
|
11970
|
-
class="djm-media-search-input"
|
|
11971
|
-
type="text"
|
|
11972
|
-
:placeholder="t('mediaPicker.search')"
|
|
11973
|
-
:aria-label="t('mediaPicker.search')"
|
|
11974
|
-
/>
|
|
11975
|
-
</label>
|
|
11976
|
-
<span class="djm-media-toolbar-slot">
|
|
11977
|
-
<slot name="toolbar"></slot>
|
|
11978
|
-
</span>
|
|
11979
|
-
</div>
|
|
11980
|
-
|
|
11981
|
-
<div class="djm-media-grid" role="listbox" :aria-multiselectable="multiple ? 'true' : 'false'">
|
|
11982
|
-
<div
|
|
11983
|
-
v-for="item in paged"
|
|
11984
|
-
:key="item.id"
|
|
11985
|
-
class="djm-media-item"
|
|
11986
|
-
:class="{
|
|
11987
|
-
'djm-is-checked': isChecked(item.id),
|
|
11988
|
-
'djm-is-disabled': multiple && atLimit() && !isChecked(item.id)
|
|
11989
|
-
}"
|
|
11990
|
-
role="option"
|
|
11991
|
-
tabindex="0"
|
|
11992
|
-
:aria-selected="String(isChecked(item.id))"
|
|
11993
|
-
@click="toggle(item)"
|
|
11994
|
-
@keydown.enter.prevent="toggle(item)"
|
|
11995
|
-
@keydown.space.prevent="toggle(item)"
|
|
11996
|
-
>
|
|
11997
|
-
<div class="djm-media-thumb">
|
|
11998
|
-
<img v-if="isImage(item)" :src="item.url" :alt="item.name" loading="lazy" />
|
|
11999
|
-
<DjmIcon v-else name="folder" :size="26" />
|
|
12000
|
-
<span v-if="isChecked(item.id)" class="djm-media-check">
|
|
12001
|
-
<DjmIcon name="check" :size="11" />
|
|
12002
|
-
</span>
|
|
12003
|
-
</div>
|
|
12004
|
-
<div class="djm-media-name" :title="item.name">{{ item.name }}</div>
|
|
12005
|
-
</div>
|
|
12006
|
-
<div v-if="!paged.length" class="djm-media-empty">{{ t('mediaPicker.empty') }}</div>
|
|
12007
|
-
</div>
|
|
12008
|
-
|
|
12009
|
-
<div class="djm-media-foot">
|
|
12010
|
-
<span class="djm-media-total">
|
|
12011
|
-
{{ t('mediaPicker.total', { total: filtered.length }) }}
|
|
12012
|
-
<em v-if="multiple && limit > 0">{{ t('mediaPicker.limit', { limit }) }}</em>
|
|
12013
|
-
</span>
|
|
12014
|
-
<DjmPagination
|
|
12015
|
-
v-if="filtered.length > pageSize"
|
|
12016
|
-
v-model:page="page"
|
|
12017
|
-
:page-size="pageSize"
|
|
12018
|
-
:total="filtered.length"
|
|
12019
|
-
/>
|
|
12020
|
-
</div>
|
|
12021
|
-
|
|
12022
|
-
<template #footer>
|
|
12023
|
-
<button class="djm-btn" type="button" @click="cancel">{{ t('common.cancel') }}</button>
|
|
12024
|
-
<button class="djm-btn djm-btn-primary" type="button" @click="commit">{{ t('common.confirm') }}</button>
|
|
12025
|
-
</template>
|
|
12026
|
-
</DjmModal>
|
|
12027
|
-
</span>
|
|
12028
|
-
`
|
|
12029
|
-
};
|
|
12030
|
-
|
|
12031
12850
|
// src/components/kanban/index.js
|
|
12032
12851
|
function cloneColumns(list) {
|
|
12033
12852
|
return (list || []).map((col) => ({ ...col, cards: (col.cards || []).slice() }));
|
|
@@ -15346,7 +16165,7 @@ Minimum version required to store current data is: ` + i + `.
|
|
|
15346
16165
|
org_chart_default,
|
|
15347
16166
|
page_tabs_default
|
|
15348
16167
|
];
|
|
15349
|
-
var version = "0.
|
|
16168
|
+
var version = "0.73.0";
|
|
15350
16169
|
function install(app, options = {}) {
|
|
15351
16170
|
if (options.locale) setLocale(options.locale);
|
|
15352
16171
|
components.forEach((component) => app.component(component.name, component));
|