vue-wiring-diagram 1.0.6 → 1.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-wiring-diagram",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "A Vue 3.x component for wiring diagram",
5
5
  "type": "module",
6
6
  "main": "dist/vue-wiring-diagram.umd.js",
@@ -12,7 +12,7 @@ export const baseShape = [
12
12
  },
13
13
  data: {
14
14
  type: 'base-shape'
15
- }
15
+ },
16
16
  },
17
17
  {
18
18
  shape: 'rect',
@@ -23,7 +23,7 @@
23
23
  </template>
24
24
  </el-dropdown>
25
25
  </div>
26
- <div class="action">
26
+ <div class="action" style="margin-right: 50px">
27
27
  <el-tooltip content="预览" placement="bottom">
28
28
  <el-button size="small" :icon="View" @click="viewJson">
29
29
  </el-button>
@@ -89,6 +89,8 @@ import {portsOptions} from "../portsOptions.js";
89
89
  import {Document, View, ArrowDown} from "@element-plus/icons-vue";
90
90
  import WiringDiagramPreview from "../preview/index.vue";
91
91
  import ImageManagement from "../image-control/image-management.vue";
92
+ import {get} from "packages/http.js";
93
+ import towerPoles from "packages/assets/image/tower-poles.svg";
92
94
 
93
95
  defineOptions({
94
96
  name: 'editor'
@@ -290,7 +292,18 @@ const stencil = ref()
290
292
  const initStencil = () => {
291
293
  stencil.value = new Stencil({
292
294
  target: graph.value,
293
- ...stencilOptions
295
+ ...stencilOptions,
296
+ groups: [
297
+ {
298
+ name: '基础组件',
299
+ graphPadding: 30
300
+ },
301
+ ],
302
+ search(cell, keyword) {
303
+ return cell.label?.indexOf(keyword) !== -1
304
+ },
305
+ placeholder: '搜索',
306
+ notFoundText: '未找到匹配项',
294
307
  })
295
308
  const text = graph.value.createNode({
296
309
  ...textOptions
@@ -299,47 +312,66 @@ const initStencil = () => {
299
312
  item.ports = portsOptions
300
313
  return graph.value.createNode(item)
301
314
  })
302
- const image = imageOptions.map(item => {
303
- item.ports = portsOptions
304
- return graph.value.createNode(item)
305
- })
306
315
  stencil.value.load([text, ...baseShapes], '基础组件')
307
- stencil.value.load(image, 'group2')
308
- document.getElementById('stencil').appendChild(stencil.value.container)
309
-
310
- // 添加 hover 事件监听器
311
- setTimeout(() => {
312
- const nodes = stencil.value.container.querySelectorAll('.x6-node');
313
- nodes.forEach(node => {
314
- const div = document.createElement('div');
315
- div.className = 'node-label';
316
- // 添加 hover 事件监听器
317
- node.addEventListener('mouseenter', (e) => {
318
- div.style.position = 'absolute';
319
- div.innerText = Math.random().toString(36).substring(2, 7);
320
- div.style.fontSize = '12px';
321
- div.style.backgroundColor = '#000';
322
- div.style.color = '#fff';
323
- div.style.padding = '2px 4px';
324
- div.style.borderRadius = '4px';
325
- div.style.zIndex = '9999';
326
- div.style.textAlign = 'center';
327
- div.style.width = div.innerText.length * 12 + 'px';
328
- const divWidth = Number(div.style.width.substring(0, div.style.width.length - 2));
329
- div.style.top = e.target.getBoundingClientRect().bottom + 4 + 'px';
330
- div.style.left = e.target.getBoundingClientRect().left + e.target.getBoundingClientRect().width / 2 - divWidth / 2 + 'px';
331
- document.body.appendChild(div);
316
+ get('/cny/custom/groupList').then(res => {
317
+ const data = res?.data || []
318
+ data?.forEach(group => {
319
+ stencil.value.addGroup({
320
+ name: group?.groupName,
321
+ graphPadding: 30
332
322
  })
333
- node.addEventListener('mouseleave', (e) => {
334
- const divs = document.querySelectorAll('.node-label');
335
- if (divs.length)
336
- divs?.forEach(div => {
337
- document.body.removeChild(div);
338
- })
323
+ const image = group?.pictureList.map(item => {
324
+ const nodeOption = {
325
+ shape: 'image',
326
+ imageUrl: item?.imageUrl,
327
+ ports: portsOptions,
328
+ width: 64,
329
+ height: 64,
330
+ label: item.imageName
331
+ }
332
+ return graph.value.createNode(nodeOption)
339
333
  })
340
- });
341
- }, 1000)
342
-
334
+ stencil.value.load(image, group?.groupName)
335
+ document.getElementById('stencil').appendChild(stencil.value.container)
336
+ })
337
+ }).finally(() => {
338
+ // 添加 hover 事件监听器
339
+ setTimeout(() => {
340
+ const nodes = stencil.value.container.querySelectorAll('.x6-node');
341
+ nodes.forEach((node,index) => {
342
+ if(index > baseShape.length) {
343
+ const text = node.querySelector('text');
344
+ node.style.position = 'relative';
345
+ if(text) {
346
+ text.style.display = 'none';
347
+ // text 为 svg 控制位置
348
+ text.setAttribute('x', '0%');
349
+ text.setAttribute('y', '30%');
350
+ text.style.fontSize = '12px';
351
+ text.style.fontWeight = 'bold';
352
+ text.style.fill = '#ffffff';
353
+ }
354
+ }
355
+ // 添加 hover 事件监听器
356
+ node.addEventListener('mouseenter', (e) => {
357
+ if(index > baseShape.length) {
358
+ const text = node.querySelector('text');
359
+ if(text) {
360
+ text.style.display = 'block';
361
+ }
362
+ }
363
+ })
364
+ node.addEventListener('mouseleave', (e) => {
365
+ if(index > baseShape.length) {
366
+ const text = node.querySelector('text');
367
+ if(text) {
368
+ text.style.display = 'none';
369
+ }
370
+ }
371
+ })
372
+ });
373
+ }, 20)
374
+ })
343
375
  }
344
376
 
345
377
  /**
@@ -4,14 +4,44 @@
4
4
  */
5
5
  <template>
6
6
  <div class="form-container">
7
- <el-form ref="form" :model="model" :rules="rules" label-width="90px" label-suffix=":"></el-form>
8
-
7
+ <el-form ref="form" :model="model" :rules="rules" label-width="90px" label-suffix=":">
8
+ <el-form-item label="图片名称" prop="imageName">
9
+ <el-input v-model="model.imageName" placeholder="请输入图片名称"></el-input>
10
+ </el-form-item>
11
+ <el-form-item label="排序" prop="sort">
12
+ <el-input-number v-model="model.sort" :min="0" :max="999" :step="1" controls-position="right"/>
13
+ </el-form-item>
14
+ <el-form-item label="是否显示" prop="isShow">
15
+ <el-switch v-model="model.isShow" :active-value="1" :inactive-value="0" active-color="#13ce66" inactive-color="#ff4949"/>
16
+ </el-form-item>
17
+ <el-form-item label="上传" prop="image">
18
+ <el-upload
19
+ action=""
20
+ list-type="picture"
21
+ class="avatar-uploader"
22
+ :show-file-list="false"
23
+ :on-change="changeImage"
24
+ :auto-upload="false"
25
+ >
26
+ <el-image v-if="imageUrl" :src="imageUrl" fit="cover" style="width: 64px; height: 64px;"/>
27
+ <el-icon v-else class="avatar-uploader-icon">
28
+ <Plus/>
29
+ </el-icon>
30
+ </el-upload>
31
+ </el-form-item>
32
+ <div class="footer">
33
+ <el-button type="primary" @click="saveBtnClick">保存</el-button>
34
+ <el-button @click="cancelBtnClick">取消</el-button>
35
+ </div>
36
+ </el-form>
9
37
  </div>
10
38
  </template>
11
39
 
12
40
  <script setup>
13
- import {reactive, ref} from 'vue'
14
- import {ElMessage} from 'element-plus'
41
+ import {onMounted, reactive, ref, shallowRef} from 'vue'
42
+ import {ElMessage} from "element-plus";
43
+ import {instance, post, upload} from "packages/http.js";
44
+ import {Plus} from "@element-plus/icons-vue";
15
45
 
16
46
  const emits = defineEmits(['close'])
17
47
  const props = defineProps({
@@ -22,11 +52,126 @@ const props = defineProps({
22
52
  }
23
53
  }
24
54
  })
25
- const form = ref(null)
26
- const model = reactive({})
27
- const rules = reactive({})
55
+ const form = shallowRef(null)
56
+ const model = reactive({
57
+ groupId: '',
58
+ id: '',
59
+ imageName: '',
60
+ sort: 0,
61
+ isShow: 1,
62
+ imageUrl: '',
63
+ })
64
+ const rules = ({
65
+ imageName: [
66
+ {required: true, message: '请输入图片名称', trigger: 'blur'},
67
+ {min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur'}
68
+ ],
69
+ sort: [
70
+ {required: true, message: '请输入排序', trigger: 'blur'},
71
+ {type: 'number', message: '排序必须为数字值'}
72
+ ]
73
+ })
74
+
75
+ const imageUrl = ref('')
76
+
77
+ /**
78
+ * 图片改变事件
79
+ * @param file
80
+ */
81
+ const changeImage = (file) => {
82
+ if (file.raw.type !== 'image/svg+xml') {
83
+ ElMessage.error('上传图片只能是 svg 格式!');
84
+ return false;
85
+ }
86
+ const formData = new FormData();
87
+ formData.append('file', file.raw);
88
+ upload('/cny/file/fileUpload', formData).then(res => {
89
+ if (res?.isOk) {
90
+ imageUrl.value = instance.defaults.baseURL + '/cny/upload/' + res?.data
91
+ model.imageUrl = res?.data
92
+ } else {
93
+ ElMessage.error(res?.msg || '上传失败')
94
+ }
95
+ })
96
+ }
97
+
98
+
99
+ /**
100
+ * 保存按钮点击事件
101
+ */
102
+ const saveBtnClick = () => {
103
+ console.log(model)
104
+ form.value.validate((valid) => {
105
+ if (valid) {
106
+ post('/cny/custom/savePicture', model).then(res => {
107
+ if (res?.isOk) {
108
+ ElMessage.success('保存成功')
109
+ emits('close', true)
110
+ } else {
111
+ ElMessage.error(res?.msg || '保存失败')
112
+ }
113
+ })
114
+ } else {
115
+ return false;
116
+ }
117
+ })
118
+ }
119
+
120
+ /**
121
+ * 取消按钮点击事件
122
+ */
123
+ const cancelBtnClick = () => {
124
+ emits('close', false)
125
+ }
126
+
127
+ onMounted(() => {
128
+ if (props.payload?.id) {
129
+ model.id = props.payload.id
130
+ model.imageName = props.payload.imageName
131
+ model.sort = props.payload.sort
132
+ model.isShow = props.payload.isShow
133
+ model.imageUrl = props.payload.imageUrl
134
+ imageUrl.value = model.imageUrl
135
+ }
136
+ model.groupId = props.payload.groupId
137
+ })
28
138
  </script>
29
139
 
30
140
  <style scoped lang="scss">
141
+ @use "../../styles/dialog.scss";
142
+
143
+ .avatar-uploader {
144
+ width: 64px;
145
+ height: 64px;
146
+
147
+
148
+ :deep(.el-upload) {
149
+ width: 100%;
150
+ height: 100%;
151
+ border: 1px dashed rgb(35, 100, 221);
152
+ border-radius: 6px;
153
+ cursor: pointer;
154
+ position: relative;
155
+ overflow: hidden;
156
+ transition: var(--el-transition-duration-fast);
157
+
158
+ :deep(.el-upload:hover) {
159
+ border-color: var(--el-color-primary);
160
+ }
161
+
162
+ .el-icon {
163
+ color: rgb(35, 100, 221);
164
+ }
165
+
166
+
167
+ :deep(.el-icon).avatar-uploader-icon {
168
+ font-size: 28px;
169
+ color: rgb(35, 100, 221);
170
+ width: 178px;
171
+ height: 178px;
172
+ text-align: center;
173
+ }
174
+ }
175
+ }
31
176
 
32
177
  </style>
@@ -11,6 +11,7 @@
11
11
  <div style="padding: 0 10px;display: flex;flex-direction: column;gap: 5px;align-items: flex-start">
12
12
  <el-button type="primary" size="small" @click="addPicture(props.row.id)">新增图片</el-button>
13
13
  <el-table :data="props.row.pictureList" border size="small">
14
+ <el-table-column label="名称" prop="imageName" align="center"></el-table-column>
14
15
  <el-table-column label="图片" prop="imageUrl" align="center">
15
16
  <template #default="scope">
16
17
  <img :src="scope.row.imageUrl" alt="" style="width: 64px; height: 64px;">
@@ -22,6 +23,12 @@
22
23
  {{ scope.row.isShow === 1 ? '是' : '否' }}
23
24
  </template>
24
25
  </el-table-column>
26
+ <el-table-column label="操作" align="center">
27
+ <template #default="scope">
28
+ <el-button type="primary" size="small" @click="editPicture(scope.row)">编辑</el-button>
29
+ <el-button type="danger" size="small" @click="deletePicture(scope.row.id)">删除</el-button>
30
+ </template>
31
+ </el-table-column>
25
32
  </el-table>
26
33
  </div>
27
34
  </template>
@@ -42,11 +49,11 @@
42
49
  </el-table>
43
50
  <el-dialog v-model="groupDialog.show" :title="groupDialog.title" width="300" :close-on-click-modal="false" :close-on-press-escape="false"
44
51
  >
45
- <group-form :payload="groupDialog.payload" @close="closeDialog"/>
52
+ <group-form v-if="groupDialog.show" :payload="groupDialog.payload" @close="closeDialog"/>
46
53
  </el-dialog>
47
54
  <el-dialog v-model="imageDialog.show" :title="imageDialog.title" width="300" :close-on-click-modal="false" :close-on-press-escape="false"
48
55
  >
49
- <image-form :payload="imageDialog.payload" @close="closeDialog"/>
56
+ <image-form v-if="imageDialog.show" :payload="imageDialog.payload" @close="closeDialog"/>
50
57
  </el-dialog>
51
58
  </div>
52
59
  </template>
@@ -55,7 +62,7 @@
55
62
  import {onMounted, reactive} from "vue";
56
63
  import groupForm from "./group-form.vue";
57
64
  import imageForm from "./image-form.vue";
58
- import {get, del} from "../../http.js";
65
+ import {get, del, instance} from "../../http.js";
59
66
  import {ElMessage, ElMessageBox} from "element-plus";
60
67
 
61
68
  // 数据集
@@ -143,6 +150,44 @@ const addPicture = (id) => {
143
150
  }
144
151
  }
145
152
 
153
+ /**
154
+ * 编辑图片
155
+ * @param row
156
+ */
157
+ const editPicture = (row) => {
158
+ imageDialog.show = true
159
+ imageDialog.title = '编辑图片'
160
+ imageDialog.payload = {
161
+ id: row.id,
162
+ groupId: row.groupId,
163
+ imageName: row.imageName,
164
+ sort: row.sort,
165
+ isShow: row.isShow,
166
+ imageUrl: row.imageUrl
167
+ }
168
+ }
169
+
170
+ /**
171
+ * 删除图片
172
+ * @param id
173
+ */
174
+ const deletePicture = (id) => {
175
+ ElMessageBox.confirm('是否确定要删除该图片?', '提示', {
176
+ confirmButtonText: '确定',
177
+ cancelButtonText: '取消',
178
+ type: 'warning'
179
+ }).then(() => {
180
+ del('/cny/custom/removePicture/' + id).then(res => {
181
+ if (res.code === 200) {
182
+ ElMessage.success('删除成功')
183
+ loadImageRecord()
184
+ } else {
185
+ ElMessage.error(res.msg || '删除失败')
186
+ }
187
+ })
188
+ })
189
+ }
190
+
146
191
  /**
147
192
  * 关闭弹窗
148
193
  * @param action
@@ -30,16 +30,6 @@ export const stencilOptions = {
30
30
  columnWidth: 70,
31
31
  resizeToFit: true
32
32
  },
33
- groups: [
34
- {
35
- name: '基础组件',
36
- graphPadding: 30
37
- },
38
- {
39
- name: 'group2',
40
- graphPadding: 30
41
- },
42
- ],
43
33
  }
44
34
 
45
35
  export const portOption = {
package/packages/http.js CHANGED
@@ -6,7 +6,7 @@ import {ElMessage} from "element-plus";
6
6
  let baseURL = 'http://localhost:8001'
7
7
 
8
8
  // 创建一个 axios 实例
9
- const instance = axios.create({
9
+ export const instance = axios.create({
10
10
  baseURL: baseURL, // 你的 API 基础 URL
11
11
  timeout: 10000, // 请求超时时间
12
12
  headers: {
@@ -93,3 +93,12 @@ export const put = (url, data = {}) => {
93
93
  export const del = (url, params = {}) => {
94
94
  return instance.delete(url, {params});
95
95
  };
96
+
97
+ // 上传文件
98
+ export const upload = (url, data = {}) => {
99
+ return instance.post(url, data, {
100
+ headers: {
101
+ 'Content-Type': 'multipart/form-data',
102
+ },
103
+ });
104
+ };
@@ -54,6 +54,17 @@
54
54
  :deep(.x6-widget-stencil-title) {
55
55
  display: none;
56
56
  }
57
+
58
+ :deep(.x6-widget-stencil.searchable > .x6-widget-stencil-content) {
59
+ top: 50px
60
+ }
61
+
62
+ :deep(.x6-widget-stencil-search-text){
63
+ background: rgba(35, 100, 221, 0.3);
64
+ color: #ffffff;
65
+ border-radius: 5px;
66
+ border: 1px solid rgb(35, 100, 221);
67
+ }
57
68
  }
58
69
 
59
70
  #controls {