refactor: 初始化项目
@ -0,0 +1,5 @@
|
|||||||
|
*.md
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
dist
|
||||||
|
node_modules
|
@ -0,0 +1,59 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es6: true
|
||||||
|
},
|
||||||
|
parser: 'vue-eslint-parser',
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
jsxPragma: 'React',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'prettier',
|
||||||
|
'plugin:prettier/recommended' // 一定要放在最后。因为 extends 中后引入的规则会覆盖前面的规则。
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
// @typescript-eslint
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off', // 需要函数和类方法的显式返回类型
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用该 any 类型
|
||||||
|
'@typescript-eslint/no-var-requires': 'off', // 不允许使用 require 语句,除了在 import 语句中
|
||||||
|
'@typescript-eslint/no-empty-function': 'off', // 禁止空函数
|
||||||
|
'@typescript-eslint/no-use-before-define': 'off', // 在定义之前禁止使用变量
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off', // 禁止 @ts-<directive> 使用评论或在指令后要求描述
|
||||||
|
'@typescript-eslint/ban-types': 'off', // 禁止使用特定类型
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off', // '!'不允许使用后缀运算符的非空断言
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off', // 需要导出函数和类的公共类方法的显式返回和参数类型
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_'
|
||||||
|
}
|
||||||
|
], // 禁止未使用的变量
|
||||||
|
// vue
|
||||||
|
'vue/custom-event-name-casing': 'off', // 为自定义事件名称强制使用特定大小写
|
||||||
|
'vue/attributes-order': 'off', // 强制执行属性顺序
|
||||||
|
'vue/one-component-per-file': 'off', // 强制每个组件都应该在自己的文件中
|
||||||
|
'vue/html-closing-bracket-newline': 'off', // 在标签的右括号之前要求或禁止换行
|
||||||
|
'vue/multiline-html-element-content-newline': 'off', // 在多行元素的内容之前和之后需要换行符
|
||||||
|
'vue/singleline-html-element-content-newline': 'off', // 在单行元素的内容之前和之后需要换行符
|
||||||
|
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
|
||||||
|
'vue/require-default-prop': 'off', // 需要 props 的默认值
|
||||||
|
'vue/html-indent': ['error', 2], // 在<template>中强制一致缩进
|
||||||
|
'vue/html-self-closing': 'off', // 执行自闭合的风格
|
||||||
|
'vue/max-attributes-per-line': 'off', // 强制每行属性的最大数量
|
||||||
|
'vue/multi-word-component-names': 'off', // 是否开启组件命名规则校验(强制多个单词以驼峰或'-'链接的命名规则)
|
||||||
|
// ESLint
|
||||||
|
'no-use-before-define': 'off', // 禁止在变量定义之前使用它们
|
||||||
|
'space-before-function-paren': 'off' // 强制在 function的左括号之前使用一致的空格
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
.DS_Store
|
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
_
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx --no -- commitlint --edit $1
|
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
command_exists () {
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Workaround for Windows 10, Git Bash and Yarn
|
||||||
|
if command_exists winpty && test -t 1; then
|
||||||
|
exec < /dev/tty
|
||||||
|
fi
|
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx lint-staged
|
||||||
|
npx lint-staged
|
@ -0,0 +1,3 @@
|
|||||||
|
/node_modules/**
|
||||||
|
/dist*
|
||||||
|
/public/*
|
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": ["johnsoncodehk.volar"]
|
"recommendations": ["Vue.volar"]
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 咬轮猫
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -0,0 +1 @@
|
|||||||
|
module.exports = { extends: ['@commitlint/config-conventional'] };
|
@ -1,39 +1,40 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-webtopo-svgeditor",
|
"name": "vue-webtopo-svgeditor",
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"module": "./dist/vue-webtopo-svgeditor.es.ts",
|
|
||||||
"main": "./dist/vue-webtopo-svgeditor.umd.ts",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": "./dist/vue-webtopo-svgeditor.es.ts",
|
|
||||||
"require": "./dist/vue-webtopo-svgeditor.umd.ts"
|
|
||||||
},
|
|
||||||
"./dist/style.css": {
|
|
||||||
"import": "./dist/style.css",
|
|
||||||
"require": "./dist/style.css"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
"serve": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"lint:eslint": "eslint --fix --ext .js,.ts,.vue,.tsx ./src",
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
|
"postinstall": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.22.0",
|
"vue": "^3.2.25",
|
||||||
"vue": "^3.2.6"
|
"vue-router": "4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vicons/fluent": "^0.11.0",
|
"@commitlint/cli": "^17.0.3",
|
||||||
"@vitejs/plugin-vue": "^1.6.1",
|
"@commitlint/config-conventional": "^17.0.3",
|
||||||
"@vue/compiler-sfc": "^3.2.6",
|
"@typescript-eslint/eslint-plugin": "^5.30.5",
|
||||||
"naive-ui": "^2.18.2",
|
"@typescript-eslint/parser": "^5.30.5",
|
||||||
"typescript": "^4.3.2",
|
"@vitejs/plugin-vue": "^2.3.3",
|
||||||
"vite": "^2.5.4",
|
"eslint": "^8.19.0",
|
||||||
"echarts": "^5.2.1",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"vue-echarts": "^6.0.0",
|
"eslint-define-config": "^1.5.1",
|
||||||
"vue-tsc": "^0.2.2"
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"eslint-plugin-vue": "^9.2.0",
|
||||||
|
"husky": "^8.0.1",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"typescript": "^4.5.4",
|
||||||
|
"vite": "^2.9.9",
|
||||||
|
"vite-plugin-eslint": "^1.6.1",
|
||||||
|
"vue-eslint-parser": "^9.0.3",
|
||||||
|
"vue-tsc": "^0.34.7"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,jsx,vue,ts,tsx}": [
|
||||||
|
"pnpm run lint:eslint"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.4 KiB |
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 3.3 KiB |
@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* @description: 数据结构
|
|
||||||
* @param {*} commonComponentList 常规组件列表
|
|
||||||
* @param {*} drawComponentList 绘制组件列表
|
|
||||||
* @param {*} chartComponentList 图表组件列表
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
export interface ILeftImgLists {
|
|
||||||
commonComponentList?: Array<IComponentInfo>,
|
|
||||||
drawComponentList?: Array<IComponentInfo>,
|
|
||||||
chartComponentList?: Array<IComponentInfo>
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 画好的组件数据列表
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
export interface ISvgDataLists {
|
|
||||||
id: string,
|
|
||||||
type?: string,
|
|
||||||
title?: string,
|
|
||||||
svgPositionX: number,
|
|
||||||
svgPositionY: number,
|
|
||||||
angle?: number,
|
|
||||||
size?: number,
|
|
||||||
extend_attr?: any
|
|
||||||
}
|
|
||||||
export interface ISvgCanvas {
|
|
||||||
width: number,
|
|
||||||
height: number
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 组件信息格式
|
|
||||||
* @param {*} type 组件类型
|
|
||||||
* @param {*} title 组件显示标题
|
|
||||||
* @param {*} panel_class 面板显示类型 拖放 绘制
|
|
||||||
* @param {*} template 组件渲染模板
|
|
||||||
* @param {*} props 组件传值
|
|
||||||
* @param {*} extend_attr 组件拓展属性
|
|
||||||
* @param {*} create_type 组件创建类型
|
|
||||||
* @param {*} priview_img 组件预览图片
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
export interface IComponentInfo {
|
|
||||||
type?: string,
|
|
||||||
title?: string,
|
|
||||||
panel_class?: string,
|
|
||||||
template?: string,//
|
|
||||||
props?: Array<string>,
|
|
||||||
extend_attr?: any,
|
|
||||||
create_type?: string,
|
|
||||||
priview_img?: string
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 鼠标信息
|
|
||||||
* @param {*} status 1按下 0弹起
|
|
||||||
* @param {*} mPositionX 鼠标x轴坐标
|
|
||||||
* @param {*} mPositionY 鼠标y轴坐标
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
export interface IMouseInfo {
|
|
||||||
status: number,
|
|
||||||
mPositionX: number
|
|
||||||
mPositionY: number
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 选中的svg属性
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
export interface ISelectSvg {
|
|
||||||
id: string,
|
|
||||||
index: number,
|
|
||||||
sPositionX: number,
|
|
||||||
sPositionY: number,
|
|
||||||
create_type: string,
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
/* 正向流动效果 */
|
|
||||||
.svg_ani_flow {
|
|
||||||
stroke-dasharray: 1000;
|
|
||||||
stroke-dashoffset: 1000;
|
|
||||||
animation: ani_flow 10s linear infinite;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
-webkit-animation: ani_flow 10s linear infinite;
|
|
||||||
-webkit-animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ani_flow {
|
|
||||||
from {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
stroke-dasharray: 13, 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes ani_flow {
|
|
||||||
from {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
stroke-dasharray: 13, 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 停止流动效果 */
|
|
||||||
.svg_ani_flow_stop {
|
|
||||||
stroke-dasharray: 1000;
|
|
||||||
stroke-dashoffset: 1000;
|
|
||||||
animation: ani_flow_stop 10s linear infinite;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
-webkit-animation: ani_flow_stop 10s linear infinite;
|
|
||||||
-webkit-animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ani_flow_stop {
|
|
||||||
from {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes ani_flow_stop {
|
|
||||||
from {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 反向流动效果 */
|
|
||||||
.svg_ani_flow_back {
|
|
||||||
stroke-dasharray: 1000;
|
|
||||||
stroke-dashoffset: 1000;
|
|
||||||
animation: ani_flow_back 10s linear infinite;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
-webkit-animation: ani_flow_back 10s linear infinite;
|
|
||||||
-webkit-animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ani_flow_back {
|
|
||||||
from {
|
|
||||||
stroke-dasharray: 13, 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes ani_flow_stop {
|
|
||||||
from {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 以最大40高度填充 */
|
|
||||||
.svg_ani_fill_h40 {
|
|
||||||
animation: ani_fill_h40 5s linear infinite;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
-webkit-animation: ani_fill_h40 5s linear infinite;
|
|
||||||
-webkit-animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ani_fill_h40 {
|
|
||||||
from {
|
|
||||||
height: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes ani_flow_stop {
|
|
||||||
from {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
stroke-dasharray: 10, 5;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
<template>
|
|
||||||
<n-card title="操作说明" embedded :bordered="false">
|
|
||||||
<ul>
|
|
||||||
<li>常规组件和图表组件用鼠标左键选中并按住可拖动至画布</li>
|
|
||||||
<li>绘制组件需先选中 再将鼠标移至画布中按住左键开始绘制 松开左键结束绘制</li>
|
|
||||||
<li>在画布上单击左键并按住可以拖动组件改变位置</li>
|
|
||||||
<li>当组件选中时会有选中效果 此时右侧面板弹出 可使用快捷键和编辑右侧面板属性更改组件</li>
|
|
||||||
<li>鼠标右键点击组件可以进行一些快捷操作</li>
|
|
||||||
<li>键盘↑↓←→可移动选中组件 ctrl+c复制当前选中组件 deleted删除当前选中组件</li>
|
|
||||||
<li>ctrl+键盘↑↓←→可移动组件图层</li>
|
|
||||||
<li>点击画布空白处可以取消选中组件 并关闭属性面板</li>
|
|
||||||
<li>点击《 符号可以显示或隐藏左侧面板</li>
|
|
||||||
<li>如果使用我的在线体验地址 请先保存绘制再切换到预览界面</li>
|
|
||||||
<li>更多使用帮助请参考<a href="https://github.com/yaolunmao/vue-webtopo-svgeditor">github</a>或<a href="https://gitee.com/yaolunmao/vue-webtopo-svgeditor">gitee</a></li>
|
|
||||||
</ul>
|
|
||||||
</n-card>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { NCard } from "naive-ui";
|
|
||||||
</script>
|
|
@ -1,155 +0,0 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
|
||||||
import { ChevronDoubleLeft20Regular, ChevronDoubleRight20Regular } from '@vicons/fluent';
|
|
||||||
import { NIcon, NCollapse, NCollapseItem, useMessage } from "naive-ui";
|
|
||||||
import { ILeftImgLists, IComponentInfo } from "../Model";
|
|
||||||
const emit = defineEmits(['setCreatSvgInfo'])
|
|
||||||
const message = useMessage();
|
|
||||||
const props = defineProps({
|
|
||||||
left_imglists: {
|
|
||||||
type: Object as () => ILeftImgLists,
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
//选中的左侧工具图标
|
|
||||||
select_toolbar: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const leftnav = reactive({
|
|
||||||
navclass: 'leftnavDisplay',
|
|
||||||
navopen: true
|
|
||||||
});
|
|
||||||
const clickHandleIcon = () => {
|
|
||||||
leftnav.navopen = !leftnav.navopen;
|
|
||||||
leftnav.navclass = leftnav.navclass == 'leftnavDisplay' ? 'leftnavNone' : 'leftnavDisplay'
|
|
||||||
};
|
|
||||||
const dragStartEvent = (leftImgItem: IComponentInfo, e: DragEvent) => {
|
|
||||||
//设置要创建的svg组件信息
|
|
||||||
emit('setCreatSvgInfo', leftImgItem);
|
|
||||||
}
|
|
||||||
const dragEndEvent = (leftImgItem: IComponentInfo, e: DragEvent) => {
|
|
||||||
//拖动时记录拖动的svg信息
|
|
||||||
if (e.dataTransfer?.dropEffect !== 'copy') {
|
|
||||||
message.error('请将组件拖到画布中!');
|
|
||||||
//清空已选择的信息
|
|
||||||
emit('setCreatSvgInfo', {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div :class="leftnav.navclass">
|
|
||||||
<div class="svgimg" v-show="leftnav.navopen">
|
|
||||||
<n-collapse default-expanded-names="1" accordion>
|
|
||||||
<n-collapse-item title="常规组件" name="1" style="margin-top: 15px;">
|
|
||||||
<ul class="leftImgUl">
|
|
||||||
<li v-for="leftImgItem in props.left_imglists.commonComponentList">
|
|
||||||
<img
|
|
||||||
:title="leftImgItem.title"
|
|
||||||
:src="leftImgItem.priview_img"
|
|
||||||
draggable="true"
|
|
||||||
@dragstart="dragStartEvent(leftImgItem, $event)"
|
|
||||||
@dragend="dragEndEvent(leftImgItem, $event)"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</n-collapse-item>
|
|
||||||
<n-collapse-item title="绘制组件" name="2">
|
|
||||||
<ul class="leftImgUl">
|
|
||||||
<li v-for="leftImgItem in props.left_imglists.drawComponentList">
|
|
||||||
<img
|
|
||||||
:class="props.select_toolbar == leftImgItem.type ? 'svg-selected' : ''"
|
|
||||||
:title="leftImgItem.title"
|
|
||||||
:src="leftImgItem.priview_img"
|
|
||||||
@click="() => { emit('setCreatSvgInfo', leftImgItem) }"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</n-collapse-item>
|
|
||||||
<n-collapse-item title="图表组件" name="3">
|
|
||||||
<ul class="leftImgUl">
|
|
||||||
<li v-for="leftImgItem in props.left_imglists.chartComponentList">
|
|
||||||
<img
|
|
||||||
:title="leftImgItem.title"
|
|
||||||
:src="leftImgItem.priview_img"
|
|
||||||
draggable="true"
|
|
||||||
@dragstart="dragStartEvent(leftImgItem, $event)"
|
|
||||||
@dragend="dragEndEvent(leftImgItem, $event)"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</n-collapse-item>
|
|
||||||
</n-collapse>
|
|
||||||
</div>
|
|
||||||
<div class="handlehidden">
|
|
||||||
<n-icon class="handleicon" size="10" @click="clickHandleIcon">
|
|
||||||
<chevron-double-left20-regular v-if="leftnav.navopen" />
|
|
||||||
<chevron-double-right20-regular v-else />
|
|
||||||
</n-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped>
|
|
||||||
.leftnavDisplay,
|
|
||||||
.leftnavNone {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.leftnavDisplay {
|
|
||||||
width: 210px;
|
|
||||||
}
|
|
||||||
.leftnavNone {
|
|
||||||
width: 10px;
|
|
||||||
}
|
|
||||||
.svgimg {
|
|
||||||
/* background-color: green; */
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
overflow: overlay;
|
|
||||||
}
|
|
||||||
.svgimg::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.handlehidden {
|
|
||||||
height: 100%;
|
|
||||||
width: 10px;
|
|
||||||
background-color: grey;
|
|
||||||
}
|
|
||||||
.handleicon {
|
|
||||||
top: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.leftImgUl {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.leftImgUl li {
|
|
||||||
width: calc(33.33% - 30px);
|
|
||||||
margin: 0 15px 15px 15px;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 1px 1px 5px #ddd;
|
|
||||||
cursor: pointer;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.leftImgUl li :hover {
|
|
||||||
box-shadow: 1px 1px 10px #ccc;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.leftImgUl img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.svg-selected {
|
|
||||||
outline: 1px solid #0cf;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,81 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { reactive, ref } from "vue";
|
|
||||||
import { NForm, NFormItem, NInput, NInputNumber, NColorPicker, NRadioGroup, NSpace, NRadio,NSelect } from "naive-ui";
|
|
||||||
import { ISvgDataLists } from "../Model";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
set_svg_info: {
|
|
||||||
type: Object as () => ISvgDataLists,
|
|
||||||
default: {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="rightnav">
|
|
||||||
<div style="margin-left: 2rem;margin-top: 1rem;">
|
|
||||||
<n-form size="small" label-placement="left">
|
|
||||||
<n-form-item label="标识">
|
|
||||||
<span>{{ props.set_svg_info.id }}</span>
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="类型">
|
|
||||||
<span>{{ props.set_svg_info.type }}</span>
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="名称">
|
|
||||||
<n-input placeholder="请输入名称" v-model:value="props.set_svg_info.title" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="x轴坐标">
|
|
||||||
<n-input-number v-model:value="props.set_svg_info.svgPositionX" :min="0" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="y轴坐标">
|
|
||||||
<n-input-number v-model:value="props.set_svg_info.svgPositionY" :min="0" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="大小">
|
|
||||||
<n-input-number v-model:value="props.set_svg_info.size" :min="1" :step="0.1" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="旋转">
|
|
||||||
<n-input-number v-model:value="props.set_svg_info.angle" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item v-for="item in props.set_svg_info.extend_attr" :label="item.title">
|
|
||||||
<n-input-number v-if="item.type == 'numberinputbox'" v-model:value="item.val" />
|
|
||||||
<n-color-picker
|
|
||||||
v-else-if="item.type == 'colorinputbox'"
|
|
||||||
:modes="['hex']"
|
|
||||||
v-model:value="item.val"
|
|
||||||
/>
|
|
||||||
<n-input
|
|
||||||
v-else-if="item.type == 'textinputbox'"
|
|
||||||
:placeholder="`请输入${item.title}`"
|
|
||||||
v-model:value="item.val"
|
|
||||||
/>
|
|
||||||
<n-input
|
|
||||||
v-else-if="item.type == 'textareainputbox'"
|
|
||||||
:placeholder="`请输入${item.title}`"
|
|
||||||
type="textarea"
|
|
||||||
v-model:value="item.val"
|
|
||||||
/>
|
|
||||||
<n-radio-group v-model:value="item.val.selectval" v-else-if="item.type == 'radiogroup'">
|
|
||||||
<n-space>
|
|
||||||
<n-radio
|
|
||||||
v-for="ridioitem in item.val.ridiogroup"
|
|
||||||
:key="ridioitem.value"
|
|
||||||
:value="ridioitem.value"
|
|
||||||
>{{ ridioitem.label }}</n-radio>
|
|
||||||
</n-space>
|
|
||||||
</n-radio-group>
|
|
||||||
<n-select v-else-if="item.type == 'select'" v-model:value="item.val.selectval" :options="item.val.selectgroup" />
|
|
||||||
|
|
||||||
</n-form-item>
|
|
||||||
</n-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped>
|
|
||||||
.rightnav {
|
|
||||||
width: 250px;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.rightnav::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,50 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { reactive, ref, createApp, markRaw,provide } from "vue";
|
|
||||||
import { use } from "echarts/core";
|
|
||||||
import { SVGRenderer } from "echarts/renderers";
|
|
||||||
import { PieChart,BarChart } from "echarts/charts";
|
|
||||||
import {
|
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
LegendComponent,
|
|
||||||
GridComponent
|
|
||||||
} from "echarts/components";
|
|
||||||
import { THEME_KEY } from "vue-echarts";
|
|
||||||
use([
|
|
||||||
SVGRenderer,
|
|
||||||
PieChart,
|
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
LegendComponent,
|
|
||||||
GridComponent,
|
|
||||||
BarChart
|
|
||||||
]);
|
|
||||||
provide(THEME_KEY, "default")
|
|
||||||
const props = defineProps({
|
|
||||||
component_type: {
|
|
||||||
type: String as () => any,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
component_template: {
|
|
||||||
type: String as () => any,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
component_props: {
|
|
||||||
type: Array as () => Array<any>,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
component_attr: {
|
|
||||||
type: Object as () => any,
|
|
||||||
default: {}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const component_info = markRaw({
|
|
||||||
template: props.component_template,
|
|
||||||
props: ['prop_data']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<component :is="component_info" :prop_data="props.component_attr"></component>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,555 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, reactive, Ref, ref, watch } from 'vue';
|
|
||||||
import TopToolBar from './TopBar.vue';
|
|
||||||
import LeftToolBar from './LeftToolBar.vue';
|
|
||||||
import RightToolBar from './RightToolBar.vue';
|
|
||||||
import BottomBar from './BottomBar.vue';
|
|
||||||
import { NMessageProvider } from "naive-ui";
|
|
||||||
import { IComponentInfo, ISvgDataLists, ISvgCanvas, ILeftImgLists, IMouseInfo, ISelectSvg } from "../Model";
|
|
||||||
import SvgDynamic from "./SvgDynamic.vue";
|
|
||||||
import "../assets/css/svgAnimation/index.css";
|
|
||||||
import { moveUp, moveDown, moveLeft, moveRight, hotkeyCopy, hotkeyDel, hotkeyPutOnTop, hotkeyPutOnButtom, hotkeyPutOnUp, hotkeyPutOnDown } from "../func/HotkeyFunc";
|
|
||||||
const emit = defineEmits(['saveSvgInfo']);
|
|
||||||
const props = defineProps({
|
|
||||||
//组件的json格式
|
|
||||||
component_infos: {
|
|
||||||
type: Array as () => Array<IComponentInfo>,
|
|
||||||
required: true,
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
svgCanvas: {
|
|
||||||
type: Object as () => ISvgCanvas,
|
|
||||||
default: { width: 1520, height: 720 }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const svg_dom_ref = ref<null | HTMLElement>(null);
|
|
||||||
const contextMenuRef = ref<HTMLElement>();
|
|
||||||
const svgLists: ISvgDataLists[] = reactive([]);
|
|
||||||
const topbar_dom_ref = ref(null);
|
|
||||||
//显示右键菜单
|
|
||||||
const display_contextmenu = ref(false);
|
|
||||||
//右键菜单数据
|
|
||||||
const contextmenu_data = reactive([{
|
|
||||||
name: "复制",
|
|
||||||
hotkey: "Ctrl+C",
|
|
||||||
enable: true,
|
|
||||||
fun: function () {
|
|
||||||
if (!this.enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hotkeyCopy(svgLists, select_svg);
|
|
||||||
display_contextmenu.value = false;
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
name: "删除",
|
|
||||||
hotkey: "Delete",
|
|
||||||
enable: false,
|
|
||||||
fun: function () {
|
|
||||||
if (!this.enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hotkeyDel(svgLists, select_svg);
|
|
||||||
display_contextmenu.value = false;
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
name: "置于顶层",
|
|
||||||
hotkey: "Ctrl+→",
|
|
||||||
enable: true,
|
|
||||||
fun: function () {
|
|
||||||
if (!this.enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hotkeyPutOnTop(svgLists, select_svg);
|
|
||||||
display_contextmenu.value = false;
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
name: "置于底层",
|
|
||||||
hotkey: "Ctrl+←",
|
|
||||||
enable: true,
|
|
||||||
fun: function () {
|
|
||||||
if (!this.enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hotkeyPutOnButtom(svgLists, select_svg);
|
|
||||||
display_contextmenu.value = false;
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
name: "置于上一层",
|
|
||||||
hotkey: "Ctrl+↑",
|
|
||||||
enable: true,
|
|
||||||
fun: function () {
|
|
||||||
if (!this.enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hotkeyPutOnUp(svgLists, select_svg);
|
|
||||||
display_contextmenu.value = false;
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
name: "置于下一层",
|
|
||||||
hotkey: "Ctrl+↓",
|
|
||||||
enable: true,
|
|
||||||
fun: function () {
|
|
||||||
if (!this.enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hotkeyPutOnDown(svgLists, select_svg);
|
|
||||||
display_contextmenu.value = false;
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
const set_svg_info: Ref<ISvgDataLists> = ref({
|
|
||||||
id: '', title: '', svgPositionX: 0, svgPositionY: 0
|
|
||||||
});
|
|
||||||
//选中的svg
|
|
||||||
const select_svg: ISelectSvg = reactive({
|
|
||||||
id: '',
|
|
||||||
index: 0,
|
|
||||||
sPositionX: 0,
|
|
||||||
sPositionY: 0,
|
|
||||||
create_type: ''
|
|
||||||
});
|
|
||||||
//选中的左侧工具栏的svg
|
|
||||||
const select_lefttool: Ref<IComponentInfo> = ref({});
|
|
||||||
//左侧工具栏svg列表
|
|
||||||
const leftimg_lists: Ref<ILeftImgLists> = ref({
|
|
||||||
commonComponentList: [],
|
|
||||||
drawComponentList: [],
|
|
||||||
chartComponentList: []
|
|
||||||
});
|
|
||||||
//选中的左侧工具栏
|
|
||||||
const select_toolbar: Ref<string | undefined> = ref('');
|
|
||||||
//记录鼠标信息
|
|
||||||
const mouseInfo: IMouseInfo = reactive({
|
|
||||||
status: 0,
|
|
||||||
mPositionX: 0,
|
|
||||||
mPositionY: 0
|
|
||||||
});
|
|
||||||
const rightnav_open = ref(false);
|
|
||||||
/**
|
|
||||||
* @description: 从左侧工具栏拖动组件到画布触发的事件
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const dropEvent = (e: DragEvent) => {
|
|
||||||
//当左侧工具栏拖动到此处时在画布上创建该组件
|
|
||||||
if (Object.keys(select_lefttool.value).length < 1) {
|
|
||||||
//未选择任何组件
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//在鼠标位置创建当前组件
|
|
||||||
const create_svg: ISvgDataLists = {
|
|
||||||
id: `${new Date().getTime()}`,
|
|
||||||
type: select_lefttool.value.type,
|
|
||||||
title: select_lefttool.value.title,
|
|
||||||
svgPositionX: e.offsetX,
|
|
||||||
svgPositionY: e.offsetY,
|
|
||||||
angle: 0,
|
|
||||||
size: 1,
|
|
||||||
extend_attr: JSON.parse(JSON.stringify(select_lefttool.value.extend_attr))
|
|
||||||
}
|
|
||||||
svgLists.push(create_svg);
|
|
||||||
//清空左侧工具选中
|
|
||||||
select_lefttool.value = {};
|
|
||||||
|
|
||||||
}
|
|
||||||
const dragEnterEvent = (e: DragEvent) => {
|
|
||||||
//dragenter和dragover一定要阻止浏览器默认行为 不然不会触发drop
|
|
||||||
rightnav_open.value = false;
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
const dragOverEvent = (e: DragEvent) => {
|
|
||||||
//dragenter和dragover一定要阻止浏览器默认行为 不然不会触发drop
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 设置要创建组件的信息
|
|
||||||
* @param {*} createsvg_info 选中的组件信息
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const setCreatSvgInfo = (createsvg_info: IComponentInfo) => {
|
|
||||||
select_lefttool.value = createsvg_info;
|
|
||||||
select_toolbar.value = createsvg_info.type;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 保存绘制组件后的数据和svgdom
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const saveSvgInfo = () => {
|
|
||||||
if (svgLists.length == 0) {
|
|
||||||
(topbar_dom_ref.value as any).saveSvgInfoRes({ code: 2, msg: '请先绘制图像!' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(topbar_dom_ref.value as any).saveSvgInfoRes(svgLists.length > 0 ? { code: 1, msg: '保存成功!' } : { code: 0, msg: '保存失败!' });
|
|
||||||
emit('saveSvgInfo', svgLists, svg_dom_ref.value)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 鼠标点击画布上svg触发事件
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const mouseDownEvent = (selectsvg: ISvgDataLists, index: number, e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.cancelBubble = true;
|
|
||||||
//清空左侧工具选中
|
|
||||||
select_lefttool.value = {};
|
|
||||||
//鼠标在画布上的组件按下记录选中的组件信息和鼠标位置信息等
|
|
||||||
({ id: select_svg.id, svgPositionX: select_svg.sPositionX, svgPositionY: select_svg.sPositionY } = selectsvg);
|
|
||||||
rightnav_open.value = false;
|
|
||||||
select_svg.index = index;
|
|
||||||
select_svg.create_type = "draggable"
|
|
||||||
mouseInfo.status = 1;
|
|
||||||
mouseInfo.mPositionX = e.clientX;
|
|
||||||
mouseInfo.mPositionY = e.clientY;
|
|
||||||
}
|
|
||||||
const mouseMoveEvent = (e: MouseEvent) => {
|
|
||||||
//如果鼠标不是按下状态或者没有选择组件
|
|
||||||
if (mouseInfo.status != 1 || !select_svg.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { clientX, clientY } = e;
|
|
||||||
if (select_svg.create_type == "draggable") {
|
|
||||||
let new_select_svg = { ...select_svg };
|
|
||||||
new_select_svg.sPositionX += clientX - mouseInfo.mPositionX;
|
|
||||||
new_select_svg.sPositionY += clientY - mouseInfo.mPositionY;
|
|
||||||
//更新视图
|
|
||||||
({ sPositionX: svgLists[select_svg.index].svgPositionX, sPositionY: svgLists[select_svg.index].svgPositionY } = new_select_svg);
|
|
||||||
}
|
|
||||||
else if (select_svg.create_type == "draw") {
|
|
||||||
//拓展属性里未配置的属性不进行赋值
|
|
||||||
if (svgLists[select_svg.index].extend_attr?.startpoint_x?.val != null) {
|
|
||||||
svgLists[select_svg.index].extend_attr.startpoint_x.val = 0;
|
|
||||||
}
|
|
||||||
if (svgLists[select_svg.index].extend_attr?.startpoint_y?.val != null) {
|
|
||||||
svgLists[select_svg.index].extend_attr.startpoint_y.val = 0;
|
|
||||||
}
|
|
||||||
if (svgLists[select_svg.index].extend_attr?.endpoint_x?.val != null) {
|
|
||||||
svgLists[select_svg.index].extend_attr.endpoint_x.val = clientX - mouseInfo.mPositionX;
|
|
||||||
}
|
|
||||||
if (svgLists[select_svg.index].extend_attr?.endpoint_y?.val != null) {
|
|
||||||
svgLists[select_svg.index].extend_attr.endpoint_y.val = clientY - mouseInfo.mPositionY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
const mouseUpEvent = (e: MouseEvent) => {
|
|
||||||
//如果鼠标不是按下状态或者没有选择组件
|
|
||||||
if (mouseInfo.status != 1 || !select_svg.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mouseInfo.status = 0;
|
|
||||||
rightnav_open.value = true;
|
|
||||||
set_svg_info.value = svgLists[select_svg.index];
|
|
||||||
//清空左侧工具选中
|
|
||||||
select_lefttool.value = {};
|
|
||||||
select_toolbar.value = '';
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 鼠标点击画布
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const mouseDownCanvasEvent = (e: MouseEvent) => {
|
|
||||||
//判断当前是否有选中的工具栏
|
|
||||||
if (Object.keys(select_lefttool.value).length < 1) {
|
|
||||||
rightnav_open.value = false;
|
|
||||||
select_svg.id = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//在当前位置创建要绘制的组件
|
|
||||||
const create_svg: ISvgDataLists = {
|
|
||||||
id: `${new Date().getTime()}`,
|
|
||||||
type: select_lefttool.value.type,
|
|
||||||
title: select_lefttool.value.title,
|
|
||||||
svgPositionX: e.offsetX,
|
|
||||||
svgPositionY: e.offsetY,
|
|
||||||
angle: 0,
|
|
||||||
size: 1,
|
|
||||||
extend_attr: JSON.parse(JSON.stringify(select_lefttool.value.extend_attr))//这个响应式对象我治不了了 所以只能写两次转换
|
|
||||||
}
|
|
||||||
svgLists.push(create_svg);
|
|
||||||
//设置全局选中的组件信息
|
|
||||||
({ id: select_svg.id, svgPositionX: select_svg.sPositionX, svgPositionY: select_svg.sPositionY } = create_svg);
|
|
||||||
rightnav_open.value = false;
|
|
||||||
select_svg.index = svgLists.length - 1;
|
|
||||||
select_svg.create_type = "draw"
|
|
||||||
mouseInfo.status = 1;
|
|
||||||
mouseInfo.mPositionX = e.clientX;
|
|
||||||
mouseInfo.mPositionY = e.clientY;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 鼠标右键
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const contextmenuEvent = (e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
display_contextmenu.value = true;
|
|
||||||
(contextMenuRef.value as any).style.left = e.pageX + 'px';
|
|
||||||
(contextMenuRef.value as any).style.top = e.pageY + 'px';
|
|
||||||
contextmenu_data.map(m => m.enable = true);
|
|
||||||
//判断当前选中组件的index
|
|
||||||
if (svgLists.length === 1) {
|
|
||||||
//禁用下移
|
|
||||||
contextmenu_data[3].enable = false;
|
|
||||||
contextmenu_data[5].enable = false;
|
|
||||||
//禁用上移
|
|
||||||
contextmenu_data[2].enable = false;
|
|
||||||
contextmenu_data[4].enable = false;
|
|
||||||
}
|
|
||||||
else if (select_svg.index === 0) {
|
|
||||||
//禁用下移
|
|
||||||
contextmenu_data[3].enable = false;
|
|
||||||
contextmenu_data[5].enable = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (select_svg.index === svgLists.length - 1) {
|
|
||||||
//禁用上移
|
|
||||||
contextmenu_data[2].enable = false;
|
|
||||||
contextmenu_data[4].enable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: 点击页面其他位置隐藏右键菜单
|
|
||||||
* @param {*}
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const documentClickEvent = (e: MouseEvent) => {
|
|
||||||
if (e.button !== 2) {
|
|
||||||
display_contextmenu.value = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
watch(() => [...props.component_infos], (newval, oldval) => {
|
|
||||||
leftimg_lists.value = {
|
|
||||||
commonComponentList: newval.filter(f => f.panel_class == 'common'),
|
|
||||||
drawComponentList: newval.filter(f => f.panel_class == 'draw'),
|
|
||||||
chartComponentList: newval.filter(f => f.panel_class == 'chart')
|
|
||||||
};
|
|
||||||
});
|
|
||||||
//监听键盘
|
|
||||||
document.onkeydown = function (e) {
|
|
||||||
//如果没选中组件
|
|
||||||
if (!select_svg.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!e.ctrlKey && e.key == 'ArrowUp') {
|
|
||||||
e.preventDefault();
|
|
||||||
moveUp(svgLists, select_svg);
|
|
||||||
} else if (!e.ctrlKey && e.key == 'ArrowDown') {
|
|
||||||
e.preventDefault();
|
|
||||||
moveDown(svgLists, select_svg);
|
|
||||||
} else if (!e.ctrlKey && e.key == 'ArrowLeft') {
|
|
||||||
e.preventDefault();
|
|
||||||
moveLeft(svgLists, select_svg);
|
|
||||||
} else if (!e.ctrlKey && e.key == 'ArrowRight') {
|
|
||||||
e.preventDefault();
|
|
||||||
moveRight(svgLists, select_svg);
|
|
||||||
}
|
|
||||||
//ctrl c
|
|
||||||
else if (e.ctrlKey && e.key.toLowerCase() == 'c') {
|
|
||||||
e.preventDefault();
|
|
||||||
hotkeyCopy(svgLists, select_svg);
|
|
||||||
}
|
|
||||||
//deleted
|
|
||||||
else if (e.key == 'Delete') {
|
|
||||||
e.preventDefault();
|
|
||||||
hotkeyDel(svgLists, select_svg);
|
|
||||||
rightnav_open.value = false;
|
|
||||||
}
|
|
||||||
//上移一层
|
|
||||||
else if (e.ctrlKey && e.key == 'ArrowUp') {
|
|
||||||
e.preventDefault();
|
|
||||||
hotkeyPutOnUp(svgLists, select_svg);
|
|
||||||
}
|
|
||||||
//下移一层
|
|
||||||
else if (e.ctrlKey && e.key == 'ArrowDown') {
|
|
||||||
e.preventDefault();
|
|
||||||
hotkeyPutOnDown(svgLists, select_svg);
|
|
||||||
}
|
|
||||||
//置于底层
|
|
||||||
else if (e.ctrlKey && e.key == 'ArrowLeft') {
|
|
||||||
e.preventDefault();
|
|
||||||
hotkeyPutOnButtom(svgLists, select_svg);
|
|
||||||
}
|
|
||||||
//置于顶层
|
|
||||||
else if (e.ctrlKey && e.key == 'ArrowRight') {
|
|
||||||
e.preventDefault();
|
|
||||||
hotkeyPutOnTop(svgLists, select_svg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const setSvgLists = (new_val: ISvgDataLists[]) => {
|
|
||||||
svgLists.length = 0;
|
|
||||||
svgLists.push(...new_val);
|
|
||||||
emit('saveSvgInfo', svgLists, svg_dom_ref.value)
|
|
||||||
}
|
|
||||||
defineExpose({
|
|
||||||
setSvgLists
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="navtop">
|
|
||||||
<n-message-provider>
|
|
||||||
<top-tool-bar @saveSvgInfo="saveSvgInfo" ref="topbar_dom_ref"></top-tool-bar>
|
|
||||||
</n-message-provider>
|
|
||||||
</div>
|
|
||||||
<div class="ancestors" @mousedown="documentClickEvent">
|
|
||||||
<div class="navleft">
|
|
||||||
<n-message-provider>
|
|
||||||
<left-tool-bar
|
|
||||||
:left_imglists="leftimg_lists"
|
|
||||||
:select_toolbar="select_toolbar"
|
|
||||||
@setCreatSvgInfo="setCreatSvgInfo"
|
|
||||||
></left-tool-bar>
|
|
||||||
</n-message-provider>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="warp"
|
|
||||||
@drop="dropEvent"
|
|
||||||
@dragenter="dragEnterEvent"
|
|
||||||
@dragover="dragOverEvent"
|
|
||||||
@mousemove="mouseMoveEvent"
|
|
||||||
@mouseup="mouseUpEvent"
|
|
||||||
@mousedown="mouseDownCanvasEvent"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
style="background-color:#000000;"
|
|
||||||
:width="svgCanvas.width"
|
|
||||||
:height="svgCanvas.height"
|
|
||||||
:viewBox="`0 0 ${svgCanvas.width} ${svgCanvas.height}`"
|
|
||||||
ref="svg_dom_ref"
|
|
||||||
>
|
|
||||||
<defs />
|
|
||||||
<filter x="0" y="0" width="1" height="1" id="solid">
|
|
||||||
<feFlood flood-color="rgb(255,255,255)" />
|
|
||||||
<feComposite in="SourceGraphic" />
|
|
||||||
</filter>
|
|
||||||
<g
|
|
||||||
style="cursor:pointer"
|
|
||||||
:class="item.id == select_svg.id ? 'svg-selected' : ''"
|
|
||||||
v-for="(item,index) in svgLists"
|
|
||||||
:key="item.id"
|
|
||||||
:id="item.id"
|
|
||||||
:transform="'translate(' + (item.svgPositionX) + ',' + (item.svgPositionY) + ')' + 'rotate(' + item.angle + ')' + 'scale(' + item.size + ')'"
|
|
||||||
@mousedown="mouseDownEvent(item, index, $event)"
|
|
||||||
@contextmenu.stop="contextmenuEvent"
|
|
||||||
>
|
|
||||||
<svg-dynamic
|
|
||||||
:component_type="item.type"
|
|
||||||
:component_template="props.component_infos.filter(f => f.type == item.type)[0].template"
|
|
||||||
:component_props="props.component_infos.filter(f => f.type == item.type)[0].props"
|
|
||||||
:component_attr="item"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="navright" v-show="rightnav_open">
|
|
||||||
<right-tool-bar :set_svg_info="set_svg_info"></right-tool-bar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="navbuttom">
|
|
||||||
<bottom-bar></bottom-bar>
|
|
||||||
</div>
|
|
||||||
<!-- 右键菜单 -->
|
|
||||||
<ul ref="contextMenuRef" class="contextMenu" v-show="display_contextmenu">
|
|
||||||
<li v-for="(item,index) in contextmenu_data" :key="index" @click="item.fun()">
|
|
||||||
<p :class="item.enable ? '' : 'disabled'">
|
|
||||||
{{ item.name }}
|
|
||||||
<span class="shortcut">{{ item.hotkey }}</span>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.contextMenu {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99999;
|
|
||||||
background: #ffffff;
|
|
||||||
padding: 5px 0;
|
|
||||||
margin: 0px;
|
|
||||||
display: block;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 2px 5px 10px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.contextMenu li {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contextMenu .shortcut {
|
|
||||||
width: 115px;
|
|
||||||
text-align: right;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contextMenu p {
|
|
||||||
text-decoration: none;
|
|
||||||
display: block;
|
|
||||||
padding: 0px 15px 1px 20px;
|
|
||||||
margin: 0;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contextMenu p:hover {
|
|
||||||
background-color: #0cf;
|
|
||||||
color: #ffffff;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contextMenu .disabled {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
.contextMenu .disabled:hover {
|
|
||||||
color: #999;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
.contextMenu li.separator {
|
|
||||||
border-top: solid 1px #e3e3e3;
|
|
||||||
padding-top: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
.ancestors {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.ancestors .navleft {
|
|
||||||
width: auto;
|
|
||||||
height: 100%;
|
|
||||||
/* border-left: solid 1px rgb(239, 239, 245); */
|
|
||||||
}
|
|
||||||
.ancestors .warp {
|
|
||||||
overflow: auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.ancestors .navright {
|
|
||||||
width: auto;
|
|
||||||
height: 100%;
|
|
||||||
/*background-color: aqua;*/
|
|
||||||
}
|
|
||||||
.navtop {
|
|
||||||
width: 100%;
|
|
||||||
height: 60px;
|
|
||||||
border-bottom: solid 1px rgb(239, 239, 245);
|
|
||||||
}
|
|
||||||
.navbuttom {
|
|
||||||
width: 100%;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
.svg-selected {
|
|
||||||
outline: 1px solid #0cf;
|
|
||||||
}
|
|
||||||
.warp::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,46 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
style="background-color:#000000;"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
:viewBox="`0 0 ${props.svgCanvas.width} ${props.svgCanvas.height}`"
|
|
||||||
>
|
|
||||||
<g
|
|
||||||
v-for="(item,index) in props.svg_data"
|
|
||||||
:key="item.id"
|
|
||||||
:id="item.id"
|
|
||||||
:transform="'translate(' + (item.svgPositionX) + ',' + (item.svgPositionY) + ')' + 'rotate(' + item.angle + ')' + 'scale(' + item.size + ')'"
|
|
||||||
>
|
|
||||||
<svg-dynamic
|
|
||||||
:component_type="item.type"
|
|
||||||
:component_template="props.component_infos.filter(f => f.type == item.type)[0].template"
|
|
||||||
:component_props="props.component_infos.filter(f => f.type == item.type)[0].props"
|
|
||||||
:component_attr="item"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import SvgDynamic from "./SvgDynamic.vue";
|
|
||||||
import { ISvgDataLists, ISvgCanvas, IComponentInfo } from "../Model";
|
|
||||||
import "../assets/css/svgAnimation/index.css";
|
|
||||||
const props = defineProps({
|
|
||||||
//组件的json格式
|
|
||||||
component_infos: {
|
|
||||||
type: Array as () => Array<IComponentInfo>,
|
|
||||||
required: true,
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
//要渲染的数据
|
|
||||||
svg_data: {
|
|
||||||
type: Array as () => Array<ISvgDataLists>,
|
|
||||||
required: true,
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
svgCanvas: {
|
|
||||||
type: Object as () => ISvgCanvas,
|
|
||||||
default: { width: 1520, height: 720 }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,68 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="padding: 10px;display: flex;">
|
|
||||||
<img title="基于vue3+ts+svg的web组件编辑器" class="logoimg" src="../assets/logo.png" />
|
|
||||||
<span class="logotext">vue-webtopo-svgeditor</span>
|
|
||||||
<div style="display: flex;width: 100%;flex-direction: row-reverse;">
|
|
||||||
<a href="https://www.cnblogs.com/Hero-/p/14784744.html" target="_blank" class="a-link">
|
|
||||||
<n-button text>帮助</n-button>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/yaolunmao/vue-webtopo-svgeditor" target="_blank" class="a-link">
|
|
||||||
<n-button text>Github</n-button>
|
|
||||||
</a>
|
|
||||||
<n-popover trigger="hover">
|
|
||||||
<template #trigger>
|
|
||||||
<n-button text class="button-class">v3.0</n-button>
|
|
||||||
</template>
|
|
||||||
<div>这个点了没什么用 不过会变色</div>
|
|
||||||
<div>说实话 这个版本号就是瞎写的 一点都不严谨</div>
|
|
||||||
</n-popover>
|
|
||||||
<n-button type="primary" class="button-class" @click="() => { emit('saveSvgInfo') }">保存</n-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { NButton, useMessage, NPopover } from "naive-ui";
|
|
||||||
const message = useMessage();
|
|
||||||
const emit = defineEmits(['saveSvgInfo']);
|
|
||||||
defineExpose({
|
|
||||||
/**
|
|
||||||
* @description: 保存svg结果 父组件调用用
|
|
||||||
* @param {*}type 0失败 1成功 2警告
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
saveSvgInfoRes(type: any) {
|
|
||||||
if (type?.code == 0) {
|
|
||||||
message.error(type?.msg);
|
|
||||||
}
|
|
||||||
else if (type?.code == 1) {
|
|
||||||
message.success(type?.msg);
|
|
||||||
}
|
|
||||||
else if (type?.code == 2) {
|
|
||||||
message.warning(type?.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
.logoimg {
|
|
||||||
height: 2rem;
|
|
||||||
min-width: 2rem;
|
|
||||||
margin-right: 0.1rem;
|
|
||||||
margin-top: 0.2rem;
|
|
||||||
}
|
|
||||||
.logotext {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-top: 0.7rem;
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
.a-link {
|
|
||||||
text-decoration: none;
|
|
||||||
margin-right: 1.5rem;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
.button-class {
|
|
||||||
margin-right: 1.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,8 +1,8 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
import { DefineComponent } from 'vue'
|
import type { DefineComponent } from 'vue';
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||||
const component: DefineComponent<{}, {}, any>
|
const component: DefineComponent<{}, {}, any>;
|
||||||
export default component
|
export default component;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
import SvgEditor from './components/SvgEditor.vue';
|
|
||||||
import SvgPrview from './components/SvgPrview.vue';
|
|
||||||
import ECharts from 'vue-echarts';
|
|
||||||
export { SvgEditor,SvgPrview,ECharts as VChart }
|
|
@ -1,105 +0,0 @@
|
|||||||
import { IComponentInfo, ISvgDataLists, ISvgCanvas, ILeftImgLists, IMouseInfo, ISelectSvg } from "../Model";
|
|
||||||
/**
|
|
||||||
* 组件上移
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function moveUp(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
svgLists[select_svg.index].svgPositionY -= 1;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件下移
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function moveDown(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
svgLists[select_svg.index].svgPositionY += 1;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件左移
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function moveLeft(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
svgLists[select_svg.index].svgPositionX -= 1;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件右移
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function moveRight(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
svgLists[select_svg.index].svgPositionX += 1;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件复制
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function hotkeyCopy(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
svgLists.push({
|
|
||||||
...(JSON.parse(JSON.stringify(svgLists[select_svg.index]))),
|
|
||||||
id: `${new Date().getTime()}`,
|
|
||||||
svgPositionX: svgLists[select_svg.index].svgPositionX + 10,
|
|
||||||
svgPositionY: svgLists[select_svg.index].svgPositionY + 10,
|
|
||||||
title: svgLists[select_svg.index].title + `-copy`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件删除
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function hotkeyDel(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
svgLists.splice(select_svg.index, 1);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件置于顶层
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function hotkeyPutOnTop(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
const temp = svgLists[select_svg.index];
|
|
||||||
svgLists.splice(select_svg.index, 1);
|
|
||||||
svgLists.push(temp);
|
|
||||||
select_svg.index = svgLists.length - 1;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件置于底层
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function hotkeyPutOnButtom(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
const temp = svgLists[select_svg.index];
|
|
||||||
svgLists.splice(select_svg.index, 1);
|
|
||||||
svgLists.unshift(temp);
|
|
||||||
select_svg.index = 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件上移一层
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function hotkeyPutOnUp(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
if (svgLists.length === 1 || select_svg.index === svgLists.length - 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const temp = svgLists[select_svg.index];
|
|
||||||
svgLists[select_svg.index] = svgLists[select_svg.index + 1];
|
|
||||||
svgLists[select_svg.index + 1] = temp;
|
|
||||||
select_svg.index += 1;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 组件下移一层
|
|
||||||
* @param svgLists
|
|
||||||
* @param select_svg
|
|
||||||
*/
|
|
||||||
export function hotkeyPutOnDown(svgLists: ISvgDataLists[], select_svg: ISelectSvg) {
|
|
||||||
if (svgLists.length === 1 || select_svg.index === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const temp = svgLists[select_svg.index];
|
|
||||||
svgLists[select_svg.index] = svgLists[select_svg.index - 1];
|
|
||||||
svgLists[select_svg.index - 1] = temp;
|
|
||||||
select_svg.index -= 1;
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue'
|
import App from './App.vue';
|
||||||
import ECharts from 'vue-echarts';
|
import router from './router/index';
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
app.component('v-chart', ECharts);
|
app.use(router);
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div>编辑页</div>
|
||||||
|
</template>
|
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div>预览页</div>
|
||||||
|
</template>
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|