@longhongguo/form-create-ant-design-vue 3.3.25 → 3.3.27
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 +184 -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.27",
|
|
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,10 @@ export default {
|
|
|
66
66
|
type: Boolean,
|
|
67
67
|
default: true
|
|
68
68
|
},
|
|
69
|
+
showSelectColumn: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
default: false
|
|
72
|
+
},
|
|
69
73
|
showOperationColumn: {
|
|
70
74
|
type: Boolean,
|
|
71
75
|
default: true
|
|
@@ -93,6 +97,10 @@ export default {
|
|
|
93
97
|
this.loadRule()
|
|
94
98
|
this.updateTable()
|
|
95
99
|
},
|
|
100
|
+
showSelectColumn() {
|
|
101
|
+
this.loadRule()
|
|
102
|
+
this.updateTable()
|
|
103
|
+
},
|
|
96
104
|
showOperationColumn() {
|
|
97
105
|
this.loadRule()
|
|
98
106
|
this.updateTable()
|
|
@@ -112,6 +120,7 @@ export default {
|
|
|
112
120
|
Form: markRaw(this.formCreateInject.form.$form()),
|
|
113
121
|
copyTrs: '',
|
|
114
122
|
oldValue: '',
|
|
123
|
+
selectedRows: new Set(), // 存储选中行的索引
|
|
115
124
|
emptyRule: {
|
|
116
125
|
type: 'tr',
|
|
117
126
|
_isEmpty: true,
|
|
@@ -143,6 +152,7 @@ export default {
|
|
|
143
152
|
return (
|
|
144
153
|
this.columns.length +
|
|
145
154
|
(this.showIndexColumn ? 1 : 0) +
|
|
155
|
+
(this.showSelectColumn ? 1 : 0) +
|
|
146
156
|
(this.showOperationColumn ? (this.formCreateInject.preview ? 0 : 1) : 0)
|
|
147
157
|
)
|
|
148
158
|
},
|
|
@@ -187,6 +197,15 @@ export default {
|
|
|
187
197
|
return
|
|
188
198
|
}
|
|
189
199
|
this.oldValue = str
|
|
200
|
+
// 清理超出范围的选中状态
|
|
201
|
+
const newSelectedRows = new Set()
|
|
202
|
+
this.selectedRows.forEach(idx => {
|
|
203
|
+
if (idx < this.modelValue.length) {
|
|
204
|
+
newSelectedRows.add(idx)
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
this.selectedRows = newSelectedRows
|
|
208
|
+
|
|
190
209
|
this.trs = this.trs.splice(0, this.modelValue.length)
|
|
191
210
|
if (!this.modelValue.length) {
|
|
192
211
|
this.addEmpty()
|
|
@@ -200,6 +219,10 @@ export default {
|
|
|
200
219
|
this.setRawData(idx, data || {})
|
|
201
220
|
})
|
|
202
221
|
this.rule[0].children[1].children = this.trs
|
|
222
|
+
// 更新全选状态
|
|
223
|
+
if (this.showSelectColumn) {
|
|
224
|
+
this.updateSelectAllState()
|
|
225
|
+
}
|
|
203
226
|
},
|
|
204
227
|
updateEmptyRule() {
|
|
205
228
|
const emptyTextValue =
|
|
@@ -242,6 +265,19 @@ export default {
|
|
|
242
265
|
) {
|
|
243
266
|
return
|
|
244
267
|
}
|
|
268
|
+
// 删除选中状态
|
|
269
|
+
this.selectedRows.delete(idx)
|
|
270
|
+
// 更新后续行的索引
|
|
271
|
+
const newSelectedRows = new Set()
|
|
272
|
+
this.selectedRows.forEach(selectedIdx => {
|
|
273
|
+
if (selectedIdx < idx) {
|
|
274
|
+
newSelectedRows.add(selectedIdx)
|
|
275
|
+
} else if (selectedIdx > idx) {
|
|
276
|
+
newSelectedRows.add(selectedIdx - 1)
|
|
277
|
+
}
|
|
278
|
+
})
|
|
279
|
+
this.selectedRows = newSelectedRows
|
|
280
|
+
|
|
245
281
|
this.trs.splice(idx, 1)
|
|
246
282
|
this.updateValue()
|
|
247
283
|
if (this.trs.length) {
|
|
@@ -250,6 +286,8 @@ export default {
|
|
|
250
286
|
this.addEmpty()
|
|
251
287
|
}
|
|
252
288
|
this.$emit('delete', idx)
|
|
289
|
+
// 触发选择变化事件
|
|
290
|
+
this.$emit('select-change', Array.from(this.selectedRows))
|
|
253
291
|
},
|
|
254
292
|
addRaw(flag) {
|
|
255
293
|
if (flag && this.disabled) {
|
|
@@ -265,13 +303,47 @@ export default {
|
|
|
265
303
|
this.$emit('add', this.trs.length)
|
|
266
304
|
this.updateValue()
|
|
267
305
|
}
|
|
306
|
+
// 更新全选状态
|
|
307
|
+
this.updateSelectAllState()
|
|
268
308
|
},
|
|
269
309
|
updateRaw(tr) {
|
|
270
310
|
const idx = this.trs.indexOf(tr)
|
|
311
|
+
let colOffset = 0
|
|
312
|
+
|
|
271
313
|
// 更新序号列
|
|
272
|
-
if (this.showIndexColumn && tr.children[
|
|
273
|
-
tr.children[
|
|
314
|
+
if (this.showIndexColumn && tr.children[colOffset]) {
|
|
315
|
+
tr.children[colOffset].props.innerText = idx + 1
|
|
316
|
+
colOffset++
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// 更新选择列
|
|
320
|
+
if (this.showSelectColumn && tr.children[colOffset]) {
|
|
321
|
+
const selectCell = tr.children[colOffset]
|
|
322
|
+
const isSelected = this.selectedRows.has(idx)
|
|
323
|
+
if (selectCell.children && selectCell.children[0]) {
|
|
324
|
+
const checkbox = selectCell.children[0]
|
|
325
|
+
checkbox.props.checked = isSelected
|
|
326
|
+
checkbox.props.onClick = (e) => {
|
|
327
|
+
e.stopPropagation()
|
|
328
|
+
this.toggleRowSelect(idx, e.target.checked)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// 通过 DOM 更新 checkbox 状态(确保视图同步)
|
|
332
|
+
this.$nextTick(() => {
|
|
333
|
+
const tableEl = this.$el?.querySelector('._fc-tf-table')
|
|
334
|
+
if (tableEl) {
|
|
335
|
+
const rows = tableEl.querySelectorAll('tbody tr')
|
|
336
|
+
if (rows[idx]) {
|
|
337
|
+
const checkbox = rows[idx].querySelector('._fc-tf-select input[type="checkbox"]')
|
|
338
|
+
if (checkbox) {
|
|
339
|
+
checkbox.checked = isSelected
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
colOffset++
|
|
274
345
|
}
|
|
346
|
+
|
|
275
347
|
// 更新操作列的删除按钮
|
|
276
348
|
if (this.showOperationColumn && tr.children[tr.children.length - 1]) {
|
|
277
349
|
const operationCell = tr.children[tr.children.length - 1]
|
|
@@ -282,6 +354,65 @@ export default {
|
|
|
282
354
|
}
|
|
283
355
|
}
|
|
284
356
|
},
|
|
357
|
+
toggleRowSelect(idx, checked) {
|
|
358
|
+
if (checked) {
|
|
359
|
+
this.selectedRows.add(idx)
|
|
360
|
+
} else {
|
|
361
|
+
this.selectedRows.delete(idx)
|
|
362
|
+
}
|
|
363
|
+
// 更新全选状态
|
|
364
|
+
this.updateSelectAllState()
|
|
365
|
+
// 触发选择变化事件
|
|
366
|
+
this.$emit('select-change', Array.from(this.selectedRows))
|
|
367
|
+
},
|
|
368
|
+
toggleSelectAll(checked) {
|
|
369
|
+
if (checked) {
|
|
370
|
+
// 全选:排除空数据行
|
|
371
|
+
this.trs.forEach((tr, idx) => {
|
|
372
|
+
if (!tr._isEmpty) {
|
|
373
|
+
this.selectedRows.add(idx)
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
} else {
|
|
377
|
+
// 取消全选
|
|
378
|
+
this.selectedRows.clear()
|
|
379
|
+
}
|
|
380
|
+
// 更新所有行的选择状态
|
|
381
|
+
this.trs.forEach((tr) => {
|
|
382
|
+
if (!tr._isEmpty) {
|
|
383
|
+
this.updateRaw(tr)
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
// 触发选择变化事件
|
|
387
|
+
this.$emit('select-change', Array.from(this.selectedRows))
|
|
388
|
+
},
|
|
389
|
+
updateSelectAllState() {
|
|
390
|
+
if (!this.showSelectColumn) return
|
|
391
|
+
|
|
392
|
+
const validRows = this.trs.filter(tr => !tr._isEmpty)
|
|
393
|
+
if (validRows.length === 0) return
|
|
394
|
+
|
|
395
|
+
const allSelected = validRows.every((tr) => {
|
|
396
|
+
const actualIdx = this.trs.indexOf(tr)
|
|
397
|
+
return this.selectedRows.has(actualIdx)
|
|
398
|
+
})
|
|
399
|
+
const someSelected = validRows.some((tr) => {
|
|
400
|
+
const actualIdx = this.trs.indexOf(tr)
|
|
401
|
+
return this.selectedRows.has(actualIdx)
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
// 更新表头的全选checkbox(通过 DOM 操作)
|
|
405
|
+
this.$nextTick(() => {
|
|
406
|
+
const tableEl = this.$el?.querySelector('._fc-tf-table')
|
|
407
|
+
if (tableEl) {
|
|
408
|
+
const headerCheckbox = tableEl.querySelector('._fc-tf-head-select input[type="checkbox"]')
|
|
409
|
+
if (headerCheckbox) {
|
|
410
|
+
headerCheckbox.checked = allSelected
|
|
411
|
+
headerCheckbox.indeterminate = !allSelected && someSelected
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
})
|
|
415
|
+
},
|
|
285
416
|
loadRule() {
|
|
286
417
|
const header = []
|
|
287
418
|
let body = []
|
|
@@ -311,6 +442,56 @@ export default {
|
|
|
311
442
|
})
|
|
312
443
|
}
|
|
313
444
|
|
|
445
|
+
// 选择列
|
|
446
|
+
if (this.showSelectColumn) {
|
|
447
|
+
header.push({
|
|
448
|
+
type: 'th',
|
|
449
|
+
native: true,
|
|
450
|
+
class: '_fc-tf-head-select',
|
|
451
|
+
style: {
|
|
452
|
+
textAlign: 'center',
|
|
453
|
+
...(this.headerBackgroundColor
|
|
454
|
+
? {
|
|
455
|
+
backgroundColor: this.headerBackgroundColor
|
|
456
|
+
}
|
|
457
|
+
: {})
|
|
458
|
+
},
|
|
459
|
+
children: [
|
|
460
|
+
{
|
|
461
|
+
type: 'input',
|
|
462
|
+
native: true,
|
|
463
|
+
props: {
|
|
464
|
+
type: 'checkbox',
|
|
465
|
+
id: `_fc-tf-select-all-${this._uid || Date.now()}`,
|
|
466
|
+
onClick: (e) => {
|
|
467
|
+
this.toggleSelectAll(e.target.checked)
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
]
|
|
472
|
+
})
|
|
473
|
+
body.push({
|
|
474
|
+
type: 'td',
|
|
475
|
+
class: '_fc-tf-select',
|
|
476
|
+
native: true,
|
|
477
|
+
style: {
|
|
478
|
+
textAlign: 'center'
|
|
479
|
+
},
|
|
480
|
+
children: [
|
|
481
|
+
{
|
|
482
|
+
type: 'input',
|
|
483
|
+
native: true,
|
|
484
|
+
props: {
|
|
485
|
+
type: 'checkbox',
|
|
486
|
+
onClick: (e) => {
|
|
487
|
+
// 这个会在 updateRaw 中被更新
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
]
|
|
492
|
+
})
|
|
493
|
+
}
|
|
494
|
+
|
|
314
495
|
// 数据列
|
|
315
496
|
this.columns.forEach((column) => {
|
|
316
497
|
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;
|