vue-wiring-diagram 1.1.22 → 1.1.23
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/README.md +93 -93
- package/dist/style.css +1 -1
- package/dist/vue-wiring-diagram.es.js +5563 -5234
- package/dist/vue-wiring-diagram.umd.js +27 -27
- package/package.json +1 -1
- package/packages/components/Shortcuts.vue +31 -31
- package/packages/components/baseShape.js +62 -62
- package/packages/components/common.js +105 -105
- package/packages/components/edge-control/arrow-line.vue +292 -292
- package/packages/components/edge-control/condition.vue +110 -110
- package/packages/components/edge-control/default-line.vue +156 -156
- package/packages/components/edge-control/index.vue +94 -94
- package/packages/components/edge-control/pipe-line.vue +354 -354
- package/packages/components/editor/index.vue +590 -590
- package/packages/components/enums.js +80 -80
- package/packages/components/graph-control/index.vue +121 -121
- package/packages/components/image-control/group-form.vue +114 -114
- package/packages/components/image-control/image-condition.vue +117 -0
- package/packages/components/image-control/image-form.vue +184 -184
- package/packages/components/image-control/image-management.vue +213 -213
- package/packages/components/image-control/index.vue +290 -124
- package/packages/components/portsOptions.js +21 -21
- package/packages/components/preview/index.vue +399 -355
- package/packages/components/settings.js +262 -262
- package/packages/components/text-control/index.vue +457 -457
- package/packages/components/tools.js +256 -256
- package/packages/http.js +104 -104
- package/packages/index.js +43 -43
- package/packages/styles/animation.scss +27 -27
- package/packages/styles/dialog.scss +4 -4
- package/packages/styles/editor.scss +165 -165
- package/packages/styles/elPath.scss +257 -257
|
@@ -1,457 +1,457 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @Author: HuaYJ
|
|
3
|
-
* @Date: 2024/10/22 15:03
|
|
4
|
-
*/
|
|
5
|
-
<template>
|
|
6
|
-
<div class="control">
|
|
7
|
-
<el-collapse v-model="activeName" accordion>
|
|
8
|
-
<el-collapse-item title="文本内容" name="0" v-if="payload.data.type === 'text'">
|
|
9
|
-
<el-input ref="inputRef" type="textarea" v-model="text" @input="changeContent" @blur="leaveInput"
|
|
10
|
-
:rows="10"></el-input>
|
|
11
|
-
<el-alert type="info" title="提示"
|
|
12
|
-
description="请先选择设备,再选择字段,最后点击确定按钮,即可绑定设备数据。绑定完成后会由 $<> 进行包裹!"
|
|
13
|
-
style="margin-top: 20px;"></el-alert>
|
|
14
|
-
<el-select v-model="deviceCode" placeholder="请选择设备" @change="checkDevice" filterable clearable
|
|
15
|
-
style="margin-top: 20px">
|
|
16
|
-
<el-option v-for="item in deviceList" :key="item.deviceCode" :label="item.deviceName"
|
|
17
|
-
:value="item.deviceCode"/>
|
|
18
|
-
</el-select>
|
|
19
|
-
<el-table :data="fieldList" border style="margin-top: 20px;" size="small" height="500"
|
|
20
|
-
@row-click="checkField">
|
|
21
|
-
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
|
22
|
-
<el-table-column prop="name" label="字段名称" show-overflow-tooltip></el-table-column>
|
|
23
|
-
<el-table-column prop="dataField" label="字段编码" show-overflow-tooltip></el-table-column>
|
|
24
|
-
<el-table-column prop="unit" label="单位" width="50"></el-table-column>
|
|
25
|
-
</el-table>
|
|
26
|
-
</el-collapse-item>
|
|
27
|
-
<el-collapse-item title="图形设置" name="1">
|
|
28
|
-
<div class="row-column">
|
|
29
|
-
<el-row :gutter="20">
|
|
30
|
-
<el-col :span="12">
|
|
31
|
-
<div class="row-label">背景颜色</div>
|
|
32
|
-
</el-col>
|
|
33
|
-
<el-col :span="12">
|
|
34
|
-
<el-color-picker v-model="background" :show-alpha="true" size="small" @change="changeText"/>
|
|
35
|
-
</el-col>
|
|
36
|
-
</el-row>
|
|
37
|
-
<el-row :gutter="20">
|
|
38
|
-
<el-col :span="12">
|
|
39
|
-
<div class="row-label">边框颜色</div>
|
|
40
|
-
</el-col>
|
|
41
|
-
<el-col :span="12">
|
|
42
|
-
<el-color-picker v-model="stroke" :show-alpha="true" size="small" @change="changeText"/>
|
|
43
|
-
</el-col>
|
|
44
|
-
</el-row>
|
|
45
|
-
<el-row :gutter="20">
|
|
46
|
-
<el-col :span="12">
|
|
47
|
-
<div class="row-label">边框宽度</div>
|
|
48
|
-
</el-col>
|
|
49
|
-
<el-col :span="12">
|
|
50
|
-
<el-input-number v-model="strokeWidth" size="small" controls-position="right"
|
|
51
|
-
@change="changeText" :min="0" :max="10" :step="1"/>
|
|
52
|
-
</el-col>
|
|
53
|
-
</el-row>
|
|
54
|
-
<el-row :gutter="20">
|
|
55
|
-
<el-col :span="12">
|
|
56
|
-
<div class="row-label">背景圆角rx</div>
|
|
57
|
-
</el-col>
|
|
58
|
-
<el-col :span="12">
|
|
59
|
-
<el-input-number v-model="rx" size="small" controls-position="right" @change="changeText"
|
|
60
|
-
:min="0" :max="50" :step="1"/>
|
|
61
|
-
</el-col>
|
|
62
|
-
</el-row>
|
|
63
|
-
<el-row :gutter="20">
|
|
64
|
-
<el-col :span="12">
|
|
65
|
-
<div class="row-label">背景圆角ry</div>
|
|
66
|
-
</el-col>
|
|
67
|
-
<el-col :span="12">
|
|
68
|
-
<el-input-number v-model="ry" size="small" controls-position="right" @change="changeText"
|
|
69
|
-
:min="0" :max="50" :step="1"/>
|
|
70
|
-
</el-col>
|
|
71
|
-
</el-row>
|
|
72
|
-
</div>
|
|
73
|
-
</el-collapse-item>
|
|
74
|
-
<el-collapse-item title="文字设置" name="2" v-if="payload.data.type === 'text'">
|
|
75
|
-
<div class="row-column">
|
|
76
|
-
<el-row :gutter="20">
|
|
77
|
-
<el-col :span="12">
|
|
78
|
-
<div class="row-label">字体颜色</div>
|
|
79
|
-
</el-col>
|
|
80
|
-
<el-col :span="12">
|
|
81
|
-
<el-color-picker v-model="color" :show-alpha="true" size="small" @change="changeText"/>
|
|
82
|
-
</el-col>
|
|
83
|
-
</el-row>
|
|
84
|
-
<el-row :gutter="20">
|
|
85
|
-
<el-col :span="12">
|
|
86
|
-
<div class="row-label">字体大小</div>
|
|
87
|
-
</el-col>
|
|
88
|
-
<el-col :span="12">
|
|
89
|
-
<el-input-number v-model="size" size="small" controls-position="right" @change="changeText"
|
|
90
|
-
:min="12" :max="48" :step="2"/>
|
|
91
|
-
</el-col>
|
|
92
|
-
</el-row>
|
|
93
|
-
<el-row :gutter="20">
|
|
94
|
-
<el-col :span="12">
|
|
95
|
-
<div class="row-label">对齐方式</div>
|
|
96
|
-
</el-col>
|
|
97
|
-
<el-col :span="12">
|
|
98
|
-
<el-select v-model="textAnchor" size="small" @change="changeText">
|
|
99
|
-
<el-option v-for="item in ['start', 'middle', 'end']" :key="item" :label="item"
|
|
100
|
-
:value="item"/>
|
|
101
|
-
</el-select>
|
|
102
|
-
</el-col>
|
|
103
|
-
</el-row>
|
|
104
|
-
<el-row :gutter="20">
|
|
105
|
-
<el-col :span="12">
|
|
106
|
-
<div class="row-label">x轴偏移</div>
|
|
107
|
-
</el-col>
|
|
108
|
-
<el-col :span="12">
|
|
109
|
-
<el-input-number v-model="x" size="small" controls-position="right" @change="changeText"
|
|
110
|
-
:min="0" :max="100" :step="1"/>
|
|
111
|
-
</el-col>
|
|
112
|
-
</el-row>
|
|
113
|
-
<el-row :gutter="20">
|
|
114
|
-
<el-col :span="12">
|
|
115
|
-
<div class="row-label">y轴偏移</div>
|
|
116
|
-
</el-col>
|
|
117
|
-
<el-col :span="12">
|
|
118
|
-
<el-input-number v-model="y" size="small" controls-position="right" @change="changeText"
|
|
119
|
-
:min="0" :max="100" :step="1"/>
|
|
120
|
-
</el-col>
|
|
121
|
-
</el-row>
|
|
122
|
-
</div>
|
|
123
|
-
</el-collapse-item>
|
|
124
|
-
<el-collapse-item title="连接桩设置" name="3">
|
|
125
|
-
<el-button type="primary" @click="addPorts" size="small">添加连接桩</el-button>
|
|
126
|
-
<el-button type="primary" @click="showPort" size="small">显示连接桩</el-button>
|
|
127
|
-
<el-table :data="ports" border size="small" style="margin-top: 10px;">
|
|
128
|
-
<el-table-column label="ID" width="50" align="center">
|
|
129
|
-
<template v-slot="{row}">
|
|
130
|
-
{{ row.attrs.text.text }}
|
|
131
|
-
</template>
|
|
132
|
-
</el-table-column>
|
|
133
|
-
<el-table-column label="x" width="100" align="center">
|
|
134
|
-
<template v-slot="{row}">
|
|
135
|
-
<el-slider v-model="row.args.x" :min="0" :max="100" :step="1"
|
|
136
|
-
@input="(e) => changePorts(row.id, 'x', e)"/>
|
|
137
|
-
</template>
|
|
138
|
-
</el-table-column>
|
|
139
|
-
<el-table-column label="y" width="100" align="center">
|
|
140
|
-
<template v-slot="{row}">
|
|
141
|
-
<el-slider v-model="row.args.y" :min="0" :max="100" :step="1"
|
|
142
|
-
@input="(e) => changePorts(row.id, 'y', e)"/>
|
|
143
|
-
</template>
|
|
144
|
-
</el-table-column>
|
|
145
|
-
<el-table-column label="操作" align="center">
|
|
146
|
-
<template v-slot="{row}">
|
|
147
|
-
<el-button type="danger" size="small" @click="deletePort(row.id)">
|
|
148
|
-
<el-icon>
|
|
149
|
-
<delete-filled/>
|
|
150
|
-
</el-icon>
|
|
151
|
-
</el-button>
|
|
152
|
-
</template>
|
|
153
|
-
</el-table-column>
|
|
154
|
-
</el-table>
|
|
155
|
-
</el-collapse-item>
|
|
156
|
-
</el-collapse>
|
|
157
|
-
</div>
|
|
158
|
-
</template>
|
|
159
|
-
|
|
160
|
-
<script setup>
|
|
161
|
-
import {nextTick, onMounted, reactive, ref,} from "vue";
|
|
162
|
-
import {DeleteFilled} from '@element-plus/icons-vue'
|
|
163
|
-
import {getFieldList, showPorts} from "../common.js";
|
|
164
|
-
import {ElMessageBox} from "element-plus";
|
|
165
|
-
import {portOption} from "../settings.js";
|
|
166
|
-
import {defaultMatch, defaultText} from "packages/components/tools.js";
|
|
167
|
-
import {getDeviceList} from "/packages/components/common.js";
|
|
168
|
-
|
|
169
|
-
defineOptions({
|
|
170
|
-
name: 'text-control'
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
const props = defineProps({
|
|
174
|
-
payload: Object,
|
|
175
|
-
itemId: {
|
|
176
|
-
type: String,
|
|
177
|
-
default: ''
|
|
178
|
-
}
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
const activeName = ref(props.payload.attrs.label?.textAnchor ? '0' : '1') // 激活的折叠面板
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const deviceList = ref([]) // 设备列表
|
|
185
|
-
const fieldList = ref([])
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* 获取设备列表
|
|
189
|
-
*/
|
|
190
|
-
const getDevices = async () => {
|
|
191
|
-
deviceList.value = await getDeviceList(props.itemId)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const deviceCode = ref() // 设备编码
|
|
195
|
-
let currentDevice = reactive({}) // 当前设备信息
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* 监听设备编码变化
|
|
199
|
-
*/
|
|
200
|
-
const checkDevice = () => {
|
|
201
|
-
currentDevice = deviceList.value.find(item => item.deviceCode === deviceCode.value)
|
|
202
|
-
if (currentDevice) {
|
|
203
|
-
getFields()
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* 获取字段列表
|
|
209
|
-
*/
|
|
210
|
-
const getFields = async () => {
|
|
211
|
-
fieldList.value = await getFieldList(currentDevice.deviceCode)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const replace = (str) => {
|
|
216
|
-
return str.replace(defaultMatch, defaultText)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* 监听字段变化
|
|
221
|
-
* @param row
|
|
222
|
-
*/
|
|
223
|
-
const checkField = (row) => {
|
|
224
|
-
ElMessageBox.confirm(`是否确定要添加${currentDevice.deviceName}-${row.name}?\n 添加后为:--${row.unit ?? ''}`, '提示', {
|
|
225
|
-
confirmButtonText: '确定',
|
|
226
|
-
cancelButtonText: '取消',
|
|
227
|
-
type: 'warning'
|
|
228
|
-
}).then(() => {
|
|
229
|
-
const unit = row.unit ?? ''
|
|
230
|
-
const content = props.payload.data.content
|
|
231
|
-
console.log(cursorPosition.value)
|
|
232
|
-
if (cursorPosition.value) {
|
|
233
|
-
// 在记录的光标位置后面添加字段
|
|
234
|
-
props.payload.data.content = text.value = content.slice(0, cursorPosition.value) + '${' + currentDevice.deviceName + '.' + row.name + '}' + unit + content.slice(cursorPosition.value)
|
|
235
|
-
} else {
|
|
236
|
-
props.payload.data.content = text.value = content + '${' + currentDevice.deviceName + '.' + row.name + '}' + unit
|
|
237
|
-
}
|
|
238
|
-
// 查找所有${}
|
|
239
|
-
const matches = content.match(defaultMatch)
|
|
240
|
-
const index = matches?.length || 0
|
|
241
|
-
props.payload.data.fields.push({
|
|
242
|
-
index: index,
|
|
243
|
-
deviceName: currentDevice.deviceName,
|
|
244
|
-
deviceCode: currentDevice.deviceCode,
|
|
245
|
-
field: row.dataField,
|
|
246
|
-
fieldName: row.name,
|
|
247
|
-
})
|
|
248
|
-
props.payload.label = replace(props.payload.data.content)
|
|
249
|
-
console.log(props.payload.data.content, props.payload.data.fields)
|
|
250
|
-
})
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const text = ref(props.payload.data.content || null) // 文本内容
|
|
254
|
-
const background = ref(props.payload.attrs.body.fill || null) // 背景颜色
|
|
255
|
-
const stroke = ref(props.payload.attrs.body.stroke || null) // 边框颜色
|
|
256
|
-
const strokeWidth = ref(props.payload.attrs.body.strokeWidth || null) // 边框宽度
|
|
257
|
-
const rx = ref(props.payload.attrs.body.rx || null) // 圆角
|
|
258
|
-
const ry = ref(props.payload.attrs.body.ry || null) // 圆角
|
|
259
|
-
const color = ref(props.payload.attrs.text.fill || null) // 文字颜色
|
|
260
|
-
const size = ref(props.payload.attrs.text.fontSize || null) // 文字大小
|
|
261
|
-
const textAnchor = ref(props.payload.attrs.label?.textAnchor || null) // 文字对齐方式
|
|
262
|
-
const x = ref(props.payload.attrs.label?.x || null) // x轴偏移
|
|
263
|
-
const y = ref(props.payload.attrs.label?.y || null)
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* 编辑文字
|
|
267
|
-
*/
|
|
268
|
-
const changeText = () => {
|
|
269
|
-
props.payload.attr('body/fill', background.value)
|
|
270
|
-
props.payload.attr('body/stroke', stroke.value)
|
|
271
|
-
props.payload.attr('body/strokeWidth', strokeWidth.value)
|
|
272
|
-
props.payload.attr('body/rx', rx.value)
|
|
273
|
-
props.payload.attr('body/ry', ry.value)
|
|
274
|
-
props.payload.attr('text/fill', color.value)
|
|
275
|
-
props.payload.attr('text/fontSize', size.value)
|
|
276
|
-
props.payload.attr('label/textAnchor', textAnchor.value)
|
|
277
|
-
props.payload.attr('label/x', x.value)
|
|
278
|
-
props.payload.attr('label/y', y.value)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const inputRef = ref(null)
|
|
282
|
-
const forbiddenRegex = /[\$\{|\}]/g;
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* 编辑内容
|
|
286
|
-
*/
|
|
287
|
-
const changeContent = (currentValue) => {
|
|
288
|
-
if (forbiddenRegex.test(currentValue)) {
|
|
289
|
-
text.value = text.value.slice(0, -1)
|
|
290
|
-
}
|
|
291
|
-
const content = props.payload.data.content
|
|
292
|
-
|
|
293
|
-
if (currentValue.length < content.length) {
|
|
294
|
-
const {deleted, start, end} = getDeletedSubstring(content, currentValue);
|
|
295
|
-
if (deleted === '}') {
|
|
296
|
-
// 找到前面的"${" 删除
|
|
297
|
-
const startIndex = content.lastIndexOf('${', start)
|
|
298
|
-
const deleteContent = content.substring(startIndex, start) + '}'
|
|
299
|
-
text.value = content.replace(deleteContent, '')
|
|
300
|
-
// 字符串去掉'${'和'}'
|
|
301
|
-
const matchedContent = deleteContent.replace(/[${}]/g, '')
|
|
302
|
-
props.payload.data.fields = props.payload.data.fields.filter(item => {
|
|
303
|
-
return item.deviceName + '.' + item.fieldName !== matchedContent
|
|
304
|
-
})
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// 正常更新
|
|
309
|
-
props.payload.data.content = text.value;
|
|
310
|
-
props.payload.label = replace(text.value);
|
|
311
|
-
console.log(props.payload.data.content, props.payload.data.fields);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const getDeletedSubstring = (oldStr, newStr) => {
|
|
315
|
-
let i = 0;
|
|
316
|
-
let j = 0;
|
|
317
|
-
|
|
318
|
-
// 找到第一个不相同的字符位置
|
|
319
|
-
while (i < oldStr.length && j < newStr.length && oldStr[i] === newStr[j]) {
|
|
320
|
-
i++;
|
|
321
|
-
j++;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// 如果新字符串已经结束,则后续都是被删除的内容
|
|
325
|
-
if (j === newStr.length && i < oldStr.length) {
|
|
326
|
-
return {
|
|
327
|
-
deleted: oldStr.slice(i),
|
|
328
|
-
start: i,
|
|
329
|
-
end: oldStr.length
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// 向后尝试匹配剩余部分,找到最短删除范围
|
|
334
|
-
let k = oldStr.length - 1;
|
|
335
|
-
let l = newStr.length - 1;
|
|
336
|
-
|
|
337
|
-
while (k > i && l > j && oldStr[k] === newStr[l]) {
|
|
338
|
-
k--;
|
|
339
|
-
l--;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const deleted = oldStr.slice(i, k);
|
|
343
|
-
return {
|
|
344
|
-
deleted,
|
|
345
|
-
start: i,
|
|
346
|
-
end: k
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// 记录光标位置
|
|
351
|
-
const cursorPosition = ref(null)
|
|
352
|
-
|
|
353
|
-
const leaveInput = () => {
|
|
354
|
-
const inputElement = inputRef.value?.$refs.textarea;
|
|
355
|
-
cursorPosition.value = inputElement.selectionStart;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const ports = ref([]) // 连接桩数据
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* 获取连接桩数据
|
|
363
|
-
*/
|
|
364
|
-
const getPorts = () => {
|
|
365
|
-
ports.value = JSON.parse(JSON.stringify(props.payload.getPorts().map((item) => {
|
|
366
|
-
return {
|
|
367
|
-
id: item.id,
|
|
368
|
-
attrs: item.attrs,
|
|
369
|
-
args: {
|
|
370
|
-
x: Number(item.args.x.replace('%', '')),
|
|
371
|
-
y: Number(item.args.y.replace('%', ''))
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
})))
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* 添加连接桩
|
|
379
|
-
*/
|
|
380
|
-
const addPorts = () => {
|
|
381
|
-
const port = portOption
|
|
382
|
-
port.attrs.text.text = `${props.payload.getPorts().length + 1}`
|
|
383
|
-
port.args.x = '50%'
|
|
384
|
-
port.args.y = '50%'
|
|
385
|
-
props.payload.addPorts([
|
|
386
|
-
{
|
|
387
|
-
group: 'ports',
|
|
388
|
-
...port
|
|
389
|
-
},
|
|
390
|
-
])
|
|
391
|
-
getPorts()
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* 显示连接桩
|
|
396
|
-
*/
|
|
397
|
-
const showPort = () => {
|
|
398
|
-
showPorts("#drawing-board", true)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* 修改连接桩
|
|
403
|
-
* @param id
|
|
404
|
-
* @param key
|
|
405
|
-
* @param value
|
|
406
|
-
*/
|
|
407
|
-
const changePorts = (id, key, value) => {
|
|
408
|
-
props.payload.portProp(id, 'args/' + key, value + '%')
|
|
409
|
-
getPorts()
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* 删除连接桩
|
|
414
|
-
* @param id
|
|
415
|
-
*/
|
|
416
|
-
const deletePort = (id) => {
|
|
417
|
-
props.payload.removePort(id)
|
|
418
|
-
getPorts()
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
onMounted(() => {
|
|
423
|
-
getPorts()
|
|
424
|
-
getDevices()
|
|
425
|
-
|
|
426
|
-
nextTick(() => {
|
|
427
|
-
document.addEventListener('selectionchange', () => {
|
|
428
|
-
if (!inputRef.value) {
|
|
429
|
-
return false;
|
|
430
|
-
}
|
|
431
|
-
const inputElement = inputRef.value?.$refs.textarea;
|
|
432
|
-
const cursorPosition = inputElement.selectionStart;
|
|
433
|
-
const content = text.value;
|
|
434
|
-
const regex = /\$\{.*?\}/g;
|
|
435
|
-
let match;
|
|
436
|
-
|
|
437
|
-
while ((match = regex.exec(content)) !== null) {
|
|
438
|
-
const start = match.index;
|
|
439
|
-
const end = regex.lastIndex;
|
|
440
|
-
|
|
441
|
-
if (cursorPosition > start && cursorPosition < end) {
|
|
442
|
-
console.log('光标位于字段:', match[0]);
|
|
443
|
-
// 设置光标到往后的第一个}符号后面
|
|
444
|
-
inputElement.setSelectionRange(end, end);
|
|
445
|
-
return true; // 光标在 ${...} 内
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
console.log('光标不在任何字段内');
|
|
449
|
-
return false;
|
|
450
|
-
})
|
|
451
|
-
})
|
|
452
|
-
})
|
|
453
|
-
</script>
|
|
454
|
-
|
|
455
|
-
<style scoped lang="scss">
|
|
456
|
-
@use "../../styles/editor.scss";
|
|
457
|
-
</style>
|
|
1
|
+
/**
|
|
2
|
+
* @Author: HuaYJ
|
|
3
|
+
* @Date: 2024/10/22 15:03
|
|
4
|
+
*/
|
|
5
|
+
<template>
|
|
6
|
+
<div class="control">
|
|
7
|
+
<el-collapse v-model="activeName" accordion>
|
|
8
|
+
<el-collapse-item title="文本内容" name="0" v-if="payload.data.type === 'text'">
|
|
9
|
+
<el-input ref="inputRef" type="textarea" v-model="text" @input="changeContent" @blur="leaveInput"
|
|
10
|
+
:rows="10"></el-input>
|
|
11
|
+
<el-alert type="info" title="提示"
|
|
12
|
+
description="请先选择设备,再选择字段,最后点击确定按钮,即可绑定设备数据。绑定完成后会由 $<> 进行包裹!"
|
|
13
|
+
style="margin-top: 20px;"></el-alert>
|
|
14
|
+
<el-select v-model="deviceCode" placeholder="请选择设备" @change="checkDevice" filterable clearable
|
|
15
|
+
style="margin-top: 20px">
|
|
16
|
+
<el-option v-for="item in deviceList" :key="item.deviceCode" :label="item.deviceName"
|
|
17
|
+
:value="item.deviceCode"/>
|
|
18
|
+
</el-select>
|
|
19
|
+
<el-table :data="fieldList" border style="margin-top: 20px;" size="small" height="500"
|
|
20
|
+
@row-click="checkField">
|
|
21
|
+
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
|
22
|
+
<el-table-column prop="name" label="字段名称" show-overflow-tooltip></el-table-column>
|
|
23
|
+
<el-table-column prop="dataField" label="字段编码" show-overflow-tooltip></el-table-column>
|
|
24
|
+
<el-table-column prop="unit" label="单位" width="50"></el-table-column>
|
|
25
|
+
</el-table>
|
|
26
|
+
</el-collapse-item>
|
|
27
|
+
<el-collapse-item title="图形设置" name="1">
|
|
28
|
+
<div class="row-column">
|
|
29
|
+
<el-row :gutter="20">
|
|
30
|
+
<el-col :span="12">
|
|
31
|
+
<div class="row-label">背景颜色</div>
|
|
32
|
+
</el-col>
|
|
33
|
+
<el-col :span="12">
|
|
34
|
+
<el-color-picker v-model="background" :show-alpha="true" size="small" @change="changeText"/>
|
|
35
|
+
</el-col>
|
|
36
|
+
</el-row>
|
|
37
|
+
<el-row :gutter="20">
|
|
38
|
+
<el-col :span="12">
|
|
39
|
+
<div class="row-label">边框颜色</div>
|
|
40
|
+
</el-col>
|
|
41
|
+
<el-col :span="12">
|
|
42
|
+
<el-color-picker v-model="stroke" :show-alpha="true" size="small" @change="changeText"/>
|
|
43
|
+
</el-col>
|
|
44
|
+
</el-row>
|
|
45
|
+
<el-row :gutter="20">
|
|
46
|
+
<el-col :span="12">
|
|
47
|
+
<div class="row-label">边框宽度</div>
|
|
48
|
+
</el-col>
|
|
49
|
+
<el-col :span="12">
|
|
50
|
+
<el-input-number v-model="strokeWidth" size="small" controls-position="right"
|
|
51
|
+
@change="changeText" :min="0" :max="10" :step="1"/>
|
|
52
|
+
</el-col>
|
|
53
|
+
</el-row>
|
|
54
|
+
<el-row :gutter="20">
|
|
55
|
+
<el-col :span="12">
|
|
56
|
+
<div class="row-label">背景圆角rx</div>
|
|
57
|
+
</el-col>
|
|
58
|
+
<el-col :span="12">
|
|
59
|
+
<el-input-number v-model="rx" size="small" controls-position="right" @change="changeText"
|
|
60
|
+
:min="0" :max="50" :step="1"/>
|
|
61
|
+
</el-col>
|
|
62
|
+
</el-row>
|
|
63
|
+
<el-row :gutter="20">
|
|
64
|
+
<el-col :span="12">
|
|
65
|
+
<div class="row-label">背景圆角ry</div>
|
|
66
|
+
</el-col>
|
|
67
|
+
<el-col :span="12">
|
|
68
|
+
<el-input-number v-model="ry" size="small" controls-position="right" @change="changeText"
|
|
69
|
+
:min="0" :max="50" :step="1"/>
|
|
70
|
+
</el-col>
|
|
71
|
+
</el-row>
|
|
72
|
+
</div>
|
|
73
|
+
</el-collapse-item>
|
|
74
|
+
<el-collapse-item title="文字设置" name="2" v-if="payload.data.type === 'text'">
|
|
75
|
+
<div class="row-column">
|
|
76
|
+
<el-row :gutter="20">
|
|
77
|
+
<el-col :span="12">
|
|
78
|
+
<div class="row-label">字体颜色</div>
|
|
79
|
+
</el-col>
|
|
80
|
+
<el-col :span="12">
|
|
81
|
+
<el-color-picker v-model="color" :show-alpha="true" size="small" @change="changeText"/>
|
|
82
|
+
</el-col>
|
|
83
|
+
</el-row>
|
|
84
|
+
<el-row :gutter="20">
|
|
85
|
+
<el-col :span="12">
|
|
86
|
+
<div class="row-label">字体大小</div>
|
|
87
|
+
</el-col>
|
|
88
|
+
<el-col :span="12">
|
|
89
|
+
<el-input-number v-model="size" size="small" controls-position="right" @change="changeText"
|
|
90
|
+
:min="12" :max="48" :step="2"/>
|
|
91
|
+
</el-col>
|
|
92
|
+
</el-row>
|
|
93
|
+
<el-row :gutter="20">
|
|
94
|
+
<el-col :span="12">
|
|
95
|
+
<div class="row-label">对齐方式</div>
|
|
96
|
+
</el-col>
|
|
97
|
+
<el-col :span="12">
|
|
98
|
+
<el-select v-model="textAnchor" size="small" @change="changeText">
|
|
99
|
+
<el-option v-for="item in ['start', 'middle', 'end']" :key="item" :label="item"
|
|
100
|
+
:value="item"/>
|
|
101
|
+
</el-select>
|
|
102
|
+
</el-col>
|
|
103
|
+
</el-row>
|
|
104
|
+
<el-row :gutter="20">
|
|
105
|
+
<el-col :span="12">
|
|
106
|
+
<div class="row-label">x轴偏移</div>
|
|
107
|
+
</el-col>
|
|
108
|
+
<el-col :span="12">
|
|
109
|
+
<el-input-number v-model="x" size="small" controls-position="right" @change="changeText"
|
|
110
|
+
:min="0" :max="100" :step="1"/>
|
|
111
|
+
</el-col>
|
|
112
|
+
</el-row>
|
|
113
|
+
<el-row :gutter="20">
|
|
114
|
+
<el-col :span="12">
|
|
115
|
+
<div class="row-label">y轴偏移</div>
|
|
116
|
+
</el-col>
|
|
117
|
+
<el-col :span="12">
|
|
118
|
+
<el-input-number v-model="y" size="small" controls-position="right" @change="changeText"
|
|
119
|
+
:min="0" :max="100" :step="1"/>
|
|
120
|
+
</el-col>
|
|
121
|
+
</el-row>
|
|
122
|
+
</div>
|
|
123
|
+
</el-collapse-item>
|
|
124
|
+
<el-collapse-item title="连接桩设置" name="3">
|
|
125
|
+
<el-button type="primary" @click="addPorts" size="small">添加连接桩</el-button>
|
|
126
|
+
<el-button type="primary" @click="showPort" size="small">显示连接桩</el-button>
|
|
127
|
+
<el-table :data="ports" border size="small" style="margin-top: 10px;">
|
|
128
|
+
<el-table-column label="ID" width="50" align="center">
|
|
129
|
+
<template v-slot="{row}">
|
|
130
|
+
{{ row.attrs.text.text }}
|
|
131
|
+
</template>
|
|
132
|
+
</el-table-column>
|
|
133
|
+
<el-table-column label="x" width="100" align="center">
|
|
134
|
+
<template v-slot="{row}">
|
|
135
|
+
<el-slider v-model="row.args.x" :min="0" :max="100" :step="1"
|
|
136
|
+
@input="(e) => changePorts(row.id, 'x', e)"/>
|
|
137
|
+
</template>
|
|
138
|
+
</el-table-column>
|
|
139
|
+
<el-table-column label="y" width="100" align="center">
|
|
140
|
+
<template v-slot="{row}">
|
|
141
|
+
<el-slider v-model="row.args.y" :min="0" :max="100" :step="1"
|
|
142
|
+
@input="(e) => changePorts(row.id, 'y', e)"/>
|
|
143
|
+
</template>
|
|
144
|
+
</el-table-column>
|
|
145
|
+
<el-table-column label="操作" align="center">
|
|
146
|
+
<template v-slot="{row}">
|
|
147
|
+
<el-button type="danger" size="small" @click="deletePort(row.id)">
|
|
148
|
+
<el-icon>
|
|
149
|
+
<delete-filled/>
|
|
150
|
+
</el-icon>
|
|
151
|
+
</el-button>
|
|
152
|
+
</template>
|
|
153
|
+
</el-table-column>
|
|
154
|
+
</el-table>
|
|
155
|
+
</el-collapse-item>
|
|
156
|
+
</el-collapse>
|
|
157
|
+
</div>
|
|
158
|
+
</template>
|
|
159
|
+
|
|
160
|
+
<script setup>
|
|
161
|
+
import {nextTick, onMounted, reactive, ref,} from "vue";
|
|
162
|
+
import {DeleteFilled} from '@element-plus/icons-vue'
|
|
163
|
+
import {getFieldList, showPorts} from "../common.js";
|
|
164
|
+
import {ElMessageBox} from "element-plus";
|
|
165
|
+
import {portOption} from "../settings.js";
|
|
166
|
+
import {defaultMatch, defaultText} from "packages/components/tools.js";
|
|
167
|
+
import {getDeviceList} from "/packages/components/common.js";
|
|
168
|
+
|
|
169
|
+
defineOptions({
|
|
170
|
+
name: 'text-control'
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const props = defineProps({
|
|
174
|
+
payload: Object,
|
|
175
|
+
itemId: {
|
|
176
|
+
type: String,
|
|
177
|
+
default: ''
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const activeName = ref(props.payload.attrs.label?.textAnchor ? '0' : '1') // 激活的折叠面板
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
const deviceList = ref([]) // 设备列表
|
|
185
|
+
const fieldList = ref([])
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 获取设备列表
|
|
189
|
+
*/
|
|
190
|
+
const getDevices = async () => {
|
|
191
|
+
deviceList.value = await getDeviceList(props.itemId)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const deviceCode = ref() // 设备编码
|
|
195
|
+
let currentDevice = reactive({}) // 当前设备信息
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 监听设备编码变化
|
|
199
|
+
*/
|
|
200
|
+
const checkDevice = () => {
|
|
201
|
+
currentDevice = deviceList.value.find(item => item.deviceCode === deviceCode.value)
|
|
202
|
+
if (currentDevice) {
|
|
203
|
+
getFields()
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 获取字段列表
|
|
209
|
+
*/
|
|
210
|
+
const getFields = async () => {
|
|
211
|
+
fieldList.value = await getFieldList(currentDevice.deviceCode)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
const replace = (str) => {
|
|
216
|
+
return str.replace(defaultMatch, defaultText)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* 监听字段变化
|
|
221
|
+
* @param row
|
|
222
|
+
*/
|
|
223
|
+
const checkField = (row) => {
|
|
224
|
+
ElMessageBox.confirm(`是否确定要添加${currentDevice.deviceName}-${row.name}?\n 添加后为:--${row.unit ?? ''}`, '提示', {
|
|
225
|
+
confirmButtonText: '确定',
|
|
226
|
+
cancelButtonText: '取消',
|
|
227
|
+
type: 'warning'
|
|
228
|
+
}).then(() => {
|
|
229
|
+
const unit = row.unit ?? ''
|
|
230
|
+
const content = props.payload.data.content
|
|
231
|
+
console.log(cursorPosition.value)
|
|
232
|
+
if (cursorPosition.value) {
|
|
233
|
+
// 在记录的光标位置后面添加字段
|
|
234
|
+
props.payload.data.content = text.value = content.slice(0, cursorPosition.value) + '${' + currentDevice.deviceName + '.' + row.name + '}' + unit + content.slice(cursorPosition.value)
|
|
235
|
+
} else {
|
|
236
|
+
props.payload.data.content = text.value = content + '${' + currentDevice.deviceName + '.' + row.name + '}' + unit
|
|
237
|
+
}
|
|
238
|
+
// 查找所有${}
|
|
239
|
+
const matches = content.match(defaultMatch)
|
|
240
|
+
const index = matches?.length || 0
|
|
241
|
+
props.payload.data.fields.push({
|
|
242
|
+
index: index,
|
|
243
|
+
deviceName: currentDevice.deviceName,
|
|
244
|
+
deviceCode: currentDevice.deviceCode,
|
|
245
|
+
field: row.dataField,
|
|
246
|
+
fieldName: row.name,
|
|
247
|
+
})
|
|
248
|
+
props.payload.label = replace(props.payload.data.content)
|
|
249
|
+
console.log(props.payload.data.content, props.payload.data.fields)
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const text = ref(props.payload.data.content || null) // 文本内容
|
|
254
|
+
const background = ref(props.payload.attrs.body.fill || null) // 背景颜色
|
|
255
|
+
const stroke = ref(props.payload.attrs.body.stroke || null) // 边框颜色
|
|
256
|
+
const strokeWidth = ref(props.payload.attrs.body.strokeWidth || null) // 边框宽度
|
|
257
|
+
const rx = ref(props.payload.attrs.body.rx || null) // 圆角
|
|
258
|
+
const ry = ref(props.payload.attrs.body.ry || null) // 圆角
|
|
259
|
+
const color = ref(props.payload.attrs.text.fill || null) // 文字颜色
|
|
260
|
+
const size = ref(props.payload.attrs.text.fontSize || null) // 文字大小
|
|
261
|
+
const textAnchor = ref(props.payload.attrs.label?.textAnchor || null) // 文字对齐方式
|
|
262
|
+
const x = ref(props.payload.attrs.label?.x || null) // x轴偏移
|
|
263
|
+
const y = ref(props.payload.attrs.label?.y || null)
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 编辑文字
|
|
267
|
+
*/
|
|
268
|
+
const changeText = () => {
|
|
269
|
+
props.payload.attr('body/fill', background.value)
|
|
270
|
+
props.payload.attr('body/stroke', stroke.value)
|
|
271
|
+
props.payload.attr('body/strokeWidth', strokeWidth.value)
|
|
272
|
+
props.payload.attr('body/rx', rx.value)
|
|
273
|
+
props.payload.attr('body/ry', ry.value)
|
|
274
|
+
props.payload.attr('text/fill', color.value)
|
|
275
|
+
props.payload.attr('text/fontSize', size.value)
|
|
276
|
+
props.payload.attr('label/textAnchor', textAnchor.value)
|
|
277
|
+
props.payload.attr('label/x', x.value)
|
|
278
|
+
props.payload.attr('label/y', y.value)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const inputRef = ref(null)
|
|
282
|
+
const forbiddenRegex = /[\$\{|\}]/g;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 编辑内容
|
|
286
|
+
*/
|
|
287
|
+
const changeContent = (currentValue) => {
|
|
288
|
+
if (forbiddenRegex.test(currentValue)) {
|
|
289
|
+
text.value = text.value.slice(0, -1)
|
|
290
|
+
}
|
|
291
|
+
const content = props.payload.data.content
|
|
292
|
+
|
|
293
|
+
if (currentValue.length < content.length) {
|
|
294
|
+
const {deleted, start, end} = getDeletedSubstring(content, currentValue);
|
|
295
|
+
if (deleted === '}') {
|
|
296
|
+
// 找到前面的"${" 删除
|
|
297
|
+
const startIndex = content.lastIndexOf('${', start)
|
|
298
|
+
const deleteContent = content.substring(startIndex, start) + '}'
|
|
299
|
+
text.value = content.replace(deleteContent, '')
|
|
300
|
+
// 字符串去掉'${'和'}'
|
|
301
|
+
const matchedContent = deleteContent.replace(/[${}]/g, '')
|
|
302
|
+
props.payload.data.fields = props.payload.data.fields.filter(item => {
|
|
303
|
+
return item.deviceName + '.' + item.fieldName !== matchedContent
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 正常更新
|
|
309
|
+
props.payload.data.content = text.value;
|
|
310
|
+
props.payload.label = replace(text.value);
|
|
311
|
+
console.log(props.payload.data.content, props.payload.data.fields);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const getDeletedSubstring = (oldStr, newStr) => {
|
|
315
|
+
let i = 0;
|
|
316
|
+
let j = 0;
|
|
317
|
+
|
|
318
|
+
// 找到第一个不相同的字符位置
|
|
319
|
+
while (i < oldStr.length && j < newStr.length && oldStr[i] === newStr[j]) {
|
|
320
|
+
i++;
|
|
321
|
+
j++;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 如果新字符串已经结束,则后续都是被删除的内容
|
|
325
|
+
if (j === newStr.length && i < oldStr.length) {
|
|
326
|
+
return {
|
|
327
|
+
deleted: oldStr.slice(i),
|
|
328
|
+
start: i,
|
|
329
|
+
end: oldStr.length
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 向后尝试匹配剩余部分,找到最短删除范围
|
|
334
|
+
let k = oldStr.length - 1;
|
|
335
|
+
let l = newStr.length - 1;
|
|
336
|
+
|
|
337
|
+
while (k > i && l > j && oldStr[k] === newStr[l]) {
|
|
338
|
+
k--;
|
|
339
|
+
l--;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const deleted = oldStr.slice(i, k);
|
|
343
|
+
return {
|
|
344
|
+
deleted,
|
|
345
|
+
start: i,
|
|
346
|
+
end: k
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 记录光标位置
|
|
351
|
+
const cursorPosition = ref(null)
|
|
352
|
+
|
|
353
|
+
const leaveInput = () => {
|
|
354
|
+
const inputElement = inputRef.value?.$refs.textarea;
|
|
355
|
+
cursorPosition.value = inputElement.selectionStart;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
const ports = ref([]) // 连接桩数据
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* 获取连接桩数据
|
|
363
|
+
*/
|
|
364
|
+
const getPorts = () => {
|
|
365
|
+
ports.value = JSON.parse(JSON.stringify(props.payload.getPorts().map((item) => {
|
|
366
|
+
return {
|
|
367
|
+
id: item.id,
|
|
368
|
+
attrs: item.attrs,
|
|
369
|
+
args: {
|
|
370
|
+
x: Number(item.args.x.replace('%', '')),
|
|
371
|
+
y: Number(item.args.y.replace('%', ''))
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
})))
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* 添加连接桩
|
|
379
|
+
*/
|
|
380
|
+
const addPorts = () => {
|
|
381
|
+
const port = portOption
|
|
382
|
+
port.attrs.text.text = `${props.payload.getPorts().length + 1}`
|
|
383
|
+
port.args.x = '50%'
|
|
384
|
+
port.args.y = '50%'
|
|
385
|
+
props.payload.addPorts([
|
|
386
|
+
{
|
|
387
|
+
group: 'ports',
|
|
388
|
+
...port
|
|
389
|
+
},
|
|
390
|
+
])
|
|
391
|
+
getPorts()
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* 显示连接桩
|
|
396
|
+
*/
|
|
397
|
+
const showPort = () => {
|
|
398
|
+
showPorts("#drawing-board", true)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* 修改连接桩
|
|
403
|
+
* @param id
|
|
404
|
+
* @param key
|
|
405
|
+
* @param value
|
|
406
|
+
*/
|
|
407
|
+
const changePorts = (id, key, value) => {
|
|
408
|
+
props.payload.portProp(id, 'args/' + key, value + '%')
|
|
409
|
+
getPorts()
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* 删除连接桩
|
|
414
|
+
* @param id
|
|
415
|
+
*/
|
|
416
|
+
const deletePort = (id) => {
|
|
417
|
+
props.payload.removePort(id)
|
|
418
|
+
getPorts()
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
onMounted(() => {
|
|
423
|
+
getPorts()
|
|
424
|
+
getDevices()
|
|
425
|
+
|
|
426
|
+
nextTick(() => {
|
|
427
|
+
document.addEventListener('selectionchange', () => {
|
|
428
|
+
if (!inputRef.value) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
const inputElement = inputRef.value?.$refs.textarea;
|
|
432
|
+
const cursorPosition = inputElement.selectionStart;
|
|
433
|
+
const content = text.value;
|
|
434
|
+
const regex = /\$\{.*?\}/g;
|
|
435
|
+
let match;
|
|
436
|
+
|
|
437
|
+
while ((match = regex.exec(content)) !== null) {
|
|
438
|
+
const start = match.index;
|
|
439
|
+
const end = regex.lastIndex;
|
|
440
|
+
|
|
441
|
+
if (cursorPosition > start && cursorPosition < end) {
|
|
442
|
+
console.log('光标位于字段:', match[0]);
|
|
443
|
+
// 设置光标到往后的第一个}符号后面
|
|
444
|
+
inputElement.setSelectionRange(end, end);
|
|
445
|
+
return true; // 光标在 ${...} 内
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
console.log('光标不在任何字段内');
|
|
449
|
+
return false;
|
|
450
|
+
})
|
|
451
|
+
})
|
|
452
|
+
})
|
|
453
|
+
</script>
|
|
454
|
+
|
|
455
|
+
<style scoped lang="scss">
|
|
456
|
+
@use "../../styles/editor.scss";
|
|
457
|
+
</style>
|