Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b678083e87 | ||
|
|
80e4d579a5 | ||
|
|
2698ca4f5c | ||
|
|
da26b1d237 | ||
|
|
d586f19bd9 | ||
|
|
be63b12db7 | ||
|
|
346346c3f9 | ||
|
|
178054426d | ||
|
|
761ad3c2f8 | ||
|
|
6d08a7673e | ||
|
|
ac0774cc74 | ||
|
|
c013158f7c | ||
|
|
f706c49e93 | ||
|
|
6a92786f60 | ||
|
|
e8ce98f80c | ||
|
|
9d7d7c0cbd | ||
|
|
9bd9403280 | ||
|
|
3d987b4761 | ||
|
|
a7a88e6706 | ||
|
|
99ad7a3021 | ||
|
|
222ec77df1 | ||
|
|
77636e502f | ||
|
|
4df52a2c87 | ||
|
|
26c2190ded | ||
|
|
e10b451e68 | ||
|
|
78a4b1685c | ||
|
|
77b35d3395 |
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/favicon.ico" type="image/svg+xml" />
|
<link rel="icon" href="/favicon.ico" type="image/svg+xml" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>南京灿能web组态编辑器</title>
|
<title>南京灿能组态</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import loadSvg from '@/utils/loadSvg'
|
import { loadSvg } from '@/utils/loadSvg'
|
||||||
loadSvg()
|
loadSvg()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,23 @@ export function addElement(params: any) {
|
|||||||
data: params
|
data: params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 批量导入
|
||||||
|
export function addByZip(params: any) {
|
||||||
|
return http.request({
|
||||||
|
url: '/cs-system-boot/csElement/addByZip',
|
||||||
|
method: 'post',
|
||||||
|
data: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 下载
|
||||||
|
export function downloadByZip(params: any) {
|
||||||
|
return http.request({
|
||||||
|
url: '/cs-system-boot/csElement/downloadByZip',
|
||||||
|
method: 'post',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 删除图元
|
// 删除图元
|
||||||
export function deleteElement(params: any) {
|
export function deleteElement(params: any) {
|
||||||
@@ -78,6 +95,13 @@ export function targetList(params: any) {
|
|||||||
params: params
|
params: params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 云平台
|
||||||
|
export function eleEpdChooseTree_ypt() {
|
||||||
|
return http.request({
|
||||||
|
url: '/cs-system-boot/csDictData/eleEpdChooseTree',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
// 无锡指标列表
|
// 无锡指标列表
|
||||||
export function eleEpdChooseTree_wx() {
|
export function eleEpdChooseTree_wx() {
|
||||||
return http.request({
|
return http.request({
|
||||||
|
|||||||
155
src/components/mt-edit/components/add-element/download.vue
Normal file
155
src/components/mt-edit/components/add-element/download.vue
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="add-element">
|
||||||
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="open"
|
||||||
|
title="批量导出"
|
||||||
|
width="500px"
|
||||||
|
destroy-on-close
|
||||||
|
@close="closeDialog"
|
||||||
|
>
|
||||||
|
<el-form :model="element" ref="ruleFormRef" :rules="rules" label-width="120px">
|
||||||
|
<el-form-item label="下载分类:" prop="name">
|
||||||
|
<el-select v-model="element.name" placeholder="请选择下载分类" style="width: 100%">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="mt-10px flex justify-end">
|
||||||
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
|
<el-button @click="addNewComponent(ruleFormRef)" type="primary">导出</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getCurrentInstance, onMounted, ref, watch, reactive } from 'vue'
|
||||||
|
import { ElInput, ElFormItem, ElForm, ElDialog, ElUpload, ElMessage, ElButton, ElSelect, ElOption } from 'element-plus'
|
||||||
|
import type { CheckboxValueType } from 'element-plus'
|
||||||
|
import type { UploadInstance, UploadProps, FormInstance, FormRules, UploadFile, UploadUserFile } from 'element-plus'
|
||||||
|
import { downloadByZip, download } from '@/api/index'
|
||||||
|
import { leftAsideStore } from '@/export'
|
||||||
|
import { getSvg } from '@/utils/loadSvg'
|
||||||
|
const instance = getCurrentInstance() // 获取当前组件实例
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
show: Boolean,
|
||||||
|
checked_keys: Array
|
||||||
|
})
|
||||||
|
|
||||||
|
const element: any = ref({
|
||||||
|
elementCode: '', //组件编码
|
||||||
|
elementForm: '', // 图元状态
|
||||||
|
elementMark: '', //组件标识
|
||||||
|
elementName: '', //图元名称
|
||||||
|
name: '', //组件子类型 图元分类
|
||||||
|
elementType: '', //组件分类
|
||||||
|
multipartFile: '' // 图元文件
|
||||||
|
})
|
||||||
|
|
||||||
|
interface RuleForm {
|
||||||
|
elementCode: string
|
||||||
|
elementForm: string
|
||||||
|
elementMark: string
|
||||||
|
elementName: string
|
||||||
|
name: string
|
||||||
|
elementType: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: any = ref([])
|
||||||
|
const isAdding = ref(false)
|
||||||
|
const value = ref<CheckboxValueType[]>([])
|
||||||
|
const optionName = ref('')
|
||||||
|
const fileList = ref<UploadFile[]>([]) // 上传文件列表
|
||||||
|
const dialogImageUrl = ref('')
|
||||||
|
const dialogVisible = ref(false) // 上传图片预览
|
||||||
|
|
||||||
|
const ruleFormRef = ref<FormInstance>()
|
||||||
|
const rules = reactive<FormRules<RuleForm>>({
|
||||||
|
name: [{ required: true, message: '请选择需要下载的分类', trigger: 'change' }]
|
||||||
|
})
|
||||||
|
onMounted(() => {})
|
||||||
|
|
||||||
|
const setOptions = () => {
|
||||||
|
let list = JSON.parse(JSON.stringify(props.checked_keys))
|
||||||
|
|
||||||
|
const uniqueArray = [...new Set(list)]
|
||||||
|
|
||||||
|
// 步骤2:定义需要删除的元素列表
|
||||||
|
const removeItems = ['基础图元', '数据绑定图元']
|
||||||
|
|
||||||
|
// 步骤3:过滤掉需要删除的元素
|
||||||
|
const finalArray = uniqueArray.filter(item => !removeItems.includes(item))
|
||||||
|
options.value = finalArray.map(item => ({
|
||||||
|
value: item,
|
||||||
|
label: item
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
open.value = false
|
||||||
|
if (instance) {
|
||||||
|
const emit = instance.emit
|
||||||
|
emit('update:show', false) // 向父组件发送更新后的状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.show,
|
||||||
|
val => {
|
||||||
|
if (val === true) {
|
||||||
|
setOptions()
|
||||||
|
open.value = true
|
||||||
|
element.value = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 新增保存
|
||||||
|
|
||||||
|
const addNewComponent = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
downloadByZip({
|
||||||
|
name: element.value.name
|
||||||
|
}).then((res: any) => {
|
||||||
|
console.log('🚀 ~ addNewComponent ~ res:', res)
|
||||||
|
downloadBlobFile(res, element.value.name + '.zip')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用Blob下载工具函数(核心复用)
|
||||||
|
* @param {Blob} blob 二进制文件流对象
|
||||||
|
* @param {String} fileName 下载后的文件名(必须带.zip后缀)
|
||||||
|
*/
|
||||||
|
function downloadBlobFile(blob: any, fileName: string) {
|
||||||
|
// 1. 创建文件下载的临时URL
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
// 2. 创建a标签(隐藏的,用于触发下载)
|
||||||
|
const aLink = document.createElement('a')
|
||||||
|
aLink.style.display = 'none'
|
||||||
|
aLink.href = url
|
||||||
|
// 3. 设置下载文件名
|
||||||
|
aLink.download = fileName
|
||||||
|
// 4. 把a标签插入到页面,触发点击事件
|
||||||
|
document.body.appendChild(aLink)
|
||||||
|
aLink.click()
|
||||||
|
// 5. ✅ 关键优化:释放临时URL,防止内存泄漏
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
// 6. 移除a标签,清理DOM
|
||||||
|
document.body.removeChild(aLink)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.option-input {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
273
src/components/mt-edit/components/add-element/importZip.vue
Normal file
273
src/components/mt-edit/components/add-element/importZip.vue
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
<template>
|
||||||
|
<div class="add-element">
|
||||||
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="open"
|
||||||
|
title="批量导入"
|
||||||
|
width="500px"
|
||||||
|
destroy-on-close
|
||||||
|
@close="closeDialog"
|
||||||
|
>
|
||||||
|
<el-form :model="element" ref="ruleFormRef" :rules="rules" label-width="120px">
|
||||||
|
<el-form-item label="图元分类:" prop="elementSonType">
|
||||||
|
<el-select v-model="element.elementSonType" placeholder="请选择图元分类" style="width: 100%">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button v-if="!isAdding" text bg size="small" type="primary" @click="onAddOption">
|
||||||
|
新增分类
|
||||||
|
</el-button>
|
||||||
|
<template v-else>
|
||||||
|
<el-input
|
||||||
|
v-model="optionName"
|
||||||
|
class="option-input"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入分类名称"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="small" @click="onConfirm">保存</el-button>
|
||||||
|
<el-button size="small" @click="clear">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="上传zip文件:">
|
||||||
|
<el-upload
|
||||||
|
ref="upload"
|
||||||
|
v-model="fileList"
|
||||||
|
style="width: 415px"
|
||||||
|
action="#"
|
||||||
|
multiple
|
||||||
|
accept=".zip"
|
||||||
|
:http-request="UploadSvg"
|
||||||
|
:on-remove="handleRemove"
|
||||||
|
:before-upload="beforeAvatarUpload"
|
||||||
|
:limit="1"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
>
|
||||||
|
<el-button type="primary">上传</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<el-dialog :close-on-click-modal="false" v-model="dialogVisible">
|
||||||
|
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||||
|
</el-dialog>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="mt-10px flex justify-end">
|
||||||
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
|
<el-button @click="addNewComponent(ruleFormRef)" type="primary">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getCurrentInstance, onMounted, ref, watch, reactive } from 'vue'
|
||||||
|
import { ElInput, ElFormItem, ElForm, ElDialog, ElUpload, ElMessage, ElButton, ElSelect, ElOption } from 'element-plus'
|
||||||
|
import type { CheckboxValueType } from 'element-plus'
|
||||||
|
import type { UploadInstance, UploadProps, FormInstance, FormRules, UploadFile, UploadUserFile } from 'element-plus'
|
||||||
|
import { addByZip, download } from '@/api/index'
|
||||||
|
import { leftAsideStore } from '@/export'
|
||||||
|
import { getSvg } from '@/utils/loadSvg'
|
||||||
|
const instance = getCurrentInstance() // 获取当前组件实例
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
show: Boolean,
|
||||||
|
checked_keys: Array
|
||||||
|
})
|
||||||
|
|
||||||
|
const element: any = ref({
|
||||||
|
elementCode: '', //组件编码
|
||||||
|
elementForm: '', // 图元状态
|
||||||
|
elementMark: '', //组件标识
|
||||||
|
elementName: '', //图元名称
|
||||||
|
elementSonType: '', //组件子类型 图元分类
|
||||||
|
elementType: '', //组件分类
|
||||||
|
multipartFile: '' // 图元文件
|
||||||
|
})
|
||||||
|
|
||||||
|
interface RuleForm {
|
||||||
|
elementCode: string
|
||||||
|
elementForm: string
|
||||||
|
elementMark: string
|
||||||
|
elementName: string
|
||||||
|
elementSonType: string
|
||||||
|
elementType: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: any = ref([])
|
||||||
|
const isAdding = ref(false)
|
||||||
|
const value = ref<CheckboxValueType[]>([])
|
||||||
|
const optionName = ref('')
|
||||||
|
const fileList = ref<UploadFile[]>([]) // 上传文件列表
|
||||||
|
const dialogImageUrl = ref('')
|
||||||
|
const dialogVisible = ref(false) // 上传图片预览
|
||||||
|
|
||||||
|
const handleRemove = (file: UploadFile) => {
|
||||||
|
fileList.value = []
|
||||||
|
}
|
||||||
|
// 文件校验
|
||||||
|
const beforeAvatarUpload: UploadProps['beforeUpload'] = rawFile => {
|
||||||
|
if (!rawFile.name.includes('.zip')) {
|
||||||
|
ElMessage.error('只能上传zip格式文件!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const handleExceed: UploadProps['onExceed'] = files => {
|
||||||
|
return ElMessage.error('只能上传1个zip文件!')
|
||||||
|
}
|
||||||
|
// 上传svg
|
||||||
|
// const UploadSvg = (params:any) => {
|
||||||
|
// fileList.value.push(params.file);
|
||||||
|
// };
|
||||||
|
|
||||||
|
const UploadSvg = (params: any) => {
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
fileList.value.push(params.file)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const ruleFormRef = ref<FormInstance>()
|
||||||
|
const rules = reactive<FormRules<RuleForm>>({
|
||||||
|
elementCode: [{ required: true, message: '请输入组件编码', trigger: 'blur' }],
|
||||||
|
elementForm: [{ required: true, message: '请选择图元状态', trigger: 'change' }],
|
||||||
|
elementMark: [{ required: true, message: '请输入组件标识', trigger: 'blur' }],
|
||||||
|
elementName: [{ required: true, message: '请输入图元名称', trigger: 'blur' }],
|
||||||
|
elementSonType: [{ required: true, message: '请选择父类型', trigger: 'change' }],
|
||||||
|
elementType: [{ required: true, message: '请选择组件分类', trigger: 'change' }]
|
||||||
|
})
|
||||||
|
onMounted(() => {})
|
||||||
|
|
||||||
|
const setOptions = () => {
|
||||||
|
let list = JSON.parse(JSON.stringify(props.checked_keys))
|
||||||
|
|
||||||
|
list.push('特殊图元')
|
||||||
|
const uniqueArray = [...new Set(list)]
|
||||||
|
|
||||||
|
// 步骤2:定义需要删除的元素列表
|
||||||
|
const removeItems = ['基础图元', '数据绑定图元']
|
||||||
|
|
||||||
|
// 步骤3:过滤掉需要删除的元素
|
||||||
|
const finalArray = uniqueArray.filter(item => !removeItems.includes(item))
|
||||||
|
options.value = finalArray.map(item => ({
|
||||||
|
value: item,
|
||||||
|
label: item
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
const onAddOption = () => {
|
||||||
|
isAdding.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
if (optionName.value) {
|
||||||
|
options.value.push({
|
||||||
|
label: optionName.value,
|
||||||
|
value: optionName.value
|
||||||
|
})
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
open.value = false
|
||||||
|
if (instance) {
|
||||||
|
const emit = instance.emit
|
||||||
|
emit('update:show', false) // 向父组件发送更新后的状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.show,
|
||||||
|
val => {
|
||||||
|
if (val === true) {
|
||||||
|
setOptions()
|
||||||
|
open.value = true
|
||||||
|
element.value = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
optionName.value = ''
|
||||||
|
isAdding.value = false
|
||||||
|
}
|
||||||
|
// 新增保存
|
||||||
|
|
||||||
|
const addNewComponent = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
if (fileList.value.length == 0) {
|
||||||
|
ElMessage({
|
||||||
|
message: '请上传zip文件!',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
return Promise.resolve() // 显式返回 Promise<void>
|
||||||
|
}
|
||||||
|
let form = new FormData()
|
||||||
|
|
||||||
|
form.append('elementCode', element.value.elementName)
|
||||||
|
form.append('elementForm', '普通图元')
|
||||||
|
form.append('elementMark', element.value.elementName)
|
||||||
|
form.append('elementName', element.value.elementName)
|
||||||
|
form.append('elementSonType', element.value.elementSonType)
|
||||||
|
form.append('elementType', 'zip文件')
|
||||||
|
form.append('zipFile', fileList.value[0])
|
||||||
|
|
||||||
|
addByZip(form).then((res: any) => {
|
||||||
|
if (res.code == 'A0000') {
|
||||||
|
ElMessage({
|
||||||
|
message: '新增成功',
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
closeDialog()
|
||||||
|
if (!props.checked_keys?.includes(element.value.elementSonType)) {
|
||||||
|
leftAsideStore.registerConfig(element.value.elementSonType, [])
|
||||||
|
}
|
||||||
|
ElMessage.info(`加载图元中,请稍等!`)
|
||||||
|
getSvg()
|
||||||
|
// setTimeout(() => {
|
||||||
|
// // 左侧列表渲染新增的图元
|
||||||
|
// download({ filePath: res.data.path }).then((Svg: any) => {
|
||||||
|
// // 动态添加svg
|
||||||
|
// leftAsideStore.svgPush(res.data.elementSonType, [
|
||||||
|
// {
|
||||||
|
// id: res.data.id,
|
||||||
|
// title: res.data.elementName,
|
||||||
|
// type: 'svg',
|
||||||
|
// thumbnail:
|
||||||
|
// 'data:image/svg+xml;utf8,' +
|
||||||
|
// encodeURIComponent(Svg.replace(/(\sfill=(["']))[^"']*(\2)/g, '$1#000000$3')),
|
||||||
|
// svg: Svg.replace(/\sfill=(["'])[^"']*\1/g, ''),
|
||||||
|
// props: {
|
||||||
|
// fill: {
|
||||||
|
// type: 'color',
|
||||||
|
// val: '#FF0000',
|
||||||
|
// title: '填充色'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ])
|
||||||
|
// })
|
||||||
|
// }, 100)
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: res.message,
|
||||||
|
type: 'info'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Promise.resolve() // 确保所有分支都返回 Promise<void>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.option-input {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,10 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="add-element">
|
<div class="add-element">
|
||||||
<el-dialog v-model="open" title="新增图元" width="500px" destroy-on-close @close="closeDialog">
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="open"
|
||||||
|
title="新增图元"
|
||||||
|
width="500px"
|
||||||
|
destroy-on-close
|
||||||
|
@close="closeDialog"
|
||||||
|
>
|
||||||
<el-form :model="element" ref="ruleFormRef" :rules="rules" label-width="120px">
|
<el-form :model="element" ref="ruleFormRef" :rules="rules" label-width="120px">
|
||||||
<el-form-item label="图元分类:" prop="elementSonType">
|
<el-form-item label="图元分类:" prop="elementSonType">
|
||||||
<el-select v-model="element.elementSonType" placeholder="请选择图元分类" style="width: 100%">
|
<el-select v-model="element.elementSonType" placeholder="请选择图元分类" style="width: 100%">
|
||||||
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button v-if="!isAdding" text bg size="small" type="primary" @click="onAddOption">
|
||||||
|
新增分类
|
||||||
|
</el-button>
|
||||||
|
<template v-else>
|
||||||
|
<el-input
|
||||||
|
v-model="optionName"
|
||||||
|
class="option-input"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入分类名称"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="small" @click="onConfirm">保存</el-button>
|
||||||
|
<el-button size="small" @click="clear">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="组件子类型:" prop="elementSonType">
|
<!-- <el-form-item label="组件子类型:" prop="elementSonType">
|
||||||
@@ -72,7 +95,14 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="图元名称:" prop="elementName">
|
<el-form-item label="图元名称:" prop="elementName">
|
||||||
<el-input v-model="element.elementName" placeholder="请选择组件名称" style="width: 100%" />
|
<el-input
|
||||||
|
v-model.trim="element.elementName"
|
||||||
|
maxlength="12"
|
||||||
|
show-word-limit
|
||||||
|
clearable
|
||||||
|
placeholder="请选择组件名称"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="组件标识:" prop="elementMark">
|
<!-- <el-form-item label="组件标识:" prop="elementMark">
|
||||||
<el-input v-model="element.elementMark" placeholder="请选择组件标识" style="width: 100%" />
|
<el-input v-model="element.elementMark" placeholder="请选择组件标识" style="width: 100%" />
|
||||||
@@ -93,32 +123,35 @@
|
|||||||
>
|
>
|
||||||
<el-button type="primary">上传</el-button>
|
<el-button type="primary">上传</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<el-dialog v-model="dialogVisible">
|
<el-dialog :close-on-click-modal="false" v-model="dialogVisible">
|
||||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="addNewComponent(ruleFormRef)" type="primary">保存</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<div class="mt-10px flex justify-end">
|
||||||
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
|
<el-button @click="addNewComponent(ruleFormRef)" type="primary">保存</el-button>
|
||||||
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineProps, getCurrentInstance, onMounted, ref, watch, reactive } from 'vue'
|
import { getCurrentInstance, onMounted, ref, watch, reactive } from 'vue'
|
||||||
import { ElInput, ElFormItem, ElForm, ElDialog, ElUpload, ElMessage, ElButton, ElSelect, ElOption } from 'element-plus'
|
import { ElInput, ElFormItem, ElForm, ElDialog, ElUpload, ElMessage, ElButton, ElSelect, ElOption } from 'element-plus'
|
||||||
|
import type { CheckboxValueType } from 'element-plus'
|
||||||
import type { UploadInstance, UploadProps, FormInstance, FormRules, UploadFile, UploadUserFile } from 'element-plus'
|
import type { UploadInstance, UploadProps, FormInstance, FormRules, UploadFile, UploadUserFile } from 'element-plus'
|
||||||
|
|
||||||
import { addElement, download } from '@/api/index'
|
import { addElement, download } from '@/api/index'
|
||||||
import { leftAsideStore } from '@/export'
|
import { leftAsideStore } from '@/export'
|
||||||
|
import { getSvg } from '@/utils/loadSvg'
|
||||||
const instance = getCurrentInstance() // 获取当前组件实例
|
const instance = getCurrentInstance() // 获取当前组件实例
|
||||||
|
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: Boolean
|
show: Boolean,
|
||||||
|
checked_keys: Array
|
||||||
})
|
})
|
||||||
|
|
||||||
const element: any = ref({
|
const element: any = ref({
|
||||||
@@ -140,17 +173,10 @@ interface RuleForm {
|
|||||||
elementType: string
|
elementType: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = [
|
const options: any = ref([])
|
||||||
{
|
const isAdding = ref(false)
|
||||||
value: '电力基础图元',
|
const value = ref<CheckboxValueType[]>([])
|
||||||
label: '电力基础图元'
|
const optionName = ref('')
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '自定义',
|
|
||||||
label: '自定义'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const fileList = ref<UploadFile[]>([]) // 上传文件列表
|
const fileList = ref<UploadFile[]>([]) // 上传文件列表
|
||||||
const dialogImageUrl = ref('')
|
const dialogImageUrl = ref('')
|
||||||
const dialogVisible = ref(false) // 上传图片预览
|
const dialogVisible = ref(false) // 上传图片预览
|
||||||
@@ -195,6 +221,36 @@ const rules = reactive<FormRules<RuleForm>>({
|
|||||||
elementSonType: [{ required: true, message: '请选择父类型', trigger: 'change' }],
|
elementSonType: [{ required: true, message: '请选择父类型', trigger: 'change' }],
|
||||||
elementType: [{ required: true, message: '请选择组件分类', trigger: 'change' }]
|
elementType: [{ required: true, message: '请选择组件分类', trigger: 'change' }]
|
||||||
})
|
})
|
||||||
|
onMounted(() => {})
|
||||||
|
const setOptions = () => {
|
||||||
|
let list = JSON.parse(JSON.stringify(props.checked_keys))
|
||||||
|
|
||||||
|
list.push('特殊图元')
|
||||||
|
const uniqueArray = [...new Set(list)]
|
||||||
|
|
||||||
|
// 步骤2:定义需要删除的元素列表
|
||||||
|
const removeItems = ['基础图元', '数据绑定图元']
|
||||||
|
|
||||||
|
// 步骤3:过滤掉需要删除的元素
|
||||||
|
const finalArray = uniqueArray.filter(item => !removeItems.includes(item))
|
||||||
|
options.value = finalArray.map(item => ({
|
||||||
|
value: item,
|
||||||
|
label: item
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
const onAddOption = () => {
|
||||||
|
isAdding.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
if (optionName.value) {
|
||||||
|
options.value.push({
|
||||||
|
label: optionName.value,
|
||||||
|
value: optionName.value
|
||||||
|
})
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const closeDialog = () => {
|
const closeDialog = () => {
|
||||||
open.value = false
|
open.value = false
|
||||||
@@ -208,12 +264,17 @@ watch(
|
|||||||
() => props.show,
|
() => props.show,
|
||||||
val => {
|
val => {
|
||||||
if (val === true) {
|
if (val === true) {
|
||||||
|
setOptions()
|
||||||
open.value = true
|
open.value = true
|
||||||
element.value = {}
|
element.value = {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
optionName.value = ''
|
||||||
|
isAdding.value = false
|
||||||
|
}
|
||||||
// 新增保存
|
// 新增保存
|
||||||
|
|
||||||
const addNewComponent = async (formEl: FormInstance | undefined) => {
|
const addNewComponent = async (formEl: FormInstance | undefined) => {
|
||||||
@@ -244,28 +305,35 @@ const addNewComponent = async (formEl: FormInstance | undefined) => {
|
|||||||
type: 'success'
|
type: 'success'
|
||||||
})
|
})
|
||||||
closeDialog()
|
closeDialog()
|
||||||
// 左侧列表渲染新增的图元
|
if (!props.checked_keys?.includes(element.value.elementSonType)) {
|
||||||
download({ filePath: res.data.path }).then((Svg: any) => {
|
leftAsideStore.registerConfig(element.value.elementSonType, [])
|
||||||
// 动态添加svg
|
}
|
||||||
leftAsideStore.svgPush(res.data.elementSonType, [
|
ElMessage.info(`加载图元中,请稍等!`)
|
||||||
{
|
getSvg()
|
||||||
id: res.data.id,
|
// setTimeout(() => {
|
||||||
title: res.data.elementName,
|
// // 左侧列表渲染新增的图元
|
||||||
type: 'svg',
|
// download({ filePath: res.data.path }).then((Svg: any) => {
|
||||||
thumbnail:
|
// // 动态添加svg
|
||||||
'data:image/svg+xml;utf8,' +
|
// leftAsideStore.svgPush(res.data.elementSonType, [
|
||||||
encodeURIComponent(Svg.replace(/(\sfill=(["']))[^"']*(\2)/g, '$1#000000$3')),
|
// {
|
||||||
svg: Svg.replace(/\sfill=(["'])[^"']*\1/g, ''),
|
// id: res.data.id,
|
||||||
props: {
|
// title: res.data.elementName,
|
||||||
fill: {
|
// type: 'svg',
|
||||||
type: 'color',
|
// thumbnail:
|
||||||
val: '#FF0000',
|
// 'data:image/svg+xml;utf8,' +
|
||||||
title: '填充色'
|
// encodeURIComponent(Svg.replace(/(\sfill=(["']))[^"']*(\2)/g, '$1#000000$3')),
|
||||||
}
|
// svg: Svg.replace(/\sfill=(["'])[^"']*\1/g, ''),
|
||||||
}
|
// props: {
|
||||||
}
|
// fill: {
|
||||||
])
|
// type: 'color',
|
||||||
})
|
// val: '#FF0000',
|
||||||
|
// title: '填充色'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ])
|
||||||
|
// })
|
||||||
|
// }, 100)
|
||||||
} else {
|
} else {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: res.message,
|
message: res.message,
|
||||||
@@ -278,3 +346,9 @@ const addNewComponent = async (formEl: FormInstance | undefined) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.option-input {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
import { VAceEditor } from 'vue3-ace-editor'
|
import { VAceEditor } from 'vue3-ace-editor'
|
||||||
import type { IDoneJson, IGlobalStoreCanvasCfg, IGlobalStoreGridCfg } from '../../store/types'
|
import type { IDoneJson, IGlobalStoreCanvasCfg, IGlobalStoreGridCfg } from '../../store/types'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { genExportJson } from '../../composables'
|
import { genExportJson } from '@/components/mt-edit/composables/index'
|
||||||
type ExportProps = {
|
type ExportProps = {
|
||||||
doneJson: IDoneJson[]
|
doneJson: IDoneJson[]
|
||||||
canvasCfg: IGlobalStoreCanvasCfg
|
canvasCfg: IGlobalStoreCanvasCfg
|
||||||
|
|||||||
@@ -25,10 +25,7 @@ const onImport = () => {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const json: IExportJson = JSON.parse(import_json.value)
|
const json: IExportJson = JSON.parse(import_json.value)
|
||||||
console.log('🚀 ~ onImport ~ json:', json)
|
|
||||||
|
|
||||||
const { canvasCfg, gridCfg, importDoneJson } = useExportJsonToDoneJson(json)
|
const { canvasCfg, gridCfg, importDoneJson } = useExportJsonToDoneJson(json)
|
||||||
|
|
||||||
globalStore.canvasCfg = canvasCfg
|
globalStore.canvasCfg = canvasCfg
|
||||||
globalStore.gridCfg = gridCfg
|
globalStore.gridCfg = gridCfg
|
||||||
globalStore.setGlobalStoreDoneJson(importDoneJson)
|
globalStore.setGlobalStoreDoneJson(importDoneJson)
|
||||||
|
|||||||
@@ -55,12 +55,12 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical"></el-divider>
|
<el-divider direction="vertical"></el-divider>
|
||||||
<el-button-group>
|
<el-button-group>
|
||||||
<el-button text circle size="small" @click="onImportClick">
|
<el-button text circle size="small" @click="onImportClick" :disabled="useData.keyName == ''">
|
||||||
<el-icon title="导入数据模型" :size="20">
|
<el-icon title="导入数据模型" :size="20">
|
||||||
<svg-analysis name="import-json"></svg-analysis>
|
<svg-analysis name="import-json"></svg-analysis>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button text circle size="small" @click="onExportClick">
|
<el-button text circle size="small" @click="onExportClick" :disabled="useData.keyName == ''">
|
||||||
<el-icon title="导出数据模型" :size="20">
|
<el-icon title="导出数据模型" :size="20">
|
||||||
<svg-analysis name="export-json"></svg-analysis>
|
<svg-analysis name="export-json"></svg-analysis>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@@ -158,11 +158,11 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mr-20px">
|
<div class="flex items-center mr-20px">
|
||||||
<el-button text circle size="small" @click="emits('onReturnClick')">
|
<!-- <el-button text circle size="small" @click="emits('onReturnClick')">
|
||||||
<el-icon title="返回" :size="20">
|
<el-icon title="返回" :size="20">
|
||||||
<svg-analysis name="return"></svg-analysis>
|
<svg-analysis name="return"></svg-analysis>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-button>
|
</el-button> -->
|
||||||
<!-- <el-divider direction="vertical"></el-divider> -->
|
<!-- <el-divider direction="vertical"></el-divider> -->
|
||||||
<!-- <el-button text circle size="small" @click="emits('onSaveClick')">
|
<!-- <el-button text circle size="small" @click="emits('onSaveClick')">
|
||||||
<el-icon title="保存" :size="20">
|
<el-icon title="保存" :size="20">
|
||||||
@@ -229,7 +229,9 @@ import { useDark, useToggle, useFullscreen } from '@vueuse/core'
|
|||||||
import { ElIcon, ElDivider, ElPopover, ElButton, ElButtonGroup, ElImage, ElText, ElTag } from 'element-plus'
|
import { ElIcon, ElDivider, ElPopover, ElButton, ElButtonGroup, ElImage, ElText, ElTag } from 'element-plus'
|
||||||
import SvgAnalysis from '@/components/mt-edit/components/svg-analysis/index.vue'
|
import SvgAnalysis from '@/components/mt-edit/components/svg-analysis/index.vue'
|
||||||
import type { IRealTimeData } from '@/components/mt-edit/store/types'
|
import type { IRealTimeData } from '@/components/mt-edit/store/types'
|
||||||
|
import { useDataStore } from '@/stores/menuList'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
const useData = useDataStore()
|
||||||
type HeaderPanelProps = {
|
type HeaderPanelProps = {
|
||||||
leftAside: boolean
|
leftAside: boolean
|
||||||
rightAside: boolean
|
rightAside: boolean
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
ref="multipleTable"
|
ref="multipleTable"
|
||||||
@row-click="onRowClick"
|
@row-click="onRowClick"
|
||||||
empty-text="暂无数据"
|
empty-text="暂无数据"
|
||||||
row-key="id"
|
row-key="name"
|
||||||
>
|
>
|
||||||
<el-table-column prop="name" label="名称" />
|
<el-table-column prop="name" label="名称" />
|
||||||
<el-table-column label="操作" align="center" width="60" #default="scope">
|
<el-table-column label="操作" align="center" width="60" #default="scope">
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="删除" placement="top">
|
<el-tooltip content="删除" placement="top">
|
||||||
<el-icon @click.stop="del(scope.$index)" style="margin-left: 5px; cursor: pointer">
|
<el-icon @click.stop="del(scope.$index, scope.row)" style="margin-left: 5px; cursor: pointer">
|
||||||
<Delete />
|
<Delete />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
@@ -30,16 +30,30 @@
|
|||||||
<el-table-column label="操作" width="40">
|
<el-table-column label="操作" width="40">
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-tooltip content="拖拽" placement="top">
|
<el-tooltip content="拖拽" placement="top">
|
||||||
<div class="drag-handle">⋮⋮⋮</div>
|
<div class="drag-handle" style="cursor: pointer">⋮⋮⋮</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<!-- 新增/修改 -->
|
<!-- 新增/修改 -->
|
||||||
<el-dialog draggable v-model="dialogFormVisible" :title="dialog_title" width="500px" destroy-on-close>
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable
|
||||||
|
v-model="dialogFormVisible"
|
||||||
|
:title="dialog_title"
|
||||||
|
width="500px"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
<el-form :model="form" ref="formRef" :rules="rules">
|
<el-form :model="form" ref="formRef" :rules="rules">
|
||||||
<el-form-item label="图纸名称" :label-width="formLabelWidth" prop="name">
|
<el-form-item label="图纸名称" :label-width="formLabelWidth" prop="name">
|
||||||
<el-input v-model="form.name" autocomplete="off" placeholder="请输入图纸名称" />
|
<el-input
|
||||||
|
v-model.trim="form.name"
|
||||||
|
maxlength="12"
|
||||||
|
show-word-limit
|
||||||
|
clearable
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="请输入图纸名称"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -80,7 +94,6 @@ const useData = useDataStore()
|
|||||||
const dialogFormVisible = ref(false)
|
const dialogFormVisible = ref(false)
|
||||||
const dialog_title = ref('新增图纸')
|
const dialog_title = ref('新增图纸')
|
||||||
const formLabelWidth = '100px'
|
const formLabelWidth = '100px'
|
||||||
const globalIndex = ref(-1)
|
|
||||||
const multipleTable: any = ref(null)
|
const multipleTable: any = ref(null)
|
||||||
const { pid } = useData // 解构出 myArray 状态
|
const { pid } = useData // 解构出 myArray 状态
|
||||||
|
|
||||||
@@ -122,31 +135,45 @@ const sortableInstance = ref<any>(null)
|
|||||||
|
|
||||||
// 修改 initSortable 方法中的实例挂载与销毁逻辑
|
// 修改 initSortable 方法中的实例挂载与销毁逻辑
|
||||||
const initSortable = () => {
|
const initSortable = () => {
|
||||||
nextTick(() => {
|
const tbody = multipleTable.value.$el.querySelector('.el-table__body-wrapper tbody')
|
||||||
const tbody = multipleTable.value.$el.querySelector('.el-table__body-wrapper tbody')
|
Sortable.create(tbody, {
|
||||||
if (!tbody) {
|
animation: 100,
|
||||||
console.error('未找到 tbody 元素')
|
onEnd: ({ oldIndex, newIndex }) => {
|
||||||
return
|
const currRow = dataTrees.value.splice(oldIndex, 1)[0]
|
||||||
|
dataTrees.value.splice(newIndex, 0, currRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 销毁旧实例
|
|
||||||
if (sortableInstance.value) {
|
|
||||||
sortableInstance.value.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建新实例并保存到 sortableInstance
|
|
||||||
sortableInstance.value = new Sortable(tbody, {
|
|
||||||
animation: 150,
|
|
||||||
ghostClass: 'sortable-ghost',
|
|
||||||
onEnd: ({ newIndex, oldIndex }) => {
|
|
||||||
// 确保 newIndex 和 oldIndex 都存在且不相等
|
|
||||||
if (newIndex === undefined || oldIndex === undefined || newIndex === oldIndex) return
|
|
||||||
|
|
||||||
const targetItem = dataTrees.value.splice(oldIndex, 1)[0]
|
|
||||||
dataTrees.value.splice(newIndex, 0, targetItem)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// nextTick(() => {
|
||||||
|
// const tbody = multipleTable.value.$el.querySelector('.el-table__body-wrapper tbody')
|
||||||
|
// if (!tbody) {
|
||||||
|
// console.error('未找到 tbody 元素')
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 销毁旧实例
|
||||||
|
// if (sortableInstance.value) {
|
||||||
|
// sortableInstance.value.destroy()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 创建新实例并保存到 sortableInstance
|
||||||
|
// sortableInstance.value = new Sortable(tbody, {
|
||||||
|
// animation: 150,
|
||||||
|
// ghostClass: 'sortable-ghost',
|
||||||
|
// onEnd: ({ newIndex, oldIndex }) => {
|
||||||
|
// // 确保 newIndex 和 oldIndex 都存在且不相等
|
||||||
|
// if (newIndex === undefined || oldIndex === undefined || newIndex === oldIndex) return
|
||||||
|
|
||||||
|
// // const targetItem = dataTrees.value.splice(oldIndex, 1)[0]
|
||||||
|
// // dataTrees.value.splice(newIndex, 0, targetItem)
|
||||||
|
// // 深拷贝避免引用问题(如果是复杂对象,建议使用结构化克隆或深拷贝工具)
|
||||||
|
// const [targetItem] = dataTrees.value.splice(oldIndex, 1)
|
||||||
|
// // 安全插入(超出范围时自动插入到数组末尾)
|
||||||
|
// const insertIndex = Math.min(newIndex, dataTrees.value.length)
|
||||||
|
// dataTrees.value.splice(insertIndex, 0, targetItem)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
//form表单校验规则
|
//form表单校验规则
|
||||||
@@ -171,13 +198,14 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const onAddClick = () => {
|
const onAddClick = () => {
|
||||||
|
dialog_title.value = '新增图纸'
|
||||||
Object.assign(form, { name: '' })
|
Object.assign(form, { name: '' })
|
||||||
//打开弹窗
|
//打开弹窗
|
||||||
dialogFormVisible.value = true
|
dialogFormVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除功能,传索引行数
|
// 删除功能,传索引行数
|
||||||
function del(index: number) {
|
function del(index: number, row: any) {
|
||||||
ElMessageBox.confirm('确定删除?', '提示', {
|
ElMessageBox.confirm('确定删除?', '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -186,6 +214,7 @@ function del(index: number) {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
// splice方法,传两个参数:第几行开始,删除多少条(如果未规定此参数,则删除从 index 开始到原数组结尾的所有元素)
|
// splice方法,传两个参数:第几行开始,删除多少条(如果未规定此参数,则删除从 index 开始到原数组结尾的所有元素)
|
||||||
dataTrees.value.splice(index, 1)
|
dataTrees.value.splice(index, 1)
|
||||||
|
useData.placeKid(row.kId)
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: '删除成功'
|
message: '删除成功'
|
||||||
@@ -213,17 +242,22 @@ const onRowClick = async (row: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const saveDialog = async (form: any) => {
|
const saveDialog = async (form: any) => {
|
||||||
|
console.log(dataTrees.value)
|
||||||
|
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!formRef) return
|
if (!formRef) return
|
||||||
const valid = await formRef.value.validate()
|
const valid = await formRef.value.validate()
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
|
|
||||||
|
if (dataTrees.value.some(item => item.name == form.name)) {
|
||||||
|
ElMessage.error('图纸名称不能重复!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 提交请求
|
// 提交请求
|
||||||
if (globalIndex.value >= 0) {
|
if (dialog_title.value == '编辑图纸') {
|
||||||
//表示编辑
|
//表示编辑
|
||||||
// dataTrees[globalIndex.value] = form;
|
|
||||||
useData.modify(form.kId, form.name)
|
useData.modify(form.kId, form.name)
|
||||||
//还原回去
|
|
||||||
globalIndex.value = -1
|
|
||||||
} else {
|
} else {
|
||||||
//新增
|
//新增
|
||||||
useData.append(form.name)
|
useData.append(form.name)
|
||||||
@@ -236,9 +270,9 @@ const saveDialog = async (form: any) => {
|
|||||||
function update(index: number, row: any) {
|
function update(index: number, row: any) {
|
||||||
// const newObj = Object.assign({}, row);
|
// const newObj = Object.assign({}, row);
|
||||||
// form = reactive(newObj);
|
// form = reactive(newObj);
|
||||||
|
dialog_title.value = '编辑图纸'
|
||||||
Object.assign(form, row)
|
Object.assign(form, row)
|
||||||
//把当前编辑的行号赋值给全局保存的行号
|
|
||||||
globalIndex.value = index
|
|
||||||
dialogFormVisible.value = true
|
dialogFormVisible.value = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -62,9 +62,15 @@
|
|||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-[calc(10%-1px)] flex justify-center items-center ct-border" style="padding-top: 10px">
|
<div class="h-[calc(10%-1px)] flex justify-center items-center ct-border" style="padding-top: 10px">
|
||||||
<el-button class="w-80/100" @click="onManageClick">管理</el-button>
|
<el-button class="w-80/100" :icon="Tools" @click="onManageClick">管理</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog v-model="manage_dialog_visiable" title="图库管理" width="50%" destroy-on-close>
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="manage_dialog_visiable"
|
||||||
|
title="图库管理"
|
||||||
|
width="50%"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
@@ -94,8 +100,6 @@
|
|||||||
>
|
>
|
||||||
<el-button type="primary">本地上传</el-button>
|
<el-button type="primary">本地上传</el-button>
|
||||||
</el-upload> -->
|
</el-upload> -->
|
||||||
<el-button type="primary" @click="checkClick()">新增图元</el-button>
|
|
||||||
<add-element :show="openCheck" @update:show="updateOpenCheck"></add-element>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -123,7 +127,8 @@
|
|||||||
v-if="
|
v-if="
|
||||||
selected_node_key !== '数据绑定图元' &&
|
selected_node_key !== '数据绑定图元' &&
|
||||||
selected_node_key !== '基础图元' &&
|
selected_node_key !== '基础图元' &&
|
||||||
show_del_local_file == item.id
|
show_del_local_file == item.id &&
|
||||||
|
hasAdmin
|
||||||
"
|
"
|
||||||
class="absolute w-160px h-160px left-0 top-0 opacity-80 bg-light-300 flex justify-center items-center"
|
class="absolute w-160px h-160px left-0 top-0 opacity-80 bg-light-300 flex justify-center items-center"
|
||||||
>
|
>
|
||||||
@@ -135,7 +140,18 @@
|
|||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="mt10">
|
||||||
|
<el-button type="primary" :icon="Plus" @click="checkClick()">新增图元</el-button>
|
||||||
|
<el-button type="primary" :icon="Upload" @click="zipCheck()">批量导入</el-button>
|
||||||
|
<el-button type="primary" :icon="Download" @click="downloadCheck()">批量导出</el-button>
|
||||||
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 新增图元 -->
|
||||||
|
<add-element :show="openCheck" :checked_keys="checked_keys" @update:show="updateOpenCheck"></add-element>
|
||||||
|
<!-- 批量导入图元 -->
|
||||||
|
<importZip :show="zipOpen" :checked_keys="checked_keys" @update:show="updateZip"></importZip>
|
||||||
|
<!-- 下载图元 -->
|
||||||
|
<download :show="downloadOpen" :checked_keys="checked_keys" @update:show="updateDown"></download>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -159,6 +175,7 @@ import {
|
|||||||
ElMessage,
|
ElMessage,
|
||||||
type UploadFile
|
type UploadFile
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
|
import { Plus, Tools, Upload, Download } from '@element-plus/icons-vue'
|
||||||
import SvgAnalysis from '@/components/mt-edit/components/svg-analysis/index.vue'
|
import SvgAnalysis from '@/components/mt-edit/components/svg-analysis/index.vue'
|
||||||
import { useDark, useLocalStorage } from '@vueuse/core'
|
import { useDark, useLocalStorage } from '@vueuse/core'
|
||||||
import type {
|
import type {
|
||||||
@@ -169,6 +186,8 @@ import type {
|
|||||||
import { globalStore } from '@/components/mt-edit/store/global'
|
import { globalStore } from '@/components/mt-edit/store/global'
|
||||||
import { blobToBase64 } from '@/components/mt-edit/utils'
|
import { blobToBase64 } from '@/components/mt-edit/utils'
|
||||||
import AddElement from '@/components/mt-edit/components/add-element/index.vue'
|
import AddElement from '@/components/mt-edit/components/add-element/index.vue'
|
||||||
|
import importZip from '@/components/mt-edit/components/add-element/importZip.vue'
|
||||||
|
import download from '@/components/mt-edit/components/add-element/download.vue'
|
||||||
import { deleteElement } from '@/api/index'
|
import { deleteElement } from '@/api/index'
|
||||||
import { leftAsideStore } from '@/export'
|
import { leftAsideStore } from '@/export'
|
||||||
import { useDataStore } from '@/stores/menuList'
|
import { useDataStore } from '@/stores/menuList'
|
||||||
@@ -182,6 +201,11 @@ const leftAsideProps = withDefaults(defineProps<LeftAsideProps>(), {
|
|||||||
const isDark = useDark({
|
const isDark = useDark({
|
||||||
selector: '#mt-edit'
|
selector: '#mt-edit'
|
||||||
})
|
})
|
||||||
|
const adminInfo: any = window.localStorage.getItem('adminInfo')
|
||||||
|
const hasAdmin =
|
||||||
|
JSON.parse(adminInfo)?.roleCode.some(item => item.includes('operation_manager') || item.includes('root')) ||
|
||||||
|
JSON.parse(adminInfo)?.userType == 1
|
||||||
|
|
||||||
const uploadRef = ref()
|
const uploadRef = ref()
|
||||||
// 从本地储存中查被禁用的类别
|
// 从本地储存中查被禁用的类别
|
||||||
const disable_classify = useLocalStorage<string[]>('mt-disable-classify', [])
|
const disable_classify = useLocalStorage<string[]>('mt-disable-classify', [])
|
||||||
@@ -276,6 +300,10 @@ const onDragStart = (config_item_key: string, item_id: string) => {
|
|||||||
}
|
}
|
||||||
const onManageClick = () => {
|
const onManageClick = () => {
|
||||||
manage_dialog_visiable.value = true
|
manage_dialog_visiable.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
treeRef.value?.setCurrentKey(classify_list.value[0])
|
||||||
|
onNodeClick(classify_list.value[0])
|
||||||
|
}, 100)
|
||||||
}
|
}
|
||||||
const handleCheckChange = (data: { label: string }, checked: boolean, indeterminate: boolean) => {
|
const handleCheckChange = (data: { label: string }, checked: boolean, indeterminate: boolean) => {
|
||||||
if (checked && !checked_keys.value.includes(data.label)) {
|
if (checked && !checked_keys.value.includes(data.label)) {
|
||||||
@@ -363,14 +391,28 @@ function onDelLocalFile(item: ILeftAsideConfigItem) {
|
|||||||
|
|
||||||
// 新增图元
|
// 新增图元
|
||||||
const openCheck = ref(false)
|
const openCheck = ref(false)
|
||||||
|
const zipOpen = ref(false)
|
||||||
|
const downloadOpen = ref(false)
|
||||||
//打开弹窗按钮
|
//打开弹窗按钮
|
||||||
function checkClick() {
|
function checkClick() {
|
||||||
openCheck.value = true
|
openCheck.value = true
|
||||||
}
|
}
|
||||||
|
function zipCheck() {
|
||||||
|
zipOpen.value = true
|
||||||
|
}
|
||||||
|
function downloadCheck() {
|
||||||
|
downloadOpen.value = true
|
||||||
|
}
|
||||||
//接收到子页面关闭弹窗的事件,改变openCheck的值,否则在刷新页面之前openCheck会一直是true,导致无法第二次打开弹窗
|
//接收到子页面关闭弹窗的事件,改变openCheck的值,否则在刷新页面之前openCheck会一直是true,导致无法第二次打开弹窗
|
||||||
const updateOpenCheck = (value: boolean) => {
|
const updateOpenCheck = (value: boolean) => {
|
||||||
openCheck.value = value
|
openCheck.value = value
|
||||||
}
|
}
|
||||||
|
const updateZip = (value: boolean) => {
|
||||||
|
zipOpen.value = value
|
||||||
|
}
|
||||||
|
const updateDown = (value: boolean) => {
|
||||||
|
downloadOpen.value = value
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
#mt-left-aside .el-collapse-item__header,
|
#mt-left-aside .el-collapse-item__header,
|
||||||
|
|||||||
@@ -1420,6 +1420,9 @@ watch(
|
|||||||
|
|
||||||
time.value = setTimeout(() => {
|
time.value = setTimeout(() => {
|
||||||
console.log('🚀 ~ globalStore:', globalStore)
|
console.log('🚀 ~ globalStore:', globalStore)
|
||||||
|
if (useData.keyName == '') {
|
||||||
|
ElMessage.warning('请选择图纸!')
|
||||||
|
}
|
||||||
|
|
||||||
const { exportJson } = genExportJson(globalStore.canvasCfg, globalStore.gridCfg, globalStore.done_json)
|
const { exportJson } = genExportJson(globalStore.canvasCfg, globalStore.gridCfg, globalStore.done_json)
|
||||||
// const data_model: any = {
|
// const data_model: any = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" plain round @click="dialogVisible = true">点击编辑</el-button>
|
<el-button type="primary" plain round @click="dialogVisible = true">点击编辑</el-button>
|
||||||
<el-dialog v-model="dialogVisible" title="配置编辑" width="60%">
|
<el-dialog :close-on-click-modal="false" v-model="dialogVisible" title="配置编辑" width="60%">
|
||||||
<v-ace-editor
|
<v-ace-editor
|
||||||
v-model:value="content"
|
v-model:value="content"
|
||||||
lang="json"
|
lang="json"
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="useData.graphicDisplay == 'zl'">
|
<div v-if="useData.graphicDisplay == 'zl'">
|
||||||
<el-form label-width="60px" label-position="left">
|
<el-form label-width="60px" label-position="left">
|
||||||
<el-form-item
|
<el-form-item label="监测点" size="small" class="mt-10px">
|
||||||
label="监测点"
|
|
||||||
size="small"
|
|
||||||
class="mt-10px"
|
|
||||||
v-if="item_title == '绑定监测点' || item_title == '绑定指标'"
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<el-cascader
|
<el-cascader
|
||||||
:key="cascaderKey"
|
:key="cascaderKey"
|
||||||
@@ -47,13 +42,13 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
<!-- 无锡监测点 指标 -->
|
<!-- 无锡监测点 指标 -->
|
||||||
<div v-if="useData.graphicDisplay == 'wx'">
|
<div v-if="useData.graphicDisplay == 'wx' || useData.graphicDisplay == 'ypt'">
|
||||||
<el-form label-width="60px" label-position="left">
|
<el-form label-width="60px" label-position="left">
|
||||||
<el-form-item label="监测点" size="small" class="mt-10px">
|
<el-form-item label="监测点" size="small" class="mt-10px">
|
||||||
<div>
|
<div>
|
||||||
<el-cascader
|
<el-cascader
|
||||||
:key="cascaderKey"
|
:key="cascaderKey"
|
||||||
:options="treeData_wx"
|
:options="useData.graphicDisplay == 'ypt' ? treeData : treeData_wx"
|
||||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
@@ -110,7 +105,7 @@ import { ElForm, ElFormItem, ElTreeSelect } from 'element-plus'
|
|||||||
import { computed, ref, watch, onMounted, reactive, nextTick, watchEffect } from 'vue'
|
import { computed, ref, watch, onMounted, reactive, nextTick, watchEffect } from 'vue'
|
||||||
import type { IDoneJson } from '@/components/mt-edit/store/types'
|
import type { IDoneJson } from '@/components/mt-edit/store/types'
|
||||||
import { globalStore } from '@/components/mt-edit/store/global'
|
import { globalStore } from '@/components/mt-edit/store/global'
|
||||||
import { lineTree, targetList, eleEpdChooseTree_wx } from '@/api/index'
|
import { lineTree, targetList, eleEpdChooseTree_wx, eleEpdChooseTree_ypt } from '@/api/index'
|
||||||
import { useDataStore } from '@/stores/menuList'
|
import { useDataStore } from '@/stores/menuList'
|
||||||
import { lineTree_wx } from '@/api/index_wx'
|
import { lineTree_wx } from '@/api/index_wx'
|
||||||
import { templateRef } from '@vueuse/core'
|
import { templateRef } from '@vueuse/core'
|
||||||
@@ -195,6 +190,11 @@ watch(
|
|||||||
if (item_title.value == '绑定指标' && deptIds.value) {
|
if (item_title.value == '绑定指标' && deptIds.value) {
|
||||||
indexList()
|
indexList()
|
||||||
}
|
}
|
||||||
|
// if (useData.graphicDisplay == 'ypt') {
|
||||||
|
// if (deptIds.value) {
|
||||||
|
// indexList()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -267,6 +267,15 @@ const fetchData = async () => {
|
|||||||
try {
|
try {
|
||||||
const response = await lineTree({})
|
const response = await lineTree({})
|
||||||
treeData.value = buildLevel3Tree(response.data) // 转换数据格式并赋值给 transformedData
|
treeData.value = buildLevel3Tree(response.data) // 转换数据格式并赋值给 transformedData
|
||||||
|
if (useData.treeIndexs.length > 0) {
|
||||||
|
treeIndexs.value = useData.treeIndexs
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (useData.graphicDisplay == 'ypt') {
|
||||||
|
const res = await eleEpdChooseTree_ypt()
|
||||||
|
useData.settreeIndexs(res.data)
|
||||||
|
treeIndexs.value = res.data // 转换数据格式并赋值给 transformedData
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching data:', error)
|
console.error('Error fetching data:', error)
|
||||||
}
|
}
|
||||||
@@ -289,12 +298,18 @@ const fetchData_wx = async () => {
|
|||||||
// 指标数据
|
// 指标数据
|
||||||
const indexList = async () => {
|
const indexList = async () => {
|
||||||
try {
|
try {
|
||||||
const lineId = deptIds.value[deptIds.value.length - 1]
|
let lineId = ''
|
||||||
|
if (typeof deptIds.value === 'string') {
|
||||||
|
lineId = deptIds.value
|
||||||
|
} else {
|
||||||
|
lineId = deptIds.value[deptIds.value.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const response = await targetList({ lineId: lineId })
|
// const response = await targetList({ lineId: lineId })
|
||||||
if (response.data) {
|
// if (response.data) {
|
||||||
treeIndexs.value = response.data // 转换数据格式并赋值给 transformedData
|
// treeIndexs.value = response.data // 转换数据格式并赋值给 transformedData
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching data:', error)
|
console.error('Error fetching data:', error)
|
||||||
@@ -304,6 +319,7 @@ const indexList = async () => {
|
|||||||
const handleDeptChange = (deptId: []) => {
|
const handleDeptChange = (deptId: []) => {
|
||||||
// labelString.value = fileRef.value.getCheckedNodes().pathLabels.join(" / ");
|
// labelString.value = fileRef.value.getCheckedNodes().pathLabels.join(" / ");
|
||||||
item_uid.value = []
|
item_uid.value = []
|
||||||
|
indexString.value = ''
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
//fileRef.value.getCheckedNodes()[0]?.label 最后一层的值
|
//fileRef.value.getCheckedNodes()[0]?.label 最后一层的值
|
||||||
let name = []
|
let name = []
|
||||||
@@ -332,6 +348,12 @@ const handleDeptChange = (deptId: []) => {
|
|||||||
indexList()
|
indexList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (useData.graphicDisplay == 'ypt') {
|
||||||
|
// if (deptId) {
|
||||||
|
// indexList()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 给每一个元件绑定下拉框数据
|
// 给每一个元件绑定下拉框数据
|
||||||
@@ -342,7 +364,6 @@ const handleSelectUID = (uid: []) => {
|
|||||||
let nodes = []
|
let nodes = []
|
||||||
if (indexRef.value) {
|
if (indexRef.value) {
|
||||||
nodes = indexRef.value.getCheckedNodes()
|
nodes = indexRef.value.getCheckedNodes()
|
||||||
console.log('🚀 ~ handleSelectUID ~ indexRef.value.getCheckedNodes():', indexRef.value.getCheckedNodes())
|
|
||||||
name = nodes[0]?.pathLabels || []
|
name = nodes[0]?.pathLabels || []
|
||||||
}
|
}
|
||||||
if (selectItemSettingProps.itemJson) {
|
if (selectItemSettingProps.itemJson) {
|
||||||
@@ -354,12 +375,16 @@ const handleSelectUID = (uid: []) => {
|
|||||||
selectItemSettingProps.itemJson.UIDName = name.join('/')
|
selectItemSettingProps.itemJson.UIDName = name.join('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (is2DArray(uid)) {
|
if (is2DArray(uid)) {
|
||||||
selectItemSettingProps.itemJson.UIDNames = name.join(' / ')
|
selectItemSettingProps.itemJson.UIDNames = name.join(' / ')
|
||||||
} else {
|
} else {
|
||||||
selectItemSettingProps.itemJson.UIDNames = [name.join(' / ')]
|
selectItemSettingProps.itemJson.UIDNames = [name.join(' / ')]
|
||||||
}
|
}
|
||||||
|
// 单个指标绑定
|
||||||
|
if (item_title.value == '绑定指标') {
|
||||||
|
console.log(123)
|
||||||
|
selectItemSettingProps.itemJson.UIDType = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取选中的数据名称
|
// 获取选中的数据名称
|
||||||
@@ -371,10 +396,13 @@ const handleSelectUID = (uid: []) => {
|
|||||||
.getCheckedNodes()
|
.getCheckedNodes()
|
||||||
.map((item: any) => item.data.unit)
|
.map((item: any) => item.data.unit)
|
||||||
.filter((text: string) => text !== '' && text !== null && text !== undefined)
|
.filter((text: string) => text !== '' && text !== null && text !== undefined)
|
||||||
|
console.log(123)
|
||||||
|
|
||||||
if (is2DArray(uid)) {
|
if (is2DArray(uid)) {
|
||||||
if (selectItemSettingProps.itemJson) {
|
if (selectItemSettingProps.itemJson) {
|
||||||
selectItemSettingProps.itemJson.UIDNames = nameList
|
selectItemSettingProps.itemJson.UIDNames = nameList
|
||||||
selectItemSettingProps.itemJson.unit = unitList
|
selectItemSettingProps.itemJson.unit = unitList
|
||||||
|
|
||||||
indexString.value = selectItemSettingProps.itemJson.UIDNames
|
indexString.value = selectItemSettingProps.itemJson.UIDNames
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -382,14 +410,28 @@ const handleSelectUID = (uid: []) => {
|
|||||||
// 配置里面的输入框内容更新
|
// 配置里面的输入框内容更新
|
||||||
if (selectItemSettingProps.itemJson && selectItemSettingProps.itemJson.props) {
|
if (selectItemSettingProps.itemJson && selectItemSettingProps.itemJson.props) {
|
||||||
if (selectItemSettingProps.itemJson?.props.text.type == 'input') {
|
if (selectItemSettingProps.itemJson?.props.text.type == 'input') {
|
||||||
|
selectItemSettingProps.itemJson.unit = unitList
|
||||||
// selectItemSettingProps.itemJson.props.text.val = name.join(' / ')
|
// selectItemSettingProps.itemJson.props.text.val = name.join(' / ')
|
||||||
let names = name.reverse()
|
let names = name.reverse()
|
||||||
let str = ''
|
let str = ''
|
||||||
|
let key =
|
||||||
|
names[0] == 'max'
|
||||||
|
? '最大值'
|
||||||
|
: names[0] == 'min'
|
||||||
|
? '最小值'
|
||||||
|
: names[0] == 'avg'
|
||||||
|
? '平均值'
|
||||||
|
: names[0] == 'cp95'
|
||||||
|
? 'CP95值'
|
||||||
|
: names[0]
|
||||||
if (names[1] == '无相别') {
|
if (names[1] == '无相别') {
|
||||||
name[1] = ''
|
name[1] = ''
|
||||||
str = names[2] + '-' + names[0] + ':###'
|
str = names[2] + '-' + key + ':###'
|
||||||
} else {
|
} else {
|
||||||
str = names[1] + '相' + names[2] + '-' + names[0] + ':###'
|
str =
|
||||||
|
names.length == 2
|
||||||
|
? names[1] + '-' + key + ':###'
|
||||||
|
: names[1] + '相' + names[2] + '-' + key + ':###'
|
||||||
}
|
}
|
||||||
selectItemSettingProps.itemJson.props.text.val = str
|
selectItemSettingProps.itemJson.props.text.val = str
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,7 +222,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
<el-dialog v-model="dialog_visiable" :title="dialog_title" :before-close="onDialogClose">
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="dialog_visiable"
|
||||||
|
:title="dialog_title"
|
||||||
|
:before-close="onDialogClose"
|
||||||
|
>
|
||||||
<v-ace-editor
|
<v-ace-editor
|
||||||
v-model:value="dialog_code"
|
v-model:value="dialog_code"
|
||||||
lang="javascript"
|
lang="javascript"
|
||||||
|
|||||||
@@ -68,12 +68,18 @@
|
|||||||
@set-intention="val => renderCoreEmits('setIntention', val)"
|
@set-intention="val => renderCoreEmits('setIntention', val)"
|
||||||
@line-mouse-up="onLineMouseUp"
|
@line-mouse-up="onLineMouseUp"
|
||||||
v-on="renderCoreProps.preivewMode ? eventToVOn(item, externalMethod) : {}"
|
v-on="renderCoreProps.preivewMode ? eventToVOn(item, externalMethod) : {}"
|
||||||
@click="() => renderCoreProps.onElementClick && item.lineId && renderCoreProps.onElementClick(item.lineId)"
|
@click="
|
||||||
|
() =>
|
||||||
|
renderCoreProps.onElementClick &&
|
||||||
|
item.lineId &&
|
||||||
|
item.title != '绑定指标' &&
|
||||||
|
renderCoreProps.onElementClick(item.lineId, item.lineName)
|
||||||
|
"
|
||||||
></render-item>
|
></render-item>
|
||||||
</mt-dzr>
|
</mt-dzr>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, reactive, ref } from 'vue'
|
import { nextTick, reactive, ref, onMounted } from 'vue'
|
||||||
import MtDzr from '@/components/mt-dzr/index.vue'
|
import MtDzr from '@/components/mt-dzr/index.vue'
|
||||||
import RenderItem from '@/components/mt-edit/components/render-item/index.vue'
|
import RenderItem from '@/components/mt-edit/components/render-item/index.vue'
|
||||||
import type {
|
import type {
|
||||||
@@ -97,6 +103,8 @@ import SysButtonVue from '@/components/custom-components/sys-button-vue/index.vu
|
|||||||
import BindDotVue from '@/components/custom-components/bind-dot-vue/index.vue'
|
import BindDotVue from '@/components/custom-components/bind-dot-vue/index.vue'
|
||||||
import BindIndexVue from '@/components/custom-components/bind-index-vue/index.vue'
|
import BindIndexVue from '@/components/custom-components/bind-index-vue/index.vue'
|
||||||
import { ElPopover } from 'element-plus'
|
import { ElPopover } from 'element-plus'
|
||||||
|
import { leftAsideStore } from '@/export'
|
||||||
|
|
||||||
const instance = getCurrentInstance()
|
const instance = getCurrentInstance()
|
||||||
const now_include_keys = Object.keys(instance?.appContext?.components as any)
|
const now_include_keys = Object.keys(instance?.appContext?.components as any)
|
||||||
if (!now_include_keys.includes('text-vue')) {
|
if (!now_include_keys.includes('text-vue')) {
|
||||||
@@ -130,7 +138,7 @@ type RenderCoreProps = {
|
|||||||
preivewMode?: boolean
|
preivewMode?: boolean
|
||||||
lineAppendEnable?: boolean
|
lineAppendEnable?: boolean
|
||||||
showPopover?: boolean
|
showPopover?: boolean
|
||||||
onElementClick?: (elementId: string) => void
|
onElementClick?: (elementId: string, lineName?: string) => void
|
||||||
}
|
}
|
||||||
const renderCoreProps = withDefaults(defineProps<RenderCoreProps>(), {
|
const renderCoreProps = withDefaults(defineProps<RenderCoreProps>(), {
|
||||||
doneJson: () => [],
|
doneJson: () => [],
|
||||||
@@ -228,6 +236,7 @@ const onItemMove = (e: any, id: string) => {
|
|||||||
})
|
})
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCoreEmits('onItemMove', item_move_params)
|
renderCoreEmits('onItemMove', item_move_params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -293,4 +302,9 @@ const onLineMouseUp = () => {
|
|||||||
cacheStore.addHistory(globalStore.done_json)
|
cacheStore.addHistory(globalStore.done_json)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// console.log(123, leftAsideStore.list)
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-1/1 h-1/1">
|
<div class="w-1/1 h-1/1">
|
||||||
<svg-render
|
<svg-render
|
||||||
v-if="item_json.type === 'svg'"
|
v-if="
|
||||||
|
item_json.type === 'svg' &&
|
||||||
|
leftAsideStore.list.some(k => k.symbol?.symbol_id === item_json.symbol?.symbol_id)
|
||||||
|
"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
:symbol-id="item_json.symbol!.symbol_id"
|
:symbol-id="item_json.symbol!.symbol_id"
|
||||||
:symbol-str="item_json.symbol!.symbol_str"
|
:symbol-str="item_json.symbol!.symbol_str"
|
||||||
@@ -53,6 +56,8 @@ import GroupRender from '@/components/mt-edit/components/group-render/index.vue'
|
|||||||
import { prosToVBind } from '@/components/mt-edit/utils'
|
import { prosToVBind } from '@/components/mt-edit/utils'
|
||||||
import LineRender from '@/components/mt-edit/components/line-render/index.vue'
|
import LineRender from '@/components/mt-edit/components/line-render/index.vue'
|
||||||
import CustomSvgRender from '@/components/mt-edit/components/custom-svg-render/index.vue'
|
import CustomSvgRender from '@/components/mt-edit/components/custom-svg-render/index.vue'
|
||||||
|
import { leftAsideStore } from '@/export'
|
||||||
|
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
type RenderItemProps = {
|
type RenderItemProps = {
|
||||||
itemJson: IDoneJson
|
itemJson: IDoneJson
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export const useExportJsonToDoneJson = (json: IExportJson) => {
|
|||||||
init_configs = [...init_configs, ...iterator]
|
init_configs = [...init_configs, ...iterator]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const importDoneJson: IDoneJson[] = json.json.map(m => {
|
const importDoneJson: IDoneJson[] = json?.json?.map(m => {
|
||||||
let props: ILeftAsideConfigItemPublicProps = {}
|
let props: ILeftAsideConfigItemPublicProps = {}
|
||||||
let symbol = undefined
|
let symbol = undefined
|
||||||
// 找到原始的props
|
// 找到原始的props
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const useGenThumbnail = async (canvas_id: string = 'mtCanvasArea') => {
|
|||||||
// //记录要移除的svg元素
|
// //记录要移除的svg元素
|
||||||
const shouldRemoveSvgNodes = []
|
const shouldRemoveSvgNodes = []
|
||||||
// 获取到所有的SVG 得到一个数组 目前只有自定义连线需要特殊处理 别的元素直接使用html2canvas就可以
|
// 获取到所有的SVG 得到一个数组 目前只有自定义连线需要特殊处理 别的元素直接使用html2canvas就可以
|
||||||
|
|
||||||
const svgElements: NodeListOf<HTMLElement> = document.body.querySelectorAll(`#${canvas_id} .mt-line-render`)
|
const svgElements: NodeListOf<HTMLElement> = document.body.querySelectorAll(`#${canvas_id} .mt-line-render`)
|
||||||
// 遍历这个数组
|
// 遍历这个数组
|
||||||
for (const item of svgElements) {
|
for (const item of svgElements) {
|
||||||
@@ -40,13 +41,14 @@ export const useGenThumbnail = async (canvas_id: string = 'mtCanvasArea') => {
|
|||||||
|
|
||||||
const width = el.offsetWidth
|
const width = el.offsetWidth
|
||||||
const height = el.offsetHeight
|
const height = el.offsetHeight
|
||||||
|
|
||||||
const canvas = await html2canvas(el, {
|
const canvas = await html2canvas(el, {
|
||||||
useCORS: true,
|
useCORS: true,
|
||||||
scale: 2,
|
scale: 2,
|
||||||
width,
|
// width,
|
||||||
height,
|
// height,
|
||||||
allowTaint: true,
|
allowTaint: true,
|
||||||
windowHeight: height,
|
// windowHeight: height,
|
||||||
logging: false,
|
logging: false,
|
||||||
ignoreElements: element => {
|
ignoreElements: element => {
|
||||||
if (element.classList.contains('mt-line-render')) {
|
if (element.classList.contains('mt-line-render')) {
|
||||||
|
|||||||
@@ -72,13 +72,36 @@
|
|||||||
<footer-panel></footer-panel>
|
<footer-panel></footer-panel>
|
||||||
</el-footer>
|
</el-footer>
|
||||||
</el-container>
|
</el-container>
|
||||||
<el-dialog v-model="import_visible" title="数据导入" @close="mainPanelRef?.beginListenerKeyDown()">
|
<!-- 上传json -->
|
||||||
|
<el-upload
|
||||||
|
:show-file-list="false"
|
||||||
|
accept=".json"
|
||||||
|
ref="uploadRef"
|
||||||
|
:auto-upload="false"
|
||||||
|
:limit="1"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
:on-change="handleOnchange"
|
||||||
|
>
|
||||||
|
<el-button type="text" v-show="false">默认上传按钮(隐藏)</el-button>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="import_visible"
|
||||||
|
title="数据导入"
|
||||||
|
@close="mainPanelRef?.beginListenerKeyDown()"
|
||||||
|
>
|
||||||
<import-json ref="importJsonRef"></import-json>
|
<import-json ref="importJsonRef"></import-json>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button type="primary" @click="onImportYes">确定</el-button>
|
<el-button type="primary" @click="onImportYes">确定</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<el-dialog v-model="export_visible" title="数据导出" @close="mainPanelRef?.beginListenerKeyDown()">
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="export_visible"
|
||||||
|
title="数据导出"
|
||||||
|
@close="mainPanelRef?.beginListenerKeyDown()"
|
||||||
|
>
|
||||||
<export-json
|
<export-json
|
||||||
:done-json="objectDeepClone(globalStore.done_json)"
|
:done-json="objectDeepClone(globalStore.done_json)"
|
||||||
:canvas-cfg="globalStore.canvasCfg"
|
:canvas-cfg="globalStore.canvasCfg"
|
||||||
@@ -114,6 +137,8 @@ import { objectDeepClone } from './utils'
|
|||||||
import { genExportJson, useExportJsonToDoneJson } from './composables'
|
import { genExportJson, useExportJsonToDoneJson } from './composables'
|
||||||
import type { IExportJson } from './components/types'
|
import type { IExportJson } from './components/types'
|
||||||
import { useDataStore } from '@/stores/menuList'
|
import { useDataStore } from '@/stores/menuList'
|
||||||
|
import type { UploadInstance, UploadProps, UploadRawFile, UploadFile } from 'element-plus'
|
||||||
|
import { genFileId } from 'element-plus'
|
||||||
type MtEditProps = {
|
type MtEditProps = {
|
||||||
useThumbnail?: boolean
|
useThumbnail?: boolean
|
||||||
}
|
}
|
||||||
@@ -150,6 +175,7 @@ const header_align_enabled = computed(() => {
|
|||||||
)
|
)
|
||||||
return selected_items.length > 1
|
return selected_items.length > 1
|
||||||
})
|
})
|
||||||
|
const uploadRef = ref()
|
||||||
const import_visible = ref(false)
|
const import_visible = ref(false)
|
||||||
const export_visible = ref(false)
|
const export_visible = ref(false)
|
||||||
const done_json_tree_visiable = ref(false)
|
const done_json_tree_visiable = ref(false)
|
||||||
@@ -159,12 +185,65 @@ const onDeleteClick = () => {
|
|||||||
cacheStore.addHistory(globalStore.done_json)
|
cacheStore.addHistory(globalStore.done_json)
|
||||||
}
|
}
|
||||||
const onImportClick = () => {
|
const onImportClick = () => {
|
||||||
import_visible.value = true
|
uploadRef.value?.$el.querySelector('input[type="file"]')?.click()
|
||||||
mainPanelRef.value?.stopListenerKeyDown()
|
// import_visible.value = true
|
||||||
|
// mainPanelRef.value?.stopListenerKeyDown()
|
||||||
}
|
}
|
||||||
|
const handleExceed: UploadProps['onExceed'] = files => {
|
||||||
|
uploadRef.value!.clearFiles()
|
||||||
|
const file = files[0] as UploadRawFile
|
||||||
|
file.uid = genFileId()
|
||||||
|
uploadRef.value!.handleStart(file)
|
||||||
|
}
|
||||||
|
// 上传json文件
|
||||||
|
const handleOnchange = (uploadFile: UploadFile) => {
|
||||||
|
let file: any = uploadFile.raw // 获取文件信息
|
||||||
|
|
||||||
|
const fileName = file.name
|
||||||
|
const isJsonSuffix = fileName.endsWith('.json')
|
||||||
|
|
||||||
|
// 方式2:通过文件 MIME 类型校验(辅助验证,部分浏览器可能不准确)
|
||||||
|
const isJsonType = file.type === 'application/json' || file.type === ''
|
||||||
|
|
||||||
|
if (!isJsonSuffix || !isJsonType) {
|
||||||
|
ElMessage.error('请上传后缀为 .json 的合法 JSON 文件!')
|
||||||
|
uploadRef.value.clearFiles()
|
||||||
|
return // 校验失败,终止后续操作
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileReader = new FileReader()
|
||||||
|
fileReader.readAsText(file!) // 开始读取文件的内容为二进制
|
||||||
|
|
||||||
|
fileReader.onload = ev => {
|
||||||
|
// 读取完成,对数据进行自己的操作
|
||||||
|
const data = ev.target?.result //获取内容
|
||||||
|
console.log('🚀 ~ handleOnchange ~ data:', data)
|
||||||
|
onImportYes(data)
|
||||||
|
// console.log(JSON.parse(data as string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onExportClick = () => {
|
const onExportClick = () => {
|
||||||
export_visible.value = true
|
// export_visible.value = true
|
||||||
mainPanelRef.value?.stopListenerKeyDown()
|
// mainPanelRef.value?.stopListenerKeyDown()
|
||||||
|
// :done-json="objectDeepClone(globalStore.done_json)"
|
||||||
|
// :canvas-cfg="globalStore.canvasCfg"
|
||||||
|
// :grid-cfg="globalStore.gridCfg"
|
||||||
|
|
||||||
|
const { exportJson } = genExportJson(
|
||||||
|
globalStore.canvasCfg,
|
||||||
|
globalStore.gridCfg,
|
||||||
|
objectDeepClone(globalStore.done_json)
|
||||||
|
)
|
||||||
|
let data = JSON.stringify(exportJson, null, 2)
|
||||||
|
let blob = new Blob([data], { type: 'text/plain' })
|
||||||
|
let url = URL.createObjectURL(blob)
|
||||||
|
let a = document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = useData.keyName + '.json'
|
||||||
|
a.click()
|
||||||
|
|
||||||
|
ElMessage.success('JSON 导出成功')
|
||||||
}
|
}
|
||||||
const onTreeUpdateSelectedItemsId = (id: string) => {
|
const onTreeUpdateSelectedItemsId = (id: string) => {
|
||||||
globalStore.setSingleSelect(id)
|
globalStore.setSingleSelect(id)
|
||||||
@@ -197,15 +276,35 @@ const onRedoClick = () => {
|
|||||||
const onUndoClick = () => {
|
const onUndoClick = () => {
|
||||||
mainPanelRef.value?.onUndo()
|
mainPanelRef.value?.onUndo()
|
||||||
}
|
}
|
||||||
const onImportYes = async () => {
|
const onImportYes = async (row: any) => {
|
||||||
const res = await importJsonRef.value?.onImport()
|
const res = await onImport(row)
|
||||||
|
// const res = await importJsonRef.value?.onImport()
|
||||||
if (res) {
|
if (res) {
|
||||||
import_visible.value = false
|
import_visible.value = false
|
||||||
|
uploadRef.value.clearFiles()
|
||||||
cacheStore.addHistory(globalStore.done_json)
|
cacheStore.addHistory(globalStore.done_json)
|
||||||
|
ElMessage.success('JSON 导入成功')
|
||||||
} else {
|
} else {
|
||||||
|
uploadRef.value.clearFiles()
|
||||||
ElMessage.error('导入失败,请检查数据格式')
|
ElMessage.error('导入失败,请检查数据格式')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onImport = (res: any) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const json: IExportJson = JSON.parse(res)
|
||||||
|
const { canvasCfg, gridCfg, importDoneJson } = useExportJsonToDoneJson(json)
|
||||||
|
globalStore.canvasCfg = canvasCfg
|
||||||
|
globalStore.gridCfg = gridCfg
|
||||||
|
globalStore.setGlobalStoreDoneJson(importDoneJson)
|
||||||
|
resolve(true)
|
||||||
|
} catch (error) {
|
||||||
|
resolve(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const onPreviewClick = () => {
|
const onPreviewClick = () => {
|
||||||
// 获取导出json
|
// 获取导出json
|
||||||
const { exportJson } = genExportJson(globalStore.canvasCfg, globalStore.gridCfg, globalStore.done_json)
|
const { exportJson } = genExportJson(globalStore.canvasCfg, globalStore.gridCfg, globalStore.done_json)
|
||||||
@@ -219,9 +318,18 @@ const onSaveClick = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const useData = useDataStore()
|
const useData = useDataStore()
|
||||||
const onSaveAll = () => {
|
const onSaveAll = async () => {
|
||||||
|
let data: any = await useData.dataTree.map((item, ind) => {
|
||||||
|
let pathList = JSON.parse(item.path)
|
||||||
|
pathList.canvasCfg.lineList = pathList.json.filter(k => k.lineId != '' && k.lineId != null).map(k => k.lineId)
|
||||||
|
item.path = JSON.stringify(pathList)
|
||||||
|
item.sort = ind
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
console.log('🚀 ~ onSaveAll ~ data:', data)
|
||||||
|
|
||||||
let form = new FormData()
|
let form = new FormData()
|
||||||
let blob = new Blob([JSON.stringify(useData.dataTree)], {
|
let blob = new Blob([JSON.stringify(data)], {
|
||||||
type: 'application/json'
|
type: 'application/json'
|
||||||
})
|
})
|
||||||
form.append('multipartFile', blob)
|
form.append('multipartFile', blob)
|
||||||
@@ -251,6 +359,7 @@ defineExpose({
|
|||||||
.mt-edit-aside {
|
.mt-edit-aside {
|
||||||
transition: width 0.3s;
|
transition: width 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt-edit-aside-left {
|
.mt-edit-aside-left {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const leftAsideStore: ILeftAside = reactive({
|
|||||||
['基础图元', configStore.sysComponent],
|
['基础图元', configStore.sysComponent],
|
||||||
['数据绑定图元', bingStore.sysComponent]
|
['数据绑定图元', bingStore.sysComponent]
|
||||||
]),
|
]),
|
||||||
|
list: [],
|
||||||
registerConfig: (title: string, config: ILeftAsideConfigItemPublic[]) => {
|
registerConfig: (title: string, config: ILeftAsideConfigItemPublic[]) => {
|
||||||
if (title == '本地文件' || title == '基础图元') {
|
if (title == '本地文件' || title == '基础图元') {
|
||||||
ElMessage.info(`title:${title}已被系统占用,请更换名称!`)
|
ElMessage.info(`title:${title}已被系统占用,请更换名称!`)
|
||||||
@@ -18,7 +19,7 @@ export const leftAsideStore: ILeftAside = reactive({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (leftAsideStore.config.has(title)) {
|
if (leftAsideStore.config.has(title)) {
|
||||||
ElMessage.info(`title:${title}已存在,已经将其配置覆盖`)
|
// ElMessage.info(`title:${title}已存在,已经将其配置覆盖`)
|
||||||
}
|
}
|
||||||
const cfg: ILeftAsideConfigItem[] = config.map(m => {
|
const cfg: ILeftAsideConfigItem[] = config.map(m => {
|
||||||
if (m.type == 'svg') {
|
if (m.type == 'svg') {
|
||||||
@@ -49,6 +50,7 @@ export const leftAsideStore: ILeftAside = reactive({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
leftAsideStore.list.push(...cfg)
|
||||||
leftAsideStore.config.set(title, cfg)
|
leftAsideStore.config.set(title, cfg)
|
||||||
},
|
},
|
||||||
svgPush: (title: string, config: ILeftAsideConfigItemPublic[]) => {
|
svgPush: (title: string, config: ILeftAsideConfigItemPublic[]) => {
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ export interface IGlobalStore {
|
|||||||
// 左侧配置
|
// 左侧配置
|
||||||
export interface ILeftAside {
|
export interface ILeftAside {
|
||||||
config: ILeftAsideConfig
|
config: ILeftAsideConfig
|
||||||
|
list: any
|
||||||
registerConfig: (title: string, config: ILeftAsideConfigItemPublic[]) => void
|
registerConfig: (title: string, config: ILeftAsideConfigItemPublic[]) => void
|
||||||
svgPush: (title: string, config: ILeftAsideConfigItemPublic[]) => void
|
svgPush: (title: string, config: ILeftAsideConfigItemPublic[]) => void
|
||||||
svgDelete: (title: string, id: string) => void
|
svgDelete: (title: string, id: string) => void
|
||||||
|
|||||||
@@ -817,6 +817,7 @@ export const eventToVOn = (item: IDoneJson, externalMethod: (kid?: string) => vo
|
|||||||
code_str += event.jump_to
|
code_str += event.jump_to
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(event_obj, event.type)) {
|
if (!Object.prototype.hasOwnProperty.call(event_obj, event.type)) {
|
||||||
event_obj[event.type] = code_str
|
event_obj[event.type] = code_str
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
262
src/components/mt-preview-ypt/iframeDia.vue
Normal file
262
src/components/mt-preview-ypt/iframeDia.vue
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<!-- 使用 v-for 遍历四个角落 -->
|
||||||
|
<div v-for="corner in corners" v-show="corner.show" :key="corner.id" :class="['corner', corner.className]">
|
||||||
|
<div class="content">
|
||||||
|
<div class="title">{{ corner.title }}</div>
|
||||||
|
<el-descriptions :column="1" size="small" label-width="70px" border>
|
||||||
|
<el-descriptions-item label="指标数据">
|
||||||
|
<div style="height: 200px; overflow-y: auto">
|
||||||
|
<div
|
||||||
|
v-for="item in props.steadyState?.filter(k => k.lineId === corner.elementId)"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
{{ item.statisticalName.replace(/\//g, '_') }}:
|
||||||
|
{{ item.value === 3.1415926 ? '/' : item.value + item.unit }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
<span class="close-btn" @click="closeCorner(corner.id)">
|
||||||
|
<Close />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, watch } from 'vue'
|
||||||
|
import { Close } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
// 定义 emits
|
||||||
|
const emit = defineEmits(['lineListChange'])
|
||||||
|
|
||||||
|
// 定义接收的 props
|
||||||
|
const props = defineProps<{
|
||||||
|
steadyState?: any[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 定义四个角落的数据
|
||||||
|
const corners = ref([
|
||||||
|
{
|
||||||
|
id: 'topLeft',
|
||||||
|
title: '左上',
|
||||||
|
className: 'top-left',
|
||||||
|
show: false,
|
||||||
|
data: [] as { label: string; value: string }[],
|
||||||
|
elementId: '' // 记录该角落对应的元素ID
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'topRight',
|
||||||
|
title: '右上',
|
||||||
|
className: 'top-right',
|
||||||
|
show: false,
|
||||||
|
data: [] as { label: string; value: string }[],
|
||||||
|
elementId: ''
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const displayOrder = ref<number[]>([])
|
||||||
|
|
||||||
|
// 截取名称的最后一部分作为标题
|
||||||
|
const extractTitleFromLineName = (lineName: string): string => {
|
||||||
|
if (!lineName) return '未知监测点'
|
||||||
|
|
||||||
|
// 按照 "/" 分割字符串,取最后一部分
|
||||||
|
const parts = lineName.split('/')
|
||||||
|
return parts.length > 0 ? parts[parts.length - 1].trim() : lineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新指定角落数据的函数
|
||||||
|
const updateCornerData = (cornerIndex: number, elementId: string, lineName: string) => {
|
||||||
|
// 更新标题为传入的 lineName 的最后一部分
|
||||||
|
corners.value[cornerIndex].title = extractTitleFromLineName(lineName)
|
||||||
|
|
||||||
|
// 格式化数据(只保留稳态指标一项)
|
||||||
|
corners.value[cornerIndex].data = [
|
||||||
|
{
|
||||||
|
label: '稳态指标',
|
||||||
|
value: '正在加载...'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 记录该角落对应的元素ID
|
||||||
|
corners.value[cornerIndex].elementId = elementId
|
||||||
|
corners.value[cornerIndex].show = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示下一个角落的函数
|
||||||
|
const showNextCorner = (elementId: string, lineName: string) => {
|
||||||
|
// 检查该元素ID是否已经显示过
|
||||||
|
const existingCornerIndex = corners.value.findIndex(corner => corner.elementId === elementId && corner.show)
|
||||||
|
|
||||||
|
if (existingCornerIndex !== -1) {
|
||||||
|
// 如果该元素已经显示过,更新标题
|
||||||
|
corners.value[existingCornerIndex].title = extractTitleFromLineName(lineName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找一个未显示的角落
|
||||||
|
const availableCornerIndex = corners.value.findIndex(corner => !corner.show)
|
||||||
|
|
||||||
|
if (availableCornerIndex !== -1) {
|
||||||
|
// 有空闲角落,显示在该角落
|
||||||
|
updateCornerData(availableCornerIndex, elementId, lineName)
|
||||||
|
|
||||||
|
// 记录显示顺序
|
||||||
|
displayOrder.value.push(availableCornerIndex)
|
||||||
|
} else {
|
||||||
|
// 没有空闲角落,按顺序替换角落
|
||||||
|
// 获取需要替换的角落索引(循环替换)
|
||||||
|
const replaceIndex = displayOrder.value.shift() || 0
|
||||||
|
updateCornerData(replaceIndex, elementId, lineName)
|
||||||
|
// 将替换的索引重新加入队列末尾
|
||||||
|
displayOrder.value.push(replaceIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateLineList()
|
||||||
|
}
|
||||||
|
const timer = ref<any>(null)
|
||||||
|
// 更新 lineList,根据 corners 的 show 状态来维护
|
||||||
|
const updateLineList = () => {
|
||||||
|
const newLineList = corners.value.filter(corner => corner.show && corner.elementId).map(corner => corner.elementId)
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
timer.value = null
|
||||||
|
}
|
||||||
|
if (newLineList.length > 0) {
|
||||||
|
emit('lineListChange', newLineList)
|
||||||
|
timer.value = setInterval(
|
||||||
|
() => {
|
||||||
|
emit('lineListChange', newLineList)
|
||||||
|
},
|
||||||
|
3 * 60 * 1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭指定角落的函数
|
||||||
|
const closeCorner = (id: string) => {
|
||||||
|
const cornerIndex = corners.value.findIndex(c => c.id === id)
|
||||||
|
if (cornerIndex !== -1) {
|
||||||
|
corners.value[cornerIndex].show = false
|
||||||
|
corners.value[cornerIndex].elementId = '' // 清空元素ID记录
|
||||||
|
|
||||||
|
// 从显示顺序中移除该角落索引
|
||||||
|
const orderIndex = displayOrder.value.indexOf(cornerIndex)
|
||||||
|
if (orderIndex !== -1) {
|
||||||
|
displayOrder.value.splice(orderIndex, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭所有角落的函数
|
||||||
|
const closeAllCorners = () => {
|
||||||
|
corners.value.forEach(corner => {
|
||||||
|
corner.show = false
|
||||||
|
corner.elementId = ''
|
||||||
|
})
|
||||||
|
displayOrder.value = []
|
||||||
|
|
||||||
|
updateLineList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 corners 的变化,特别是 show 状态的变化
|
||||||
|
watch(
|
||||||
|
() => corners.value.map(corner => ({ id: corner.id, show: corner.show, elementId: corner.elementId })),
|
||||||
|
(newCorners, oldCorners) => {
|
||||||
|
// 检查是否有 show 状态的变化
|
||||||
|
const showStateChanged = newCorners.some((corner, index) => corner.show !== oldCorners[index]?.show)
|
||||||
|
|
||||||
|
if (showStateChanged) {
|
||||||
|
updateLineList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 暴露方法给父组件使用
|
||||||
|
defineExpose({
|
||||||
|
showNextCorner,
|
||||||
|
closeCorner,
|
||||||
|
closeAllCorners
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.corner {
|
||||||
|
width: 260px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-left {
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-right {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-left {
|
||||||
|
top: 170px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-right {
|
||||||
|
top: 170px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-item {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 55px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 关闭按钮样式 */
|
||||||
|
.close-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
width: 14px;
|
||||||
|
color: #000;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicator {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3
src/components/mt-preview-ypt/index.ts
Normal file
3
src/components/mt-preview-ypt/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import MtPreviewYPT from './index.vue'
|
||||||
|
|
||||||
|
export default MtPreviewYPT
|
||||||
922
src/components/mt-preview-ypt/index.vue
Normal file
922
src/components/mt-preview-ypt/index.vue
Normal file
@@ -0,0 +1,922 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:style="{ backgroundColor: useData.display ? '' : canvas_cfg.color }"
|
||||||
|
@mousedown="onMouseDown"
|
||||||
|
@mousemove="onMouseMove"
|
||||||
|
@mouseup="onMouseUp"
|
||||||
|
@mouseleave="onMouseUp"
|
||||||
|
@wheel="onMouseWheel"
|
||||||
|
style="height: 100vh; overflow: hidden"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-loading="useData.loading"
|
||||||
|
:style="canvasStyle"
|
||||||
|
style="overflow: hidden"
|
||||||
|
:class="`canvasArea ${isDragging ? 'cursor-grabbing' : mtPreviewProps.canDrag ? 'cursor-grab' : ''} `"
|
||||||
|
>
|
||||||
|
<!-- <el-button type="primary" class="backBtn" @click="onBack" v-if="!useData.display">返回</el-button> -->
|
||||||
|
<!-- 缩放控制按钮 (默认注释,需要时可开启) -->
|
||||||
|
<!-- <div class="zoom-controls">
|
||||||
|
<el-button icon="ZoomIn" size="small" @click="zoomIn"></el-button>
|
||||||
|
<el-button icon="ZoomOut" size="small" @click="zoomOut"></el-button>
|
||||||
|
<el-button icon="RefreshLeft" size="small" @click="resetTransform"></el-button>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<!-- <el-scrollbar ref="elScrollbarRef" class="w-1/1 h-1/1" @scroll="onScroll" > -->
|
||||||
|
<div ref="canvasAreaRef">
|
||||||
|
<render-core
|
||||||
|
v-model:done-json="done_json"
|
||||||
|
:canvas-cfg="canvas_cfg"
|
||||||
|
:grid-cfg="grid_cfg"
|
||||||
|
:show-ghost-dom="false"
|
||||||
|
:canvas-dom="canvasAreaRef"
|
||||||
|
:global-lock="false"
|
||||||
|
:preivew-mode="true"
|
||||||
|
:show-popover="mtPreviewProps.showPopover"
|
||||||
|
@setDoneJson="setDoneJson"
|
||||||
|
@element-click="handleElementClick"
|
||||||
|
></render-core>
|
||||||
|
</div>
|
||||||
|
<drag-canvas
|
||||||
|
ref="dragCanvasRef"
|
||||||
|
:scale-ratio="canvas_cfg.scale"
|
||||||
|
@drag-canvas-mouse-down="dragCanvasMouseDown"
|
||||||
|
@drag-canvas-mouse-move="dragCanvasMouseMove"
|
||||||
|
@drag-canvas-mouse-up="dragCanvasMouseUp"
|
||||||
|
></drag-canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 弹框 -->
|
||||||
|
<iframeDia :steadyState="dataList" ref="iframeDiaRef" @lineListChange="indicator"></iframeDia>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, watch, computed, onUnmounted, nextTick } from 'vue'
|
||||||
|
import RenderCore from '@/components/mt-edit/components/render-core/index.vue'
|
||||||
|
import type { IExportJson, IExportDoneJson } from '../mt-edit/components/types'
|
||||||
|
import { useExportJsonToDoneJson } from '../mt-edit/composables'
|
||||||
|
import type { IDoneJson } from '../mt-edit/store/types'
|
||||||
|
import { getItemAttr, previewCompareVal, setItemAttr } from '../mt-edit/utils'
|
||||||
|
import { ElScrollbar, ElMessage, ElMessageBox, ElButton } from 'element-plus'
|
||||||
|
import DragCanvas from '@/components/mt-edit/components/drag-canvas/index.vue'
|
||||||
|
import { useDataStore } from '@/stores/menuList'
|
||||||
|
import { globalStore } from '../mt-edit/store/global'
|
||||||
|
import type { IDoneJsonEventList } from '../mt-edit/store/types'
|
||||||
|
import IframeDia from './iframeDia.vue'
|
||||||
|
import MQTT from '@/utils/mqtt'
|
||||||
|
const iframeDiaRef = ref<any>(null)
|
||||||
|
|
||||||
|
const dataList = ref([])
|
||||||
|
|
||||||
|
// 节流函数实现 (替代lodash,减少依赖)
|
||||||
|
const throttle = (func: (...args: any[]) => void, wait: number) => {
|
||||||
|
let lastTime = 0
|
||||||
|
return (...args: any[]) => {
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - lastTime >= wait) {
|
||||||
|
func.apply(this, args)
|
||||||
|
lastTime = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MtPreviewProps = {
|
||||||
|
exportJson?: IExportJson
|
||||||
|
canZoom?: boolean
|
||||||
|
canDrag?: boolean
|
||||||
|
showPopover?: boolean
|
||||||
|
}
|
||||||
|
const mtPreviewProps = withDefaults(defineProps<MtPreviewProps>(), {
|
||||||
|
canDrag: true,
|
||||||
|
canZoom: true,
|
||||||
|
showPopover: true
|
||||||
|
})
|
||||||
|
const emits = defineEmits(['onEventCallBack'])
|
||||||
|
const useData = useDataStore()
|
||||||
|
const canvasAreaRef = ref<HTMLDivElement | null>(null)
|
||||||
|
const savedExportJson = ref<IExportJson>()
|
||||||
|
|
||||||
|
// 画布配置 - 扩展支持平移
|
||||||
|
const canvas_cfg = ref({
|
||||||
|
// width: 1920,
|
||||||
|
// height: 1080,
|
||||||
|
width: globalStore.canvasCfg.width,
|
||||||
|
height: globalStore.canvasCfg.height,
|
||||||
|
scale: 1,
|
||||||
|
color: '',
|
||||||
|
img: '',
|
||||||
|
guide: true,
|
||||||
|
adsorp: true,
|
||||||
|
adsorp_diff: 3,
|
||||||
|
transform_origin: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
drag_offset: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
// 平移属性
|
||||||
|
pan: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const grid_cfg = ref({
|
||||||
|
enabled: true,
|
||||||
|
align: true,
|
||||||
|
size: 10
|
||||||
|
})
|
||||||
|
const done_json = ref<IDoneJson[]>([])
|
||||||
|
const elScrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
|
||||||
|
const dragCanvasRef = ref<InstanceType<typeof DragCanvas>>()
|
||||||
|
const scroll_info = reactive({
|
||||||
|
begin_left: 0,
|
||||||
|
begin_top: 0,
|
||||||
|
left: 0,
|
||||||
|
top: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 拖拽状态变量
|
||||||
|
const isDragging = ref(false)
|
||||||
|
const startPos = ref({ x: 0, y: 0 })
|
||||||
|
// 非响应式临时变量,减少响应式更新频率
|
||||||
|
const tempPan = { x: 0, y: 0 }
|
||||||
|
let animationFrameId: number | null = null
|
||||||
|
|
||||||
|
// 计算画布样式
|
||||||
|
const canvasStyle = computed(() => ({
|
||||||
|
transform: `translate(${canvas_cfg.value.pan.x}px, ${canvas_cfg.value.pan.y}px) scale(${canvas_cfg.value.scale})`,
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
// width: `100vw`,
|
||||||
|
// height: `100vh`,
|
||||||
|
|
||||||
|
width: canvas_cfg.value.width + 'px',
|
||||||
|
height: canvas_cfg.value.height + 'px',
|
||||||
|
backgroundColor: useData.display ? '' : canvas_cfg.value.color,
|
||||||
|
backgroundImage: canvas_cfg.value.img ? `url(${canvas_cfg.value.img})` : 'none',
|
||||||
|
backgroundSize: '100% 100%',
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
marginLeft: (document.documentElement.clientWidth - canvas_cfg.value.width * canvas_cfg.value.scale) / 2 + 'px'
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 鼠标按下事件 - 开始拖拽
|
||||||
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
|
if (mtPreviewProps.canDrag && e.button === 0) {
|
||||||
|
// 仅响应左键
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
isDragging.value = true
|
||||||
|
startPos.value = {
|
||||||
|
x: e.clientX - canvas_cfg.value.pan.x,
|
||||||
|
y: e.clientY - canvas_cfg.value.pan.y
|
||||||
|
}
|
||||||
|
// 初始化临时位置
|
||||||
|
tempPan.x = canvas_cfg.value.pan.x
|
||||||
|
tempPan.y = canvas_cfg.value.pan.y
|
||||||
|
|
||||||
|
// 隐藏默认拖拽行为
|
||||||
|
if (canvasAreaRef.value) {
|
||||||
|
canvasAreaRef.value.style.userSelect = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动动画帧同步
|
||||||
|
if (!animationFrameId) {
|
||||||
|
const syncPosition = () => {
|
||||||
|
if (isDragging.value) {
|
||||||
|
canvas_cfg.value.pan.x = tempPan.x
|
||||||
|
canvas_cfg.value.pan.y = tempPan.y
|
||||||
|
animationFrameId = requestAnimationFrame(syncPosition)
|
||||||
|
} else {
|
||||||
|
animationFrameId = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animationFrameId = requestAnimationFrame(syncPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 节流处理鼠标移动事件 (16ms约等于60fps)
|
||||||
|
const throttledMouseMove = throttle((e: MouseEvent) => {
|
||||||
|
if (isDragging.value && mtPreviewProps.canDrag) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
// 只更新临时变量,通过动画帧同步到响应式对象
|
||||||
|
tempPan.x = e.clientX - startPos.value.x
|
||||||
|
tempPan.y = e.clientY - startPos.value.y
|
||||||
|
}
|
||||||
|
}, 16)
|
||||||
|
|
||||||
|
// 鼠标移动事件 - 处理拖拽
|
||||||
|
const onMouseMove = throttledMouseMove
|
||||||
|
|
||||||
|
// 鼠标释放事件 - 结束拖拽
|
||||||
|
const onMouseUp = (e: MouseEvent) => {
|
||||||
|
if (isDragging.value) {
|
||||||
|
isDragging.value = false
|
||||||
|
|
||||||
|
// 恢复选择功能
|
||||||
|
if (canvasAreaRef.value) {
|
||||||
|
canvasAreaRef.value.style.userSelect = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消动画帧
|
||||||
|
if (animationFrameId) {
|
||||||
|
cancelAnimationFrame(animationFrameId)
|
||||||
|
animationFrameId = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缩放控制函数
|
||||||
|
const zoomIn = () => {
|
||||||
|
if (mtPreviewProps.canZoom) {
|
||||||
|
canvas_cfg.value.scale = Math.min(canvas_cfg.value.scale + 0.1, 3) // 最大放大到3倍
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const zoomOut = () => {
|
||||||
|
if (mtPreviewProps.canZoom) {
|
||||||
|
canvas_cfg.value.scale = Math.max(canvas_cfg.value.scale - 0.1, 0.3) // 最小缩小到0.3倍
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置变换
|
||||||
|
const resetTransform = () => {
|
||||||
|
// 定义重置变换的函数
|
||||||
|
canvas_cfg.value.scale = useData.display ? 1 : document.documentElement.clientHeight / globalStore.canvasCfg.height
|
||||||
|
canvas_cfg.value.pan = { x: 0, y: 0 }
|
||||||
|
tempPan.x = 0
|
||||||
|
tempPan.y = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鼠标滚轮事件 - 缩放
|
||||||
|
const onMouseWheel = (e: WheelEvent) => {
|
||||||
|
if (mtPreviewProps.canZoom) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
// 计算缩放因子
|
||||||
|
const delta = e.deltaY < 0 ? 0.1 : -0.1
|
||||||
|
const newScale = Math.max(0.3, Math.min(3, canvas_cfg.value.scale + delta))
|
||||||
|
|
||||||
|
// 如果缩放中心是鼠标位置,计算新的平移值以保持视觉中心
|
||||||
|
if (canvasAreaRef.value) {
|
||||||
|
const rect = canvasAreaRef.value?.getBoundingClientRect()
|
||||||
|
const mouseX = e.clientX - rect.left
|
||||||
|
const mouseY = e.clientY - rect.top
|
||||||
|
|
||||||
|
// 计算缩放前后的鼠标位置差异,调整平移量
|
||||||
|
const scaleRatio = newScale / canvas_cfg.value.scale
|
||||||
|
canvas_cfg.value.pan.x = mouseX - (mouseX - canvas_cfg.value.pan.x) * scaleRatio
|
||||||
|
canvas_cfg.value.pan.y = mouseY - (mouseY - canvas_cfg.value.pan.y) * scaleRatio
|
||||||
|
|
||||||
|
// 同步到临时变量
|
||||||
|
tempPan.x = canvas_cfg.value.pan.x
|
||||||
|
tempPan.y = canvas_cfg.value.pan.y
|
||||||
|
|
||||||
|
// 应用新缩放值
|
||||||
|
canvas_cfg.value.scale = newScale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragCanvasMouseDown = () => {
|
||||||
|
scroll_info.begin_left = scroll_info.left
|
||||||
|
scroll_info.begin_top = scroll_info.top
|
||||||
|
}
|
||||||
|
const dragCanvasMouseMove = (move_x: number, move_y: number) => {
|
||||||
|
let new_left = scroll_info.begin_left - move_x
|
||||||
|
let new_top = scroll_info.begin_top - move_y
|
||||||
|
elScrollbarRef.value?.setScrollLeft(new_left)
|
||||||
|
elScrollbarRef.value?.setScrollTop(new_top)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 画布拖动结束事件
|
||||||
|
*/
|
||||||
|
const dragCanvasMouseUp = () => {}
|
||||||
|
const setItemAttrByID = (id: string, key: string, val: any) => {
|
||||||
|
return setItemAttr(id, key, val, done_json.value)
|
||||||
|
}
|
||||||
|
const setItemAttrs = (info: { id: string; key: string; val: any }[]) => {
|
||||||
|
info.forEach(f => {
|
||||||
|
setItemAttr(f.id, f.key, f.val, done_json.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getItemAttrByID = (id: string, key: string, val: any) => {
|
||||||
|
return getItemAttr(id, key, done_json.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setItemAttrByIDAsync = (id: string, key: string, val: any) => {
|
||||||
|
// 通过改变属性的事件去设置值时 需要转换成宏任务 不然多个事件判断会有问题
|
||||||
|
setTimeout(() => {
|
||||||
|
setItemAttrByID(id, key, val)
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
;(window as any).$mtElMessage = ElMessage
|
||||||
|
;(window as any).$mtElMessageBox = ElMessageBox
|
||||||
|
;(window as any).$setItemAttrByID = (id: string, key: string, val: any) => setItemAttrByIDAsync(id, key, val)
|
||||||
|
;(window as any).$getItemAttrByID = getItemAttrByID
|
||||||
|
;(window as any).$previewCompareVal = previewCompareVal
|
||||||
|
;(window as any).$mtEventCallBack = (type: string, item_id: string, ...args: any[]) =>
|
||||||
|
emits('onEventCallBack', type, item_id, ...args)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// 启动消息监听 iframe传过来的参数
|
||||||
|
receiveMessage()
|
||||||
|
|
||||||
|
if (mtPreviewProps.exportJson) {
|
||||||
|
await setImportJson(mtPreviewProps.exportJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接mqtt
|
||||||
|
await setMqtt()
|
||||||
|
// await sendTableData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// mqtt推过来的 lineId
|
||||||
|
let keyList = ref<any>([])
|
||||||
|
|
||||||
|
// 实时数据表格
|
||||||
|
const tableData = ref()
|
||||||
|
|
||||||
|
const sendTableData = () => {
|
||||||
|
try {
|
||||||
|
// 类型检查,确保 tableData.value 是数组
|
||||||
|
if (!Array.isArray(tableData.value)) {
|
||||||
|
console.warn('tableData is not an array, current value:', tableData.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保只传输可序列化的数据
|
||||||
|
const cleanData = tableData.value.map(item => ({
|
||||||
|
name: item.name,
|
||||||
|
date: item.timeId,
|
||||||
|
address: item.eventDesc
|
||||||
|
}))
|
||||||
|
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
action: 'securityDetailData',
|
||||||
|
data: cleanData
|
||||||
|
},
|
||||||
|
'*'
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('数据传输失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let transmissionDeviceIds: string[] = []
|
||||||
|
|
||||||
|
let eventListAll = ref<any>([])
|
||||||
|
|
||||||
|
const receiveMessage = () => {
|
||||||
|
// 在 iframe 内的页面中
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
// 验证消息来源(在生产环境中应该验证 origin)
|
||||||
|
// if (event.origin !== 'trusted-origin') return;
|
||||||
|
|
||||||
|
const { type, payload } = event.data
|
||||||
|
|
||||||
|
if (type === 'RESET_EVENT') {
|
||||||
|
// 处理复位事件
|
||||||
|
handleResetEvent()
|
||||||
|
} else if (type === 'ANALYSIS_KEYS') {
|
||||||
|
// 处理 ANALYSIS_KEYS 消息
|
||||||
|
// 在处理新数据前,先清理现有的动态文字
|
||||||
|
if (savedExportJson.value) {
|
||||||
|
savedExportJson.value.json =
|
||||||
|
savedExportJson.value.json?.filter(item => !item.id?.startsWith('auto-text-')) || []
|
||||||
|
done_json.value = done_json.value.filter(item => !item.id?.startsWith('auto-text-'))
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
// 对于其他类型的消息,我们仍然调用 init()
|
||||||
|
else if (type) {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复位事件处理函数
|
||||||
|
const handleResetEvent = () => {
|
||||||
|
// 清空或重置 表格数据
|
||||||
|
// if (tableData.value && Array.isArray(tableData.value)) {
|
||||||
|
// tableData.value = []
|
||||||
|
// } else {
|
||||||
|
// // 确保 tableData 是一个空数组
|
||||||
|
// tableData.value = []
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 接线图数据
|
||||||
|
keyList.value = []
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 表格数据
|
||||||
|
sendTableData()
|
||||||
|
// 接线图数据
|
||||||
|
if (savedExportJson.value) {
|
||||||
|
setImportJson(savedExportJson.value)
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
console.log('执行复位操作完成')
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据 lineId 查找传输设备 ID 的函数
|
||||||
|
const findTransmissionDeviceIdsByKeyList = (array: any) => {
|
||||||
|
if (!savedExportJson.value?.json) return []
|
||||||
|
|
||||||
|
const deviceIds = savedExportJson.value.json.filter(item => array.includes(item.lineId ?? '')).map(item => item.id)
|
||||||
|
|
||||||
|
// console.log('传输设备 ID 列表:', deviceIds)
|
||||||
|
|
||||||
|
return deviceIds
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为每个图元动态添加点击事件
|
||||||
|
const addClickEventsToElements = () => {
|
||||||
|
if (savedExportJson.value && savedExportJson.value.json) {
|
||||||
|
savedExportJson.value.json.forEach(item => {
|
||||||
|
// 检查是否已经有点击事件
|
||||||
|
const hasClickEvent = item.events && item.events.some(event => event.type === 'click')
|
||||||
|
|
||||||
|
if (!hasClickEvent) {
|
||||||
|
// 创建点击事件对象
|
||||||
|
const clickEvent: IDoneJsonEventList = {
|
||||||
|
id: generateRandomId(), // 生成随机ID
|
||||||
|
type: 'click', // 明确指定为字面量类型
|
||||||
|
action: 'customCode', // 自定义操作
|
||||||
|
jump_to: '',
|
||||||
|
change_attr: [],
|
||||||
|
custom_code: '', // 可以在这里添加自定义代码
|
||||||
|
trigger_rule: {
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果图元还没有events数组,则创建一个
|
||||||
|
if (!item.events) {
|
||||||
|
item.events = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加点击事件
|
||||||
|
item.events.push(clickEvent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成随机ID的辅助函数
|
||||||
|
const generateRandomId = () => {
|
||||||
|
return Math.random().toString(36).substr(2, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setImportJson = (exportJson: IExportJson) => {
|
||||||
|
// 保存exportJson供后续使用
|
||||||
|
savedExportJson.value = exportJson
|
||||||
|
// 定义要执行的操作函数
|
||||||
|
const executeOperations = () => {
|
||||||
|
const { canvasCfg, gridCfg, importDoneJson } = useExportJsonToDoneJson(savedExportJson.value)
|
||||||
|
// 保留现有的平移和缩放设置
|
||||||
|
const currentPan = canvas_cfg.value.pan
|
||||||
|
const currentScale = canvas_cfg.value.scale
|
||||||
|
|
||||||
|
canvas_cfg.value = {
|
||||||
|
...canvasCfg,
|
||||||
|
pan: currentPan,
|
||||||
|
scale: currentScale
|
||||||
|
}
|
||||||
|
grid_cfg.value = gridCfg
|
||||||
|
done_json.value = importDoneJson
|
||||||
|
console.log(11111111111)
|
||||||
|
// 为图元添加点击事件
|
||||||
|
addClickEventsToElements()
|
||||||
|
|
||||||
|
// 首页初始化的时候
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log(22222222222222)
|
||||||
|
|
||||||
|
done_json.value.forEach(item => {
|
||||||
|
//报警设备闪烁
|
||||||
|
if (findTransmissionDeviceIdsByKeyList(keyList.value).includes(item.id)) {
|
||||||
|
item.props.fill.val = 'red'
|
||||||
|
item.common_animations.val = 'flash'
|
||||||
|
} else {
|
||||||
|
item.common_animations.val = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!useData.loading) {
|
||||||
|
if (exportJson == null) {
|
||||||
|
setDoneJson(useData.dataTree[0].kId)
|
||||||
|
} else {
|
||||||
|
executeOperations()
|
||||||
|
publish(useData.dataTree[0])
|
||||||
|
singlePublish(useData.dataTree[0])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果不是true,添加监听
|
||||||
|
const stopWatch = watch(
|
||||||
|
() => useData.loading,
|
||||||
|
newVal => {
|
||||||
|
if (newVal === false) {
|
||||||
|
// 当loading变为true时执行操作
|
||||||
|
if (exportJson == null) {
|
||||||
|
setDoneJson(useData.dataTree[0].kId)
|
||||||
|
} else {
|
||||||
|
executeOperations()
|
||||||
|
singlePublish(useData.dataTree[0])
|
||||||
|
publish(useData.dataTree[0])
|
||||||
|
}
|
||||||
|
// 执行后停止监听
|
||||||
|
stopWatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加一个新的 ref 来存储当前点击的设备ID
|
||||||
|
const currentClickedElementId = ref<string | null>(null)
|
||||||
|
|
||||||
|
// 图纸的kId
|
||||||
|
let storedSelectedId = ''
|
||||||
|
|
||||||
|
storedSelectedId = localStorage.getItem('selectedId') || ''
|
||||||
|
|
||||||
|
// 当前点击的元素lineId 通过mt-edit/render-core/index.vue传过来的click事件
|
||||||
|
const handleElementClick = (elementId: string, lineName: string) => {
|
||||||
|
iframeDiaRef.value.showNextCorner(elementId, lineName)
|
||||||
|
// 保存当前点击的设备ID
|
||||||
|
// indicator(['00B78D0171091', '00B78D0171092'])
|
||||||
|
// currentClickedElementId.value = elementId
|
||||||
|
// const item = done_json.value.find(item => item.lineId === elementId)
|
||||||
|
// if (item && item.events && item.events.some(event => event.type === 'click')) {
|
||||||
|
// // 发送设备ID到父级iframe
|
||||||
|
// window.parent.postMessage(
|
||||||
|
// {
|
||||||
|
// action: 'coreClick',
|
||||||
|
// coreId: elementId.toString(), // 确保是字符串
|
||||||
|
// selectedId: storedSelectedId // 新增的字段
|
||||||
|
// // elementData: item // 可以发送整个元素数据
|
||||||
|
// },
|
||||||
|
// '*'
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
const searchDevicesConnect = (transmissionDeviceIds: string[]) => {
|
||||||
|
// 确保 savedExportJson.value 存在
|
||||||
|
if (!savedExportJson.value?.json) {
|
||||||
|
console.warn('savedExportJson.value 或 json 未定义')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找所有连线元素
|
||||||
|
const lineElements = savedExportJson.value.json.filter(item => item.type === 'sys-line' && item.props?.bind_anchors)
|
||||||
|
|
||||||
|
// 查找所有开关元素
|
||||||
|
const switchElements = savedExportJson.value.json.filter(item => item.title?.includes('开关'))
|
||||||
|
|
||||||
|
// 存储连接线的ID
|
||||||
|
const connectedLineIds: string[] = []
|
||||||
|
|
||||||
|
// 存储找到的开关ID
|
||||||
|
const switchIds: string[] = []
|
||||||
|
|
||||||
|
// 首先找出所有传输设备连接的开关
|
||||||
|
for (const deviceId of transmissionDeviceIds) {
|
||||||
|
for (const line of lineElements) {
|
||||||
|
const bindAnchors = line.props.bind_anchors as { start?: { id: string }; end?: { id: string } } | undefined
|
||||||
|
if (!bindAnchors) continue
|
||||||
|
|
||||||
|
const startId = bindAnchors.start?.id
|
||||||
|
const endId = bindAnchors.end?.id
|
||||||
|
|
||||||
|
// 检查连线是否连接传输设备和开关
|
||||||
|
if (startId === deviceId && switchElements.some(s => s.id === endId)) {
|
||||||
|
if (endId && !switchIds.includes(endId)) {
|
||||||
|
switchIds.push(endId)
|
||||||
|
}
|
||||||
|
if (!connectedLineIds.includes(line.id!)) {
|
||||||
|
connectedLineIds.push(line.id!)
|
||||||
|
}
|
||||||
|
} else if (endId === deviceId && switchElements.some(s => s.id === startId)) {
|
||||||
|
if (startId && !switchIds.includes(startId)) {
|
||||||
|
switchIds.push(startId)
|
||||||
|
}
|
||||||
|
if (!connectedLineIds.includes(line.id!)) {
|
||||||
|
connectedLineIds.push(line.id!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 然后找出开关之间的连线
|
||||||
|
for (const line of lineElements) {
|
||||||
|
const bindAnchors = line.props.bind_anchors as { start?: { id: string }; end?: { id: string } } | undefined
|
||||||
|
if (!bindAnchors) continue
|
||||||
|
|
||||||
|
const startId = bindAnchors.start?.id
|
||||||
|
const endId = bindAnchors.end?.id
|
||||||
|
|
||||||
|
// 检查连线是否连接两个开关
|
||||||
|
if (startId && endId && switchIds.includes(startId) && switchIds.includes(endId)) {
|
||||||
|
if (!connectedLineIds.includes(line.id!)) {
|
||||||
|
connectedLineIds.push(line.id!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('连接的连线ID列表:', connectedLineIds)
|
||||||
|
|
||||||
|
return connectedLineIds
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览时候绑定指标等的点击事件
|
||||||
|
const setDoneJson = async (kId: string) => {
|
||||||
|
const filteredItems = useData.dataTree.filter(item => item.kId == kId)
|
||||||
|
|
||||||
|
if (filteredItems.length === 0) {
|
||||||
|
console.error(`No item found with kId: ${kId}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = filteredItems[0]
|
||||||
|
|
||||||
|
// 根据传过来的kId找到所在的id
|
||||||
|
const foundId = item.id
|
||||||
|
|
||||||
|
if (foundId) {
|
||||||
|
// 将当前选中的行数据存储到本地存储
|
||||||
|
localStorage.setItem('selectedId', foundId)
|
||||||
|
}
|
||||||
|
|
||||||
|
storedSelectedId = localStorage.getItem('selectedId') || ''
|
||||||
|
|
||||||
|
if (!item.path) {
|
||||||
|
console.error(`Item with kId: ${kId} does not have a path property`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setImportJson(JSON.parse(item.path))
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = async () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// 执行动态添加文本的操作
|
||||||
|
// const updatedDoneJson = addTextNextToTransport()
|
||||||
|
|
||||||
|
// 调用函数获取传输设备 ID
|
||||||
|
transmissionDeviceIds = findTransmissionDeviceIdsByKeyList(keyList.value)
|
||||||
|
|
||||||
|
// 重新设置导入的JSON以触发界面更新
|
||||||
|
setImportJson(savedExportJson.value)
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
const timer = ref()
|
||||||
|
const list: any = ref([])
|
||||||
|
// 连接mqtt
|
||||||
|
const mqttClient = ref()
|
||||||
|
const setMqtt = async () => {
|
||||||
|
mqttClient.value = new MQTT('/zl/TemperData/#')
|
||||||
|
|
||||||
|
// 设置消息接收回调
|
||||||
|
try {
|
||||||
|
await mqttClient.value.init()
|
||||||
|
|
||||||
|
// 订阅主题
|
||||||
|
await mqttClient.value.subscribe('/zl/rtData/#')
|
||||||
|
await mqttClient.value.subscribe('/zl/TemperData/#') //实时数据
|
||||||
|
await mqttClient.value.subscribe('/zl/csConfigRtData/#') //指标
|
||||||
|
// 设置消息接收回调
|
||||||
|
mqttClient.value.onMessage((subscribe: string, message: any) => {
|
||||||
|
const msg: any = uint8ArrayToObject(message)
|
||||||
|
console.log('🚀 ~ setMqtt ~ msg:', msg)
|
||||||
|
|
||||||
|
if (subscribe.split('/')[2] === 'rtData') {
|
||||||
|
// 指标数据
|
||||||
|
|
||||||
|
// await setImportJson(savedExportJson.value)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (done_json.value) {
|
||||||
|
done_json.value?.forEach(item => {
|
||||||
|
msg.forEach((msgValue: any) => {
|
||||||
|
if (item.id == msgValue.id) {
|
||||||
|
const unit = item?.unit && Array.isArray(item.unit) ? item.unit[0] : ''
|
||||||
|
item.props.text.val = item.props.text.val.replace(
|
||||||
|
/#{3}/g,
|
||||||
|
msgValue.value == 3.1415926 ? '暂无数据' : msgValue.value + unit
|
||||||
|
) //'B相负载电流-CP95:31'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
if (subscribe.split('/')[2] === 'csConfigRtData') {
|
||||||
|
// 指标数据
|
||||||
|
dataList.value = JSON.parse(msg.message)
|
||||||
|
//console.log('🚀 ~ setMqtt ~ dataList:', dataList.value)
|
||||||
|
// keyList.value = JSON.parse(list.path).canvasCfg.lineList
|
||||||
|
} else if (subscribe.split('/')[2] === 'TemperData') {
|
||||||
|
// 表格数据
|
||||||
|
tableData.value = []
|
||||||
|
tableData.value = JSON.parse(msg.message)
|
||||||
|
//console.log('🚀 ~ setMqtt ~ tableData:', tableData.value)
|
||||||
|
// 闪烁点
|
||||||
|
// if (Array.isArray(tableData.value) && tableData.value.length > 0) {
|
||||||
|
// // 提取所有的 id 并去重(使用 Set 方式,性能更好)
|
||||||
|
// const uniqueIds = [
|
||||||
|
// ...new Set(
|
||||||
|
// tableData.value
|
||||||
|
// .filter(item => item.id) // 确保 id 存在
|
||||||
|
// .map(item => item.id) // 提取 id
|
||||||
|
// )
|
||||||
|
// ]
|
||||||
|
|
||||||
|
// keyList.value = uniqueIds
|
||||||
|
// console.log('提取的唯一ID列表:', keyList.value)
|
||||||
|
// }
|
||||||
|
sendTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('MQTT 初始化失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const singlePublish = async (id: string) => {
|
||||||
|
if (mqttClient.value) {
|
||||||
|
await mqttClient.value.subscribe('zl/askRtData/' + storedSelectedId)
|
||||||
|
// 发送消息
|
||||||
|
await mqttClient.value.publish('/zl/askRtData/' + storedSelectedId, '{}')
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
timer.value = null
|
||||||
|
}
|
||||||
|
timer.value = setInterval(
|
||||||
|
() => {
|
||||||
|
mqttClient.value.publish('/zl/askRtData/' + storedSelectedId, '{}')
|
||||||
|
},
|
||||||
|
3 * 60 * 1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const publish = async (list: any) => {
|
||||||
|
console.log('🚀 ~ publish ~ list:', JSON.parse(list.path).canvasCfg.lineList)
|
||||||
|
if (mqttClient.value) {
|
||||||
|
// 发送消息
|
||||||
|
await mqttClient.value.publish(
|
||||||
|
'/zl/askCSConfigWarnData/' + storedSelectedId,
|
||||||
|
`[${JSON.parse(list.path).canvasCfg.lineList}]`
|
||||||
|
)
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
timer.value = null
|
||||||
|
}
|
||||||
|
timer.value = setInterval(
|
||||||
|
() => {
|
||||||
|
mqttClient.value.publish(
|
||||||
|
'/zl/askCSConfigWarnData/' + storedSelectedId,
|
||||||
|
`[${JSON.parse(list.path).canvasCfg.lineList}]`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
3 * 60 * 1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 绑定指标
|
||||||
|
const indicator = async (ids: string[]) => {
|
||||||
|
if (mqttClient.value) {
|
||||||
|
// await mqttClient.value.subscribe('zl/askCSConfigRtData/' + useData.dataTree[0].id)
|
||||||
|
// 发送消息
|
||||||
|
await mqttClient.value.publish('/zl/askCSConfigRtData/' + storedSelectedId, `[${ids}]`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
timer.value = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 方法1: 使用 mqtt消息转换
|
||||||
|
function uint8ArrayToObject(uint8Array: Uint8Array) {
|
||||||
|
try {
|
||||||
|
// 将 Uint8Array 解码为字符串
|
||||||
|
const decoder = new TextDecoder('utf-8')
|
||||||
|
const jsonString = decoder.decode(uint8Array)
|
||||||
|
|
||||||
|
// 将 JSON 字符串解析为对象
|
||||||
|
return JSON.parse(jsonString)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('转换失败:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onBack = () => {
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => useData.display,
|
||||||
|
newVal => {
|
||||||
|
if (newVal) {
|
||||||
|
canvas_cfg.value.scale = 1
|
||||||
|
canvas_cfg.value.pan = {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
canvas_cfg.value.scale = document.documentElement.clientHeight / globalStore.canvasCfg.height
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
defineExpose({
|
||||||
|
setItemAttrByID,
|
||||||
|
setImportJson,
|
||||||
|
setItemAttrs,
|
||||||
|
zoomIn,
|
||||||
|
zoomOut,
|
||||||
|
resetTransform
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.canvasArea {
|
||||||
|
position: relative;
|
||||||
|
/* 移除过渡效果,避免拖拽延迟 */
|
||||||
|
will-change: transform;
|
||||||
|
/* 提示浏览器优化transform属性 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.backBtn {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zoom-controls {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor-grab {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor-grabbing {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
/* --el-table-border-color: #0a73ff;
|
||||||
|
--el-table-row-hover-bg-color: #3d4862;
|
||||||
|
--el-table-header-bg-color: #2a3b62;
|
||||||
|
--el-table-bg-color: #343849c7; */
|
||||||
|
--el-table-border-color: #0a73ff;
|
||||||
|
--el-table-row-hover-bg-color: #0a73ff20;
|
||||||
|
--el-table-header-bg-color: #0a73ff40;
|
||||||
|
--el-table-bg-color: #ffffff00;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table tr) {
|
||||||
|
/* background-color: #242936; */
|
||||||
|
background-color: #00000090 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table .cell) {
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell) {
|
||||||
|
/* background-color: #303b54; */
|
||||||
|
background-color: #5aa1ff29;
|
||||||
|
}
|
||||||
|
/* .aaaa{
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 111123;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
background-color: #ccc;
|
||||||
|
} */
|
||||||
|
</style>
|
||||||
3
src/components/mt-preview-zl/index.ts
Normal file
3
src/components/mt-preview-zl/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import MtPreview from './index.vue'
|
||||||
|
|
||||||
|
export default MtPreview
|
||||||
1173
src/components/mt-preview-zl/index.vue
Normal file
1173
src/components/mt-preview-zl/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,11 @@
|
|||||||
v-loading="useData.loading"
|
v-loading="useData.loading"
|
||||||
element-loading-background="#343849c7"
|
element-loading-background="#343849c7"
|
||||||
:style="{ backgroundColor: useData.display ? '' : canvas_cfg.color }"
|
:style="{ backgroundColor: useData.display ? '' : canvas_cfg.color }"
|
||||||
|
@mousedown="onMouseDown"
|
||||||
|
@mousemove="onMouseMove"
|
||||||
|
@mouseup="onMouseUp"
|
||||||
|
@mouseleave="onMouseUp"
|
||||||
|
@wheel="onMouseWheel"
|
||||||
>
|
>
|
||||||
<el-button type="primary" class="backBtn" @click="onBack" v-if="!useData.display">返回</el-button>
|
<el-button type="primary" class="backBtn" @click="onBack" v-if="!useData.display">返回</el-button>
|
||||||
<!-- 缩放控制按钮 (默认注释,需要时可开启) -->
|
<!-- 缩放控制按钮 (默认注释,需要时可开启) -->
|
||||||
@@ -18,11 +23,6 @@
|
|||||||
ref="canvasAreaRef"
|
ref="canvasAreaRef"
|
||||||
:class="`canvasArea ${isDragging ? 'cursor-grabbing' : mtPreviewProps.canDrag ? 'cursor-grab' : ''} `"
|
:class="`canvasArea ${isDragging ? 'cursor-grabbing' : mtPreviewProps.canDrag ? 'cursor-grab' : ''} `"
|
||||||
style=""
|
style=""
|
||||||
@mousedown="onMouseDown"
|
|
||||||
@mousemove="onMouseMove"
|
|
||||||
@mouseup="onMouseUp"
|
|
||||||
@mouseleave="onMouseUp"
|
|
||||||
@wheel="onMouseWheel"
|
|
||||||
:style="canvasStyle"
|
:style="canvasStyle"
|
||||||
>
|
>
|
||||||
<render-core
|
<render-core
|
||||||
@@ -92,6 +92,8 @@ const tableData = [
|
|||||||
// }
|
// }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const targetKeywords = ['开关', '器', '阀门', '控制']
|
||||||
|
|
||||||
const showDetail = ref(false)
|
const showDetail = ref(false)
|
||||||
|
|
||||||
const showDetailClick = () => {
|
const showDetailClick = () => {
|
||||||
@@ -581,11 +583,12 @@ const setImportJson = (exportJson: IExportJson) => {
|
|||||||
addClickEventsToElements()
|
addClickEventsToElements()
|
||||||
|
|
||||||
// 首页初始化的时候
|
// 首页初始化的时候
|
||||||
setTimeout(() => {
|
nextTick(() => {
|
||||||
done_json.value.forEach(item => {
|
done_json.value.forEach(item => {
|
||||||
//报警设备闪烁
|
//报警设备闪烁
|
||||||
if (findTransmissionDeviceIdsByKeyList(list.value).includes(item.id)) {
|
if (findTransmissionDeviceIdsByKeyList(list.value).includes(item.id)) {
|
||||||
// item.props.fill.val = '#fcfc57'
|
// item.props.fill.val = '#fcfc57'
|
||||||
|
|
||||||
item.props.fill.val = sendColor.value
|
item.props.fill.val = sendColor.value
|
||||||
item.common_animations.val = 'flash'
|
item.common_animations.val = 'flash'
|
||||||
if (findTransmissionDeviceIdsByKeyList(listMax.value).includes(item.id)) {
|
if (findTransmissionDeviceIdsByKeyList(listMax.value).includes(item.id)) {
|
||||||
@@ -644,7 +647,7 @@ const setImportJson = (exportJson: IExportJson) => {
|
|||||||
// item.props.ani_color.val = '#8c0ae2'
|
// item.props.ani_color.val = '#8c0ae2'
|
||||||
// }
|
// }
|
||||||
})
|
})
|
||||||
}, 1000)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!useData.loading) {
|
if (!useData.loading) {
|
||||||
@@ -691,7 +694,7 @@ const findSwitchByLineEndpoint = (lineId: string): string | null => {
|
|||||||
const startId = bindAnchors.start?.id
|
const startId = bindAnchors.start?.id
|
||||||
if (startId) {
|
if (startId) {
|
||||||
const startElement = savedExportJson.value.json.find(item => item.id === startId)
|
const startElement = savedExportJson.value.json.find(item => item.id === startId)
|
||||||
if (startElement && startElement.title?.includes('开关')) {
|
if (startElement && targetKeywords.some(keyword => startElement.title?.includes(keyword))) {
|
||||||
return startElement.id!
|
return startElement.id!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -700,7 +703,7 @@ const findSwitchByLineEndpoint = (lineId: string): string | null => {
|
|||||||
const endId = bindAnchors.end?.id
|
const endId = bindAnchors.end?.id
|
||||||
if (endId) {
|
if (endId) {
|
||||||
const endElement = savedExportJson.value.json.find(item => item.id === endId)
|
const endElement = savedExportJson.value.json.find(item => item.id === endId)
|
||||||
if (endElement && endElement.title?.includes('开关')) {
|
if (endElement && targetKeywords.some(keyword => endElement.title?.includes(keyword))) {
|
||||||
return endElement.id!
|
return endElement.id!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -777,6 +780,7 @@ const handleElementClick = (elementId: string) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const bindList = ref<string[]>([])
|
||||||
const searchDevicesConnect = (transmissionDeviceIds: string[]) => {
|
const searchDevicesConnect = (transmissionDeviceIds: string[]) => {
|
||||||
// 确保 savedExportJson.value 存在
|
// 确保 savedExportJson.value 存在
|
||||||
if (!savedExportJson.value?.json) {
|
if (!savedExportJson.value?.json) {
|
||||||
@@ -786,9 +790,23 @@ const searchDevicesConnect = (transmissionDeviceIds: string[]) => {
|
|||||||
|
|
||||||
// 查找所有连线元素
|
// 查找所有连线元素
|
||||||
const lineElements = savedExportJson.value.json.filter(item => item.type === 'sys-line' && item.props?.bind_anchors)
|
const lineElements = savedExportJson.value.json.filter(item => item.type === 'sys-line' && item.props?.bind_anchors)
|
||||||
|
bindList.value = [
|
||||||
|
...new Set(
|
||||||
|
lineElements
|
||||||
|
.map(item => {
|
||||||
|
return [item.props?.bind_anchors.start?.id, item.props?.bind_anchors.end?.id]
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
// 查找所有开关元素
|
// 查找所有开关元素
|
||||||
const switchElements = savedExportJson.value.json.filter(item => item.title?.includes('开关'))
|
const switchElements = savedExportJson.value.json.filter(item =>
|
||||||
|
targetKeywords.some(keyword => item.title?.includes(keyword))
|
||||||
|
)
|
||||||
|
// const switchElements = savedExportJson.value.json.filter(item =>
|
||||||
|
// bindList.value.some(keyword => item.id?.includes(keyword) && item.lineId=='')
|
||||||
|
// )
|
||||||
|
|
||||||
// 存储连接线的ID
|
// 存储连接线的ID
|
||||||
const connectedLineIds: string[] = []
|
const connectedLineIds: string[] = []
|
||||||
@@ -1010,16 +1028,26 @@ const setMqtt = async () => {
|
|||||||
await mqttClient.value.subscribe('/zl/rtData/#')
|
await mqttClient.value.subscribe('/zl/rtData/#')
|
||||||
|
|
||||||
// 设置消息接收回调
|
// 设置消息接收回调
|
||||||
mqttClient.value.onMessage((subscribe: string, message: any) => {
|
mqttClient.value.onMessage(async (subscribe: string, message: any) => {
|
||||||
const msg: any = uint8ArrayToObject(message)
|
const msg: any = uint8ArrayToObject(message)
|
||||||
console.log('🚀 ~ 接受消息:', msg)
|
console.log('🚀 ~ 接受消息:', msg)
|
||||||
setTimeout(() => {
|
list.value = [...new Set(msg.filter((item: any) => item.devStatus === 1).map((item: any) => item.lineId))]
|
||||||
|
sendColor.value = '#ff0000'
|
||||||
|
// await setImportJson(savedExportJson.value)
|
||||||
|
|
||||||
|
await setTimeout(() => {
|
||||||
done_json.value.forEach(item => {
|
done_json.value.forEach(item => {
|
||||||
msg.forEach((msgValue: any) => {
|
msg.forEach((msgValue: any) => {
|
||||||
if (item.id == msgValue.id) {
|
if (item.id == msgValue.id) {
|
||||||
item.props.text.val = item.props.text.val.replace(/#{3}/g, msgValue.value) //'B相负载电流-CP95:31'
|
item.props.text.val = item.props.text.val.replace(/#{3}/g, msgValue.value) //'B相负载电流-CP95:31'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
list.value.forEach((listValue: any) => {
|
||||||
|
if (listValue == item.lineId && item.type == 'svg') {
|
||||||
|
item.props.fill.val = '#ff0000'
|
||||||
|
// item.common_animations.val = 'flash'
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ import '@/components/mt-edit/assets/css/custom_ani.css'
|
|||||||
import MtDzr from '@/components/mt-dzr'
|
import MtDzr from '@/components/mt-dzr'
|
||||||
import MtEdit from '@/components/mt-edit'
|
import MtEdit from '@/components/mt-edit'
|
||||||
import MtPreview from '@/components/mt-preview'
|
import MtPreview from '@/components/mt-preview'
|
||||||
|
import MtPreviewYpt from '@/components/mt-preview-ypt'
|
||||||
|
import MtPreviewZl from '@/components/mt-preview-zl'
|
||||||
import { leftAsideStore } from '@/components/mt-edit/store/left-aside'
|
import { leftAsideStore } from '@/components/mt-edit/store/left-aside'
|
||||||
export { MtDzr, MtEdit, MtPreview, leftAsideStore }
|
export { MtDzr, MtEdit, MtPreview, leftAsideStore, MtPreviewYpt, MtPreviewZl }
|
||||||
|
|||||||
@@ -19,6 +19,18 @@ export const constantRoutes = [
|
|||||||
path: '/preview',
|
path: '/preview',
|
||||||
component: () => import('../views/preview/index.vue')
|
component: () => import('../views/preview/index.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// 云平台预览页面
|
||||||
|
name: 'preview_YPT',
|
||||||
|
path: '/preview_YPT',
|
||||||
|
component: () => import('../views/preview/index_YPT.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 云平台预览页面
|
||||||
|
name: 'preview_ZL',
|
||||||
|
path: '/preview_ZL',
|
||||||
|
component: () => import('../views/preview/index_ZL.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'edit-load',
|
name: 'edit-load',
|
||||||
path: '/edit-load',
|
path: '/edit-load',
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ export const useDataStore = defineStore('data-store', {
|
|||||||
identifying: '0',
|
identifying: '0',
|
||||||
mqttID: '',
|
mqttID: '',
|
||||||
preview: '',
|
preview: '',
|
||||||
|
keyName: '', //选中的name
|
||||||
wxqr: '',
|
wxqr: '',
|
||||||
loading: true,
|
loading: true,
|
||||||
display: false, //无锡项目进去是true,其他项目是false 控制预览的时候返回按钮的展示
|
display: false, //无锡项目进去是true,其他项目是false 控制预览的时候返回按钮的展示
|
||||||
graphicDisplay: 'zl' //无锡项目进去是true,其他项目是false 控制点击设计的时候左侧列表数据绑定图元的展示
|
graphicDisplay: 'zl', //无锡项目进去是true,其他项目是false 控制点击设计的时候左侧列表数据绑定图元的展示
|
||||||
|
treeIndexs: [] //树形结构选中的索引
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -67,13 +69,18 @@ export const useDataStore = defineStore('data-store', {
|
|||||||
modify(kId: number, val: string) {
|
modify(kId: number, val: string) {
|
||||||
this.dataTree.forEach((item: any) => {
|
this.dataTree.forEach((item: any) => {
|
||||||
if (item.kId == kId) {
|
if (item.kId == kId) {
|
||||||
item.name = val
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 放置kid
|
// 放置kid
|
||||||
placeKid(id: String) {
|
placeKid(id: String) {
|
||||||
this.identifying = id
|
this.identifying = id
|
||||||
|
this.keyName = ''
|
||||||
|
this.dataTree.forEach((item: any) => {
|
||||||
|
if (item.kId == id) {
|
||||||
|
this.keyName = item.name
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
setUpPath(data: String) {
|
setUpPath(data: String) {
|
||||||
this.dataTree.forEach((item: any) => {
|
this.dataTree.forEach((item: any) => {
|
||||||
@@ -81,6 +88,9 @@ export const useDataStore = defineStore('data-store', {
|
|||||||
item.path = data
|
item.path = data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
settreeIndexs(data: any) {
|
||||||
|
this.treeIndexs = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface DataStoreState {
|
|||||||
preview?: String
|
preview?: String
|
||||||
wxqr?: String
|
wxqr?: String
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
|
keyName?: String
|
||||||
display?: Boolean //是否展示返回按钮
|
display?: Boolean //是否展示返回按钮
|
||||||
graphicDisplay?: string //是否展示数据绑定图元
|
graphicDisplay?: string //是否展示数据绑定图元
|
||||||
|
treeIndexs?: any //是否展示数据绑定图元
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { leftAsideStore } from '@/export'
|
|||||||
import demo from '/svgs/demo.svg?raw'
|
import demo from '/svgs/demo.svg?raw'
|
||||||
import { find, download, queryPage } from '@/api/index'
|
import { find, download, queryPage } from '@/api/index'
|
||||||
import { useDataStore } from '@/stores/menuList'
|
import { useDataStore } from '@/stores/menuList'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
// 定义类型接口
|
// 定义类型接口
|
||||||
interface ElementItem {
|
interface ElementItem {
|
||||||
id: string
|
id: string
|
||||||
@@ -25,14 +26,15 @@ interface SvgConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 处理单个SVG元素的函数
|
// 处理单个SVG元素的函数
|
||||||
const processSvgItem = async (item: ElementItem): Promise<SvgConfig> => {
|
const processSvgItem = async (item: ElementItem, type: string): Promise<SvgConfig> => {
|
||||||
try {
|
try {
|
||||||
const svgContent = await download({ filePath: item.path })
|
const svgContent = await download({ filePath: item.path })
|
||||||
|
|
||||||
// 替换填充色用于缩略图
|
// 替换填充色用于缩略图
|
||||||
const filledSvg = svgContent.replace(/(\sfill=(["']))[^"']*(\2)/g, '$1#000000$3')
|
const filledSvg =
|
||||||
|
type == '特殊图元' ? svgContent : svgContent.replace(/(\sfill=(["']))[^"']*(\2)/g, '$1#000000$3')
|
||||||
// 移除原始填充色
|
// 移除原始填充色
|
||||||
const cleanSvg = svgContent.replace(/\sfill=(["'])[^"']*\1/g, '')
|
const cleanSvg = type == '特殊图元' ? svgContent : svgContent.replace(/\sfill=(["'])[^"']*\1/g, '')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
@@ -82,73 +84,7 @@ const loadSvg = async () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 添加页面svg
|
await getSvg()
|
||||||
// await find().then(async res => {
|
|
||||||
// let result: any = {}
|
|
||||||
// res.data.forEach((item: any) => {
|
|
||||||
// const type = item.elementSonType
|
|
||||||
|
|
||||||
// // 如果该类型还没有在结果中,初始化一个空数组
|
|
||||||
// if (!result[type]) {
|
|
||||||
// result[type] = []
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 将当前项添加到对应类型的数组中
|
|
||||||
// result[type].push(item)
|
|
||||||
// })
|
|
||||||
// for (const key in result) {
|
|
||||||
// // 创建一个存放所有Promise的数组
|
|
||||||
// const promises = result[key].map((item: any) => {
|
|
||||||
// return download({ filePath: item.path }).then((Svg: any) => {
|
|
||||||
// return {
|
|
||||||
// id: item.id,
|
|
||||||
// title: item.elementName,
|
|
||||||
// type: 'svg',
|
|
||||||
// thumbnail:
|
|
||||||
// 'data:image/svg+xml;utf8,' +
|
|
||||||
// encodeURIComponent(Svg.replace(/(\sfill=(["']))[^"']*(\2)/g, '$1#FF0000$3')),
|
|
||||||
// svg: Svg.replace(/\sfill=(["'])[^"']*\1/g, ''),
|
|
||||||
// props: {
|
|
||||||
// fill: {
|
|
||||||
// type: 'color',
|
|
||||||
// val: '#FF0000',
|
|
||||||
// title: '填充色'
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
|
|
||||||
// // 等待所有下载操作完成
|
|
||||||
// const svgList = await Promise.all(promises)
|
|
||||||
|
|
||||||
// // 所有异步操作完成后再执行注册
|
|
||||||
// leftAsideStore.registerConfig(key, svgList)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// 主逻辑
|
|
||||||
try {
|
|
||||||
const res = await find()
|
|
||||||
const groupedElements: Record<string, ElementItem[]> = {}
|
|
||||||
|
|
||||||
// 按类型分组元素
|
|
||||||
res.data.forEach((item: ElementItem) => {
|
|
||||||
const { elementSonType: type } = item
|
|
||||||
if (!groupedElements[type]) {
|
|
||||||
groupedElements[type] = []
|
|
||||||
}
|
|
||||||
groupedElements[type].push(item)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 处理每个类型的元素并注册
|
|
||||||
for (const [type, items] of Object.entries(groupedElements)) {
|
|
||||||
const svgConfigs = await Promise.all(items.map(item => processSvgItem(item)))
|
|
||||||
leftAsideStore.registerConfig(type, svgConfigs)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载和处理SVG元素时发生错误:', error)
|
|
||||||
// 可以在这里添加错误恢复逻辑或用户提示
|
|
||||||
}
|
|
||||||
await useData.set('loading', false)
|
await useData.set('loading', false)
|
||||||
|
|
||||||
// const electrical_modules_files = import.meta.glob('@/assets/svgs/electrical/face/**.svg', {
|
// const electrical_modules_files = import.meta.glob('@/assets/svgs/electrical/face/**.svg', {
|
||||||
@@ -337,4 +273,31 @@ const loadSvg = async () => {
|
|||||||
// }
|
// }
|
||||||
// ])
|
// ])
|
||||||
}
|
}
|
||||||
export default loadSvg
|
|
||||||
|
const getSvg = async () => {
|
||||||
|
// 主逻辑
|
||||||
|
try {
|
||||||
|
const res = await find()
|
||||||
|
const groupedElements: Record<string, ElementItem[]> = {}
|
||||||
|
|
||||||
|
// 按类型分组元素
|
||||||
|
res.data.forEach((item: ElementItem) => {
|
||||||
|
const { elementSonType: type } = item
|
||||||
|
if (!groupedElements[type]) {
|
||||||
|
groupedElements[type] = []
|
||||||
|
}
|
||||||
|
groupedElements[type].push(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理每个类型的元素并注册
|
||||||
|
for (const [type, items] of Object.entries(groupedElements)) {
|
||||||
|
const svgConfigs = await Promise.all(items.map(item => processSvgItem(item, type)))
|
||||||
|
leftAsideStore.registerConfig(type, svgConfigs)
|
||||||
|
}
|
||||||
|
ElMessage.success(`图元加载完成!`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载和处理SVG元素时发生错误:', error)
|
||||||
|
// 可以在这里添加错误恢复逻辑或用户提示
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export { loadSvg, getSvg }
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ class MQTT {
|
|||||||
try {
|
try {
|
||||||
// const mqttUrl = 'ws://192.168.1.103:8083/mqtt'
|
// const mqttUrl = 'ws://192.168.1.103:8083/mqtt'
|
||||||
const mqttUrl =
|
const mqttUrl =
|
||||||
localStorage.getItem('MqttUrl') == 'null'
|
localStorage.getItem('MQTTZUTAI') == 'null'
|
||||||
? 'ws://192.168.1.24:8085/mqtt'
|
? 'ws://192.168.1.103:8083/mqtt'
|
||||||
: localStorage.getItem('MqttUrl')
|
: localStorage.getItem('MQTTZUTAI')
|
||||||
console.log('🚀 ~ MQTT ~ init ~ mqttUrl:', mqttUrl)
|
console.log('🚀 ~ MQTT ~ init ~ mqttUrl:', mqttUrl)
|
||||||
this.client = mqtt.connect(mqttUrl, this.defaultOptions as IClientOptions)
|
this.client = mqtt.connect(mqttUrl, this.defaultOptions as IClientOptions)
|
||||||
this.setupEventListeners()
|
this.setupEventListeners()
|
||||||
@@ -135,7 +135,7 @@ class MQTT {
|
|||||||
console.error('订阅失败:', error)
|
console.error('订阅失败:', error)
|
||||||
reject(error)
|
reject(error)
|
||||||
} else {
|
} else {
|
||||||
console.log('订阅成功')
|
console.log('订阅成功', subscribe)
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,16 +19,10 @@ class HttpRequest {
|
|||||||
instance.interceptors.request.use(
|
instance.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
// 添加全局的loading..
|
// 添加全局的loading..
|
||||||
// config.headers['Authorization'] =
|
|
||||||
// 'bearer ' + JSON.parse(window.localStorage.getItem('adminInfo') || '{}').access_token; // 请求头带上token token要在登录的时候保存在localStorage中
|
|
||||||
// console.log(
|
|
||||||
// "🚀 ~ requestHandler ~ config.headers['Authorization']:",
|
|
||||||
// JSON.parse(window.localStorage.getItem('adminInfo') || '{}'),
|
|
||||||
// config.headers
|
|
||||||
// );
|
|
||||||
|
|
||||||
config.headers['Authorization'] =
|
config.headers['Authorization'] =
|
||||||
'bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5kZXgiOiJmYTM3YjkzY2M5MGQ0YzE3ODRjYThmNmRlYmRkZWUxYSIsInVzZXJfbmFtZSI6InJvb3QiLCJzY29wZSI6WyJhbGwiXSwibmlja25hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJ1c2VyVHlwZSI6MCwiZGVwdEluZGV4IjoiNTY5OWU1OTE2YTE4YTYzODFlMWFjOTJkYTViZDI2MjgiLCJleHAiOjE4MjE4MTc2MTksImF1dGhvcml0aWVzIjpbInJvb3QiXSwianRpIjoiMmJiM2Q5ZTYtNmY3Yy00Yjg1LThiM2EtZDI2ODdmMTUzMDg5IiwiY2xpZW50X2lkIjoibmpjbnRlc3QiLCJoZWFkU2N1bHB0dXJlIjoicmVzb3VyY2VEYXRhLzMxNzRDRUFFOUQ0MjRGMjJCQjkxQTU4OURENjdCMDUxLmpwZyJ9.WjeYl1lvvJdDE1FUGIhS99rE5qKaBXOypWxmxK0svWweGqEbu1XCLjKm_YkiTwjZJ_oIcn5JOO9rvHFkkea76BUsYo5wlzuBBiy7sKqM1fFzOFQq6hdFevNTJAbYH9FiBxYxI-e9DZ5mvLGE6umOjUfn_FAsku2w6Uj5DtvpOKBWYzLEPTEifOqNI9he4zJAmVZniUUMf26SDoEdfu0TyrIS1j_qKaEb-cqR1XDhivdthEBK5m9vxJyXFZ5kofNxwQQkit_oiqJRkCZIt9TWAjCh-frzMHCvA30hkAr-VCD2JfCmmEr3hW_lmwfINaPtFVbHCdCKqdrl6VmF1HObaQ'
|
'bearer ' + JSON.parse(window.localStorage.getItem('adminInfo') || '{}').access_token // 请求头带上token token要在登录的时候保存在localStorage中
|
||||||
|
// config.headers['Authorization'] =
|
||||||
|
// 'bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5kZXgiOiJmYTM3YjkzY2M5MGQ0YzE3ODRjYThmNmRlYmRkZWUxYSIsInVzZXJfbmFtZSI6InJvb3QiLCJzY29wZSI6WyJhbGwiXSwibmlja25hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJ1c2VyVHlwZSI6MCwiZGVwdEluZGV4IjoiNTY5OWU1OTE2YTE4YTYzODFlMWFjOTJkYTViZDI2MjgiLCJleHAiOjE4MjE4MTc2MTksImF1dGhvcml0aWVzIjpbInJvb3QiXSwianRpIjoiMmJiM2Q5ZTYtNmY3Yy00Yjg1LThiM2EtZDI2ODdmMTUzMDg5IiwiY2xpZW50X2lkIjoibmpjbnRlc3QiLCJoZWFkU2N1bHB0dXJlIjoicmVzb3VyY2VEYXRhLzMxNzRDRUFFOUQ0MjRGMjJCQjkxQTU4OURENjdCMDUxLmpwZyJ9.WjeYl1lvvJdDE1FUGIhS99rE5qKaBXOypWxmxK0svWweGqEbu1XCLjKm_YkiTwjZJ_oIcn5JOO9rvHFkkea76BUsYo5wlzuBBiy7sKqM1fFzOFQq6hdFevNTJAbYH9FiBxYxI-e9DZ5mvLGE6umOjUfn_FAsku2w6Uj5DtvpOKBWYzLEPTEifOqNI9he4zJAmVZniUUMf26SDoEdfu0TyrIS1j_qKaEb-cqR1XDhivdthEBK5m9vxJyXFZ5kofNxwQQkit_oiqJRkCZIt9TWAjCh-frzMHCvA30hkAr-VCD2JfCmmEr3hW_lmwfINaPtFVbHCdCKqdrl6VmF1HObaQ'
|
||||||
// 请求头携带token
|
// 请求头携带token
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const MtEditRef = ref<InstanceType<typeof MtEdit>>()
|
|||||||
const onPreviewClick = (exportJson: IExportJson) => {
|
const onPreviewClick = (exportJson: IExportJson) => {
|
||||||
sessionStorage.setItem('exportJson', JSON.stringify(exportJson))
|
sessionStorage.setItem('exportJson', JSON.stringify(exportJson))
|
||||||
const routeUrl = router.resolve({
|
const routeUrl = router.resolve({
|
||||||
name: 'preview'
|
name: 'preview_YPT'
|
||||||
})
|
})
|
||||||
window.open(routeUrl.href, '_blank')
|
window.open(routeUrl.href, '_blank')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const MtEditRef = ref<InstanceType<typeof MtEdit>>()
|
|||||||
const onPreviewClick = (exportJson: IExportJson) => {
|
const onPreviewClick = (exportJson: IExportJson) => {
|
||||||
sessionStorage.setItem('exportJson', JSON.stringify(exportJson))
|
sessionStorage.setItem('exportJson', JSON.stringify(exportJson))
|
||||||
const routeUrl = router.resolve({
|
const routeUrl = router.resolve({
|
||||||
name: 'preview'
|
name: 'preview_YPT'
|
||||||
})
|
})
|
||||||
window.open(routeUrl.href, '_blank')
|
window.open(routeUrl.href, '_blank')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const useData = useDataStore()
|
|||||||
const onPreviewClick = (exportJson: IExportJson) => {
|
const onPreviewClick = (exportJson: IExportJson) => {
|
||||||
sessionStorage.setItem('exportJson', JSON.stringify(exportJson))
|
sessionStorage.setItem('exportJson', JSON.stringify(exportJson))
|
||||||
const routeUrl = router.resolve({
|
const routeUrl = router.resolve({
|
||||||
name: 'preview'
|
name: 'preview_YPT'
|
||||||
})
|
})
|
||||||
window.open(routeUrl.href, '_blank')
|
window.open(routeUrl.href, '_blank')
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/views/preview/index_YPT.vue
Normal file
19
src/views/preview/index_YPT.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<mt-preview-ypt ref="MtPreviewRef" @on-event-call-back="onEventCallBack"></mt-preview-ypt>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { MtPreviewYpt } from '@/export'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const MtPreviewRef = ref<InstanceType<typeof MtPreviewYpt>>()
|
||||||
|
const onEventCallBack = (type: string, item_id: string) => {
|
||||||
|
console.log(type, item_id)
|
||||||
|
|
||||||
|
if (type == 'test-dialog') {
|
||||||
|
ElMessage.success(`获取到了id:${item_id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
MtPreviewRef.value?.setImportJson(JSON.parse(sessionStorage.getItem('exportJson') as any))
|
||||||
|
})
|
||||||
|
</script>
|
||||||
19
src/views/preview/index_ZL.vue
Normal file
19
src/views/preview/index_ZL.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<mt-preview-zl ref="MtPreviewRef" @on-event-call-back="onEventCallBack"></mt-preview-zl>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { MtPreviewZl } from '@/export'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const MtPreviewRef = ref<InstanceType<typeof MtPreviewZl>>()
|
||||||
|
const onEventCallBack = (type: string, item_id: string) => {
|
||||||
|
console.log(type, item_id)
|
||||||
|
|
||||||
|
if (type == 'test-dialog') {
|
||||||
|
ElMessage.success(`获取到了id:${item_id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
MtPreviewRef.value?.setImportJson(JSON.parse(sessionStorage.getItem('exportJson') as any))
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user