@longhongguo/form-create-ant-design-vue 3.3.25 → 3.3.28
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 +15 -0
- package/dist/form-create.esm.css +15 -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 +174 -103
- package/src/components/tableForm/TableForm.vue +211 -3
- package/src/style/index.css +15 -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.28",
|
|
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",
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
<div class="fc-editor-wrapper">
|
|
3
|
+
<!-- 只读/预览模式:直接用 v-html 渲染,并添加图片点击监听 -->
|
|
4
|
+
<div
|
|
5
|
+
v-if="readOnly"
|
|
6
|
+
ref="readOnlyContainer"
|
|
7
|
+
class="fc-editor-readonly"
|
|
8
|
+
v-html="modelValue"
|
|
9
|
+
></div>
|
|
10
|
+
<!-- 编辑模式:使用富文本编辑器 -->
|
|
11
|
+
<FcEditor
|
|
12
|
+
v-else
|
|
13
|
+
:model-value="modelValue"
|
|
14
|
+
:disabled="disabled"
|
|
15
|
+
:config="editorConfig"
|
|
16
|
+
:init="initEditor"
|
|
17
|
+
@update:model-value="$emit('update:modelValue', $event)"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
10
20
|
</template>
|
|
11
21
|
|
|
12
22
|
<script>
|
|
@@ -49,16 +59,33 @@ export default defineComponent({
|
|
|
49
59
|
emits: ['update:modelValue'],
|
|
50
60
|
watch: {
|
|
51
61
|
readOnly(newVal) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.setReadOnlyMode(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
if (!newVal && this._editor) {
|
|
63
|
+
// 切换到编辑模式
|
|
64
|
+
this.setReadOnlyMode(false)
|
|
65
|
+
} else if (newVal) {
|
|
66
|
+
// 切换到只读模式,设置图片点击监听
|
|
67
|
+
this.$nextTick(() => {
|
|
68
|
+
this.setupImageClickHandlers()
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
modelValue() {
|
|
73
|
+
// 内容变化时,重新设置图片点击监听(仅只读模式)
|
|
74
|
+
if (this.readOnly) {
|
|
75
|
+
this.$nextTick(() => {
|
|
76
|
+
this.setupImageClickHandlers()
|
|
77
|
+
})
|
|
59
78
|
}
|
|
60
79
|
}
|
|
61
80
|
},
|
|
81
|
+
mounted() {
|
|
82
|
+
// 如果是只读模式,设置图片点击监听
|
|
83
|
+
if (this.readOnly) {
|
|
84
|
+
this.$nextTick(() => {
|
|
85
|
+
this.setupImageClickHandlers()
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
},
|
|
62
89
|
computed: {
|
|
63
90
|
editorConfig() {
|
|
64
91
|
const config = {}
|
|
@@ -217,7 +244,20 @@ export default defineComponent({
|
|
|
217
244
|
return config
|
|
218
245
|
}
|
|
219
246
|
},
|
|
247
|
+
mounted() {
|
|
248
|
+
// 如果是只读模式,设置图片点击监听
|
|
249
|
+
if (this.readOnly) {
|
|
250
|
+
this.$nextTick(() => {
|
|
251
|
+
this.setupImageClickHandlers()
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
},
|
|
220
255
|
beforeUnmount() {
|
|
256
|
+
// 清理图片点击监听器
|
|
257
|
+
if (this._imageClickCleanup) {
|
|
258
|
+
this._imageClickCleanup()
|
|
259
|
+
this._imageClickCleanup = null
|
|
260
|
+
}
|
|
221
261
|
// 清理内容变化事件监听器
|
|
222
262
|
if (this._editor && this._editor._fcChangeHandlers) {
|
|
223
263
|
this._editor._fcChangeHandlers.forEach((item) => {
|
|
@@ -346,6 +386,98 @@ export default defineComponent({
|
|
|
346
386
|
}
|
|
347
387
|
},
|
|
348
388
|
methods: {
|
|
389
|
+
// 设置只读模式下图片的点击监听器
|
|
390
|
+
setupImageClickHandlers() {
|
|
391
|
+
if (!this.readOnly || !this.$refs.readOnlyContainer) {
|
|
392
|
+
console.log(
|
|
393
|
+
'[FcEditorWrapper] setupImageClickHandlers: Not in readOnly mode or container not found.'
|
|
394
|
+
)
|
|
395
|
+
return
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const container = this.$refs.readOnlyContainer
|
|
399
|
+
|
|
400
|
+
// 清理旧的监听器
|
|
401
|
+
if (this._imageClickCleanup) {
|
|
402
|
+
console.log('[FcEditorWrapper] Cleaning up old image click handlers.')
|
|
403
|
+
this._imageClickCleanup()
|
|
404
|
+
this._imageClickCleanup = null
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 使用事件委托,在容器上监听点击事件
|
|
408
|
+
const handleContainerClick = (e) => {
|
|
409
|
+
// 查找被点击的图片元素
|
|
410
|
+
let target = e.target
|
|
411
|
+
while (target && target !== container) {
|
|
412
|
+
if (target.tagName === 'IMG') {
|
|
413
|
+
// 找到了图片
|
|
414
|
+
const img = target
|
|
415
|
+
console.log('[FcEditorWrapper] Image clicked:', img.src)
|
|
416
|
+
|
|
417
|
+
const imgSrc = img.getAttribute('src') || img.src
|
|
418
|
+
const imgAlt = img.getAttribute('alt') || ''
|
|
419
|
+
const imgTitle = img.getAttribute('title') || imgAlt
|
|
420
|
+
|
|
421
|
+
// 检查图片是否在链接内
|
|
422
|
+
const parentLink = img.closest('a')
|
|
423
|
+
let imgUrl = imgSrc
|
|
424
|
+
if (parentLink) {
|
|
425
|
+
imgUrl = parentLink.getAttribute('href') || imgSrc
|
|
426
|
+
console.log('[FcEditorWrapper] Image is inside a link:', imgUrl)
|
|
427
|
+
// 不阻止默认行为,让链接正常跳转
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// 生成 uid(基于图片 URL)
|
|
431
|
+
const uid = imgSrc.split('').reduce((acc, char) => {
|
|
432
|
+
return ((acc << 5) - acc + char.charCodeAt(0)) | 0
|
|
433
|
+
}, 0)
|
|
434
|
+
|
|
435
|
+
// 发送预览消息到父窗口(类似 Upload 组件)
|
|
436
|
+
if (window.parent && window.parent !== window) {
|
|
437
|
+
const message = {
|
|
438
|
+
type: 'upload-preview',
|
|
439
|
+
file: {
|
|
440
|
+
url: imgUrl,
|
|
441
|
+
name: imgTitle || imgAlt || '图片',
|
|
442
|
+
uid: uid,
|
|
443
|
+
size: 0,
|
|
444
|
+
type: 'image'
|
|
445
|
+
},
|
|
446
|
+
timestamp: Date.now()
|
|
447
|
+
}
|
|
448
|
+
console.log('[FcEditorWrapper] Sending postMessage:', message)
|
|
449
|
+
window.parent.postMessage(message, '*')
|
|
450
|
+
} else {
|
|
451
|
+
console.warn('[FcEditorWrapper] No parent window to send message')
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// 不阻止默认行为和事件冒泡,让链接和图片都能正常工作
|
|
455
|
+
// 如果图片在链接内,链接会正常跳转
|
|
456
|
+
// 如果图片不在链接内,图片也没有默认行为需要阻止
|
|
457
|
+
break
|
|
458
|
+
}
|
|
459
|
+
target = target.parentElement
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// 在容器上添加点击事件监听器
|
|
464
|
+
container.addEventListener('click', handleContainerClick, true) // 使用 capture 阶段
|
|
465
|
+
console.log('[FcEditorWrapper] Added container click listener for images')
|
|
466
|
+
|
|
467
|
+
// 为所有图片设置样式
|
|
468
|
+
const images = container.querySelectorAll('img')
|
|
469
|
+
console.log('[FcEditorWrapper] Found images:', images.length)
|
|
470
|
+
images.forEach((img) => {
|
|
471
|
+
img.style.cursor = 'pointer'
|
|
472
|
+
img.style.pointerEvents = 'auto'
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
// 保存清理函数
|
|
476
|
+
this._imageClickCleanup = () => {
|
|
477
|
+
console.log('[FcEditorWrapper] Executing image click cleanup.')
|
|
478
|
+
container.removeEventListener('click', handleContainerClick, true)
|
|
479
|
+
}
|
|
480
|
+
},
|
|
349
481
|
initEditor(editor) {
|
|
350
482
|
if (!editor) return
|
|
351
483
|
|
|
@@ -541,21 +673,17 @@ export default defineComponent({
|
|
|
541
673
|
// 立即设置一次
|
|
542
674
|
this.$nextTick(() => {
|
|
543
675
|
this.setReadOnlyMode(true)
|
|
544
|
-
this.setupImageClickHandler(editor)
|
|
545
676
|
})
|
|
546
677
|
|
|
547
678
|
// 第一次延迟:等待编辑器 DOM 创建
|
|
548
679
|
setTimeout(() => {
|
|
549
680
|
this.setReadOnlyMode(true)
|
|
550
|
-
this.setupImageClickHandler(editor)
|
|
551
681
|
// 第二次延迟:确保设置生效
|
|
552
682
|
setTimeout(() => {
|
|
553
683
|
this.setReadOnlyMode(true)
|
|
554
|
-
this.setupImageClickHandler(editor)
|
|
555
684
|
// 第三次延迟:确保完全生效
|
|
556
685
|
setTimeout(() => {
|
|
557
686
|
this.setReadOnlyMode(true)
|
|
558
|
-
this.setupImageClickHandler(editor)
|
|
559
687
|
}, 300)
|
|
560
688
|
}, 200)
|
|
561
689
|
}, 100)
|
|
@@ -1045,90 +1173,6 @@ export default defineComponent({
|
|
|
1045
1173
|
}
|
|
1046
1174
|
})
|
|
1047
1175
|
},
|
|
1048
|
-
// 设置图片点击处理器(用于只读/预览模式下向父窗口发送预览消息)
|
|
1049
|
-
setupImageClickHandler(editor) {
|
|
1050
|
-
if (!editor) return
|
|
1051
|
-
|
|
1052
|
-
// 如果已经有处理器,先清理
|
|
1053
|
-
if (this._imageClickHandler && this._imageClickContainer) {
|
|
1054
|
-
this._imageClickContainer.removeEventListener(
|
|
1055
|
-
'click',
|
|
1056
|
-
this._imageClickHandler,
|
|
1057
|
-
true
|
|
1058
|
-
)
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// 查找编辑器文本容器
|
|
1062
|
-
let textContainer = null
|
|
1063
|
-
if (editor.$textElem && editor.$textElem[0]) {
|
|
1064
|
-
textContainer = editor.$textElem[0]
|
|
1065
|
-
} else if (editor.id) {
|
|
1066
|
-
const editorEl = document.getElementById(editor.id)
|
|
1067
|
-
if (editorEl) {
|
|
1068
|
-
textContainer = editorEl.querySelector('.w-e-text')
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
if (!textContainer) {
|
|
1073
|
-
// 如果找不到容器,延迟重试
|
|
1074
|
-
setTimeout(() => {
|
|
1075
|
-
this.setupImageClickHandler(editor)
|
|
1076
|
-
}, 100)
|
|
1077
|
-
return
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
// 创建图片点击处理器
|
|
1081
|
-
const handleImageClick = (event) => {
|
|
1082
|
-
// 检查是否点击的是图片
|
|
1083
|
-
const target = event.target
|
|
1084
|
-
if (target && target.tagName === 'IMG') {
|
|
1085
|
-
event.preventDefault()
|
|
1086
|
-
event.stopPropagation()
|
|
1087
|
-
|
|
1088
|
-
// 提取图片信息
|
|
1089
|
-
const imgSrc = target.getAttribute('src') || target.src
|
|
1090
|
-
const imgAlt = target.getAttribute('alt') || ''
|
|
1091
|
-
const imgTitle = target.getAttribute('title') || imgAlt
|
|
1092
|
-
|
|
1093
|
-
// 如果图片有链接,提取链接地址
|
|
1094
|
-
let imgUrl = imgSrc
|
|
1095
|
-
const parentLink = target.closest('a')
|
|
1096
|
-
if (parentLink) {
|
|
1097
|
-
imgUrl = parentLink.getAttribute('href') || imgSrc
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
// 发送预览消息到父窗口(类似 Upload 组件)
|
|
1101
|
-
if (window.parent && window.parent !== window) {
|
|
1102
|
-
// 生成一个基于图片 URL 的 uid(用于标识图片)
|
|
1103
|
-
const uid = imgSrc.split('').reduce((acc, char) => {
|
|
1104
|
-
return ((acc << 5) - acc + char.charCodeAt(0)) | 0
|
|
1105
|
-
}, 0)
|
|
1106
|
-
|
|
1107
|
-
window.parent.postMessage(
|
|
1108
|
-
{
|
|
1109
|
-
type: 'upload-preview',
|
|
1110
|
-
file: {
|
|
1111
|
-
url: imgUrl, // 使用图片 URL 或链接地址
|
|
1112
|
-
name: imgTitle || imgAlt || '图片', // 使用 title、alt 或默认值
|
|
1113
|
-
uid: uid, // 基于 URL 生成的标识
|
|
1114
|
-
size: 0, // 图片大小未知
|
|
1115
|
-
type: 'image' // 文件类型
|
|
1116
|
-
},
|
|
1117
|
-
timestamp: Date.now()
|
|
1118
|
-
},
|
|
1119
|
-
'*'
|
|
1120
|
-
)
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
// 在捕获阶段添加点击监听器(优先级高)
|
|
1126
|
-
textContainer.addEventListener('click', handleImageClick, true)
|
|
1127
|
-
|
|
1128
|
-
// 保存引用以便清理
|
|
1129
|
-
this._imageClickHandler = handleImageClick
|
|
1130
|
-
this._imageClickContainer = textContainer
|
|
1131
|
-
},
|
|
1132
1176
|
// 验证是否为有效的URL
|
|
1133
1177
|
isValidUrl(str) {
|
|
1134
1178
|
if (!str || typeof str !== 'string') return false
|
|
@@ -1184,3 +1228,30 @@ export default defineComponent({
|
|
|
1184
1228
|
}
|
|
1185
1229
|
})
|
|
1186
1230
|
</script>
|
|
1231
|
+
|
|
1232
|
+
<style scoped>
|
|
1233
|
+
.fc-editor-wrapper {
|
|
1234
|
+
width: 100%;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.fc-editor-readonly {
|
|
1238
|
+
padding: 0 10px;
|
|
1239
|
+
border-radius: 4px;
|
|
1240
|
+
/* 覆盖预览模式的全局 pointer-events: none 规则 */
|
|
1241
|
+
pointer-events: auto !important;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.fc-editor-readonly img {
|
|
1245
|
+
cursor: pointer;
|
|
1246
|
+
max-width: 100%;
|
|
1247
|
+
height: auto;
|
|
1248
|
+
/* 确保图片可以点击,覆盖预览模式的全局规则 */
|
|
1249
|
+
pointer-events: auto !important;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
.fc-editor-readonly a {
|
|
1253
|
+
/* 确保链接可以点击,覆盖预览模式的全局规则 */
|
|
1254
|
+
pointer-events: auto !important;
|
|
1255
|
+
cursor: pointer;
|
|
1256
|
+
}
|
|
1257
|
+
</style>
|
|
@@ -27,7 +27,7 @@ import { markRaw, reactive } from 'vue'
|
|
|
27
27
|
|
|
28
28
|
export default {
|
|
29
29
|
name: 'TableForm',
|
|
30
|
-
emits: ['change', 'add', 'delete', 'update:modelValue'],
|
|
30
|
+
emits: ['change', 'add', 'delete', 'update:modelValue', 'select-change'],
|
|
31
31
|
props: {
|
|
32
32
|
formCreateInject: Object,
|
|
33
33
|
modelValue: {
|
|
@@ -66,6 +66,14 @@ export default {
|
|
|
66
66
|
type: Boolean,
|
|
67
67
|
default: true
|
|
68
68
|
},
|
|
69
|
+
showSelectColumn: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
default: false
|
|
72
|
+
},
|
|
73
|
+
selectField: {
|
|
74
|
+
type: String,
|
|
75
|
+
default: 'checked'
|
|
76
|
+
},
|
|
69
77
|
showOperationColumn: {
|
|
70
78
|
type: Boolean,
|
|
71
79
|
default: true
|
|
@@ -93,6 +101,10 @@ export default {
|
|
|
93
101
|
this.loadRule()
|
|
94
102
|
this.updateTable()
|
|
95
103
|
},
|
|
104
|
+
showSelectColumn() {
|
|
105
|
+
this.loadRule()
|
|
106
|
+
this.updateTable()
|
|
107
|
+
},
|
|
96
108
|
showOperationColumn() {
|
|
97
109
|
this.loadRule()
|
|
98
110
|
this.updateTable()
|
|
@@ -112,6 +124,7 @@ export default {
|
|
|
112
124
|
Form: markRaw(this.formCreateInject.form.$form()),
|
|
113
125
|
copyTrs: '',
|
|
114
126
|
oldValue: '',
|
|
127
|
+
selectedRows: new Set(), // 存储选中行的索引
|
|
115
128
|
emptyRule: {
|
|
116
129
|
type: 'tr',
|
|
117
130
|
_isEmpty: true,
|
|
@@ -143,6 +156,7 @@ export default {
|
|
|
143
156
|
return (
|
|
144
157
|
this.columns.length +
|
|
145
158
|
(this.showIndexColumn ? 1 : 0) +
|
|
159
|
+
(this.showSelectColumn ? 1 : 0) +
|
|
146
160
|
(this.showOperationColumn ? (this.formCreateInject.preview ? 0 : 1) : 0)
|
|
147
161
|
)
|
|
148
162
|
},
|
|
@@ -187,6 +201,17 @@ export default {
|
|
|
187
201
|
return
|
|
188
202
|
}
|
|
189
203
|
this.oldValue = str
|
|
204
|
+
|
|
205
|
+
// 从数据中读取选中状态,初始化 selectedRows
|
|
206
|
+
if (this.showSelectColumn) {
|
|
207
|
+
this.selectedRows.clear()
|
|
208
|
+
this.modelValue.forEach((data, idx) => {
|
|
209
|
+
if (data && data[this.selectField] === true) {
|
|
210
|
+
this.selectedRows.add(idx)
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
190
215
|
this.trs = this.trs.splice(0, this.modelValue.length)
|
|
191
216
|
if (!this.modelValue.length) {
|
|
192
217
|
this.addEmpty()
|
|
@@ -200,6 +225,10 @@ export default {
|
|
|
200
225
|
this.setRawData(idx, data || {})
|
|
201
226
|
})
|
|
202
227
|
this.rule[0].children[1].children = this.trs
|
|
228
|
+
// 更新全选状态
|
|
229
|
+
if (this.showSelectColumn) {
|
|
230
|
+
this.updateSelectAllState()
|
|
231
|
+
}
|
|
203
232
|
},
|
|
204
233
|
updateEmptyRule() {
|
|
205
234
|
const emptyTextValue =
|
|
@@ -242,7 +271,20 @@ export default {
|
|
|
242
271
|
) {
|
|
243
272
|
return
|
|
244
273
|
}
|
|
274
|
+
// 删除数据行
|
|
245
275
|
this.trs.splice(idx, 1)
|
|
276
|
+
// 更新 selectedRows,删除当前索引,后续索引前移
|
|
277
|
+
this.selectedRows.delete(idx)
|
|
278
|
+
const newSelectedRows = new Set()
|
|
279
|
+
this.selectedRows.forEach(selectedIdx => {
|
|
280
|
+
if (selectedIdx < idx) {
|
|
281
|
+
newSelectedRows.add(selectedIdx)
|
|
282
|
+
} else if (selectedIdx > idx) {
|
|
283
|
+
newSelectedRows.add(selectedIdx - 1)
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
this.selectedRows = newSelectedRows
|
|
287
|
+
|
|
246
288
|
this.updateValue()
|
|
247
289
|
if (this.trs.length) {
|
|
248
290
|
this.trs.forEach((tr) => this.updateRaw(tr))
|
|
@@ -250,6 +292,8 @@ export default {
|
|
|
250
292
|
this.addEmpty()
|
|
251
293
|
}
|
|
252
294
|
this.$emit('delete', idx)
|
|
295
|
+
// 触发选择变化事件
|
|
296
|
+
this.$emit('select-change', Array.from(this.selectedRows))
|
|
253
297
|
},
|
|
254
298
|
addRaw(flag) {
|
|
255
299
|
if (flag && this.disabled) {
|
|
@@ -265,13 +309,49 @@ export default {
|
|
|
265
309
|
this.$emit('add', this.trs.length)
|
|
266
310
|
this.updateValue()
|
|
267
311
|
}
|
|
312
|
+
// 更新全选状态
|
|
313
|
+
this.updateSelectAllState()
|
|
268
314
|
},
|
|
269
315
|
updateRaw(tr) {
|
|
270
316
|
const idx = this.trs.indexOf(tr)
|
|
317
|
+
let colOffset = 0
|
|
318
|
+
|
|
271
319
|
// 更新序号列
|
|
272
|
-
if (this.showIndexColumn && tr.children[
|
|
273
|
-
tr.children[
|
|
320
|
+
if (this.showIndexColumn && tr.children[colOffset]) {
|
|
321
|
+
tr.children[colOffset].props.innerText = idx + 1
|
|
322
|
+
colOffset++
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 更新选择列
|
|
326
|
+
if (this.showSelectColumn && tr.children[colOffset]) {
|
|
327
|
+
const selectCell = tr.children[colOffset]
|
|
328
|
+
// 从数据对象中读取选中状态
|
|
329
|
+
const rowData = this.modelValue[idx]
|
|
330
|
+
const isSelected = rowData && rowData[this.selectField] === true
|
|
331
|
+
if (selectCell.children && selectCell.children[0]) {
|
|
332
|
+
const checkbox = selectCell.children[0]
|
|
333
|
+
checkbox.props.checked = isSelected
|
|
334
|
+
checkbox.props.onClick = (e) => {
|
|
335
|
+
e.stopPropagation()
|
|
336
|
+
this.toggleRowSelect(idx, e.target.checked)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// 通过 DOM 更新 checkbox 状态(确保视图同步)
|
|
340
|
+
this.$nextTick(() => {
|
|
341
|
+
const tableEl = this.$el?.querySelector('._fc-tf-table')
|
|
342
|
+
if (tableEl) {
|
|
343
|
+
const rows = tableEl.querySelectorAll('tbody tr')
|
|
344
|
+
if (rows[idx]) {
|
|
345
|
+
const checkbox = rows[idx].querySelector('._fc-tf-select input[type="checkbox"]')
|
|
346
|
+
if (checkbox) {
|
|
347
|
+
checkbox.checked = isSelected
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
colOffset++
|
|
274
353
|
}
|
|
354
|
+
|
|
275
355
|
// 更新操作列的删除按钮
|
|
276
356
|
if (this.showOperationColumn && tr.children[tr.children.length - 1]) {
|
|
277
357
|
const operationCell = tr.children[tr.children.length - 1]
|
|
@@ -282,6 +362,78 @@ export default {
|
|
|
282
362
|
}
|
|
283
363
|
}
|
|
284
364
|
},
|
|
365
|
+
toggleRowSelect(idx, checked) {
|
|
366
|
+
// 更新数据对象中的选中状态
|
|
367
|
+
const rowData = this.modelValue[idx]
|
|
368
|
+
if (rowData) {
|
|
369
|
+
if (checked) {
|
|
370
|
+
this.selectedRows.add(idx)
|
|
371
|
+
rowData[this.selectField] = true
|
|
372
|
+
} else {
|
|
373
|
+
this.selectedRows.delete(idx)
|
|
374
|
+
rowData[this.selectField] = false
|
|
375
|
+
}
|
|
376
|
+
// 触发数据更新
|
|
377
|
+
this.updateValue()
|
|
378
|
+
}
|
|
379
|
+
// 更新全选状态
|
|
380
|
+
this.updateSelectAllState()
|
|
381
|
+
// 触发选择变化事件
|
|
382
|
+
this.$emit('select-change', Array.from(this.selectedRows))
|
|
383
|
+
},
|
|
384
|
+
toggleSelectAll(checked) {
|
|
385
|
+
// 更新所有数据对象中的选中状态
|
|
386
|
+
this.modelValue.forEach((rowData, idx) => {
|
|
387
|
+
if (rowData) {
|
|
388
|
+
if (checked) {
|
|
389
|
+
this.selectedRows.add(idx)
|
|
390
|
+
rowData[this.selectField] = true
|
|
391
|
+
} else {
|
|
392
|
+
this.selectedRows.delete(idx)
|
|
393
|
+
rowData[this.selectField] = false
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
})
|
|
397
|
+
// 触发数据更新
|
|
398
|
+
this.updateValue()
|
|
399
|
+
// 更新所有行的选择状态
|
|
400
|
+
this.trs.forEach((tr) => {
|
|
401
|
+
if (!tr._isEmpty) {
|
|
402
|
+
this.updateRaw(tr)
|
|
403
|
+
}
|
|
404
|
+
})
|
|
405
|
+
// 触发选择变化事件
|
|
406
|
+
this.$emit('select-change', Array.from(this.selectedRows))
|
|
407
|
+
},
|
|
408
|
+
updateSelectAllState() {
|
|
409
|
+
if (!this.showSelectColumn) return
|
|
410
|
+
|
|
411
|
+
const validRows = this.modelValue.filter((rowData, idx) => {
|
|
412
|
+
const tr = this.trs[idx]
|
|
413
|
+
return tr && !tr._isEmpty
|
|
414
|
+
})
|
|
415
|
+
if (validRows.length === 0) return
|
|
416
|
+
|
|
417
|
+
// 从数据对象中读取选中状态
|
|
418
|
+
const allSelected = validRows.every((rowData) => {
|
|
419
|
+
return rowData && rowData[this.selectField] === true
|
|
420
|
+
})
|
|
421
|
+
const someSelected = validRows.some((rowData) => {
|
|
422
|
+
return rowData && rowData[this.selectField] === true
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
// 更新表头的全选checkbox(通过 DOM 操作)
|
|
426
|
+
this.$nextTick(() => {
|
|
427
|
+
const tableEl = this.$el?.querySelector('._fc-tf-table')
|
|
428
|
+
if (tableEl) {
|
|
429
|
+
const headerCheckbox = tableEl.querySelector('._fc-tf-head-select input[type="checkbox"]')
|
|
430
|
+
if (headerCheckbox) {
|
|
431
|
+
headerCheckbox.checked = allSelected
|
|
432
|
+
headerCheckbox.indeterminate = !allSelected && someSelected
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
},
|
|
285
437
|
loadRule() {
|
|
286
438
|
const header = []
|
|
287
439
|
let body = []
|
|
@@ -311,6 +463,62 @@ export default {
|
|
|
311
463
|
})
|
|
312
464
|
}
|
|
313
465
|
|
|
466
|
+
// 选择列
|
|
467
|
+
if (this.showSelectColumn) {
|
|
468
|
+
header.push({
|
|
469
|
+
type: 'th',
|
|
470
|
+
native: true,
|
|
471
|
+
class: '_fc-tf-head-select',
|
|
472
|
+
style: {
|
|
473
|
+
width: '40px',
|
|
474
|
+
minWidth: '40px',
|
|
475
|
+
maxWidth: '40px',
|
|
476
|
+
textAlign: 'center',
|
|
477
|
+
...(this.headerBackgroundColor
|
|
478
|
+
? {
|
|
479
|
+
backgroundColor: this.headerBackgroundColor
|
|
480
|
+
}
|
|
481
|
+
: {})
|
|
482
|
+
},
|
|
483
|
+
children: [
|
|
484
|
+
{
|
|
485
|
+
type: 'input',
|
|
486
|
+
native: true,
|
|
487
|
+
props: {
|
|
488
|
+
type: 'checkbox',
|
|
489
|
+
id: `_fc-tf-select-all-${this._uid || Date.now()}`,
|
|
490
|
+
onClick: (e) => {
|
|
491
|
+
this.toggleSelectAll(e.target.checked)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
]
|
|
496
|
+
})
|
|
497
|
+
body.push({
|
|
498
|
+
type: 'td',
|
|
499
|
+
class: '_fc-tf-select',
|
|
500
|
+
native: true,
|
|
501
|
+
style: {
|
|
502
|
+
width: '40px',
|
|
503
|
+
minWidth: '40px',
|
|
504
|
+
maxWidth: '40px',
|
|
505
|
+
textAlign: 'center'
|
|
506
|
+
},
|
|
507
|
+
children: [
|
|
508
|
+
{
|
|
509
|
+
type: 'input',
|
|
510
|
+
native: true,
|
|
511
|
+
props: {
|
|
512
|
+
type: 'checkbox',
|
|
513
|
+
onClick: (e) => {
|
|
514
|
+
// 这个会在 updateRaw 中被更新
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
]
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
|
|
314
522
|
// 数据列
|
|
315
523
|
this.columns.forEach((column) => {
|
|
316
524
|
header.push({
|
package/src/style/index.css
CHANGED
|
@@ -488,6 +488,21 @@ textarea[readonly].ant-input {
|
|
|
488
488
|
-ms-user-select: none !important;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
+
/* 允许只读模式下的富文本编辑器容器(v-html 渲染)中的链接和图片可以点击 */
|
|
492
|
+
.form-create.is-preview .fc-editor-readonly {
|
|
493
|
+
pointer-events: auto !important;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.form-create.is-preview .fc-editor-readonly a {
|
|
497
|
+
pointer-events: auto !important;
|
|
498
|
+
cursor: pointer !important;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.form-create.is-preview .fc-editor-readonly img {
|
|
502
|
+
pointer-events: auto !important;
|
|
503
|
+
cursor: pointer !important;
|
|
504
|
+
}
|
|
505
|
+
|
|
491
506
|
/* 禁止编辑:通过 contenteditable 属性控制 */
|
|
492
507
|
.form-create.is-preview .w-e-text {
|
|
493
508
|
user-select: text !important;
|