Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front
This commit is contained in:
4
src/assets/icons/dc/hide.svg
Normal file
4
src/assets/icons/dc/hide.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.52856 1.52876C1.78891 1.26841 2.21102 1.26841 2.47137 1.52876L14.4714 13.5288C14.7317 13.7891 14.7317 14.2112 14.4714 14.4716C14.211 14.7319 13.7889 14.7319 13.5286 14.4716L11.8523 12.7953C10.8026 13.4938 9.51597 14.0002 7.99997 14.0002C5.89718 14.0002 4.24037 13.0275 3.03903 11.9196C1.84104 10.8148 1.0565 9.5415 0.674858 8.83761C0.390009 8.31223 0.390478 7.68717 0.675086 7.1623C1.06023 6.45201 1.86133 5.15114 3.09055 4.03355L1.52856 2.47157C1.26822 2.21122 1.26822 1.78911 1.52856 1.52876ZM4.03457 4.97757C2.93812 5.95924 2.20475 7.13846 1.8472 7.79786C1.77728 7.92679 1.77735 8.07364 1.84699 8.20209C2.19241 8.83919 2.89458 9.97267 3.94294 10.9395C4.98794 11.9032 6.33712 12.6668 7.99997 12.6668C9.09957 12.6668 10.0599 12.3336 10.8868 11.8298L9.59732 10.5403C8.43827 11.2706 6.8882 11.131 5.87864 10.1215C4.86909 9.11193 4.72948 7.56186 5.45981 6.40281L4.03457 4.97757ZM6.44811 7.39112L8.60901 9.55201C8.01093 9.78639 7.30472 9.66194 6.82145 9.17867C6.33819 8.69541 6.21374 7.9892 6.44811 7.39112Z" fill="#0D0D0D"/>
|
||||
<path d="M6.81556 3.46675C7.18897 3.38117 7.58347 3.3335 7.99997 3.3335C9.66282 3.3335 11.012 4.09716 12.057 5.06087C13.1054 6.02766 13.8075 7.16114 14.153 7.79823C14.2223 7.92613 14.2225 8.07385 14.1523 8.2033C13.9632 8.55189 13.664 9.05544 13.2557 9.60051C13.035 9.89521 13.095 10.313 13.3897 10.5338C13.6844 10.7545 14.1022 10.6945 14.3229 10.3998C14.7784 9.79162 15.1118 9.23078 15.3243 8.83897C15.6087 8.31464 15.6102 7.68865 15.3251 7.16272C14.9434 6.45883 14.1589 5.18548 12.9609 4.0807C11.7596 2.97282 10.1028 2.00016 7.99997 2.00016C7.48067 2.00016 6.98628 2.05972 6.51772 2.16711C6.15883 2.24935 5.93457 2.60696 6.01682 2.96585C6.09907 3.32473 6.45668 3.54899 6.81556 3.46675Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,28 +1,64 @@
|
||||
<template>
|
||||
<div class="layer-item">
|
||||
<div class="layer-item" @click="onClickLayer">
|
||||
<div class="drag"><svg-icon name="dc-drag" size="18" /></div>
|
||||
<div class="thumb"></div>
|
||||
<div class="name">
|
||||
<div @dblclick="editName = true" v-if="!editName">{{ layer.name }}</div>
|
||||
<input type="text" v-model="layer.name" v-else @blur="editName = false" />
|
||||
<div @dblclick="onClickEditName" v-if="!editName">
|
||||
{{ layer.info.name || '未命名图层' }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
type="text"
|
||||
ref="nameInputRef"
|
||||
:value="layer.info.name"
|
||||
@blur="onChangeName"
|
||||
@keyup.enter="onChangeName"
|
||||
/>
|
||||
</div>
|
||||
<div class="icons">
|
||||
<span><svg-icon name="dc-show" size="15" /></span>
|
||||
<span><svg-icon name="dc-delete" size="13" /></span>
|
||||
<span @click="onClickShowHide"
|
||||
><svg-icon :name="layer.visible ? 'dc-show' : 'dc-hide'" size="15"
|
||||
/></span>
|
||||
<span @click="onClickDelete"><svg-icon name="dc-delete" size="13" /></span>
|
||||
<span><svg-icon name="dc-down_arrow" size="11" /></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import { ref, inject, nextTick } from 'vue'
|
||||
const layerManager = inject('layerManager') as any
|
||||
const editName = ref(false)
|
||||
const props = defineProps({
|
||||
layer: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const editName = ref(false)
|
||||
const nameInputRef = ref(null)
|
||||
const onClickEditName = () => {
|
||||
editName.value = true
|
||||
nextTick(() => {
|
||||
nameInputRef.value.focus()
|
||||
})
|
||||
}
|
||||
const onChangeName = (e) => {
|
||||
const value = e.target.value
|
||||
editName.value = false
|
||||
const name = props.layer.info.name
|
||||
if (name !== value) {
|
||||
layerManager.setLayerNameByID(props.layer.info.id, value)
|
||||
}
|
||||
}
|
||||
const onClickShowHide = () => {
|
||||
layerManager.setLayerVisibleByID(props.layer.info.id, !props.layer.visible)
|
||||
}
|
||||
const onClickDelete = () => {
|
||||
layerManager.deleteLayerByID(props.layer.info.id)
|
||||
}
|
||||
const onClickLayer = () => {
|
||||
layerManager.setActiveID(props.layer.info.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -39,11 +75,11 @@
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:not([draging='true']) {
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: #ededed;
|
||||
}
|
||||
&:not([draging='true']):hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
&.active {
|
||||
background-color: #ededed !important;
|
||||
}
|
||||
&.drag {
|
||||
opacity: 0;
|
||||
@@ -51,7 +87,7 @@
|
||||
&.ghost,
|
||||
&.chosen {
|
||||
box-shadow: inset 0 0 5px #aaa;
|
||||
background-color: #ededed;
|
||||
background-color: #ededed !important;
|
||||
}
|
||||
> .drag {
|
||||
padding: 0.3rem;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<div class="content">
|
||||
<VueDraggable
|
||||
:model-value="sortableRootLayers"
|
||||
:model-value="list"
|
||||
@start="handleDragStart"
|
||||
@end="handleDragEnd"
|
||||
class="sortable-layers"
|
||||
@@ -36,10 +36,11 @@
|
||||
:scroll-speed="10"
|
||||
>
|
||||
<layer-item
|
||||
v-for="layer in sortableRootLayers"
|
||||
:key="layer.id"
|
||||
v-for="layer in list"
|
||||
:key="layer.info.id"
|
||||
:layer="layer"
|
||||
:draging="draging"
|
||||
:class="{ active: layer.info.id === layerManager.activeID.value }"
|
||||
/>
|
||||
</VueDraggable>
|
||||
</div>
|
||||
@@ -51,20 +52,16 @@
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import layerItem from './layer-item.vue'
|
||||
const draging = ref(false)
|
||||
const sortableRootLayers = ref([
|
||||
{ id: 1, name: 'layer1' },
|
||||
{ id: 2, name: 'layer2' },
|
||||
{ id: 3, name: 'layer3' }
|
||||
])
|
||||
|
||||
const layerManager = inject('layerManager') as any
|
||||
const list = computed(() => layerManager.layers.value)
|
||||
const handleDragStart = () => {
|
||||
draging.value = true
|
||||
}
|
||||
const handleDragEnd = (event) => {
|
||||
draging.value = false
|
||||
const { from, to, oldIndex, newIndex, item } = event
|
||||
console.log('🔄 拖拽结束事件:', event)
|
||||
sortableRootLayers.value.splice(newIndex, 0, sortableRootLayers.value.splice(oldIndex, 1)[0])
|
||||
const { from, to, oldIndex, newIndex, data } = event
|
||||
console.log('oldIndex', data)
|
||||
layerManager.dragSort(data.info.id, newIndex)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -107,6 +104,7 @@
|
||||
> .content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 20rem;
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<layer-panel />
|
||||
<details-panel />
|
||||
<header-tools />
|
||||
<header-tools @export="exportCanvas" />
|
||||
<zoom
|
||||
:zoom="canvasManager.currentZoom.value / 100"
|
||||
:step="0.1"
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
// 管理器
|
||||
import { StateManager } from './manager/StateManager'
|
||||
import { LayerManager } from './manager/LayerManager'
|
||||
import { EventManager } from './manager/EventManager'
|
||||
import { CanvasManager } from './manager/CanvasManager'
|
||||
import { ToolManager } from './manager/ToolManager'
|
||||
@@ -50,6 +51,11 @@
|
||||
stateManager.setManager({ canvasManager, canvasRef })
|
||||
provide('canvasManager', canvasManager)
|
||||
|
||||
// 图层管理器
|
||||
const layerManager = new LayerManager({ stateManager, canvasManager })
|
||||
stateManager.setManager({ layerManager })
|
||||
provide('layerManager', layerManager)
|
||||
|
||||
// 事件管理器
|
||||
const eventManager = new EventManager({ stateManager })
|
||||
stateManager.setManager({ eventManager })
|
||||
@@ -90,6 +96,9 @@
|
||||
})
|
||||
canvasManager.resetZoom()
|
||||
}
|
||||
const exportCanvas = () => {
|
||||
console.log(canvasManager.getBitObjects())
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import '@vue-flow/core/dist/style.css';
|
||||
|
||||
@@ -13,7 +13,7 @@ export class AnimationManager {
|
||||
constructor(canvas, options = {}) {
|
||||
this.canvasManager = options.canvasManager;
|
||||
this.canvas = canvas;
|
||||
this.currentZoom = options.currentZoom || { value: 100 };
|
||||
this.currentZoom = options.currentZoom;
|
||||
|
||||
// 动画相关属性
|
||||
this._zoomAnimation = null;
|
||||
|
||||
@@ -15,6 +15,9 @@ interface CanvasInitOptions {
|
||||
}
|
||||
export class CanvasManager {
|
||||
stateManager: any
|
||||
layerManager: any
|
||||
animationManager: any
|
||||
eventManager: any
|
||||
deviceInfo: any
|
||||
canvas: any
|
||||
canvasViewWidth: number
|
||||
@@ -22,8 +25,6 @@ export class CanvasManager {
|
||||
canvasWidth: number
|
||||
canvasHeight: number
|
||||
currentZoom: any
|
||||
animationManager: any
|
||||
eventManager: any
|
||||
constructor(options) {
|
||||
this.stateManager = options.stateManager;
|
||||
this.deviceInfo = detectDeviceType();
|
||||
@@ -34,6 +35,7 @@ export class CanvasManager {
|
||||
this.canvasViewHeight = options.canvasViewHeight || 1080
|
||||
}
|
||||
initCanvas(options: CanvasInitOptions) {
|
||||
this.layerManager = this.stateManager.layerManager
|
||||
this.setCanvasViewSize(options)
|
||||
this.canvasWidth = options.canvasWidth || 750
|
||||
this.canvasHeight = options.canvasHeight || 600
|
||||
@@ -62,7 +64,11 @@ export class CanvasManager {
|
||||
top: 20,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#f00'
|
||||
fill: '#f00',
|
||||
info: {
|
||||
id: 'rect1',
|
||||
name: 'rect1',
|
||||
}
|
||||
})
|
||||
this.canvas.add(rect)
|
||||
//创建圆形
|
||||
@@ -70,9 +76,37 @@ export class CanvasManager {
|
||||
left: 200,
|
||||
top: 200,
|
||||
radius: 50,
|
||||
fill: '#0f0'
|
||||
fill: '#0f0',
|
||||
info: {
|
||||
id: 'circle',
|
||||
name: 'circle',
|
||||
}
|
||||
})
|
||||
this.canvas.add(circle)
|
||||
// 文字
|
||||
const text = new fabric.Text('Hello World', {
|
||||
left: 300,
|
||||
top: 300,
|
||||
fontSize: 24,
|
||||
fill: '#000',
|
||||
info: {
|
||||
id: 'text1',
|
||||
name: 'text',
|
||||
}
|
||||
})
|
||||
this.canvas.add(text)
|
||||
// 文字
|
||||
const text2 = new fabric.Text('Hello World', {
|
||||
left: 300,
|
||||
top: 300,
|
||||
fontSize: 24,
|
||||
fill: '#000',
|
||||
info: {
|
||||
id: 'text2',
|
||||
name: 'tex2t',
|
||||
}
|
||||
})
|
||||
this.canvas.add(text2)
|
||||
this.animationManager = new AnimationManager(this.canvas, {
|
||||
currentZoom: this.currentZoom,
|
||||
canvasManager: this,
|
||||
@@ -81,9 +115,9 @@ export class CanvasManager {
|
||||
defaultDuration: 0.3, // 缩短默认动画时间
|
||||
});
|
||||
this.setupCanvasEvents()
|
||||
this.stateManager.toolManager.setTool(OperationType.PAN)
|
||||
|
||||
|
||||
this.stateManager.toolManager.setTool(OperationType.SELECT)
|
||||
this.layerManager.updateLayers()
|
||||
this.layerManager.setActiveID(text2.info.id)
|
||||
}
|
||||
setupCanvasEvents() {
|
||||
// 创建画布事件管理器
|
||||
@@ -98,7 +132,36 @@ export class CanvasManager {
|
||||
resetZoom() {
|
||||
this.animationManager.resetZoom()
|
||||
}
|
||||
getObjects() {
|
||||
return this.canvas.getObjects() || []
|
||||
}
|
||||
getObjectById(id: string) {
|
||||
return this.getObjects().find((item: any) => item.info.id === id)
|
||||
}
|
||||
renderAll() {
|
||||
this.canvas.renderAll()
|
||||
}
|
||||
|
||||
deleteObjectById(id: string) {
|
||||
const object = this.getObjectById(id)
|
||||
if (object) {
|
||||
this.canvas.remove(object)
|
||||
this.layerManager.updateLayers()
|
||||
this.renderAll()
|
||||
}
|
||||
}
|
||||
// 拖拽排序
|
||||
dragSort(id, newIndex) {
|
||||
this.canvas.moveTo(this.getObjectById(id), newIndex)
|
||||
this.layerManager.updateLayers()
|
||||
this.renderAll()
|
||||
}
|
||||
getBitObjects() {
|
||||
return this.getObjects().map(v => {
|
||||
const object = v.toJSON("info");
|
||||
return object
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
46
src/components/Canvas/DepthCanvas/manager/LayerManager.ts
Normal file
46
src/components/Canvas/DepthCanvas/manager/LayerManager.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
export class LayerManager {
|
||||
stateManager: any
|
||||
canvasManager: any
|
||||
layers: any
|
||||
activeID: any
|
||||
constructor(options) {
|
||||
this.stateManager = options.stateManager;
|
||||
this.canvasManager = options.canvasManager;
|
||||
this.layers = ref([])
|
||||
this.activeID = ref("")
|
||||
}
|
||||
setActiveID(id: string) { this.activeID.value = id }
|
||||
getLayerByID(id) {
|
||||
return this.layers.value.find((item: any) => item.info.id === id)
|
||||
}
|
||||
setLayerNameByID(id, name: string) {
|
||||
const layer = this.getLayerByID(id)
|
||||
if (layer) {
|
||||
layer.info.name = name
|
||||
this.canvasManager.renderAll()
|
||||
}
|
||||
}
|
||||
setLayerVisibleByID(id, visible: boolean) {
|
||||
const layer = this.getLayerByID(id)
|
||||
if (layer) {
|
||||
layer.set({
|
||||
visible: visible
|
||||
})
|
||||
this.canvasManager.renderAll()
|
||||
}
|
||||
}
|
||||
deleteLayerByID(id) {
|
||||
this.canvasManager.deleteObjectById(id)
|
||||
}
|
||||
// 拖拽排序
|
||||
dragSort(id, newIndex) {
|
||||
const index = Math.abs(this.layers.value.length - newIndex - 1)
|
||||
this.canvasManager.dragSort(id, index)
|
||||
}
|
||||
// 更新图层列表
|
||||
updateLayers() {
|
||||
this.layers.value = this.canvasManager.getObjects().reverse()
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,15 @@ export class StateManager {
|
||||
historyIndex: any
|
||||
|
||||
// 管理器
|
||||
eventManager: any
|
||||
canvasManager: any
|
||||
layerManager: any
|
||||
eventManager: any
|
||||
toolManager: any
|
||||
// 设置管理器
|
||||
setManager(options) {
|
||||
options.eventManager && (this.eventManager = options.eventManager)
|
||||
options.canvasManager && (this.canvasManager = options.canvasManager)
|
||||
options.layerManager && (this.layerManager = options.layerManager)
|
||||
options.toolManager && (this.toolManager = options.toolManager)
|
||||
}
|
||||
constructor(options) {
|
||||
|
||||
@@ -603,12 +603,12 @@ export class CanvasEventManager {
|
||||
handleDragEnd(opt, isTouch = false) {
|
||||
if (this.canvas.isDragging) {
|
||||
// 使用动画管理器处理惯性效果
|
||||
if (this.lastMousePositions.length > 1 && opt && opt.e) {
|
||||
this.animationManager.applyInertiaEffect(
|
||||
this.lastMousePositions,
|
||||
isTouch
|
||||
);
|
||||
}
|
||||
// if (this.lastMousePositions.length > 1 && opt && opt.e) {
|
||||
// this.animationManager.applyInertiaEffect(
|
||||
// this.lastMousePositions,
|
||||
// isTouch
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
this.canvas.isDragging = false;
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
@home="() => fitView({ maxZoom: 1 })"
|
||||
/>
|
||||
<image-preview ref="imagePreviewRef" />
|
||||
<baseModal ref="three">
|
||||
|
||||
</baseModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -64,6 +67,7 @@
|
||||
import headerTools from './components/header-tools.vue'
|
||||
import zoom from '../components/zoom.vue'
|
||||
import imagePreview from '../components/image-preview.vue'
|
||||
import baseModal from '../components/base-modal.vue'
|
||||
// 节点
|
||||
import node from './components/node.vue'
|
||||
import resultImage from './components/nodes/result-image.vue'
|
||||
|
||||
51
src/components/Canvas/components/base-modal.vue
Normal file
51
src/components/Canvas/components/base-modal.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="base-modal"
|
||||
v-model="showDialog"
|
||||
align-center
|
||||
:show-close="false"
|
||||
width="70vw"
|
||||
style="border-radius: 20px; padding: 0; --el-dialog-padding-primary: 0"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<div class="header-close" @click="close">
|
||||
<svg-icon name="close" size="23" size-unit="px" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="modal-box">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, onBeforeUnmount, shallowRef } from 'vue'
|
||||
const showDialog = ref(false)
|
||||
const open = (url_: any) => {
|
||||
showDialog.value = true
|
||||
}
|
||||
const close = () => {
|
||||
showDialog.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.header-close {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 40px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.modal-box {
|
||||
width: 100%;
|
||||
height: 70vh;
|
||||
padding: 67px;
|
||||
}
|
||||
</style>
|
||||
@@ -69,6 +69,7 @@
|
||||
position: absolute;
|
||||
top: 3rem;
|
||||
right: 3rem;
|
||||
--my-info-bgColor: #fff;
|
||||
}
|
||||
> .close-btn {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -72,9 +72,9 @@
|
||||
min-width: 18rem;
|
||||
height: 4.3rem;
|
||||
margin-right: 1rem;
|
||||
background-color: rgba(255, 252, 244, 1);
|
||||
border: 1px solid #ffcf90;
|
||||
border-radius: 0.8rem;
|
||||
background-color: var(--my-info-bgColor, rgba(255, 252, 244, 1));
|
||||
> .credits {
|
||||
flex: 1;
|
||||
font-size: 1.3rem;
|
||||
|
||||
Reference in New Issue
Block a user