vue-editify 0.1.22 → 0.1.24
Sign up to get free protection for your applications and to get access to all the features.
- 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 +577 -182
- 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 +54 -10
- 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 +123 -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
|
}
|
@@ -646,15 +684,21 @@ const handleDeleteInStart = (element: AlexElement) => {
|
|
646
684
|
}
|
647
685
|
//编辑器删除完成后事件
|
648
686
|
const handleDeleteComplete = () => {
|
649
|
-
const uneditable = editor.value!.range!.anchor.element.getUneditableElement()
|
650
|
-
if (uneditable) {
|
651
|
-
|
652
|
-
}
|
687
|
+
// const uneditable = editor.value!.range!.anchor.element.getUneditableElement()
|
688
|
+
// if (uneditable) {
|
689
|
+
// uneditable.toEmpty()
|
690
|
+
// }
|
653
691
|
}
|
654
692
|
//编辑器dom渲染
|
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.24'
|
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'
|