@longhongguo/form-create-ant-design-vue 3.3.10 → 3.3.12
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 +22 -0
- package/dist/form-create.esm.css +22 -0
- 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/FcEditorWrapper.vue +94 -2
- package/src/core/manager.js +34 -0
- package/src/parsers/accTable.js +114 -0
- package/src/style/index.css +22 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longhongguo/form-create-ant-design-vue",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.12",
|
|
4
4
|
"description": "AntDesignVue版本低代码表单|FormCreate 是一个可以通过 JSON 生成具有动态渲染、数据收集、验证和提交功能的低代码表单生成组件。支持6个UI框架,适配移动端,并且支持生成任何 Vue 组件。内置20种常用表单组件和自定义组件,再复杂的表单都可以轻松搞定。",
|
|
5
5
|
"main": "./dist/form-create.min.js",
|
|
6
6
|
"module": "./dist/form-create.esm.js",
|
|
@@ -36,7 +36,15 @@ export default defineComponent({
|
|
|
36
36
|
uploadImgCustomInsert: [String, Function],
|
|
37
37
|
withCredentials: Boolean,
|
|
38
38
|
// form-create 注入的 API
|
|
39
|
-
formCreateInject: Object
|
|
39
|
+
formCreateInject: Object,
|
|
40
|
+
placeholder: {
|
|
41
|
+
type: String,
|
|
42
|
+
default: '请输入正文'
|
|
43
|
+
},
|
|
44
|
+
height: {
|
|
45
|
+
type: [String, Number],
|
|
46
|
+
default: 300
|
|
47
|
+
}
|
|
40
48
|
},
|
|
41
49
|
emits: ['update:modelValue'],
|
|
42
50
|
watch: {
|
|
@@ -51,6 +59,31 @@ export default defineComponent({
|
|
|
51
59
|
editorConfig() {
|
|
52
60
|
const config = {}
|
|
53
61
|
|
|
62
|
+
// 设置 placeholder
|
|
63
|
+
if (this.placeholder) {
|
|
64
|
+
config.placeholder = this.placeholder
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 设置高度
|
|
68
|
+
if (this.height) {
|
|
69
|
+
// 如果是数字,转换为像素字符串;如果是字符串,直接使用
|
|
70
|
+
config.height = this.height
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 排除不需要的工具栏菜单项
|
|
74
|
+
// 移除:全屏、代码、表情、删除线、缩进、待办事项、引用、分割线、斜体
|
|
75
|
+
config.excludeMenus = [
|
|
76
|
+
'fullScreen', // 全屏
|
|
77
|
+
'code', // 代码
|
|
78
|
+
'emoticon', // 表情
|
|
79
|
+
'strikeThrough', // 删除线
|
|
80
|
+
'indent', // 缩进
|
|
81
|
+
'todo', // 待办事项
|
|
82
|
+
'quote', // 引用
|
|
83
|
+
'splitLine', // 分割线
|
|
84
|
+
'italic' // 斜体
|
|
85
|
+
]
|
|
86
|
+
|
|
54
87
|
// 如果设置了 readOnly,配置只读模式
|
|
55
88
|
if (this.readOnly) {
|
|
56
89
|
// readOnly: true // 启用只读模式
|
|
@@ -61,6 +94,10 @@ export default defineComponent({
|
|
|
61
94
|
// 禁用自动聚焦,避免在只读模式下获得焦点
|
|
62
95
|
config.focus = false
|
|
63
96
|
config.autoFocus = false
|
|
97
|
+
// 只读模式下隐藏 placeholder
|
|
98
|
+
delete config.placeholder
|
|
99
|
+
// 只读模式下移除固定高度,使用自适应
|
|
100
|
+
delete config.height
|
|
64
101
|
}
|
|
65
102
|
|
|
66
103
|
// 如果设置了 uploadImgServer,配置图片上传
|
|
@@ -506,8 +543,16 @@ export default defineComponent({
|
|
|
506
543
|
}
|
|
507
544
|
|
|
508
545
|
this.$nextTick(() => {
|
|
546
|
+
// 查找编辑器的根元素
|
|
547
|
+
let editorRootElement = null
|
|
548
|
+
if (this._editor.id) {
|
|
549
|
+
editorRootElement = document.getElementById(this._editor.id)
|
|
550
|
+
}
|
|
551
|
+
|
|
509
552
|
// 查找编辑器的文本容器
|
|
510
553
|
let textContainer = null
|
|
554
|
+
let containerElement = null
|
|
555
|
+
|
|
511
556
|
if (this._editor.$textElem && this._editor.$textElem[0]) {
|
|
512
557
|
textContainer = this._editor.$textElem[0]
|
|
513
558
|
} else {
|
|
@@ -516,6 +561,7 @@ export default defineComponent({
|
|
|
516
561
|
const editorEl = document.getElementById(editorId)
|
|
517
562
|
if (editorEl) {
|
|
518
563
|
textContainer = editorEl.querySelector('.w-e-text')
|
|
564
|
+
containerElement = editorEl.querySelector('.w-e-text-container')
|
|
519
565
|
}
|
|
520
566
|
}
|
|
521
567
|
}
|
|
@@ -526,12 +572,43 @@ export default defineComponent({
|
|
|
526
572
|
this._editor.$textContainerElem &&
|
|
527
573
|
this._editor.$textContainerElem[0]
|
|
528
574
|
) {
|
|
529
|
-
|
|
575
|
+
containerElement = this._editor.$textContainerElem[0]
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// 查找容器元素(如果还没找到)
|
|
580
|
+
if (
|
|
581
|
+
!containerElement &&
|
|
582
|
+
this._editor.$textContainerElem &&
|
|
583
|
+
this._editor.$textContainerElem[0]
|
|
584
|
+
) {
|
|
585
|
+
containerElement = this._editor.$textContainerElem[0]
|
|
586
|
+
} else if (!containerElement && this._editor.id) {
|
|
587
|
+
const editorEl = document.getElementById(this._editor.id)
|
|
588
|
+
if (editorEl) {
|
|
589
|
+
containerElement = editorEl.querySelector('.w-e-text-container')
|
|
530
590
|
}
|
|
531
591
|
}
|
|
532
592
|
|
|
533
593
|
if (textContainer) {
|
|
534
594
|
if (readOnly) {
|
|
595
|
+
// 设置根元素为自适应高度(移除内联样式中的固定高度)
|
|
596
|
+
if (editorRootElement) {
|
|
597
|
+
editorRootElement.style.height = 'auto'
|
|
598
|
+
editorRootElement.style.minHeight = 'auto'
|
|
599
|
+
editorRootElement.style.maxHeight = 'none'
|
|
600
|
+
}
|
|
601
|
+
// 设置容器和文本区域为自适应高度
|
|
602
|
+
if (containerElement) {
|
|
603
|
+
containerElement.style.height = 'auto'
|
|
604
|
+
containerElement.style.minHeight = 'auto'
|
|
605
|
+
containerElement.style.maxHeight = 'none'
|
|
606
|
+
}
|
|
607
|
+
if (textContainer) {
|
|
608
|
+
textContainer.style.height = 'auto'
|
|
609
|
+
textContainer.style.minHeight = 'auto'
|
|
610
|
+
textContainer.style.maxHeight = 'none'
|
|
611
|
+
}
|
|
535
612
|
// 强制设置为只读:禁用编辑,但允许选择和点击链接
|
|
536
613
|
const forceReadOnly = () => {
|
|
537
614
|
// 强制设置 contenteditable
|
|
@@ -542,6 +619,21 @@ export default defineComponent({
|
|
|
542
619
|
textContainer.style.mozUserSelect = 'text'
|
|
543
620
|
textContainer.style.msUserSelect = 'text'
|
|
544
621
|
textContainer.style.cursor = 'text'
|
|
622
|
+
// 保持根元素自适应高度
|
|
623
|
+
if (editorRootElement) {
|
|
624
|
+
editorRootElement.style.height = 'auto'
|
|
625
|
+
editorRootElement.style.minHeight = 'auto'
|
|
626
|
+
editorRootElement.style.maxHeight = 'none'
|
|
627
|
+
}
|
|
628
|
+
// 保持容器和文本区域自适应高度
|
|
629
|
+
if (containerElement) {
|
|
630
|
+
containerElement.style.height = 'auto'
|
|
631
|
+
containerElement.style.minHeight = 'auto'
|
|
632
|
+
containerElement.style.maxHeight = 'none'
|
|
633
|
+
}
|
|
634
|
+
textContainer.style.height = 'auto'
|
|
635
|
+
textContainer.style.minHeight = 'auto'
|
|
636
|
+
textContainer.style.maxHeight = 'none'
|
|
545
637
|
}
|
|
546
638
|
|
|
547
639
|
forceReadOnly()
|
package/src/core/manager.js
CHANGED
|
@@ -105,6 +105,10 @@ export default {
|
|
|
105
105
|
// 只读模式允许复制和点击链接,但禁止编辑
|
|
106
106
|
if (ctx.prop.props) {
|
|
107
107
|
ctx.prop.props.readOnly = true
|
|
108
|
+
// 预览模式和只读模式下隐藏 placeholder
|
|
109
|
+
delete ctx.prop.props.placeholder
|
|
110
|
+
// 预览模式和只读模式下移除固定高度,使用自适应
|
|
111
|
+
delete ctx.prop.props.height
|
|
108
112
|
}
|
|
109
113
|
} else if (
|
|
110
114
|
ctx.rule.type === 'input' &&
|
|
@@ -118,6 +122,10 @@ export default {
|
|
|
118
122
|
ctx.prop.props.autoSize = true
|
|
119
123
|
// 移除 rows 属性,让 autoSize 完全控制高度
|
|
120
124
|
delete ctx.prop.props.rows
|
|
125
|
+
// 预览模式和只读模式下隐藏 placeholder
|
|
126
|
+
delete ctx.prop.props.placeholder
|
|
127
|
+
// 预览模式和只读模式下隐藏字符计数
|
|
128
|
+
ctx.prop.props.showCount = false
|
|
121
129
|
}
|
|
122
130
|
} else if (ctx.rule.type === 'select') {
|
|
123
131
|
// select 组件在预览模式下使用 disabled 实现只读效果
|
|
@@ -126,6 +134,8 @@ export default {
|
|
|
126
134
|
// 强制设置 disabled = true,覆盖用户可能设置的 disabled: false
|
|
127
135
|
if (ctx.prop.props) {
|
|
128
136
|
ctx.prop.props.disabled = true
|
|
137
|
+
// 预览模式和只读模式下隐藏 placeholder
|
|
138
|
+
delete ctx.prop.props.placeholder
|
|
129
139
|
// 如果原本设置了 readOnly,保留这个标记以便 CSS 识别
|
|
130
140
|
if (isReadOnly) {
|
|
131
141
|
// 可以在这里添加一个标记,但 Select 组件不支持 readOnly
|
|
@@ -137,6 +147,30 @@ export default {
|
|
|
137
147
|
// 如果设置了 readOnly,直接使用组件的 readOnly 属性
|
|
138
148
|
if (ctx.prop.props && isReadOnly) {
|
|
139
149
|
ctx.prop.props.readOnly = true
|
|
150
|
+
// 预览模式和只读模式下隐藏 placeholder
|
|
151
|
+
delete ctx.prop.props.placeholder
|
|
152
|
+
// 预览模式和只读模式下隐藏字符计数
|
|
153
|
+
ctx.prop.props.showCount = false
|
|
154
|
+
} else if (ctx.prop.props && isPreviewMode) {
|
|
155
|
+
// 全局预览模式下也隐藏 placeholder
|
|
156
|
+
delete ctx.prop.props.placeholder
|
|
157
|
+
// 全局预览模式下隐藏字符计数
|
|
158
|
+
ctx.prop.props.showCount = false
|
|
159
|
+
}
|
|
160
|
+
} else if (
|
|
161
|
+
ctx.rule.type === 'datePicker' ||
|
|
162
|
+
ctx.rule.type === 'timePicker' ||
|
|
163
|
+
ctx.rule.type === 'timeRangePicker' ||
|
|
164
|
+
ctx.rule.type === 'rangePicker' ||
|
|
165
|
+
ctx.rule.type === 'cascader'
|
|
166
|
+
) {
|
|
167
|
+
// 日期选择器、时间选择器、级联选择器等组件
|
|
168
|
+
// 预览模式和只读模式下隐藏 placeholder
|
|
169
|
+
if (ctx.prop.props) {
|
|
170
|
+
if (ctx.rule.type === 'cascader') {
|
|
171
|
+
ctx.prop.props.disabled = true
|
|
172
|
+
}
|
|
173
|
+
delete ctx.prop.props.placeholder
|
|
140
174
|
}
|
|
141
175
|
}
|
|
142
176
|
}
|
package/src/parsers/accTable.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hasProperty } from '@form-create/utils/lib/type'
|
|
2
2
|
import { deepCopy } from '@form-create/utils/lib/deepextend'
|
|
3
|
+
import { h } from 'vue'
|
|
3
4
|
|
|
4
5
|
// 简单的 getValue 函数,根据路径字符串获取嵌套对象的值
|
|
5
6
|
function getValue(obj, path) {
|
|
@@ -165,6 +166,119 @@ export default {
|
|
|
165
166
|
// 如果已经是数字,直接使用
|
|
166
167
|
}
|
|
167
168
|
|
|
169
|
+
// 处理单元格表单组件
|
|
170
|
+
if (
|
|
171
|
+
column.cellType &&
|
|
172
|
+
['input', 'select', 'radio'].includes(column.cellType)
|
|
173
|
+
) {
|
|
174
|
+
const cellType = column.cellType
|
|
175
|
+
const dataIndex = column.dataIndex
|
|
176
|
+
const cellOptions = column.cellOptions || []
|
|
177
|
+
const cellProps = column.cellProps || {}
|
|
178
|
+
|
|
179
|
+
// 创建 customRender 函数
|
|
180
|
+
column.customRender = ({ text, record, index }) => {
|
|
181
|
+
const cellValue = record && dataIndex ? record[dataIndex] : text
|
|
182
|
+
|
|
183
|
+
if (cellType === 'input') {
|
|
184
|
+
// 渲染 input 组件 - 使用组件名字符串
|
|
185
|
+
return h('a-input', {
|
|
186
|
+
value: cellValue,
|
|
187
|
+
onChange: (e) => {
|
|
188
|
+
const val =
|
|
189
|
+
e.target?.value !== undefined
|
|
190
|
+
? e.target.value
|
|
191
|
+
: e?.target?.inputValue !== undefined
|
|
192
|
+
? e.target.inputValue
|
|
193
|
+
: e
|
|
194
|
+
if (record && dataIndex) {
|
|
195
|
+
record[dataIndex] = val
|
|
196
|
+
// 触发表格数据更新
|
|
197
|
+
if (ctx.api && ctx.api.form && rule.field) {
|
|
198
|
+
ctx.api.form[rule.field] = props.dataSource
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
placeholder: cellProps.placeholder || '',
|
|
203
|
+
disabled: cellProps.disabled || false,
|
|
204
|
+
size: cellProps.size || 'small',
|
|
205
|
+
...cellProps
|
|
206
|
+
})
|
|
207
|
+
} else if (cellType === 'select') {
|
|
208
|
+
// 渲染 select 组件 - 使用组件名字符串
|
|
209
|
+
return h(
|
|
210
|
+
'a-select',
|
|
211
|
+
{
|
|
212
|
+
value: cellValue,
|
|
213
|
+
onChange: (val) => {
|
|
214
|
+
if (record && dataIndex) {
|
|
215
|
+
record[dataIndex] = val
|
|
216
|
+
// 触发表格数据更新
|
|
217
|
+
if (ctx.api && ctx.api.form && rule.field) {
|
|
218
|
+
ctx.api.form[rule.field] = props.dataSource
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
placeholder: cellProps.placeholder || '',
|
|
223
|
+
disabled: cellProps.disabled || false,
|
|
224
|
+
size: cellProps.size || 'small',
|
|
225
|
+
...cellProps
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
default: () =>
|
|
229
|
+
cellOptions.map((opt) => {
|
|
230
|
+
return h('a-select-option', {
|
|
231
|
+
key: opt.value,
|
|
232
|
+
value: opt.value,
|
|
233
|
+
label: opt.label
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
} else if (cellType === 'radio') {
|
|
239
|
+
// 渲染 radio 组件 - 使用组件名字符串
|
|
240
|
+
return h(
|
|
241
|
+
'a-radio-group',
|
|
242
|
+
{
|
|
243
|
+
value: cellValue,
|
|
244
|
+
onChange: (e) => {
|
|
245
|
+
const val =
|
|
246
|
+
e.target?.value !== undefined ? e.target.value : e
|
|
247
|
+
if (record && dataIndex) {
|
|
248
|
+
record[dataIndex] = val
|
|
249
|
+
// 触发表格数据更新
|
|
250
|
+
if (ctx.api && ctx.api.form && rule.field) {
|
|
251
|
+
ctx.api.form[rule.field] = props.dataSource
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
disabled: cellProps.disabled || false,
|
|
256
|
+
size: cellProps.size || 'small',
|
|
257
|
+
...cellProps
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
default: () =>
|
|
261
|
+
cellOptions.map((opt) => {
|
|
262
|
+
return h(
|
|
263
|
+
'a-radio',
|
|
264
|
+
{
|
|
265
|
+
key: opt.value,
|
|
266
|
+
value: opt.value
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
default: () => opt.label || opt.value
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 如果没有匹配的类型,返回原始文本
|
|
278
|
+
return text
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
168
282
|
// 确保其他列属性也被保留(align、fixed、title、dataIndex 等)
|
|
169
283
|
return column
|
|
170
284
|
})
|
package/src/style/index.css
CHANGED
|
@@ -420,6 +420,28 @@ textarea[readonly].ant-input {
|
|
|
420
420
|
max-height: none !important;
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
/* 只读模式:富文本编辑器样式(通过 contenteditable="false" 识别) */
|
|
424
|
+
.w-e-text[contenteditable='false'] {
|
|
425
|
+
border: none !important;
|
|
426
|
+
box-shadow: none !important;
|
|
427
|
+
background: transparent !important;
|
|
428
|
+
/* 移除高度限制,让内容自适应 */
|
|
429
|
+
height: auto !important;
|
|
430
|
+
min-height: auto !important;
|
|
431
|
+
max-height: none !important;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* 预览模式:编辑器根元素自适应高度 */
|
|
435
|
+
.form-create.is-preview div[id^='editor'][readonly='true'],
|
|
436
|
+
.form-create.is-preview div[id^='editor'][readonly] {
|
|
437
|
+
height: auto !important;
|
|
438
|
+
min-height: auto !important;
|
|
439
|
+
max-height: none !important;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* 只读模式下,通过 JavaScript 设置容器自适应高度 */
|
|
443
|
+
/* 容器样式会在 setReadOnlyMode 中通过 JavaScript 动态设置 */
|
|
444
|
+
|
|
423
445
|
/* 富文本内容只读:允许选择文本和点击链接,但禁止编辑 */
|
|
424
446
|
.form-create.is-preview .w-e-text p,
|
|
425
447
|
.form-create.is-preview .w-e-text div,
|