提交代码
This commit is contained in:
160
src/components/mt-edit/composables/index.ts
Normal file
160
src/components/mt-edit/composables/index.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import type { IExportDoneJson, IExportJson } from '../components/types'
|
||||
import { leftAsideStore } from '../store/left-aside'
|
||||
import type {
|
||||
IDoneJson,
|
||||
IGlobalStoreCanvasCfg,
|
||||
IGlobalStoreGridCfg,
|
||||
ILeftAsideConfigItem,
|
||||
ILeftAsideConfigItemPublicProps
|
||||
} from '../store/types'
|
||||
import { objectDeepClone } from '../utils'
|
||||
|
||||
export const genExportJson = (
|
||||
canvasCfg: IGlobalStoreCanvasCfg,
|
||||
gridCfg: IGlobalStoreGridCfg,
|
||||
doneJson: IDoneJson[]
|
||||
) => {
|
||||
// 先创建原始的 export_done_json
|
||||
let export_done_json: IExportDoneJson[] = []
|
||||
export_done_json = objectDeepClone<IDoneJson[]>(doneJson).map(m => {
|
||||
if (m.symbol) {
|
||||
delete m.symbol
|
||||
}
|
||||
let new_props = {}
|
||||
for (const key in m.props) {
|
||||
new_props = { ...new_props, ...{ [key]: m.props[key].val } }
|
||||
}
|
||||
return {
|
||||
...m,
|
||||
props: new_props,
|
||||
active: false
|
||||
}
|
||||
})
|
||||
|
||||
// const list = [
|
||||
// 'c53cccb8c65201c192d8c57fbdb4d993-RdNsoqHYOZ',
|
||||
// 'c53cccb8c65201c192d8c57fbdb4d993-O4jAyCBz1A',
|
||||
// 'c53cccb8c65201c192d8c57fbdb4d993-XBd70oZ3kH'
|
||||
// ]
|
||||
|
||||
// const message = [
|
||||
// { id: 'c53cccb8c65201c192d8c57fbdb4d993-RdNsoqHYOZ', text: '发生时刻:2023-07-05 12:00:00' },
|
||||
// { id: 'c53cccb8c65201c192d8c57fbdb4d993-O4jAyCBz1A', text: '传输中1111......' },
|
||||
// { id: 'c53cccb8c65201c192d8c57fbdb4d993-XBd70oZ3kH', text: '发生时刻:2023-07-06 14:20:00' }
|
||||
// ]
|
||||
|
||||
// 查找传输设备图元并添加文字图元
|
||||
// const transportDevices = export_done_json.filter(item =>
|
||||
// // 假设传输设备有特定的标识,比如ID包含特定关键词或type为特定值
|
||||
// // item.title?.includes('传输')
|
||||
// // list.some(id => item.id?.includes(id))
|
||||
// list.includes(item.id)
|
||||
// )
|
||||
|
||||
// 为每个传输设备添加旁边的文字图元
|
||||
const textElementsToAdd: IExportDoneJson[] = []
|
||||
|
||||
// 先删除旧图元
|
||||
// 用于存储需要移除的旧文本图元的 ID
|
||||
const idsToRemove: string[] = []
|
||||
|
||||
// transportDevices.forEach((device, index) => {
|
||||
// // 构造预期的旧文本图元 ID 模式 (基于设备 ID)
|
||||
// const expectedIdPrefix = `auto-text-${device.id}-`
|
||||
|
||||
// // 查找所有与当前设备关联的现有文本图元
|
||||
// const existingTextElements = export_done_json.filter(item => item.id?.startsWith(expectedIdPrefix))
|
||||
|
||||
// // 将这些旧图元的 ID 添加到待删除列表
|
||||
// idsToRemove.push(...existingTextElements.map(item => item.id!))
|
||||
|
||||
// // 获取对应的消息文本
|
||||
// const deviceMessage = message.find(m => m.id === device.id)?.text || '默认提示信息'
|
||||
|
||||
// // 创建新的文本图元
|
||||
// const textElement: IExportDoneJson = {
|
||||
// id: `auto-text-${device.id}-${index}`, // 使用时间戳确保唯一性
|
||||
// title: '动态文字',
|
||||
// type: 'vue',
|
||||
// tag: 'text-vue',
|
||||
// props: {
|
||||
// text: deviceMessage || '默认提示信息', // 添加安全检查
|
||||
// fontFamily: '黑体',
|
||||
// fontSize: 14,
|
||||
// fill: 'red',
|
||||
// vertical: false
|
||||
// },
|
||||
// common_animations: {
|
||||
// val: '',
|
||||
// delay: 'delay-0s',
|
||||
// speed: 'slow',
|
||||
// repeat: 'infinite'
|
||||
// },
|
||||
// binfo: {
|
||||
// left: (device.binfo?.left || 0) + (device.binfo?.width || 0) + 10,
|
||||
// top: (device.binfo?.top || 0) + (device.binfo?.height || 0) / 2 - 10 + (index % 2 === 0 ? 20 : -20), // 偶数下移20px,奇数上移20px
|
||||
// width: 200,
|
||||
// height: 50,
|
||||
// angle: 0
|
||||
// },
|
||||
// resize: true,
|
||||
// rotate: true,
|
||||
// lock: false,
|
||||
// active: false,
|
||||
// hide: false,
|
||||
// UIDName: '',
|
||||
// events: []
|
||||
// }
|
||||
// textElementsToAdd.push(textElement)
|
||||
// })
|
||||
|
||||
// // 从 export_done_json 中移除旧的文本图元
|
||||
// export_done_json = export_done_json.filter(item => !idsToRemove.includes(item.id!))
|
||||
|
||||
// // 合并原始图元和新增的文字图元
|
||||
// export_done_json = [...export_done_json, ...textElementsToAdd]
|
||||
|
||||
const exportJson: IExportJson = {
|
||||
canvasCfg,
|
||||
gridCfg,
|
||||
json: export_done_json
|
||||
}
|
||||
return { exportJson }
|
||||
}
|
||||
export const useExportJsonToDoneJson = (json: IExportJson) => {
|
||||
// 取出所有图形的初始配置
|
||||
let init_configs: ILeftAsideConfigItem[] = []
|
||||
for (const iterator of leftAsideStore.config.values()) {
|
||||
if (iterator.length > 0) {
|
||||
init_configs = [...init_configs, ...iterator]
|
||||
}
|
||||
}
|
||||
const importDoneJson: IDoneJson[] = json.json.map(m => {
|
||||
let props: ILeftAsideConfigItemPublicProps = {}
|
||||
let symbol = undefined
|
||||
// 找到原始的props
|
||||
const find_item = init_configs.find(f => f?.id == m.tag)
|
||||
const find_props = find_item?.props
|
||||
if (find_props) {
|
||||
props = { ...props, ...objectDeepClone(find_props) }
|
||||
}
|
||||
for (const key in m.props) {
|
||||
if (props[key] !== undefined) {
|
||||
props[key].val = m.props[key]
|
||||
}
|
||||
}
|
||||
if (find_item?.symbol) {
|
||||
symbol = find_item.symbol
|
||||
}
|
||||
return {
|
||||
...m,
|
||||
props,
|
||||
symbol
|
||||
}
|
||||
})
|
||||
return {
|
||||
canvasCfg: json.canvasCfg,
|
||||
gridCfg: json.gridCfg,
|
||||
importDoneJson
|
||||
}
|
||||
}
|
||||
200
src/components/mt-edit/composables/sys-line.ts
Normal file
200
src/components/mt-edit/composables/sys-line.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import { nextTick } from 'vue'
|
||||
import type { IDoneJson, IDoneJsonBinfo } from '../store/types'
|
||||
import { getRectCenterCoordinate, getRectCoordinate, rotatePoint } from '../utils'
|
||||
|
||||
/**
|
||||
* 更新系统连线实际宽高
|
||||
* @param sys_lines
|
||||
* @param scale
|
||||
*/
|
||||
export const useUpdateSysLineRect = (sys_lines: IDoneJson[], canvasDom: HTMLElement, scale: number) => {
|
||||
sys_lines.forEach(f => {
|
||||
const itemRect = document.querySelector(`#${f.id} g .real`)!.getBoundingClientRect()
|
||||
const canvas_area_bounding_info = canvasDom!.getBoundingClientRect()
|
||||
const new_left = (itemRect?.left - canvas_area_bounding_info?.left) / scale
|
||||
const new_top = (itemRect?.top - canvas_area_bounding_info?.top) / scale
|
||||
const move_x = new_left - f.binfo.left
|
||||
const move_y = new_top - f.binfo.top
|
||||
f.binfo.left = new_left
|
||||
f.binfo.top = new_top
|
||||
f.binfo.width = itemRect?.width / scale
|
||||
f.binfo.height = itemRect?.height / scale
|
||||
f.props.point_position = {
|
||||
...f.props.point_position,
|
||||
val: f.props.point_position.val.map((m: { x: number; y: number }) => {
|
||||
return {
|
||||
x: m.x - move_x,
|
||||
y: m.y - move_y
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 更新系统连线
|
||||
* @param sys_lines 要更新的连线列表
|
||||
* @param done_json 所有组件信息
|
||||
* @param canvasDom 画布dom
|
||||
* @param scale 画布缩放
|
||||
*/
|
||||
export const useUpdateSysLine = (
|
||||
sys_lines: IDoneJson[],
|
||||
done_json: IDoneJson[],
|
||||
canvasDom: HTMLElement,
|
||||
scale: number,
|
||||
move_binfo?: IDoneJsonBinfo & { id: string }
|
||||
) => {
|
||||
const temp_done_json = [...done_json]
|
||||
sys_lines.forEach(f => {
|
||||
if (!f.props.bind_anchors.val.start && !f.props.bind_anchors.val.end) {
|
||||
return
|
||||
}
|
||||
const itemRect = document.querySelector(`#${f.id} g .real`)!.getBoundingClientRect()
|
||||
const canvas_area_bounding_info = canvasDom!.getBoundingClientRect()
|
||||
const new_left = (itemRect?.left - canvas_area_bounding_info?.left) / scale
|
||||
const new_top = (itemRect?.top - canvas_area_bounding_info?.top) / scale
|
||||
|
||||
// 处理起点绑定
|
||||
if (f.props.bind_anchors.val.start) {
|
||||
// 根据id和类型找到锚点坐标
|
||||
const find_item = temp_done_json.find(m => m.id === f.props.bind_anchors.val.start.id)
|
||||
if (find_item) {
|
||||
const b_info = find_item.id === move_binfo?.id ? move_binfo : find_item.binfo
|
||||
// 四个角原始坐标
|
||||
const { topLeft, topRight, bottomLeft, bottomRight } = getRectCoordinate(b_info)
|
||||
// 四条边中点坐标
|
||||
const { topCenter, bottomCenter, leftCenter, rightCenter } = getRectCenterCoordinate(
|
||||
topLeft,
|
||||
topRight,
|
||||
bottomLeft,
|
||||
bottomRight
|
||||
)
|
||||
// 旋转中心
|
||||
const centerX = topCenter.x
|
||||
const centerY = leftCenter.y
|
||||
|
||||
// 旋转角度(弧度)
|
||||
const angleRad = (Math.PI / 180) * find_item.binfo.angle
|
||||
|
||||
if (f.props.bind_anchors.val.start.type === 'tc') {
|
||||
const new_tc = rotatePoint(topCenter.x, topCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[0] = {
|
||||
x: new_tc.x - f.binfo.left,
|
||||
y: new_tc.y - f.binfo.top
|
||||
}
|
||||
} else if (f.props.bind_anchors.val.start.type === 'bc') {
|
||||
const new_bc = rotatePoint(bottomCenter.x, bottomCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[0] = {
|
||||
x: new_bc.x - f.binfo.left,
|
||||
y: new_bc.y - f.binfo.top
|
||||
}
|
||||
} else if (f.props.bind_anchors.val.start.type === 'lc') {
|
||||
const new_lc = rotatePoint(leftCenter.x, leftCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[0] = {
|
||||
x: new_lc.x - f.binfo.left,
|
||||
y: new_lc.y - f.binfo.top
|
||||
}
|
||||
} else if (f.props.bind_anchors.val.start.type === 'rc') {
|
||||
const new_rc = rotatePoint(rightCenter.x, rightCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[0] = {
|
||||
x: new_rc.x - f.binfo.left,
|
||||
y: new_rc.y - f.binfo.top
|
||||
}
|
||||
}
|
||||
const move_x = new_left - f.binfo.left
|
||||
const move_y = new_top - f.binfo.top
|
||||
f.binfo = {
|
||||
...f.binfo,
|
||||
left: new_left,
|
||||
top: new_top,
|
||||
width: itemRect?.width / scale,
|
||||
height: itemRect?.height / scale
|
||||
}
|
||||
f.props.point_position = {
|
||||
...f.props.point_position,
|
||||
val: f.props.point_position.val.map((m: { x: number; y: number }) => {
|
||||
return {
|
||||
x: m.x - move_x,
|
||||
y: m.y - move_y
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
f.props.bind_anchors.val.start = null
|
||||
}
|
||||
}
|
||||
// 处理终点绑定
|
||||
if (f.props.bind_anchors.val.end) {
|
||||
// 根据id和类型找到锚点坐标
|
||||
const find_item = temp_done_json.find(m => m.id === f.props.bind_anchors.val.end.id)
|
||||
if (find_item) {
|
||||
const b_info = find_item.id === move_binfo?.id ? move_binfo : find_item.binfo
|
||||
// 四个角原始坐标
|
||||
const { topLeft, topRight, bottomLeft, bottomRight } = getRectCoordinate(b_info)
|
||||
// 四条边中点坐标
|
||||
const { topCenter, bottomCenter, leftCenter, rightCenter } = getRectCenterCoordinate(
|
||||
topLeft,
|
||||
topRight,
|
||||
bottomLeft,
|
||||
bottomRight
|
||||
)
|
||||
// 旋转中心
|
||||
const centerX = topCenter.x
|
||||
const centerY = leftCenter.y
|
||||
|
||||
// 旋转角度(弧度)
|
||||
const angleRad = (Math.PI / 180) * find_item.binfo.angle
|
||||
|
||||
if (f.props.bind_anchors.val.end.type === 'tc') {
|
||||
const new_tc = rotatePoint(topCenter.x, topCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[f.props.point_position.val.length - 1] = {
|
||||
x: new_tc.x - f.binfo.left,
|
||||
y: new_tc.y - f.binfo.top
|
||||
}
|
||||
} else if (f.props.bind_anchors.val.end.type === 'bc') {
|
||||
const new_bc = rotatePoint(bottomCenter.x, bottomCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[f.props.point_position.val.length - 1] = {
|
||||
x: new_bc.x - f.binfo.left,
|
||||
y: new_bc.y - f.binfo.top
|
||||
}
|
||||
} else if (f.props.bind_anchors.val.end.type === 'lc') {
|
||||
const new_lc = rotatePoint(leftCenter.x, leftCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[f.props.point_position.val.length - 1] = {
|
||||
x: new_lc.x - f.binfo.left,
|
||||
y: new_lc.y - f.binfo.top
|
||||
}
|
||||
} else if (f.props.bind_anchors.val.end.type === 'rc') {
|
||||
const new_rc = rotatePoint(rightCenter.x, rightCenter.y, centerX, centerY, angleRad)
|
||||
f.props.point_position.val[f.props.point_position.val.length - 1] = {
|
||||
x: new_rc.x - f.binfo.left,
|
||||
y: new_rc.y - f.binfo.top
|
||||
}
|
||||
}
|
||||
const move_x = new_left - f.binfo.left
|
||||
const move_y = new_top - f.binfo.top
|
||||
f.binfo = {
|
||||
...f.binfo,
|
||||
left: new_left,
|
||||
top: new_top,
|
||||
width: itemRect?.width / scale,
|
||||
height: itemRect?.height / scale
|
||||
}
|
||||
f.props.point_position = {
|
||||
...f.props.point_position,
|
||||
val: f.props.point_position.val.map((m: { x: number; y: number }) => {
|
||||
return {
|
||||
x: m.x - move_x,
|
||||
y: m.y - move_y
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
f.props.bind_anchors.val.end = null
|
||||
}
|
||||
}
|
||||
})
|
||||
// 直接写在这里会损失一部分性能 也可以注释掉下面的 然后根据需求在useUpdateSysLine之后手动调用useUpdateSysLineRect
|
||||
nextTick(() => {
|
||||
useUpdateSysLineRect(sys_lines, canvasDom, scale)
|
||||
})
|
||||
}
|
||||
72
src/components/mt-edit/composables/thumbnail.ts
Normal file
72
src/components/mt-edit/composables/thumbnail.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Canvg } from 'canvg'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { ElMessage } from 'element-plus'
|
||||
export const useGenThumbnail = async (canvas_id: string = 'mtCanvasArea') => {
|
||||
const el = <HTMLElement | null>document.querySelector(`#${canvas_id}`)
|
||||
if (!el) {
|
||||
ElMessage.error('没有找到canvas元素,请检查!')
|
||||
return
|
||||
}
|
||||
// //记录要移除的svg元素
|
||||
const shouldRemoveSvgNodes = []
|
||||
// 获取到所有的SVG 得到一个数组 目前只有自定义连线需要特殊处理 别的元素直接使用html2canvas就可以
|
||||
const svgElements: NodeListOf<HTMLElement> = document.body.querySelectorAll(`#${canvas_id} .mt-line-render`)
|
||||
// 遍历这个数组
|
||||
for (const item of svgElements) {
|
||||
//去除空白字符
|
||||
const svg = item.outerHTML.trim()
|
||||
// 创建一个 canvas DOM元素
|
||||
const canvas = document.createElement('canvas')
|
||||
//设置 canvas 元素的宽高
|
||||
canvas.width = item.getBoundingClientRect().width
|
||||
canvas.height = item.getBoundingClientRect().height
|
||||
const ctx = canvas.getContext('2d')
|
||||
// 将 SVG转化 成 canvas
|
||||
const v = Canvg.fromString(ctx!, svg)
|
||||
await v.render()
|
||||
|
||||
//设置生成 canvas 元素的坐标 保证与原SVG坐标保持一致
|
||||
if (item.style.position) {
|
||||
canvas.style.position += item.style.position
|
||||
canvas.style.left += item.style.left
|
||||
canvas.style.top += item.style.top
|
||||
}
|
||||
|
||||
//添加到需要截图的DOM节点中
|
||||
item.parentNode!.appendChild(canvas)
|
||||
// 删除这个元素
|
||||
shouldRemoveSvgNodes.push(canvas)
|
||||
}
|
||||
|
||||
const width = el.offsetWidth
|
||||
const height = el.offsetHeight
|
||||
const canvas = await html2canvas(el, {
|
||||
useCORS: true,
|
||||
scale: 2,
|
||||
width,
|
||||
height,
|
||||
allowTaint: true,
|
||||
windowHeight: height,
|
||||
logging: false,
|
||||
ignoreElements: element => {
|
||||
if (element.classList.contains('mt-line-render')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
// const img_link = document.createElement('a')
|
||||
// img_link.href = canvas.toDataURL('image/png') // 转换后的图片地址
|
||||
// img_link.download = Date.now().toString()
|
||||
// document.body.appendChild(img_link)
|
||||
// // 触发点击
|
||||
// img_link.click()
|
||||
// // 然后移除
|
||||
// document.body.removeChild(img_link)
|
||||
// 移除需要移除掉的svg节点
|
||||
shouldRemoveSvgNodes.forEach(item => {
|
||||
item.remove()
|
||||
})
|
||||
|
||||
return canvas.toDataURL('image/png')
|
||||
}
|
||||
Reference in New Issue
Block a user