feat: 导入导出组件树预览等功能完成

Re-1.0
咬轮猫 3 years ago
parent e02f7349ff
commit 8209dae48e

@ -10,12 +10,14 @@
"postinstall": "husky install"
},
"dependencies": {
"ace-builds": "^1.14.0",
"echarts": "^5.4.1",
"element-plus": "^2.2.9",
"pinia": "^2.0.16",
"vue": "^3.2.25",
"vue-echarts": "^6.5.1",
"vue-router": "4"
"vue-router": "4",
"vue3-ace-editor": "^2.2.2"
},
"devDependencies": {
"@commitlint/cli": "^17.0.3",

@ -6,6 +6,7 @@ specifiers:
'@typescript-eslint/eslint-plugin': ^5.30.5
'@typescript-eslint/parser': ^5.30.5
'@vitejs/plugin-vue': ^2.3.3
ace-builds: ^1.14.0
echarts: ^5.4.1
element-plus: ^2.2.9
eslint: ^8.19.0
@ -27,15 +28,18 @@ specifiers:
vue-eslint-parser: ^9.0.3
vue-router: '4'
vue-tsc: ^0.34.7
vue3-ace-editor: ^2.2.2
windicss: ^3.5.6
dependencies:
ace-builds: 1.14.0
echarts: 5.4.1
element-plus: 2.2.9_vue@3.2.37
pinia: 2.0.17_j6bzmzd4ujpabbp5objtwxyjp4
vue: 3.2.37
vue-echarts: 6.5.1_echarts@5.4.1+vue@3.2.37
vue-router: 4.1.1_vue@3.2.37
vue3-ace-editor: 2.2.2
devDependencies:
'@commitlint/cli': 17.0.3
@ -770,6 +774,10 @@ packages:
through: 2.3.8
dev: true
/ace-builds/1.14.0:
resolution: {integrity: sha512-3q8LvawomApRCt4cC0OzxVjDsZ609lDbm8l0Xl9uqG06dKEq4RT0YXLUyk7J2SxmqIp5YXzZNw767Dr8GKUruw==}
dev: false
/acorn-jsx/5.3.2_acorn@8.7.1:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@ -3219,6 +3227,10 @@ packages:
resolution: {integrity: sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==}
dev: false
/resize-observer-polyfill/1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
dev: false
/resolve-from/4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@ -3933,6 +3945,14 @@ packages:
'@vue/server-renderer': 3.2.37_vue@3.2.37
'@vue/shared': 3.2.37
/vue3-ace-editor/2.2.2:
resolution: {integrity: sha512-fZ6OWosbU+odLrtrcGC/536QjCigujYJB0Hf6/tBp+ef/ohTadwQAqyBlVzOmvrmzZyubphpV9zkaZcx5Fuivw==}
dependencies:
ace-builds: 1.14.0
resize-observer-polyfill: 1.5.1
vue: 3.2.37
dev: false
/which/2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}

@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 48 48" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M9 10V44H39V10H9Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"/>
<path d="M20 20V33" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M28 20V33" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 10H44" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 10L19.289 4H28.7771L32 10H16Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"/>
<g stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 10V44H39V10H9Z" fill="none" />
<path d="M20 20V33" />
<path d="M28 20V33" />
<path d="M4 10H44" />
<path d="M16 10L19.289 4H28.7771L32 10H16Z" fill="none" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 674 B

After

Width:  |  Height:  |  Size: 451 B

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.9998 8L6 14L12.9998 21" stroke="#333" stroke-width="4" stroke-linecap="round"
stroke-linejoin="round" />
<path
d="M6 14H28.9938C35.8768 14 41.7221 19.6204 41.9904 26.5C42.2739 33.7696 36.2671 40 28.9938 40H11.9984"
stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 482 B

