@longhongguo/form-create-ant-design-vue 3.2.43 → 3.2.45
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/dist/form-create.css +274 -10
- package/dist/form-create.esm.css +274 -10
- package/dist/form-create.esm.js +2 -2
- package/dist/form-create.esm.js.map +1 -1
- package/dist/form-create.js +2 -2
- package/dist/form-create.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CusSelect/index.vue +336 -0
- package/src/components/CusStoreSelect/index.vue +91 -0
- package/src/components/FcEditorWrapper.vue +178 -0
- package/src/components/index.js +11 -1
- package/src/core/api.js +0 -1
- package/src/core/maker.js +6 -0
- package/src/core/manager.js +442 -442
- package/src/core/modelFields.js +10 -9
- package/src/parsers/cusStoreSelect.js +17 -0
- package/src/parsers/index.js +3 -1
- package/src/style/index.css +274 -10
- package/types/maker.d.ts +3 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- 单个展示模式 -->
|
|
3
|
+
<div
|
|
4
|
+
v-if="!multiple"
|
|
5
|
+
class="fc-cus-select fc-cus-select-single"
|
|
6
|
+
:class="{
|
|
7
|
+
'fc-cus-select-disabled': disabled
|
|
8
|
+
}"
|
|
9
|
+
:style="style"
|
|
10
|
+
:tabindex="disabled ? -1 : 0"
|
|
11
|
+
>
|
|
12
|
+
<div class="fc-cus-select-selector">
|
|
13
|
+
<span
|
|
14
|
+
v-if="displayValue"
|
|
15
|
+
class="fc-cus-select-selection-item"
|
|
16
|
+
:title="displayLabel"
|
|
17
|
+
>
|
|
18
|
+
{{ displayLabel }}
|
|
19
|
+
</span>
|
|
20
|
+
<span v-else class="fc-cus-select-selection-placeholder">
|
|
21
|
+
{{ placeholder || '请选择' }}
|
|
22
|
+
</span>
|
|
23
|
+
<span v-if="showClear" class="fc-cus-select-clear" @click="clearValue">
|
|
24
|
+
<span
|
|
25
|
+
role="img"
|
|
26
|
+
aria-label="close-circle"
|
|
27
|
+
class="anticon anticon-close-circle"
|
|
28
|
+
>
|
|
29
|
+
<svg
|
|
30
|
+
focusable="false"
|
|
31
|
+
data-icon="close-circle"
|
|
32
|
+
width="1em"
|
|
33
|
+
height="1em"
|
|
34
|
+
fill="currentColor"
|
|
35
|
+
aria-hidden="true"
|
|
36
|
+
fill-rule="evenodd"
|
|
37
|
+
viewBox="64 64 896 896"
|
|
38
|
+
>
|
|
39
|
+
<path
|
|
40
|
+
d="M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm127.98 274.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"
|
|
41
|
+
></path>
|
|
42
|
+
</svg>
|
|
43
|
+
</span>
|
|
44
|
+
</span>
|
|
45
|
+
<span v-else class="fc-cus-select-arrow">
|
|
46
|
+
<span role="img" aria-label="down" class="anticon anticon-down">
|
|
47
|
+
<svg
|
|
48
|
+
focusable="false"
|
|
49
|
+
data-icon="down"
|
|
50
|
+
width="1em"
|
|
51
|
+
height="1em"
|
|
52
|
+
fill="currentColor"
|
|
53
|
+
aria-hidden="true"
|
|
54
|
+
viewBox="64 64 896 896"
|
|
55
|
+
>
|
|
56
|
+
<path
|
|
57
|
+
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
|
58
|
+
></path>
|
|
59
|
+
</svg>
|
|
60
|
+
</span>
|
|
61
|
+
</span>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- 多个展示模式 -->
|
|
66
|
+
<div
|
|
67
|
+
v-else
|
|
68
|
+
class="fc-cus-select fc-cus-select-multiple"
|
|
69
|
+
:class="{
|
|
70
|
+
'fc-cus-select-disabled': disabled
|
|
71
|
+
}"
|
|
72
|
+
:style="style"
|
|
73
|
+
:tabindex="disabled ? -1 : 0"
|
|
74
|
+
>
|
|
75
|
+
<div class="fc-cus-select-selector">
|
|
76
|
+
<div class="fc-cus-select-selection-overflow">
|
|
77
|
+
<!-- 显示的 tags -->
|
|
78
|
+
<div
|
|
79
|
+
v-for="(item, index) in displayItems"
|
|
80
|
+
:key="index"
|
|
81
|
+
class="fc-cus-select-selection-overflow-item"
|
|
82
|
+
style="opacity: 1"
|
|
83
|
+
>
|
|
84
|
+
<span class="fc-cus-select-selection-item" :title="item.label">
|
|
85
|
+
<span class="fc-cus-select-selection-item-content">{{
|
|
86
|
+
item.label
|
|
87
|
+
}}</span>
|
|
88
|
+
<span
|
|
89
|
+
class="fc-cus-select-selection-item-remove"
|
|
90
|
+
@click="removeItem(item.value, $event)"
|
|
91
|
+
>
|
|
92
|
+
<span role="img" aria-label="close" class="anticon anticon-close">
|
|
93
|
+
<svg
|
|
94
|
+
focusable="false"
|
|
95
|
+
data-icon="close"
|
|
96
|
+
width="1em"
|
|
97
|
+
height="1em"
|
|
98
|
+
fill="currentColor"
|
|
99
|
+
aria-hidden="true"
|
|
100
|
+
fill-rule="evenodd"
|
|
101
|
+
viewBox="64 64 896 896"
|
|
102
|
+
>
|
|
103
|
+
<path
|
|
104
|
+
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
|
|
105
|
+
></path>
|
|
106
|
+
</svg>
|
|
107
|
+
</span>
|
|
108
|
+
</span>
|
|
109
|
+
</span>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<!-- 折叠提示 -->
|
|
113
|
+
<div
|
|
114
|
+
v-if="remainingCount > 0"
|
|
115
|
+
class="fc-cus-select-selection-overflow-item"
|
|
116
|
+
style="opacity: 1"
|
|
117
|
+
>
|
|
118
|
+
<span class="fc-cus-select-selection-item">
|
|
119
|
+
<span class="fc-cus-select-selection-item-content">
|
|
120
|
+
+{{ remainingCount }}
|
|
121
|
+
</span>
|
|
122
|
+
</span>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<!-- 空值占位符 -->
|
|
126
|
+
<span v-if="!hasValue" class="fc-cus-select-selection-placeholder">
|
|
127
|
+
{{ placeholder || '请选择' }}
|
|
128
|
+
</span>
|
|
129
|
+
</div>
|
|
130
|
+
<span v-if="showClear" class="fc-cus-select-clear" @click="clearValue">
|
|
131
|
+
<span
|
|
132
|
+
role="img"
|
|
133
|
+
aria-label="close-circle"
|
|
134
|
+
class="anticon anticon-close-circle"
|
|
135
|
+
>
|
|
136
|
+
<svg
|
|
137
|
+
focusable="false"
|
|
138
|
+
data-icon="close-circle"
|
|
139
|
+
width="1em"
|
|
140
|
+
height="1em"
|
|
141
|
+
fill="currentColor"
|
|
142
|
+
aria-hidden="true"
|
|
143
|
+
fill-rule="evenodd"
|
|
144
|
+
viewBox="64 64 896 896"
|
|
145
|
+
>
|
|
146
|
+
<path
|
|
147
|
+
d="M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm127.98 274.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"
|
|
148
|
+
></path>
|
|
149
|
+
</svg>
|
|
150
|
+
</span>
|
|
151
|
+
</span>
|
|
152
|
+
<span v-else class="fc-cus-select-arrow">
|
|
153
|
+
<span role="img" aria-label="down" class="anticon anticon-down">
|
|
154
|
+
<svg
|
|
155
|
+
focusable="false"
|
|
156
|
+
data-icon="down"
|
|
157
|
+
width="1em"
|
|
158
|
+
height="1em"
|
|
159
|
+
fill="currentColor"
|
|
160
|
+
aria-hidden="true"
|
|
161
|
+
viewBox="64 64 896 896"
|
|
162
|
+
>
|
|
163
|
+
<path
|
|
164
|
+
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
|
165
|
+
></path>
|
|
166
|
+
</svg>
|
|
167
|
+
</span>
|
|
168
|
+
</span>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</template>
|
|
172
|
+
|
|
173
|
+
<script>
|
|
174
|
+
import { defineComponent } from 'vue'
|
|
175
|
+
|
|
176
|
+
export default defineComponent({
|
|
177
|
+
name: 'CusSelect',
|
|
178
|
+
props: {
|
|
179
|
+
// 当前值:单个时为字符串/数字,多个时为数组(支持 v-model)
|
|
180
|
+
modelValue: {
|
|
181
|
+
type: [String, Number, Array],
|
|
182
|
+
default: null
|
|
183
|
+
},
|
|
184
|
+
// 选项列表
|
|
185
|
+
options: {
|
|
186
|
+
type: Array,
|
|
187
|
+
default: () => []
|
|
188
|
+
},
|
|
189
|
+
// 是否多选
|
|
190
|
+
multiple: {
|
|
191
|
+
type: Boolean,
|
|
192
|
+
default: false
|
|
193
|
+
},
|
|
194
|
+
// 最多显示的 tag 数量(多选模式下有效)
|
|
195
|
+
maxTagCount: {
|
|
196
|
+
type: Number,
|
|
197
|
+
default: undefined // 不限制时显示所有
|
|
198
|
+
},
|
|
199
|
+
// 占位符
|
|
200
|
+
placeholder: {
|
|
201
|
+
type: String,
|
|
202
|
+
default: ''
|
|
203
|
+
},
|
|
204
|
+
// 是否禁用样式
|
|
205
|
+
disabled: {
|
|
206
|
+
type: Boolean,
|
|
207
|
+
default: false
|
|
208
|
+
},
|
|
209
|
+
// 自定义样式
|
|
210
|
+
style: {
|
|
211
|
+
type: [String, Object],
|
|
212
|
+
default: () => ({ width: '100%' })
|
|
213
|
+
},
|
|
214
|
+
// 选项的 value 字段名
|
|
215
|
+
valueKey: {
|
|
216
|
+
type: String,
|
|
217
|
+
default: 'value'
|
|
218
|
+
},
|
|
219
|
+
// 选项的 label 字段名
|
|
220
|
+
labelKey: {
|
|
221
|
+
type: String,
|
|
222
|
+
default: 'label'
|
|
223
|
+
},
|
|
224
|
+
// 是否允许清除
|
|
225
|
+
allowClear: {
|
|
226
|
+
type: Boolean,
|
|
227
|
+
default: false
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
emits: ['update:modelValue', 'change'],
|
|
231
|
+
computed: {
|
|
232
|
+
// 统一处理值(支持 v-model)
|
|
233
|
+
currentValue: {
|
|
234
|
+
get() {
|
|
235
|
+
return this.modelValue
|
|
236
|
+
},
|
|
237
|
+
set(val) {
|
|
238
|
+
// 触发 update:modelValue 事件以支持 v-model
|
|
239
|
+
this.$emit('update:modelValue', val)
|
|
240
|
+
this.$emit('change', val)
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
// 是否有值
|
|
244
|
+
hasValue() {
|
|
245
|
+
const val = this.currentValue
|
|
246
|
+
if (this.multiple) {
|
|
247
|
+
return Array.isArray(val) && val.length > 0
|
|
248
|
+
} else {
|
|
249
|
+
return val !== null && val !== undefined && val !== ''
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
// 单个模式:显示的值
|
|
253
|
+
displayValue() {
|
|
254
|
+
if (!this.hasValue) return null
|
|
255
|
+
return this.currentValue
|
|
256
|
+
},
|
|
257
|
+
// 单个模式:显示的标签
|
|
258
|
+
displayLabel() {
|
|
259
|
+
if (!this.displayValue) return ''
|
|
260
|
+
return this.getLabel(this.displayValue)
|
|
261
|
+
},
|
|
262
|
+
// 多个模式:所有选中的项(包含 label 的对象数组)
|
|
263
|
+
allSelectedItems() {
|
|
264
|
+
const val = this.currentValue
|
|
265
|
+
if (!this.multiple || !Array.isArray(val) || val.length === 0) {
|
|
266
|
+
return []
|
|
267
|
+
}
|
|
268
|
+
return val.map((v) => ({
|
|
269
|
+
value: v,
|
|
270
|
+
label: this.getLabel(v)
|
|
271
|
+
}))
|
|
272
|
+
},
|
|
273
|
+
// 多个模式:显示的项(根据 maxTagCount 限制)
|
|
274
|
+
displayItems() {
|
|
275
|
+
const items = this.allSelectedItems
|
|
276
|
+
if (this.maxTagCount === undefined || this.maxTagCount === null) {
|
|
277
|
+
return items
|
|
278
|
+
}
|
|
279
|
+
return items.slice(0, this.maxTagCount)
|
|
280
|
+
},
|
|
281
|
+
// 多个模式:剩余未显示的项数量
|
|
282
|
+
remainingCount() {
|
|
283
|
+
if (this.maxTagCount === undefined || this.maxTagCount === null) {
|
|
284
|
+
return 0
|
|
285
|
+
}
|
|
286
|
+
const total = this.allSelectedItems.length
|
|
287
|
+
return Math.max(0, total - this.maxTagCount)
|
|
288
|
+
},
|
|
289
|
+
// 是否显示清除图标
|
|
290
|
+
showClear() {
|
|
291
|
+
return this.allowClear && this.hasValue && !this.disabled
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
methods: {
|
|
295
|
+
// 根据 value 查找对应的 option
|
|
296
|
+
findOptionByValue(val) {
|
|
297
|
+
if (!this.options || this.options.length === 0) {
|
|
298
|
+
return null
|
|
299
|
+
}
|
|
300
|
+
return this.options.find((opt) => {
|
|
301
|
+
const optValue = typeof opt === 'object' ? opt[this.valueKey] : opt
|
|
302
|
+
return optValue === val || String(optValue) === String(val)
|
|
303
|
+
})
|
|
304
|
+
},
|
|
305
|
+
// 获取显示的 label
|
|
306
|
+
getLabel(val) {
|
|
307
|
+
const option = this.findOptionByValue(val)
|
|
308
|
+
if (option) {
|
|
309
|
+
return typeof option === 'object' ? option[this.labelKey] : option
|
|
310
|
+
}
|
|
311
|
+
// 如果找不到对应的 option,直接显示 value
|
|
312
|
+
return String(val)
|
|
313
|
+
},
|
|
314
|
+
// 删除单个项(多选模式)
|
|
315
|
+
removeItem(itemValue, event) {
|
|
316
|
+
if (this.disabled) return
|
|
317
|
+
event.stopPropagation()
|
|
318
|
+
const val = this.currentValue
|
|
319
|
+
if (this.multiple && Array.isArray(val)) {
|
|
320
|
+
const newValue = val.filter((v) => v !== itemValue)
|
|
321
|
+
this.currentValue = newValue
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
// 清除所有值
|
|
325
|
+
clearValue(event) {
|
|
326
|
+
if (this.disabled) return
|
|
327
|
+
event.stopPropagation()
|
|
328
|
+
if (this.multiple) {
|
|
329
|
+
this.currentValue = []
|
|
330
|
+
} else {
|
|
331
|
+
this.currentValue = null
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
</script>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<CusSelect
|
|
4
|
+
:model-value="modelValue"
|
|
5
|
+
:options="options"
|
|
6
|
+
:multiple="multiple"
|
|
7
|
+
:max-tag-count="maxTagCount"
|
|
8
|
+
:placeholder="placeholder"
|
|
9
|
+
:disabled="disabled"
|
|
10
|
+
:style="style"
|
|
11
|
+
:valueKey="valueKey"
|
|
12
|
+
:labelKey="labelKey"
|
|
13
|
+
:allowClear="allowClear"
|
|
14
|
+
@update:model-value="handleUpdate"
|
|
15
|
+
@change="handleChange"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
import { defineComponent } from 'vue'
|
|
22
|
+
import CusSelect from '../CusSelect/index.vue'
|
|
23
|
+
|
|
24
|
+
export default defineComponent({
|
|
25
|
+
name: 'CusStoreSelect',
|
|
26
|
+
components: {
|
|
27
|
+
CusSelect
|
|
28
|
+
},
|
|
29
|
+
props: {
|
|
30
|
+
// 当前值:单个时为字符串/数字,多个时为数组(支持 v-model)
|
|
31
|
+
modelValue: {
|
|
32
|
+
type: [String, Number, Array],
|
|
33
|
+
default: null
|
|
34
|
+
},
|
|
35
|
+
// 选项列表
|
|
36
|
+
options: {
|
|
37
|
+
type: Array,
|
|
38
|
+
default: () => []
|
|
39
|
+
},
|
|
40
|
+
// 是否多选
|
|
41
|
+
multiple: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
default: false
|
|
44
|
+
},
|
|
45
|
+
// 最多显示的 tag 数量(多选模式下有效)
|
|
46
|
+
maxTagCount: {
|
|
47
|
+
type: Number,
|
|
48
|
+
default: undefined // 不限制时显示所有
|
|
49
|
+
},
|
|
50
|
+
// 占位符
|
|
51
|
+
placeholder: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: '请选择'
|
|
54
|
+
},
|
|
55
|
+
// 是否禁用样式
|
|
56
|
+
disabled: {
|
|
57
|
+
type: Boolean,
|
|
58
|
+
default: false
|
|
59
|
+
},
|
|
60
|
+
// 自定义样式
|
|
61
|
+
style: {
|
|
62
|
+
type: [String, Object],
|
|
63
|
+
default: () => ({ width: '100%' })
|
|
64
|
+
},
|
|
65
|
+
// 选项的 value 字段名
|
|
66
|
+
valueKey: {
|
|
67
|
+
type: String,
|
|
68
|
+
default: 'value'
|
|
69
|
+
},
|
|
70
|
+
// 选项的 label 字段名
|
|
71
|
+
labelKey: {
|
|
72
|
+
type: String,
|
|
73
|
+
default: 'label'
|
|
74
|
+
},
|
|
75
|
+
// 是否允许清除
|
|
76
|
+
allowClear: {
|
|
77
|
+
type: Boolean,
|
|
78
|
+
default: false
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
emits: ['update:modelValue', 'change'],
|
|
82
|
+
methods: {
|
|
83
|
+
handleUpdate(value) {
|
|
84
|
+
this.$emit('update:modelValue', value)
|
|
85
|
+
},
|
|
86
|
+
handleChange(value) {
|
|
87
|
+
this.$emit('change', value)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
</script>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FcEditor
|
|
3
|
+
:model-value="modelValue"
|
|
4
|
+
:disabled="disabled"
|
|
5
|
+
:config="editorConfig"
|
|
6
|
+
:init="initEditor"
|
|
7
|
+
@update:model-value="$emit('update:modelValue', $event)"
|
|
8
|
+
/>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
import { defineComponent } from 'vue'
|
|
13
|
+
import FcEditor from '@form-create/component-wangeditor/src'
|
|
14
|
+
import { parseFn } from '@form-create/utils/lib/json'
|
|
15
|
+
|
|
16
|
+
export default defineComponent({
|
|
17
|
+
name: 'FcEditorWrapper',
|
|
18
|
+
components: {
|
|
19
|
+
FcEditor
|
|
20
|
+
},
|
|
21
|
+
props: {
|
|
22
|
+
modelValue: String,
|
|
23
|
+
disabled: Boolean,
|
|
24
|
+
// 图片上传相关配置
|
|
25
|
+
uploadImgServer: String,
|
|
26
|
+
uploadImgFieldName: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: 'file'
|
|
29
|
+
},
|
|
30
|
+
uploadImgMaxSize: Number,
|
|
31
|
+
accept: String,
|
|
32
|
+
uploadImgHeaders: Object,
|
|
33
|
+
uploadImgParams: Object,
|
|
34
|
+
uploadImgCustomInsert: [String, Function],
|
|
35
|
+
withCredentials: Boolean,
|
|
36
|
+
// form-create 注入的 API
|
|
37
|
+
formCreateInject: Object
|
|
38
|
+
},
|
|
39
|
+
emits: ['update:modelValue'],
|
|
40
|
+
computed: {
|
|
41
|
+
editorConfig() {
|
|
42
|
+
const config = {}
|
|
43
|
+
|
|
44
|
+
// 如果设置了 uploadImgServer,配置图片上传
|
|
45
|
+
if (this.uploadImgServer) {
|
|
46
|
+
// wangeditor 4.x 使用这些配置项
|
|
47
|
+
config.uploadImgServer = this.uploadImgServer
|
|
48
|
+
config.uploadFileName = this.uploadImgFieldName || 'file'
|
|
49
|
+
config.uploadImgMaxSize = this.uploadImgMaxSize || 5 * 1024 * 1024 // 默认5MB
|
|
50
|
+
|
|
51
|
+
// 处理 accept 格式
|
|
52
|
+
if (this.accept) {
|
|
53
|
+
if (this.accept === 'image/*') {
|
|
54
|
+
config.uploadImgAccept = [
|
|
55
|
+
'jpg',
|
|
56
|
+
'jpeg',
|
|
57
|
+
'png',
|
|
58
|
+
'gif',
|
|
59
|
+
'bmp',
|
|
60
|
+
'webp'
|
|
61
|
+
]
|
|
62
|
+
} else {
|
|
63
|
+
// 处理类似 "image/png,image/jpeg" 的格式
|
|
64
|
+
const types = this.accept
|
|
65
|
+
.split(',')
|
|
66
|
+
.map((t) => {
|
|
67
|
+
const match = t.trim().match(/image\/(\w+)/)
|
|
68
|
+
return match ? match[1] : null
|
|
69
|
+
})
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
if (types.length > 0) {
|
|
72
|
+
config.uploadImgAccept = types
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 上传参数
|
|
78
|
+
if (this.uploadImgParams) {
|
|
79
|
+
config.uploadImgParams = this.uploadImgParams
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 请求头(wangeditor 4.x 可能不支持,但可以通过 customUploadImg 实现)
|
|
83
|
+
// withCredentials
|
|
84
|
+
if (this.withCredentials !== undefined) {
|
|
85
|
+
config.uploadImgParamsWithUrl = false // 参数放在 body 中
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 自定义上传函数 - 使用封装好的 api.request 方法
|
|
89
|
+
config.customUploadImg = (files, insertImgFn) => {
|
|
90
|
+
// files 是文件列表,insertImgFn 是插入函数
|
|
91
|
+
const file = files[0]
|
|
92
|
+
|
|
93
|
+
// 获取 form-create 注入的 API
|
|
94
|
+
const api = this.formCreateInject?.api
|
|
95
|
+
if (!api || !api.request) {
|
|
96
|
+
console.error('未找到 form-create API,无法使用封装的上传接口')
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 准备上传数据对象
|
|
101
|
+
const uploadData = {
|
|
102
|
+
// 文件字段
|
|
103
|
+
[this.uploadImgFieldName || 'file']: file
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 添加额外参数
|
|
107
|
+
if (this.uploadImgParams) {
|
|
108
|
+
Object.keys(this.uploadImgParams).forEach((key) => {
|
|
109
|
+
uploadData[key] = this.uploadImgParams[key]
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 使用封装好的 api.request 方法上传
|
|
114
|
+
// fetch.js 会自动将 data 转换为 FormData(当 dataType 不是 'json' 时)
|
|
115
|
+
api
|
|
116
|
+
.request({
|
|
117
|
+
action: this.uploadImgServer,
|
|
118
|
+
method: 'post',
|
|
119
|
+
data: uploadData,
|
|
120
|
+
dataType: 'form', // 使用表单格式,会自动创建 FormData
|
|
121
|
+
headers: this.uploadImgHeaders || {},
|
|
122
|
+
withCredentials: this.withCredentials || false
|
|
123
|
+
})
|
|
124
|
+
.then((res) => {
|
|
125
|
+
// 如果有自定义插入函数,使用它
|
|
126
|
+
if (this.uploadImgCustomInsert) {
|
|
127
|
+
const customInsert =
|
|
128
|
+
typeof this.uploadImgCustomInsert === 'function'
|
|
129
|
+
? this.uploadImgCustomInsert
|
|
130
|
+
: parseFn(this.uploadImgCustomInsert)
|
|
131
|
+
|
|
132
|
+
if (customInsert) {
|
|
133
|
+
// 调用自定义插入函数处理响应
|
|
134
|
+
// customInsert 接收 (res, insertFn) 参数
|
|
135
|
+
// insertFn 接收 (url, alt, href) 参数
|
|
136
|
+
customInsert(res, (url, alt, href) => {
|
|
137
|
+
insertImgFn(url, alt || '', href || '')
|
|
138
|
+
})
|
|
139
|
+
} else {
|
|
140
|
+
// 如果解析失败,使用默认格式
|
|
141
|
+
// this.handleDefaultResponse(res, insertImgFn)
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
// 使用默认响应格式处理
|
|
145
|
+
// this.handleDefaultResponse(res, insertImgFn)
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
.catch((error) => {
|
|
149
|
+
console.error('上传失败', error)
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return config
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
methods: {
|
|
158
|
+
initEditor(editor) {
|
|
159
|
+
// 可以在这里添加额外的编辑器初始化逻辑
|
|
160
|
+
},
|
|
161
|
+
handleDefaultResponse(res, insertImgFn) {
|
|
162
|
+
// 处理标准响应格式: {errno: 0, data: {url: "...", alt: "...", href: "..."}}
|
|
163
|
+
if (res.code === 200 && res.data) {
|
|
164
|
+
insertImgFn(
|
|
165
|
+
res.data.realURL,
|
|
166
|
+
res.data.alt || '图片',
|
|
167
|
+
res.data.realURL || ''
|
|
168
|
+
)
|
|
169
|
+
} else if (res.url) {
|
|
170
|
+
// 兼容简单格式: {url: "..."}
|
|
171
|
+
insertImgFn(res.url, res.alt || '', res.href || '')
|
|
172
|
+
} else {
|
|
173
|
+
console.error('上传失败,响应格式不正确', res)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
</script>
|
package/src/components/index.js
CHANGED
|
@@ -3,5 +3,15 @@ import frame from '@form-create/component-antdv-frame/src/index'
|
|
|
3
3
|
import group from '@form-create/component-antdv-group/src/index'
|
|
4
4
|
import subForm from '@form-create/component-subform/src/index'
|
|
5
5
|
import QuestionCircleOutlined from './icon/QuestionCircleOutlined.vue'
|
|
6
|
+
import CusSelect from './CusSelect/index.vue'
|
|
7
|
+
import CusStoreSelect from './CusStoreSelect/index.vue'
|
|
6
8
|
|
|
7
|
-
export default [
|
|
9
|
+
export default [
|
|
10
|
+
upload,
|
|
11
|
+
frame,
|
|
12
|
+
group,
|
|
13
|
+
subForm,
|
|
14
|
+
QuestionCircleOutlined,
|
|
15
|
+
CusSelect,
|
|
16
|
+
CusStoreSelect
|
|
17
|
+
]
|
package/src/core/api.js
CHANGED
package/src/core/maker.js
CHANGED
|
@@ -84,10 +84,16 @@ function useUpload(maker) {
|
|
|
84
84
|
maker.uploadFile = maker.file
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function useCusStoreSelect(maker) {
|
|
88
|
+
maker.cusStoreSelect = creatorFactory('cusStoreSelect')
|
|
89
|
+
maker.storeSelect = maker.cusStoreSelect
|
|
90
|
+
}
|
|
91
|
+
|
|
87
92
|
useAlias(maker)
|
|
88
93
|
useSlider(maker)
|
|
89
94
|
useFrame(maker)
|
|
90
95
|
useUpload(maker)
|
|
91
96
|
useSelect(maker)
|
|
97
|
+
useCusStoreSelect(maker)
|
|
92
98
|
|
|
93
99
|
export default maker
|