vue-editify 0.1.22 → 0.1.23
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/examples/App.vue +8 -2
- package/lib/components/button/button.vue.d.ts +3 -3
- package/lib/components/button/props.d.ts +1 -1
- package/lib/components/checkbox/checkbox.vue.d.ts +3 -3
- package/lib/components/checkbox/props.d.ts +1 -1
- package/lib/components/colors/colors.vue.d.ts +3 -3
- package/lib/components/colors/props.d.ts +1 -1
- package/lib/components/insertImage/insertImage.vue.d.ts +6 -6
- package/lib/components/insertImage/props.d.ts +2 -2
- package/lib/components/insertLink/insertLink.vue.d.ts +3 -3
- package/lib/components/insertLink/props.d.ts +2 -2
- package/lib/components/insertTable/insertTable.vue.d.ts +3 -3
- package/lib/components/insertTable/props.d.ts +2 -2
- package/lib/components/insertVideo/insertVideo.vue.d.ts +6 -6
- package/lib/components/insertVideo/props.d.ts +2 -2
- package/lib/components/menu/menu.vue.d.ts +3 -3
- package/lib/components/menu/props.d.ts +1 -1
- package/lib/components/toolbar/props.d.ts +1 -1
- package/lib/components/toolbar/toolbar.vue.d.ts +3 -3
- package/lib/core/rule.d.ts +1 -1
- package/lib/core/tool.d.ts +19 -9
- package/lib/editify/editify.vue.d.ts +12 -3
- package/lib/editify/props.d.ts +6 -2
- package/lib/editify.es.js +576 -178
- package/lib/editify.umd.js +1 -1
- package/lib/index.d.ts +7 -4
- package/lib/plugins/attachment/index.d.ts +17 -0
- package/lib/plugins/attachment/insertAttachment/insertAttachment.vue.d.ts +83 -0
- package/lib/plugins/attachment/insertAttachment/props.d.ts +38 -0
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/src/components/button/button.vue +4 -1
- package/src/components/button/props.ts +1 -1
- package/src/components/checkbox/props.ts +1 -1
- package/src/components/colors/colors.vue +1 -1
- package/src/components/colors/props.ts +1 -1
- package/src/components/insertImage/insertImage.vue +12 -10
- package/src/components/insertImage/props.ts +2 -2
- package/src/components/insertLink/insertLink.vue +1 -1
- package/src/components/insertLink/props.ts +2 -2
- package/src/components/insertTable/props.ts +2 -2
- package/src/components/insertVideo/insertVideo.vue +12 -10
- package/src/components/insertVideo/props.ts +2 -2
- package/src/components/menu/menu.vue +28 -13
- package/src/components/menu/props.ts +1 -1
- package/src/components/toolbar/props.ts +1 -1
- package/src/components/toolbar/toolbar.vue +3 -4
- package/src/core/function.ts +5 -5
- package/src/core/rule.ts +1 -1
- package/src/core/tool.ts +24 -11
- package/src/editify/editify.less +27 -0
- package/src/editify/editify.vue +50 -6
- package/src/editify/props.ts +9 -2
- package/src/icon/iconfont.css +4 -0
- package/src/icon/iconfont.ttf +0 -0
- package/src/icon/iconfont.woff +0 -0
- package/src/index.ts +13 -8
- package/src/locale/en_US.ts +9 -1
- package/src/locale/zh_CN.ts +9 -1
- package/src/plugins/attachment/images/attachment.png +0 -0
- package/src/plugins/attachment/index.ts +121 -0
- package/src/plugins/attachment/insertAttachment/insertAttachment.less +135 -0
- package/src/plugins/attachment/insertAttachment/insertAttachment.vue +189 -0
- package/src/plugins/attachment/insertAttachment/props.ts +48 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ExtractPublicPropTypes } from 'vue'
|
1
|
+
import { ExtractPublicPropTypes, PropType } from 'vue'
|
2
2
|
|
3
3
|
export type InsertTableGridType = {
|
4
4
|
x: number
|
@@ -9,7 +9,7 @@ export type InsertTableGridType = {
|
|
9
9
|
export const InsertTableProps = {
|
10
10
|
//主题色
|
11
11
|
color: {
|
12
|
-
type: String
|
12
|
+
type: String as PropType<string | null>,
|
13
13
|
default: ''
|
14
14
|
},
|
15
15
|
//最大行数
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<!-- 网络视频 -->
|
9
9
|
<div class="editify-video-remote" v-if="current == 'remote'">
|
10
10
|
<input v-model.trim="remoteUrl" :placeholder="$editTrans('videoUrlPlaceholder')" @blur="handleInputBlur" @focus="handleInputFocus" />
|
11
|
-
<div class="editify-video-remote-footer" :style="{ color: color }">
|
11
|
+
<div class="editify-video-remote-footer" :style="{ color: color || '' }">
|
12
12
|
<span @click="insertRemoteVideo">{{ $editTrans('insert') }}</span>
|
13
13
|
</div>
|
14
14
|
</div>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
import { file as DapFile } from 'dap-util'
|
24
24
|
import Icon from '../icon/icon.vue'
|
25
25
|
import { InsertVideoProps } from './props'
|
26
|
-
import {
|
26
|
+
import { computed, inject, ref, watch } from 'vue'
|
27
27
|
import { ObjectType } from '../../core/tool'
|
28
28
|
|
29
29
|
defineOptions({
|
@@ -33,7 +33,6 @@ const props = defineProps(InsertVideoProps)
|
|
33
33
|
const emits = defineEmits(['change', 'insert'])
|
34
34
|
|
35
35
|
const $editTrans = inject<(key: string) => any>('$editTrans')!
|
36
|
-
const editify = inject<ComponentInternalInstance>('editify')!
|
37
36
|
|
38
37
|
//当前展示的面板,取值remote和upload
|
39
38
|
const current = ref<'remote' | 'upload'>('upload')
|
@@ -84,14 +83,17 @@ const selectFile = async (e: Event) => {
|
|
84
83
|
for (let i = 0; i < files.length; i++) {
|
85
84
|
const file = files[i]
|
86
85
|
const suffix = getSuffix(file)
|
87
|
-
const isMatch =
|
88
|
-
|
89
|
-
|
86
|
+
const isMatch =
|
87
|
+
props.allowedFileType && Array.isArray(props.allowedFileType) && props.allowedFileType.length
|
88
|
+
? props.allowedFileType.some(item => {
|
89
|
+
return item.toLocaleLowerCase() == suffix.toLocaleLowerCase()
|
90
|
+
})
|
91
|
+
: true
|
90
92
|
//后缀不符合
|
91
93
|
if (!isMatch) {
|
92
94
|
//如果自定义了异常处理
|
93
95
|
if (typeof props.handleError == 'function') {
|
94
|
-
props.handleError
|
96
|
+
props.handleError('suffixError', file)
|
95
97
|
}
|
96
98
|
continue
|
97
99
|
}
|
@@ -99,7 +101,7 @@ const selectFile = async (e: Event) => {
|
|
99
101
|
if (props.maxSize && file.size / 1024 > props.maxSize) {
|
100
102
|
//如果自定义了异常处理
|
101
103
|
if (typeof props.handleError == 'function') {
|
102
|
-
props.handleError
|
104
|
+
props.handleError('maxSizeError', file)
|
103
105
|
}
|
104
106
|
continue
|
105
107
|
}
|
@@ -107,7 +109,7 @@ const selectFile = async (e: Event) => {
|
|
107
109
|
if (props.minSize && file.size / 1024 < props.minSize) {
|
108
110
|
//如果自定义了异常处理
|
109
111
|
if (typeof props.handleError == 'function') {
|
110
|
-
props.handleError
|
112
|
+
props.handleError('minSizeError', file)
|
111
113
|
}
|
112
114
|
continue
|
113
115
|
}
|
@@ -118,7 +120,7 @@ const selectFile = async (e: Event) => {
|
|
118
120
|
let videos = []
|
119
121
|
//自定义上传方法
|
120
122
|
if (typeof props.customUpload == 'function') {
|
121
|
-
videos = (await props.customUpload
|
123
|
+
videos = (await props.customUpload(filterFiles)) || []
|
122
124
|
}
|
123
125
|
//默认上传方法
|
124
126
|
else {
|
@@ -5,11 +5,11 @@ export type InsertVideoUploadErrorType = 'suffixError' | 'maxSizeError' | 'minSi
|
|
5
5
|
export const InsertVideoProps = {
|
6
6
|
//主题色
|
7
7
|
color: {
|
8
|
-
type: String
|
8
|
+
type: String as PropType<string | null>,
|
9
9
|
default: ''
|
10
10
|
},
|
11
11
|
//支持的视频类型数组
|
12
|
-
|
12
|
+
allowedFileType: {
|
13
13
|
type: Array as PropType<string[]>,
|
14
14
|
default: null
|
15
15
|
},
|
@@ -17,7 +17,7 @@ import { common as DapCommon } from 'dap-util'
|
|
17
17
|
import { getRangeText, setHeading, setIndentIncrease, setIndentDecrease, setQuote, setAlign, setList, setTask, setTextStyle, setTextMark, removeTextStyle, removeTextMark, setLineHeight, insertLink, insertImage, insertVideo, insertTable, insertCodeBlock, hasPreInRange, hasTableInRange, hasQuoteInRange, hasLinkInRange, isRangeInQuote, isRangeInList, isRangeInTask, queryTextStyle, queryTextMark, getCurrentParsedomElement } from '../../core/function'
|
18
18
|
import { MenuProps } from './props'
|
19
19
|
import { MenuModeType, ObjectType } from '../../core/tool'
|
20
|
-
import AlexEditor,
|
20
|
+
import { AlexEditor, AlexElementsRangeType } from 'alex-editor'
|
21
21
|
import { ButtonOptionsItemType } from '../button/props'
|
22
22
|
|
23
23
|
defineOptions({
|
@@ -273,7 +273,7 @@ const imageConfig = ref<ObjectType>({
|
|
273
273
|
rightBorder: props.config.image!.rightBorder,
|
274
274
|
active: false,
|
275
275
|
disabled: false,
|
276
|
-
|
276
|
+
allowedFileType: props.config.image!.allowedFileType,
|
277
277
|
multiple: props.config.image!.multiple,
|
278
278
|
maxSize: props.config.image!.maxSize,
|
279
279
|
minSize: props.config.image!.minSize,
|
@@ -287,7 +287,7 @@ const videoConfig = ref<ObjectType>({
|
|
287
287
|
rightBorder: props.config.video!.rightBorder,
|
288
288
|
active: false,
|
289
289
|
disabled: false,
|
290
|
-
|
290
|
+
allowedFileType: props.config.video!.allowedFileType,
|
291
291
|
multiple: props.config.video!.multiple,
|
292
292
|
maxSize: props.config.video!.maxSize,
|
293
293
|
minSize: props.config.video!.minSize,
|
@@ -668,7 +668,7 @@ const handleRangeUpdate = () => {
|
|
668
668
|
//额外禁用判定
|
669
669
|
const extraDisabled = (name: string) => {
|
670
670
|
if (typeof props.config.extraDisabled == 'function') {
|
671
|
-
return props.config.extraDisabled
|
671
|
+
return props.config.extraDisabled(name) || false
|
672
672
|
}
|
673
673
|
return false
|
674
674
|
}
|
@@ -1342,7 +1342,7 @@ const MenuItem = defineComponent(
|
|
1342
1342
|
layer: () =>
|
1343
1343
|
h(InsertImage, {
|
1344
1344
|
color: props.color,
|
1345
|
-
|
1345
|
+
allowedFileType: imageConfig.value.allowedFileType,
|
1346
1346
|
multiple: imageConfig.value.multiple,
|
1347
1347
|
maxSize: imageConfig.value.maxSize,
|
1348
1348
|
minSize: imageConfig.value.minSize,
|
@@ -1386,7 +1386,7 @@ const MenuItem = defineComponent(
|
|
1386
1386
|
layer: () =>
|
1387
1387
|
h(InsertVideo, {
|
1388
1388
|
color: props.color,
|
1389
|
-
|
1389
|
+
allowedFileType: videoConfig.value.allowedFileType,
|
1390
1390
|
multiple: videoConfig.value.multiple,
|
1391
1391
|
maxSize: videoConfig.value.maxSize,
|
1392
1392
|
minSize: videoConfig.value.minSize,
|
@@ -1524,29 +1524,44 @@ const MenuItem = defineComponent(
|
|
1524
1524
|
color: props.color,
|
1525
1525
|
onLayerShow: () => {
|
1526
1526
|
if (typeof configuration.onLayerShow == 'function') {
|
1527
|
-
configuration.onLayerShow
|
1527
|
+
configuration.onLayerShow(itemProps.name, <InstanceType<typeof Button>>itemInstance.proxy!.$refs.btnRef)
|
1528
1528
|
}
|
1529
1529
|
},
|
1530
1530
|
onLayerShown: () => {
|
1531
1531
|
if (typeof configuration.onLayerShown == 'function') {
|
1532
|
-
configuration.onLayerShown
|
1532
|
+
configuration.onLayerShown(itemProps.name, <InstanceType<typeof Button>>itemInstance.proxy!.$refs.btnRef)
|
1533
1533
|
}
|
1534
1534
|
},
|
1535
1535
|
onLayerHidden: () => {
|
1536
1536
|
if (typeof configuration.onLayerHidden == 'function') {
|
1537
|
-
configuration.onLayerHidden
|
1537
|
+
configuration.onLayerHidden(itemProps.name, <InstanceType<typeof Button>>itemInstance.proxy!.$refs.btnRef)
|
1538
1538
|
}
|
1539
1539
|
},
|
1540
1540
|
onOperate: (name, val) => {
|
1541
1541
|
if (typeof configuration.onOperate == 'function') {
|
1542
|
-
configuration.onOperate
|
1542
|
+
configuration.onOperate(name, val, <InstanceType<typeof Button>>itemInstance.proxy!.$refs.btnRef)
|
1543
1543
|
}
|
1544
1544
|
}
|
1545
1545
|
},
|
1546
1546
|
{
|
1547
|
-
default:
|
1548
|
-
|
1549
|
-
|
1547
|
+
default: () => {
|
1548
|
+
if (configuration.default) {
|
1549
|
+
return configuration.default(itemProps.name, <InstanceType<typeof Button>>itemInstance.proxy!.$refs.btnRef)
|
1550
|
+
}
|
1551
|
+
return null
|
1552
|
+
},
|
1553
|
+
layer: () => {
|
1554
|
+
if (configuration.layer) {
|
1555
|
+
return configuration.layer(itemProps.name, <InstanceType<typeof Button>>itemInstance.proxy!.$refs.btnRef)
|
1556
|
+
}
|
1557
|
+
return null
|
1558
|
+
},
|
1559
|
+
option: () => {
|
1560
|
+
if (configuration.option) {
|
1561
|
+
return configuration.option(itemProps.name, <InstanceType<typeof Button>>itemInstance.proxy!.$refs.btnRef)
|
1562
|
+
}
|
1563
|
+
return null
|
1564
|
+
}
|
1550
1565
|
}
|
1551
1566
|
)
|
1552
1567
|
}
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<Checkbox @change="modifyLink" v-model="linkConfig.newOpen" :label="$editTrans('newWindowOpen')" :color="color" :size="10"></Checkbox>
|
11
11
|
<div class="editify-toolbar-link-operations">
|
12
12
|
<span @click="removeLink">{{ $editTrans('removeLink') }}</span>
|
13
|
-
<a :href="linkConfig.url" target="_blank" :style="{ color: color }">{{ $editTrans('viewLink') }}</a>
|
13
|
+
<a :href="linkConfig.url" target="_blank" :style="{ color: color || '' }">{{ $editTrans('viewLink') }}</a>
|
14
14
|
</div>
|
15
15
|
</div>
|
16
16
|
</div>
|
@@ -203,7 +203,7 @@ import { AlexEditor, AlexElement, AlexElementsRangeType } from 'alex-editor'
|
|
203
203
|
import { common as DapCommon } from 'dap-util'
|
204
204
|
import { getCurrentParsedomElement, removeTextStyle, removeTextMark, setTextStyle, setLineHeight, setTextMark, setList, setTask, setHeading, setAlign, isRangeInList, isRangeInTask, queryTextStyle, queryTextMark } from '../../core/function'
|
205
205
|
import { ToolbarProps } from './props'
|
206
|
-
import {
|
206
|
+
import { Ref, computed, inject, ref } from 'vue'
|
207
207
|
import { ObjectType } from '../../core/tool'
|
208
208
|
import { ButtonOptionsItemType } from '../button/props'
|
209
209
|
|
@@ -213,7 +213,6 @@ defineOptions({
|
|
213
213
|
const props = defineProps(ToolbarProps)
|
214
214
|
const emits = defineEmits(['update:modelValue'])
|
215
215
|
|
216
|
-
const editify = inject<ComponentInternalInstance>('editify')!
|
217
216
|
const editor = inject<Ref<AlexEditor>>('editor')!
|
218
217
|
const dataRangeCaches = inject<Ref<AlexElementsRangeType>>('dataRangeCaches')!
|
219
218
|
const $editTrans = inject<(key: string) => any>('$editTrans')!
|
@@ -953,7 +952,7 @@ const layerShow = () => {
|
|
953
952
|
//额外禁用判定
|
954
953
|
const extraDisabled = (name: string) => {
|
955
954
|
if (typeof props.config.extraDisabled == 'function') {
|
956
|
-
return props.config.extraDisabled
|
955
|
+
return props.config.extraDisabled(name) || false
|
957
956
|
}
|
958
957
|
return false
|
959
958
|
}
|
package/src/core/function.ts
CHANGED
@@ -77,7 +77,7 @@ export const elementIsInTask = (element: AlexElement): boolean => {
|
|
77
77
|
}
|
78
78
|
|
79
79
|
//判断元素是否有序或者无序列表
|
80
|
-
export const isList =
|
80
|
+
export const isList = (element: AlexElement, ordered: boolean | undefined = false) => {
|
81
81
|
if (element.isEmpty()) {
|
82
82
|
return false
|
83
83
|
}
|
@@ -85,7 +85,7 @@ export const isList = function (element: AlexElement, ordered: boolean | undefin
|
|
85
85
|
}
|
86
86
|
|
87
87
|
//判断元素是否任务列表
|
88
|
-
export const isTask =
|
88
|
+
export const isTask = (element: AlexElement) => {
|
89
89
|
if (element.isEmpty()) {
|
90
90
|
return false
|
91
91
|
}
|
@@ -394,14 +394,14 @@ export const getFlatElementsByRange = (editor: AlexEditor, dataRangeCaches: Alex
|
|
394
394
|
}
|
395
395
|
|
396
396
|
//将某个元素转为段落标签
|
397
|
-
export const elementToParagraph =
|
397
|
+
export const elementToParagraph = (element: AlexElement) => {
|
398
398
|
element.marks = null
|
399
399
|
element.styles = null
|
400
400
|
element.parsedom = AlexElement.BLOCK_NODE
|
401
401
|
}
|
402
402
|
|
403
403
|
//其他元素转为有序或者无序列表
|
404
|
-
export const elementToList =
|
404
|
+
export const elementToList = (element: AlexElement, ordered: boolean | undefined = false) => {
|
405
405
|
//如果是列表则返回
|
406
406
|
if (isList(element, ordered)) {
|
407
407
|
return
|
@@ -417,7 +417,7 @@ export const elementToList = function (element: AlexElement, ordered: boolean |
|
|
417
417
|
}
|
418
418
|
|
419
419
|
//其他元素转为任务列表
|
420
|
-
export const elementToTask =
|
420
|
+
export const elementToTask = (element: AlexElement) => {
|
421
421
|
//如果是任务列表则返回
|
422
422
|
if (isTask(element)) {
|
423
423
|
return
|
package/src/core/rule.ts
CHANGED
package/src/core/tool.ts
CHANGED
@@ -4,7 +4,7 @@ import { AlexElement } from 'alex-editor'
|
|
4
4
|
import { ButtonOptionsItemType, ButtonTypeType } from '../components/button/props'
|
5
5
|
import { LocaleType } from '../locale'
|
6
6
|
import { InsertImageUploadErrorType } from '../components/insertImage/props'
|
7
|
-
import { VNode } from 'vue'
|
7
|
+
import { ComponentInternalInstance, VNode } from 'vue'
|
8
8
|
import Button from '../components/button/button.vue'
|
9
9
|
|
10
10
|
export type ObjectType = {
|
@@ -39,7 +39,7 @@ export interface MenuDisplayButtonType extends MenuSelectButtonType {
|
|
39
39
|
}
|
40
40
|
|
41
41
|
export interface MenuImageButtonType extends MenuButtonType {
|
42
|
-
|
42
|
+
allowedFileType?: string[]
|
43
43
|
multiple?: boolean
|
44
44
|
maxSize?: number | null
|
45
45
|
minSize?: number | null
|
@@ -48,7 +48,7 @@ export interface MenuImageButtonType extends MenuButtonType {
|
|
48
48
|
}
|
49
49
|
|
50
50
|
export interface MenuVideoButtonType extends MenuButtonType {
|
51
|
-
|
51
|
+
allowedFileType?: string[]
|
52
52
|
multiple?: boolean
|
53
53
|
maxSize?: number | null
|
54
54
|
minSize?: number | null
|
@@ -77,9 +77,9 @@ export type MenuCustomButtonType = {
|
|
77
77
|
onLayerShown?: (name: string, btnInstance: InstanceType<typeof Button>) => void
|
78
78
|
onLayerHidden?: (name: string, btnInstance: InstanceType<typeof Button>) => void
|
79
79
|
onOperate?: (name: string, value: string | number | undefined, btnInstance: InstanceType<typeof Button>) => void
|
80
|
-
default?: () => VNode
|
81
|
-
layer?: () => VNode
|
82
|
-
option?: () => VNode
|
80
|
+
default?: (name: string, btnInstance: InstanceType<typeof Button>) => VNode
|
81
|
+
layer?: (name: string, btnInstance: InstanceType<typeof Button>) => VNode
|
82
|
+
option?: (name: string, btnInstance: InstanceType<typeof Button>) => VNode
|
83
83
|
}
|
84
84
|
|
85
85
|
export type CodeBlockToolbarType = {
|
@@ -151,6 +151,10 @@ export type MenuSequenceType = {
|
|
151
151
|
|
152
152
|
export type MenuModeType = 'default' | 'inner' | 'fixed'
|
153
153
|
|
154
|
+
export type MenuExtendType = {
|
155
|
+
[name: string]: MenuCustomButtonType
|
156
|
+
}
|
157
|
+
|
154
158
|
export type MenuConfigType = {
|
155
159
|
use?: boolean
|
156
160
|
tooltip?: boolean
|
@@ -191,11 +195,20 @@ export type MenuConfigType = {
|
|
191
195
|
//全屏
|
192
196
|
fullScreen?: MenuButtonType
|
193
197
|
//拓展菜单,每个key表示拓展菜单的唯一名称,value是对象,包含type/title/rightBorder/leftBorder/disabled/active/width/maxHeight/options/value/hideScroll/onLayerShow/onLayerShown/onLayerHidden/onOperate/default/layer/option属性
|
194
|
-
extends?:
|
195
|
-
[name: string]: MenuCustomButtonType
|
196
|
-
}
|
198
|
+
extends?: MenuExtendType
|
197
199
|
}
|
198
200
|
|
201
|
+
export type PluginResultType = {
|
202
|
+
menu?: MenuConfigType
|
203
|
+
updateView?: () => void
|
204
|
+
customParseNode?: (element: AlexElement) => AlexElement
|
205
|
+
renderRule?: (el: AlexElement) => void
|
206
|
+
pasteKeepStyles?: ObjectType
|
207
|
+
pasteKeepMarks?: ObjectType
|
208
|
+
}
|
209
|
+
|
210
|
+
export type PluginType = (editifyInstance: ComponentInternalInstance, color: string | null, editTrans: (key: string) => any) => PluginResultType
|
211
|
+
|
199
212
|
//粘贴html时保留的数据
|
200
213
|
export const pasteKeepData: ObjectType = {
|
201
214
|
//粘贴html时元素保留的样式(全部元素)
|
@@ -1058,7 +1071,7 @@ export const getMenuConfig = function (editTrans: (key: string) => any, editLoca
|
|
1058
1071
|
//右侧边框是否显示
|
1059
1072
|
rightBorder: false,
|
1060
1073
|
//图片支持上传的类型,不区分大小写
|
1061
|
-
|
1074
|
+
allowedFileType: ['jpg', 'png', 'jpeg', 'webp', 'jfif', 'ico', 'gif', 'svg', 'psd'],
|
1062
1075
|
//是否多选图片
|
1063
1076
|
multiple: false,
|
1064
1077
|
//单张图片的最大值,单位kb
|
@@ -1079,7 +1092,7 @@ export const getMenuConfig = function (editTrans: (key: string) => any, editLoca
|
|
1079
1092
|
//右侧边框是否显示
|
1080
1093
|
rightBorder: false,
|
1081
1094
|
//视频支持上传的类型,不区分大小写
|
1082
|
-
|
1095
|
+
allowedFileType: ['mp4', 'avi', 'mpg', 'wmv', 'mov', 'rm', 'swf', 'flv'],
|
1083
1096
|
//是否多选视频
|
1084
1097
|
multiple: false,
|
1085
1098
|
//单个视频的的最大值,单位kb
|
package/src/editify/editify.less
CHANGED
@@ -342,6 +342,33 @@
|
|
342
342
|
}
|
343
343
|
}
|
344
344
|
|
345
|
+
//附件样式
|
346
|
+
:deep(span[data-attachment]) {
|
347
|
+
display: inline-block;
|
348
|
+
width: 40px;
|
349
|
+
height: 40px;
|
350
|
+
background-color: @font-color-link;
|
351
|
+
transition: all 200ms;
|
352
|
+
position: relative;
|
353
|
+
border-radius: 4px;
|
354
|
+
|
355
|
+
&:hover {
|
356
|
+
background-color: @font-color-link-dark;
|
357
|
+
cursor: pointer;
|
358
|
+
}
|
359
|
+
|
360
|
+
&::before {
|
361
|
+
content: '';
|
362
|
+
position: absolute;
|
363
|
+
left: 8px;
|
364
|
+
top: 8px;
|
365
|
+
width: 24px;
|
366
|
+
height: 24px;
|
367
|
+
background: url(../plugins/attachment/images/attachment.png) no-repeat center;
|
368
|
+
background-size: cover;
|
369
|
+
}
|
370
|
+
}
|
371
|
+
|
345
372
|
//禁用样式
|
346
373
|
&.editify-disabled {
|
347
374
|
cursor: auto !important;
|
package/src/editify/editify.vue
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
|
23
23
|
import { AlexEditor, AlexElement, AlexElementRangeType, AlexElementsRangeType } from 'alex-editor'
|
24
24
|
import { element as DapElement, event as DapEvent, data as DapData, number as DapNumber, color as DapColor } from 'dap-util'
|
25
|
-
import { pasteKeepData, mergeObject, getToolbarConfig, getMenuConfig, MenuConfigType, ObjectType, ToolbarConfigType } from '../core/tool'
|
25
|
+
import { pasteKeepData, mergeObject, getToolbarConfig, getMenuConfig, MenuConfigType, ObjectType, ToolbarConfigType, PluginResultType } from '../core/tool'
|
26
26
|
import { parseList, orderdListHandle, mediaHandle, tableHandle, preHandle, specialInblockHandle } from '../core/rule'
|
27
27
|
import { isTask, elementToParagraph, getCurrentParsedomElement, hasTableInRange, hasLinkInRange, hasPreInRange, hasImageInRange, hasVideoInRange } from '../core/function'
|
28
28
|
import Toolbar from '../components/toolbar/toolbar.vue'
|
@@ -124,9 +124,26 @@ const showBorder = computed<boolean>(() => {
|
|
124
124
|
const toolbarConfig = computed<ToolbarConfigType>(() => {
|
125
125
|
return <ToolbarConfigType>mergeObject(getToolbarConfig($editTrans, props.locale), props.toolbar || {})
|
126
126
|
})
|
127
|
+
//插件配置读取
|
128
|
+
const pluginResultList = computed<PluginResultType[]>(() => {
|
129
|
+
const pluginResultList: PluginResultType[] = []
|
130
|
+
props.plugins.forEach(plugin => {
|
131
|
+
let pluginResult = plugin(instance, props.color, $editTrans)
|
132
|
+
pluginResultList.push(pluginResult)
|
133
|
+
})
|
134
|
+
return pluginResultList
|
135
|
+
})
|
127
136
|
//最终生效的菜单栏配置
|
128
137
|
const menuConfig = computed<MenuConfigType>(() => {
|
129
|
-
|
138
|
+
let menu: MenuConfigType = {}
|
139
|
+
//注册插件:自定义菜单栏
|
140
|
+
pluginResultList.value.forEach(pluginResult => {
|
141
|
+
menu = <MenuConfigType>mergeObject(menu, pluginResult.menu || {})
|
142
|
+
})
|
143
|
+
//加入自定义menu配置
|
144
|
+
menu = <MenuConfigType>mergeObject(menu, props.menu || {})
|
145
|
+
//返回最终配置
|
146
|
+
return <MenuConfigType>mergeObject(getMenuConfig($editTrans, props.locale), menu)
|
130
147
|
})
|
131
148
|
|
132
149
|
//编辑器内部修改值的方法
|
@@ -235,6 +252,13 @@ const handleToolbar = () => {
|
|
235
252
|
}
|
236
253
|
//初始创建编辑器
|
237
254
|
const createEditor = () => {
|
255
|
+
//注册插件:自定义规则校验函数
|
256
|
+
let pluginRules: ((el: AlexElement) => void)[] = []
|
257
|
+
pluginResultList.value.forEach(pluginResult => {
|
258
|
+
if (pluginResult.renderRule) {
|
259
|
+
pluginRules.push(pluginResult.renderRule)
|
260
|
+
}
|
261
|
+
})
|
238
262
|
//创建编辑器
|
239
263
|
editor.value = new AlexEditor(contentRef.value!, {
|
240
264
|
value: value.value,
|
@@ -258,6 +282,7 @@ const createEditor = () => {
|
|
258
282
|
el => {
|
259
283
|
specialInblockHandle(editor.value!, el)
|
260
284
|
},
|
285
|
+
...pluginRules,
|
261
286
|
...props.renderRules
|
262
287
|
],
|
263
288
|
allowCopy: props.allowCopy,
|
@@ -418,8 +443,15 @@ const documentClick = (e: Event) => {
|
|
418
443
|
}
|
419
444
|
//重新定义编辑器粘贴html
|
420
445
|
const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
|
421
|
-
|
422
|
-
|
446
|
+
let keepStyles = pasteKeepData.styles
|
447
|
+
let keepMarks = pasteKeepData.marks
|
448
|
+
//注册插件:自定义html粘贴保留
|
449
|
+
pluginResultList.value.forEach(pluginResult => {
|
450
|
+
keepStyles = Object.assign(keepStyles, pluginResult.pasteKeepStyles || {})
|
451
|
+
keepMarks = Object.assign(keepMarks, pluginResult.pasteKeepMarks || {})
|
452
|
+
})
|
453
|
+
keepStyles = Object.assign(keepStyles, props.pasteKeepStyles || {})
|
454
|
+
keepMarks = Object.assign(keepMarks, props.pasteKeepMarks || {})
|
423
455
|
//粘贴html时过滤元素的样式和属性
|
424
456
|
AlexElement.flatElements(elements).forEach(el => {
|
425
457
|
let marks: ObjectType = {}
|
@@ -443,7 +475,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
|
|
443
475
|
})
|
444
476
|
//如果使用了自定义粘贴html的功能
|
445
477
|
if (typeof props.customHtmlPaste == 'function') {
|
446
|
-
await props.customHtmlPaste
|
478
|
+
await props.customHtmlPaste(elements)
|
447
479
|
}
|
448
480
|
//默认粘贴html
|
449
481
|
else {
|
@@ -478,8 +510,14 @@ const handleCustomParseNode = (ele: AlexElement) => {
|
|
478
510
|
ele.marks = marks
|
479
511
|
}
|
480
512
|
}
|
513
|
+
//注册插件:自定义元素转换处理
|
514
|
+
pluginResultList.value.forEach(pluginResult => {
|
515
|
+
if (pluginResult.customParseNode) {
|
516
|
+
ele = pluginResult.customParseNode(ele)
|
517
|
+
}
|
518
|
+
})
|
481
519
|
if (typeof props.customParseNode == 'function') {
|
482
|
-
ele = props.customParseNode
|
520
|
+
ele = props.customParseNode(ele)
|
483
521
|
}
|
484
522
|
return ele
|
485
523
|
}
|
@@ -655,6 +693,12 @@ const handleDeleteComplete = () => {
|
|
655
693
|
const handleAfterRender = () => {
|
656
694
|
//设定视频高度
|
657
695
|
setVideoHeight()
|
696
|
+
//注册插件:自定义dom渲染后处理
|
697
|
+
pluginResultList.value.forEach(pluginResult => {
|
698
|
+
if (pluginResult.updateView) {
|
699
|
+
pluginResult.updateView()
|
700
|
+
}
|
701
|
+
})
|
658
702
|
emits('updateview')
|
659
703
|
}
|
660
704
|
//api:光标设置到文档底部
|
package/src/editify/props.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { common as DapCommon } from 'dap-util'
|
2
2
|
import { ExtractPublicPropTypes, PropType } from 'vue'
|
3
|
-
import { MenuConfigType, ObjectType, ToolbarConfigType } from '../core/tool'
|
3
|
+
import { PluginType, MenuConfigType, ObjectType, ToolbarConfigType } from '../core/tool'
|
4
4
|
import { AlexElement } from 'alex-editor'
|
5
5
|
import { LocaleType } from '../locale'
|
6
6
|
|
@@ -68,7 +68,7 @@ export const EditifyProps = {
|
|
68
68
|
},
|
69
69
|
//主题色
|
70
70
|
color: {
|
71
|
-
type: String
|
71
|
+
type: String as PropType<string | null>,
|
72
72
|
default: '#03a8f3',
|
73
73
|
validator(value: any) {
|
74
74
|
return DapCommon.matchingText(value, 'hex')
|
@@ -150,6 +150,13 @@ export const EditifyProps = {
|
|
150
150
|
tab: {
|
151
151
|
type: Boolean,
|
152
152
|
default: true
|
153
|
+
},
|
154
|
+
//插件数组
|
155
|
+
plugins: {
|
156
|
+
type: Array as PropType<PluginType[]>,
|
157
|
+
default: function () {
|
158
|
+
return []
|
159
|
+
}
|
153
160
|
}
|
154
161
|
}
|
155
162
|
|
package/src/icon/iconfont.css
CHANGED
package/src/icon/iconfont.ttf
CHANGED
Binary file
|
package/src/icon/iconfont.woff
CHANGED
Binary file
|
package/src/index.ts
CHANGED
@@ -6,23 +6,28 @@ import Editify from './editify/editify.vue'
|
|
6
6
|
|
7
7
|
//导出类型
|
8
8
|
export type { ButtonTypeType, ButtonOptionsItemType, ButtonSelectConfigType, ButtonDisplayConfigType } from './components/button/props'
|
9
|
-
export type { MenuButtonType, MenuSelectButtonType, MenuDisplayButtonType, MenuImageButtonType, MenuVideoButtonType, MenuTableButtonType, MenuCustomButtonType, CodeBlockToolbarType, TextToolbarType, ToolbarConfigType, MenuSequenceType, MenuModeType, MenuConfigType } from './core/tool'
|
10
9
|
export type { InsertImageUploadErrorType } from './components/insertImage/props'
|
11
10
|
export type { InsertVideoUploadErrorType } from './components/insertVideo/props'
|
11
|
+
export type { MenuButtonType, MenuSelectButtonType, MenuDisplayButtonType, MenuImageButtonType, MenuVideoButtonType, MenuTableButtonType, MenuCustomButtonType, CodeBlockToolbarType, TextToolbarType, ToolbarConfigType, MenuSequenceType, MenuModeType, MenuExtendType, MenuConfigType, PluginType, PluginResultType } from './core/tool'
|
12
|
+
//插件相关类型
|
13
|
+
export type { AttachmentOptionsType } from './plugins/attachment'
|
14
|
+
export type { InsertAttachmentUploadErrorType } from './plugins/attachment/insertAttachment/props'
|
12
15
|
|
13
16
|
//导出编辑器操作方法
|
14
17
|
export { getParsedomElementByElement, getCurrentParsedomElement, elementIsInList, elementIsInTask, isList, isTask, hasPreInRange, isRangeInPre, hasQuoteInRange, isRangeInQuote, hasListInRange, isRangeInList, hasTaskInRange, isRangeInTask, hasLinkInRange, hasTableInRange, hasImageInRange, hasVideoInRange, queryTextStyle, queryTextMark, getRangeText, setIndentIncrease, setIndentDecrease, setQuote, setAlign, setList, setTask, setTextStyle, setTextMark, removeTextStyle, removeTextMark, setLineHeight, insertLink, insertImage, insertVideo, insertTable, insertCodeBlock } from './core/function'
|
15
18
|
|
16
|
-
//导出AlexElement元素
|
17
|
-
export { AlexElement } from 'alex-editor'
|
18
|
-
|
19
|
-
//导出版本号
|
20
|
-
export const version = '0.1.22'
|
21
|
-
|
22
19
|
//安装函数
|
23
20
|
const install: FunctionPlugin = (app: App) => {
|
24
21
|
app.component(Editify.name!, Editify)
|
25
22
|
}
|
23
|
+
//版本号
|
24
|
+
const version = '0.1.23'
|
25
|
+
|
26
|
+
//导出AlexElement元素
|
27
|
+
export { AlexElement } from 'alex-editor'
|
26
28
|
|
27
29
|
//导出组件和安装函数
|
28
|
-
export { install as default, install, Editify }
|
30
|
+
export { install as default, install, Editify, version }
|
31
|
+
|
32
|
+
//导出插件
|
33
|
+
export { attachment } from './plugins/attachment'
|
package/src/locale/en_US.ts
CHANGED
@@ -84,5 +84,13 @@ export const en_US: ObjectType = {
|
|
84
84
|
alignJustify: 'Align justify',
|
85
85
|
defaultLineHeight: 'Default',
|
86
86
|
auto: 'auto',
|
87
|
-
fullScreen: 'Full screen'
|
87
|
+
fullScreen: 'Full screen',
|
88
|
+
|
89
|
+
//插件语言配置
|
90
|
+
insertAttachment: 'Insert attachment',
|
91
|
+
uploadAttachment: 'Upload',
|
92
|
+
remoteAttachment: 'Remote',
|
93
|
+
attachmentUrlPlaceholder: 'Please enter the attachment address',
|
94
|
+
downloadAttachment: 'Click to download attachment',
|
95
|
+
attachmentDownloadName: 'attachment'
|
88
96
|
}
|