@ -0,0 +1,242 @@
<!-- eslint-disable prettier/prettier -->
<template>
<div
class="canvas"
@mousedown="onCanvasMouseDown"
@mousemove="onCanvasMouseMove"
@mouseup="onCanvasMouseUp"
>
<svg
xmlns="http://www.w3.org/2000/svg"
:style="{ backgroundColor: preview_data.config.background_color }"
width="100%"
height="100%"
>
<g
:transform="`translate(${
preview_data.config.position_center.x + preview_data.layout_center.x
},${
preview_data.config.position_center.y + preview_data.layout_center.y
})rotate(${0})scale(${preview_data.config.scale})`"
>
<g
v-for="item in preview_data.done_json"
:key="item.id"
:transform="`translate(${item.x},${item.y})rotate(0)scale(1)`"
v-show="item.display"
>
<g
:transform="`translate(${item.actual_bound.x + item.actual_bound.width / 2},${
item.actual_bound.y + item.actual_bound.height / 2
})rotate(${item.rotate}) scale(1) translate(${-(
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
>
<connection-line
v-if="item.type === EDoneJsonType.ConnectionLine"
:item-info="item"
></connection-line>
<use
v-else-if="item.type === EDoneJsonType.File"
:xlink:href="`#svg-${item.name}`"
v-bind="prosToVBind(item)"
width="100"
height="100"
:id="item.id"
:transform="`translate(${item.actual_bound.x + item.actual_bound.width / 2},${
item.actual_bound.y + item.actual_bound.height / 2
}) scale(${item.scale_x},${item.scale_y}) translate(${-(
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
></use>
<component
v-else-if="item.type === EDoneJsonType.CustomSvg"
:is="item.tag"
v-bind="prosToVBind(item)"
width="100"
height="100"
:id="item.id"
:transform="`translate(${item.actual_bound.x + item.actual_bound.width / 2},${
item.actual_bound.y + item.actual_bound.height / 2
}) scale(${item.scale_x},${item.scale_y}) translate(${-(
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
></component>
<foreignObject
v-else-if="item.type === EDoneJsonType.Vue"
v-bind="getActualBoundScale(item.actual_bound, item.scale_x, item.scale_y)"
:id="`foreign-object${item.id}`"
>
<component
:is="item.tag"
v-bind="prosToVBind(item)"
:id="item.id"
:transform="`translate(${item.actual_bound.x + item.actual_bound.width / 2},${
item.actual_bound.y + item.actual_bound.height / 2
}) scale(${item.scale_x},${item.scale_y}) translate(${-(
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
>{{ item.tag_slot }}</component
>
</foreignObject>
</g>
</g>
</g>
</svg>
</div>
</template>
<script setup lang="ts">
import { getCurrentInstance, PropType, reactive } from 'vue';
import { useGlobalStore } from '@/store/global';
import { EGlobalStoreIntention, EMouseInfoState } from '@/store/global/types';
import { prosToVBind } from '@/utils';
import { EDoneJsonType } from '@/config-center/types';
import ConnectionLine from '@/components/webtopo-svgedit/components/connection-line/index.vue';
import { ComponentImport } from '@/config-center';
import { IDataModel } from '../webtopo-svgedit/types';
import 'element-plus/dist/index.css';
// import HandlePanel from '../handle-panel/index.vue';
//
const instance = getCurrentInstance();
Object.keys(ComponentImport).forEach((key) => {
if (!Object.keys(instance?.appContext?.components as any).includes(key)) {
instance?.appContext.app.component(key, ComponentImport[key]);
}
});
const props = defineProps({
dataModel: {
type: [Object, null] as PropType<IDataModel | null>,
default: null
},
canvasDrag: {
type: Boolean,
default: true
}
});
const preview_data = reactive(
props.dataModel ?? {
layout_center: {
x: 0,
y: 0
},
config: {
background_color: '#fff',
scale: 1,
position_center: {
x: -295,
y: -95
},
svg_position_center: {
x: 50,
y: 50
}
},
done_json: []
}
);
const globalStore = useGlobalStore();
const onCanvasMouseMove = (e: MouseEvent) => {
// 线
if (
globalStore.mouse_info.state != EMouseInfoState.Down &&
globalStore.intention !== EGlobalStoreIntention.Connection
) {
return;
}
if (!props.canvasDrag) {
console.log(props.canvasDrag);
return;
}
const { clientX, clientY } = e;
globalStore.mouse_info.new_position_x =
globalStore.mouse_info.now_position_x + clientX - globalStore.mouse_info.position_x;
globalStore.mouse_info.new_position_y =
globalStore.mouse_info.now_position_y + clientY - globalStore.mouse_info.position_y;
if (globalStore.intention == EGlobalStoreIntention.MoveCanvas) {
//
preview_data.layout_center.x = globalStore.mouse_info.new_position_x;
preview_data.layout_center.y = globalStore.mouse_info.new_position_y;
}
};
const onCanvasMouseUp = () => {
//
if (globalStore.mouse_info.state != EMouseInfoState.Down) {
return;
}
if (globalStore.intention != EGlobalStoreIntention.Select) {
globalStore.intention = EGlobalStoreIntention.None;
}
globalStore.setMouseInfo({
state: EMouseInfoState.Up,
position_x: 0,
position_y: 0,
now_position_x: 0,
now_position_y: 0,
new_position_x: 0,
new_position_y: 0
});
};
const onCanvasMouseDown = (e: MouseEvent) => {
console.log('onCanvasMouseDown', e);
const { clientX, clientY } = e;
//
globalStore.intention = EGlobalStoreIntention.MoveCanvas;
globalStore.setMouseInfo({
state: EMouseInfoState.Down,
position_x: clientX,
position_y: clientY,
now_position_x: preview_data.layout_center.x,
now_position_y: preview_data.layout_center.y,
new_position_x: preview_data.layout_center.x,
new_position_y: preview_data.layout_center.y
});
};
const getActualBoundScale = (
actual_bound: {
x: number;
y: number;
width: number;
height: number;
},
scale_x: number,
scale_y: number
) => {
return {
x: actual_bound.x - (actual_bound.width / 2) * scale_x + actual_bound.width / 2,
y: actual_bound.y - (actual_bound.height / 2) * scale_y + actual_bound.height / 2,
width: actual_bound.width * scale_x,
height: actual_bound.height * scale_y
};
};
</script>
<style lang="less" scoped>
.canvas {
width: 100%;
height: 100vh;
}
.svg-item-none {
cursor: move;
&:hover {
outline: 1px solid #0cf;
}
}
.svg-item-move {
cursor: move;
outline: 1px dashed rgb(23, 222, 30);
}
.svg-item-select {
cursor: move;
outline: 1px solid rgb(23, 222, 30);
}
</style>

@ -571,7 +571,7 @@
globalStore.mouse_info.new_position_y;
// globalStore.setDoneJson(globalStore.done_json);
setSvgActualInfo(globalStore.done_json[globalStore.handle_svg_info.index]);
globalStore.intention = EGlobalStoreIntention.Select;
globalStore.intention = EGlobalStoreIntention.None;
//
globalStore.setDoneJson(globalStore.done_json);
// globalStore.setHandleSvgInfo(undefined, 0);

@ -0,0 +1,35 @@
<template>
<el-tree
:data="data"
:props="defaultProps"
@node-click="handleNodeClick"
:default-expand-all="true"
:expand-on-click-node="false"
:highlight-current="true"
node-key="id"
:current-node-key="current_node_key"
/>
</template>
<script lang="ts" setup>
import { useGlobalStore } from '@/store/global';
import { EGlobalStoreIntention, IDoneJson } from '@/store/global/types';
import { ElTree } from 'element-plus';
import { onMounted, ref } from 'vue';
const global_store = useGlobalStore();
const data = ref<IDoneJson[]>([]);
const current_node_key = ref(global_store.handle_svg_info?.info.id);
const handleNodeClick = (data: IDoneJson) => {
global_store.intention = EGlobalStoreIntention.Select;
global_store.setHandleSvgInfo(data);
};
onMounted(() => {
data.value = global_store.done_json;
});
const defaultProps = {
children: 'children',
label: 'title'
};
</script>

@ -10,11 +10,13 @@
? props.itemInfo.animations.color.val
: props.itemInfo.props.stroke.val
"
stroke-width="2"
:stroke-width="props.itemInfo.props['stroke-width'].val"
style="cursor: move"
stroke-dashoffset="0"
:stroke-dasharray="
props.itemInfo.animations?.type.val === EConfigAnimationsType.Electricity ? 6 : 0
props.itemInfo.animations?.type.val === EConfigAnimationsType.Electricity
? props.itemInfo.props['stroke-width'].val * 3
: 0
"
>
<animate
@ -33,8 +35,8 @@
fill="none"
fill-opacity="0"
:stroke="props.itemInfo.animations.color.val"
stroke-width="2"
stroke-dasharray="6"
:stroke-width="props.itemInfo.props['stroke-width'].val"
:stroke-dasharray="props.itemInfo.props['stroke-width'].val * 3"
stroke-dashoffset="0"
stroke-linecap="round"
>
@ -52,7 +54,7 @@
v-else-if="props.itemInfo.animations?.type.val === EConfigAnimationsType.Track"
cx="0"
cy="0"
r="5"
:r="props.itemInfo.props['stroke-width'].val * 2"
:fill="props.itemInfo.animations.color.val"
>
<animateMotion
@ -75,7 +77,7 @@
:key="index"
:cx="item.x"
:cy="item.y"
r="4"
r="6"
stroke-width="1"
:stroke="props.itemInfo.props.stroke.val"
fill="#fff"

@ -1,5 +1,5 @@
<template>
<v-chart ref="pieChartRef" class="chart" :option="option" autoresize />
<v-chart class="chart" :option="option" autoresize />
</template>
<script lang="ts" setup>
@ -8,14 +8,29 @@
import { PieChart } from 'echarts/charts';
import { TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components';
import VChart, { THEME_KEY } from 'vue-echarts';
import { ref, provide } from 'vue';
import { watch, provide, reactive } from 'vue';
use([SVGRenderer, PieChart, TitleComponent, TooltipComponent, LegendComponent]);
provide(THEME_KEY, 'dark');
const option = ref({
const props = defineProps({
title: {
text: 'Traffic Sources',
type: String,
default: '标题'
},
seriesName: {
type: String,
default: '详情'
},
seriesData: {
type: Array,
default: () => []
}
});
const option = reactive({
title: {
text: props.title,
left: 'center'
},
tooltip: {
@ -24,22 +39,15 @@
},
legend: {
orient: 'vertical',
left: 'left',
data: ['Direct', 'Email', 'Ad Networks', 'Video Ads', 'Search Engines']
left: 'left'
},
series: [
{
name: 'Traffic Sources',
name: props.seriesName,
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: [
{ value: 335, name: 'Direct' },
{ value: 310, name: 'Email' },
{ value: 234, name: 'Ad Networks' },
{ value: 135, name: 'Video Ads' },
{ value: 1548, name: 'Search Engines' }
],
data: props.seriesData,
emphasis: {
itemStyle: {
shadowBlur: 10,
@ -50,7 +58,11 @@
}
]
});
const pieChartRef = ref();
watch(props, (new_val) => {
option.title.text = new_val.title;
option.series[0].name = new_val.seriesName;
option.series[0].data = new_val.seriesData;
});
</script>
<style scoped>

@ -0,0 +1,36 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { VAceEditor } from 'vue3-ace-editor';
import '@/config-center/ace-edit';
import { useGlobalStore } from '@/store/global';
import { useSvgEditLayoutStore } from '@/store/svgedit-layout';
import { useConfigStore } from '@/store/config';
import { IDataModel } from '../../types';
const content = ref('');
const globalStore = useGlobalStore();
const svgEditLayoutStore = useSvgEditLayoutStore();
const configStore = useConfigStore();
onMounted(() => {
const export_json: IDataModel = {
layout_center: svgEditLayoutStore.center_offset,
config: configStore.svg,
done_json: globalStore.done_json
};
content.value = JSON.stringify(export_json, null, 2);
});
</script>
<template>
<v-ace-editor
v-model:value="content"
lang="json"
theme="monokai"
style="height: 400px"
:options="{
useWorker: true,
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true
}"
/>
</template>

@ -0,0 +1,50 @@
<script setup lang="ts">
import { ref } from 'vue';
import { VAceEditor } from 'vue3-ace-editor';
import '@/config-center/ace-edit';
import { useGlobalStore } from '@/store/global';
import { useSvgEditLayoutStore } from '@/store/svgedit-layout';
import { useConfigStore } from '@/store/config';
import { IDataModel } from '../../types';
import { ElMessage } from 'element-plus';
const content = ref<string>('');
const globalStore = useGlobalStore();
const svgEditLayoutStore = useSvgEditLayoutStore();
const configStore = useConfigStore();
const onImportJson = () => {
try {
const json: IDataModel = JSON.parse(content.value);
console.log(json, json.layout_center, configStore, 15);
if (!json.config || !json.layout_center || !json.done_json) {
ElMessage.error('请导入正确的数据模型!');
return;
}
configStore.svg = json.config;
svgEditLayoutStore.center_offset = json.layout_center;
globalStore.setDoneJson(json.done_json);
ElMessage.success('导入成功');
} catch (error) {
ElMessage.error('请导入正确的数据模型!');
console.error(error);
return;
}
};
defineExpose({
onImportJson
});
</script>
<template>
<v-ace-editor
v-model:value="content"
lang="json"
theme="monokai"
style="height: 400px"
:options="{
useWorker: true,
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true
}"
/>
</template>

@ -39,6 +39,12 @@
v-model="attr_item.val"
:disabled="attr_item?.disabled"
></el-switch>
<json-edit
v-else-if="attr_item.type === EConfigItemPropsType.JsonEdit"
:content-obj="attr_item.val"
:disabled="attr_item?.disabled"
@update-attr-item-val="(val) => updateAttrItemVal(attr_item, val)"
></json-edit>
</el-form-item>
</template>
<script setup lang="ts">
@ -53,10 +59,23 @@
ElSwitch
} from 'element-plus';
import { PropType } from 'vue';
import JsonEdit from '@/components/webtopo-svgedit/components/right-panel/json-edit.vue';
const props = defineProps({
objInfo: {
type: Object as PropType<IConfigItemProps>,
default: () => {}
}
});
const updateAttrItemVal = (
attr_item: {
title: string;
type: EConfigItemPropsType;
val: any;
options?: any;
disabled?: boolean | undefined;
},
val: any
) => {
attr_item.val = val;
};
</script>

@ -0,0 +1,44 @@
<template>
<div>
<el-button type="primary" plain round @click="dialogVisible = true">点击编辑</el-button>
<el-dialog v-model="dialogVisible" title="配置编辑" width="60%">
<v-ace-editor
v-model:value="content"
lang="json"
theme="monokai"
style="height: 400px"
:options="{
useWorker: true,
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true
}"
/>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="onYesBtnClick"></el-button>
<el-button type="primary" @click="dialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { VAceEditor } from 'vue3-ace-editor';
import '@/config-center/ace-edit';
import { ElButton, ElDialog } from 'element-plus';
import { ref } from 'vue';
const props = defineProps({
contentObj: {
type: Object,
default: () => {}
}
});
const dialogVisible = ref(false);
const emits = defineEmits(['updateAttrItemVal']);
const content = ref(JSON.stringify(props.contentObj, null, 2));
const onYesBtnClick = () => {
emits('updateAttrItemVal', JSON.parse(content.value));
dialogVisible.value = false;
};
</script>

@ -41,14 +41,29 @@
<svg-analysis name="delete"></svg-analysis>
</el-icon>
<el-divider direction="vertical"></el-divider>
<el-icon title="组件树" class="icon-normal" :size="20">
<el-icon
title="组件树"
class="icon-normal"
:size="20"
@click="emits('changeVisible', EVisibleConfKey.ComponentTree, true)"
>
<svg-analysis name="tree-list"></svg-analysis>
</el-icon>
<el-divider direction="vertical"></el-divider>
<el-icon title="导入json" class="icon-normal" :size="20">
<el-icon
title="导入数据模型"
class="icon-normal"
:size="20"
@click="emits('changeVisible', EVisibleConfKey.ImportJson, true)"
>
<svg-analysis name="import-json"></svg-analysis>
</el-icon>
<el-icon title="导出json" :size="20" class="icon-normal ml-5px">
<el-icon
title="导出数据模型"
:size="20"
class="icon-normal ml-5px"
@click="emits('changeVisible', EVisibleConfKey.ExportJson, true)"
>
<svg-analysis name="export-json"></svg-analysis>
</el-icon>
<!-- <el-divider direction="vertical"></el-divider>
@ -65,7 +80,11 @@
</el-icon> -->
</div>
<div class="flex items-center mr-20px">
<el-icon title="预览" class="icon-normal" :size="20">
<el-icon title="返回" class="icon-normal" :size="20" @click="emits('onReturn')">
<svg-analysis name="return"></svg-analysis>
</el-icon>
<el-divider direction="vertical"></el-divider>
<el-icon title="预览" class="icon-normal" :size="20" @click="onPreviewClick">
<svg-analysis name="preview"></svg-analysis>
</el-icon>
</div>
@ -83,17 +102,34 @@
</div>
</template>
<script setup lang="ts">
import { useConfigStore } from '@/store/config';
import { useGlobalStore } from '@/store/global';
import { useEditPrivateStore } from '@/store/system';
import { ElIcon, ElDivider } from 'element-plus';
import { useRouter } from 'vue-router';
import SvgAnalysis from '../../../../components/svg-analysis/index.vue';
import { useSvgEditLayoutStore } from '../../../../store/svgedit-layout';
import { EVisibleConfKey, IDataModel } from '../../types';
const router = useRouter();
const svgEditLayoutStore = useSvgEditLayoutStore();
const globalStore = useGlobalStore();
const editPrivateStore = useEditPrivateStore();
const configStore = useConfigStore();
const emits = defineEmits(['changeVisible', 'onReturn']);
const onDeleteBtnClick = () => {
globalStore.done_json.length <= 0 || globalStore.setDoneJson([]);
};
const onPreviewClick = () => {
const data_model: IDataModel = {
layout_center: svgEditLayoutStore.center_offset,
config: configStore.svg,
done_json: globalStore.done_json
};
router.push({
name: 'preview',
params: { data_model: JSON.stringify(data_model) }
});
};
</script>
<style scoped lang="less">
.logoimg {

@ -2,7 +2,7 @@
<div>
<el-container>
<el-header class="top-el-header">
<top-panel></top-panel>
<top-panel @change-visible="changeVisible" @on-return="emits('onReturn')"></top-panel>
</el-header>
<el-container class="middle">
<el-aside class="side-nav" :class="svgEditLayoutStore.left_nav ? 'show-nav' : 'hide-nav'">
@ -25,10 +25,46 @@
<bottom-panel></bottom-panel>
</el-footer>
</el-container>
<el-dialog v-model="visible_conf.ImportJson" title="导入" width="60%" destroy-on-close>
<import-json ref="importJsonRef"></import-json>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="onImportJson"></el-button>
<el-button type="primary" @click="visible_conf.ImportJson = false">关闭</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="visible_conf.ExportJson" title="导出" width="60%" destroy-on-close>
<export-json></export-json>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="visible_conf.ExportJson = false">关闭</el-button>
</span>
</template>
</el-dialog>
<el-drawer
v-if="visible_conf.ComponentTree"
v-model="visible_conf.ComponentTree"
title="组件树"
direction="ltr"
size="30%"
>
<component-tree></component-tree>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { ElContainer, ElHeader, ElAside, ElFooter, ElMain, ElScrollbar } from 'element-plus';
import {
ElContainer,
ElHeader,
ElAside,
ElFooter,
ElMain,
ElScrollbar,
ElDialog,
ElButton,
ElDrawer
} from 'element-plus';
import 'element-plus/dist/index.css';
import TopPanel from './components/top-panel/index.vue';
import LeftPanel from './components/left-panel/index.vue';
@ -36,7 +72,27 @@
import RightPanel from './components/right-panel/index.vue';
import BottomPanel from './components/bottom-panel/index.vue';
import { useSvgEditLayoutStore } from '../../store/svgedit-layout';
import { reactive, ref } from 'vue';
import ExportJson from '@/components/webtopo-svgedit/components/export-json/index.vue';
import ImportJson from '@/components/webtopo-svgedit/components/import-json/index.vue';
import ComponentTree from '@/components/webtopo-svgedit/components/component-tree/index.vue';
import { IVisibleConf, EVisibleConfKey } from './types';
const svgEditLayoutStore = useSvgEditLayoutStore();
const importJsonRef = ref<InstanceType<typeof ImportJson>>();
const visible_conf: IVisibleConf = reactive({
[EVisibleConfKey.ExportJson]: false,
[EVisibleConfKey.ImportJson]: false,
[EVisibleConfKey.ComponentTree]: false
});
const emits = defineEmits(['onReturn']);
const changeVisible = (key: EVisibleConfKey, val: boolean) => {
visible_conf[key] = val;
};
const onImportJson = () => {
importJsonRef.value?.onImportJson();
changeVisible(EVisibleConfKey.ImportJson, false);
};
</script>
<style scoped lang="less">
@headerHeight: 60px;

@ -0,0 +1,19 @@
import { IPositionCenterSvg } from '@/store/config/types';
import { IDoneJson } from '@/store/global/types';
export type IVisibleConf = {
[key in EVisibleConfKey]: boolean;
};
export enum EVisibleConfKey {
ImportJson = 'ImportJson',
ExportJson = 'ExportJson',
ComponentTree = 'ComponentTree'
}
export interface IDataModel {
layout_center: {
x: number;
y: number;
};
config: IPositionCenterSvg;
done_json: IDoneJson[];
}

@ -0,0 +1,24 @@
import ace from 'ace-builds';
import themeMonokaiUrl from 'ace-builds/src-noconflict/theme-monokai?url';
ace.config.setModuleUrl('ace/theme/monokai', themeMonokaiUrl);
import workerBaseUrl from 'ace-builds/src-noconflict/worker-base?url';
ace.config.setModuleUrl('ace/mode/base', workerBaseUrl);
import modeJsonUrl from 'ace-builds/src-noconflict/mode-json?url';
ace.config.setModuleUrl('ace/mode/json', modeJsonUrl);
import workerJsonUrl from 'ace-builds/src-noconflict/worker-json?url';
ace.config.setModuleUrl('ace/mode/json_worker', workerJsonUrl);
import snippetsJsonUrl from 'ace-builds/src-noconflict/snippets/json?url';
ace.config.setModuleUrl('ace/snippets/json', snippetsJsonUrl);
import modeJavascriptUrl from 'ace-builds/src-noconflict/mode-javascript?url';
ace.config.setModuleUrl('ace/mode/javascript', modeJavascriptUrl);
import workerJavascriptUrl from 'ace-builds/src-noconflict/worker-javascript?url';
ace.config.setModuleUrl('ace/mode/javascript_worker', workerJavascriptUrl);
import snippetsJavascriptUrl from 'ace-builds/src-noconflict/snippets/javascript?url';
ace.config.setModuleUrl('ace/snippets/javascript', snippetsJavascriptUrl);
import 'ace-builds/src-noconflict/ext-language_tools';
ace.require('ace/ext/language_tools');

@ -59,6 +59,11 @@ export const connection_line_system: ISystemStraightLine = Object.freeze({
type: EConfigItemPropsType.Color,
val: '#ff0000'
},
'stroke-width': {
title: '线条宽度',
type: EConfigItemPropsType.InputNumber,
val: 2
},
point_position: {
title: '点坐标',
type: EConfigItemPropsType.JsonEdit,

@ -1,4 +1,4 @@
import { EDoneJsonType, IConfigItem } from '../../../types';
import { EConfigItemPropsType, EDoneJsonType, IConfigItem } from '../../../types';
export const pie_charts: IConfigItem = {
name: 'pie-charts',
@ -11,5 +11,42 @@ export const pie_charts: IConfigItem = {
actual_rect: true
},
display: true,
props: {}
props: {
title: {
title: '标题',
type: EConfigItemPropsType.Input,
val: '默认标题'
},
seriesName: {
title: '详情',
type: EConfigItemPropsType.Input,
val: '详情标题'
},
seriesData: {
title: 'legendData',
type: EConfigItemPropsType.JsonEdit,
val: [
{
value: 1048,
name: '办公楼A'
},
{
value: 735,
name: '办公楼B'
},
{
value: 580,
name: '保安室'
},
{
value: 484,
name: '地下车库'
},
{
value: 300,
name: '食堂'
}
]
}
}
};

@ -1,14 +1,15 @@
export interface IPositionCenter {
svg: {
background_color: string;
scale: number;
position_center: {
x: number;
y: number;
};
svg_position_center: {
x: number;
y: number;
};
svg: IPositionCenterSvg;
}
export interface IPositionCenterSvg {
background_color: string;
scale: number;
position_center: {
x: number;
y: number;
};
svg_position_center: {
x: number;
y: number;
};
}

@ -1,6 +1,9 @@
<template>
<webtopo-svgedit></webtopo-svgedit>
<webtopo-svgedit @on-return="onReturn"></webtopo-svgedit>
</template>
<script setup lang="ts">
import WebtopoSvgedit from '../../components/webtopo-svgedit/index.vue';
const onReturn = () => {
console.log('点击了返回按钮');
};
</script>

@ -1,3 +1,15 @@
<template>
<div>预览页</div>
<webtopo-svg-preview :data-model="data_model" :canvas-drag="true"></webtopo-svg-preview>
</template>
<script setup lang="ts">
import WebtopoSvgPreview from '@/components/webtopo-svg-preview/index.vue';
import { IDataModel } from '@/components/webtopo-svgedit/types';
import { ref } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const data_model = ref<IDataModel | null>(null);
if (route.params.data_model) {
data_model.value = JSON.parse(route.params.data_model as string);
}
</script>

Loading…
Cancel
Save