Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front
3
src/assets/icons/dc/arrow.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9.085 5.92551H0.54125C0.387639 5.92551 0.259028 5.87405 0.155417 5.77113C0.0518057 5.66835 0 5.54071 0 5.38821C0 5.23571 0.0518057 5.10662 0.155417 5.00092C0.259028 4.89509 0.387639 4.84217 0.54125 4.84217H9.085L5.18958 0.946756C5.0825 0.839673 5.02625 0.713491 5.02083 0.568214C5.01556 0.422936 5.07056 0.289255 5.18583 0.167172C5.30125 0.0531442 5.43028 -0.00254935 5.57292 8.95351e-05C5.71556 0.00272842 5.84319 0.0605064 5.95583 0.173423L10.6935 4.91717C10.7633 4.98703 10.8141 5.06044 10.846 5.13738C10.8781 5.21433 10.8942 5.29759 10.8942 5.38717C10.8942 5.47662 10.8781 5.55988 10.846 5.63696C10.8141 5.71391 10.766 5.78446 10.7019 5.84863L5.96 10.5905C5.84458 10.7059 5.71903 10.7615 5.58333 10.7572C5.44764 10.7529 5.32208 10.6937 5.20667 10.5797C5.09139 10.4576 5.03375 10.3254 5.03375 10.1832C5.03375 10.041 5.09139 9.91446 5.20667 9.80363L9.085 5.92551Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 995 B |
4
src/assets/icons/dc/copy.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.725098" y="0.724609" width="13.0481" height="13.0481" rx="2.17468" stroke="black" stroke-width="1.44978"/>
|
||||||
|
<path d="M16.6733 5.79687V12.3209C16.6733 14.723 14.726 16.6703 12.3239 16.6703H4.3501" stroke="black" stroke-width="1.44978" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 372 B |
3
src/assets/icons/dc/dui.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="10" height="7" viewBox="0 0 10 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.84125 5.40854L8.06875 0.172916C8.18375 0.0576386 8.31264 0 8.45542 0C8.59833 0 8.7275 0.0575006 8.84292 0.172501C8.9582 0.287501 9.01583 0.416458 9.01583 0.559375C9.01583 0.70243 8.9582 0.831667 8.84292 0.947084L3.30604 6.47583C3.16924 6.61264 3.01083 6.68104 2.83083 6.68104C2.65083 6.68104 2.49243 6.61264 2.35563 6.47583L0.172917 4.30125C0.0576393 4.18625 0 4.05736 0 3.91458C0 3.77167 0.0575 3.6425 0.1725 3.52708C0.2875 3.41181 0.416458 3.35417 0.559375 3.35417C0.702431 3.35417 0.831667 3.41181 0.947083 3.52708L2.84125 5.40854Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 664 B |
3
src/assets/icons/dc/ellipse.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M7.59042 15.1667C6.54722 15.1667 5.56472 14.9693 4.64292 14.5746C3.72111 14.1799 2.91431 13.6367 2.2225 12.9452C1.53069 12.2537 0.987292 11.4475 0.592292 10.5267C0.19743 9.60597 0 8.62215 0 7.57521C0 6.52826 0.197361 5.54736 0.592083 4.6325C0.986806 3.71764 1.52993 2.91431 2.22146 2.2225C2.91299 1.53069 3.71917 0.987292 4.64 0.592292C5.56069 0.19743 6.54451 0 7.59146 0C8.6384 0 9.61931 0.197361 10.5342 0.592083C11.449 0.986805 12.2524 1.52993 12.9442 2.22146C13.636 2.91299 14.1794 3.7175 14.5744 4.635C14.9692 5.55264 15.1667 6.53306 15.1667 7.57625C15.1667 8.61944 14.9693 9.60194 14.5746 10.5238C14.1799 11.4456 13.6367 12.2524 12.9452 12.9442C12.2537 13.636 11.4492 14.1794 10.5317 14.5744C9.61403 14.9692 8.63361 15.1667 7.59042 15.1667ZM7.58333 14.0833C9.38889 14.0833 10.9236 13.4514 12.1875 12.1875C13.4514 10.9236 14.0833 9.38889 14.0833 7.58333C14.0833 5.77778 13.4514 4.24306 12.1875 2.97917C10.9236 1.71528 9.38889 1.08333 7.58333 1.08333C5.77778 1.08333 4.24306 1.71528 2.97917 2.97917C1.71528 4.24306 1.08333 5.77778 1.08333 7.58333C1.08333 9.38889 1.71528 10.9236 2.97917 12.1875C4.24306 13.4514 5.77778 14.0833 7.58333 14.0833Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
3
src/assets/icons/dc/line.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0.197083 9.55862C0.0656943 9.42723 0 9.28244 0 9.12424C0 8.96619 0.0656943 8.82251 0.197083 8.6932L8.70521 0.193203C8.8366 0.0670915 8.97944 0.0027174 9.13375 7.85068e-05C9.28819 -0.00256038 9.43062 0.0613275 9.56104 0.191744C9.6934 0.324105 9.75958 0.469384 9.75958 0.627578C9.75958 0.785634 9.69389 0.929315 9.5625 1.05862L1.0625 9.55862C0.933194 9.69001 0.789514 9.7557 0.631458 9.7557C0.473264 9.7557 0.328472 9.69001 0.197083 9.55862Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 569 B |
3
src/assets/icons/dc/star.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="15" viewBox="0 0 16 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4.33333 3.41354L6.54479 0.519166C6.67799 0.340555 6.83611 0.209376 7.01917 0.125626C7.20208 0.0418756 7.39556 0 7.59958 0C7.80347 0 7.99438 0.0434724 8.17229 0.130417C8.35021 0.217222 8.50701 0.346805 8.64271 0.519166L10.8542 3.41354L14.2579 4.53521C14.5454 4.64104 14.7674 4.80819 14.9238 5.03667C15.0803 5.26514 15.1585 5.5175 15.1585 5.79375C15.1585 5.92528 15.14 6.05632 15.1029 6.18687C15.0658 6.31757 15.0048 6.44278 14.9198 6.5625L12.7179 9.57208L12.8013 12.835C12.8013 13.2182 12.6705 13.5428 12.409 13.809C12.1476 14.0752 11.8348 14.2083 11.4706 14.2083C11.4613 14.2083 11.344 14.1901 11.1185 14.1538L7.58333 13.149L4.04688 14.1546C3.97826 14.1818 3.91743 14.1976 3.86437 14.2019C3.81132 14.2062 3.75722 14.2083 3.70208 14.2083C3.33694 14.2083 3.01938 14.0752 2.74938 13.809C2.47951 13.5428 2.35153 13.2182 2.36542 12.835L2.44875 9.57208L0.23875 6.5625C0.152917 6.43986 0.0916667 6.31799 0.055 6.19688C0.0183333 6.07563 0 5.94729 0 5.81187C0 5.52576 0.0824306 5.26479 0.247292 5.02896C0.412014 4.79326 0.642083 4.62604 0.9375 4.52729L4.33333 3.41354ZM4.96 4.34458L1.27562 5.57208C1.19549 5.59875 1.14076 5.65354 1.11146 5.73646C1.08201 5.81924 1.09403 5.89535 1.1475 5.96479L3.53792 9.26375L3.44875 12.8606C3.44333 12.9515 3.47535 13.0235 3.54479 13.0769C3.61424 13.1303 3.69174 13.1438 3.77729 13.1171L7.58333 12.0192L11.4102 13.0963C11.4958 13.1229 11.5733 13.1095 11.6427 13.056C11.7122 13.0027 11.7442 12.9306 11.7388 12.8398L11.6267 9.24208L14.0192 5.94396C14.0726 5.87451 14.0847 5.7984 14.0552 5.71562C14.0259 5.63271 13.9712 5.57792 13.891 5.55125L10.1858 4.34458L7.78375 1.23729C7.73556 1.16771 7.66875 1.13292 7.58333 1.13292C7.49792 1.13292 7.43111 1.16771 7.38292 1.23729L4.96 4.34458Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
3
src/assets/icons/dc/triangle.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="14" height="12" viewBox="0 0 14 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0.706857 12C0.414255 12 0.206211 11.8612 0.0827259 11.5837C-0.0406133 11.3062 -0.0260342 11.0369 0.126463 10.7758L6.41961 0.342906C6.56321 0.114302 6.75667 0 7 0C7.24333 0 7.43679 0.114302 7.58039 0.342906L13.8735 10.7758C14.026 11.0369 14.0406 11.3062 13.9173 11.5837C13.7938 11.8612 13.5857 12 13.2931 12H0.706857ZM1.53284 10.7327H12.4672L7 1.61777L1.53284 10.7327Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 497 B |
@@ -2,14 +2,31 @@
|
|||||||
<div class="header-tools">
|
<div class="header-tools">
|
||||||
<template v-for="(v, i) in tools" :key="i">
|
<template v-for="(v, i) in tools" :key="i">
|
||||||
<span class="line" v-if="v.type === 'line'"></span>
|
<span class="line" v-if="v.type === 'line'"></span>
|
||||||
<span
|
<div v-else class="item">
|
||||||
v-else
|
<span
|
||||||
class="icon"
|
class="icon"
|
||||||
@click="onClickTool(v)"
|
@click="onClickTool(v)"
|
||||||
:class="{ active: v.name === tool, disabled: v.disabled }"
|
:class="{
|
||||||
>
|
active: v.name === tool,
|
||||||
<svg-icon :name="v.icon" :size="v.iconSize" />
|
disabled: v.disabled
|
||||||
</span>
|
}"
|
||||||
|
>
|
||||||
|
<svg-icon :name="v.icon" :size="v.iconSize" />
|
||||||
|
</span>
|
||||||
|
<span class="more" v-if="v.child" @click="onClickMore(v)">
|
||||||
|
<svg-icon name="dc-down_arrow2" size="7" />
|
||||||
|
</span>
|
||||||
|
<div v-if="v.child" class="child" v-show="v.showChild">
|
||||||
|
<div v-for="(v_, i_) in v.child" :key="i_" @click="onClickTool(v_, v)">
|
||||||
|
<span v-show="tool === v_.name" class="dui">
|
||||||
|
<svg-icon name="dc-dui" size="9" />
|
||||||
|
</span>
|
||||||
|
<span class="icon"><svg-icon :name="v_.icon" :size="v_.iconSize" /></span>
|
||||||
|
<span class="label">{{ v_.label }}</span>
|
||||||
|
<span class="tip">{{ v_.tip }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<button class="export" @click="emit('export')">
|
<button class="export" @click="emit('export')">
|
||||||
<span class="icon"><svg-icon name="export" size="12" /></span>
|
<span class="icon"><svg-icon name="export" size="12" /></span>
|
||||||
@@ -42,48 +59,108 @@
|
|||||||
const toolManager = inject('toolManager') as any
|
const toolManager = inject('toolManager') as any
|
||||||
const objectManager = inject('objectManager') as any
|
const objectManager = inject('objectManager') as any
|
||||||
const canvasManager = inject('canvasManager') as any
|
const canvasManager = inject('canvasManager') as any
|
||||||
|
const layerManager = inject('layerManager') as any
|
||||||
const tool = computed(() => toolManager.currentTool.value)
|
const tool = computed(() => toolManager.currentTool.value)
|
||||||
const historyIndex = computed(() => stateManager.historyIndex.value)
|
const historyIndex = computed(() => stateManager.historyIndex.value)
|
||||||
const historyList = computed(() => stateManager.historyList.value)
|
const historyList = computed(() => stateManager.historyList.value)
|
||||||
const isUndo = computed(() => !historyList.value[historyIndex.value - 1])
|
const isUndo = computed(() => !historyList.value[historyIndex.value - 1])
|
||||||
const isRedo = computed(() => !historyList.value[historyIndex.value + 1])
|
const isRedo = computed(() => !historyList.value[historyIndex.value + 1])
|
||||||
|
const activeLayerId = computed(() => layerManager.activeID.value)
|
||||||
const tools = ref([
|
const tools = ref([
|
||||||
{ name: OperationType.SELECT, icon: 'dc-select', iconSize: 16, disabled: ref(false) },
|
{ name: OperationType.SELECT, icon: 'dc-select', iconSize: 16 },
|
||||||
{ name: OperationType.PAN, icon: 'dc-move', iconSize: 18, disabled: ref(false) },
|
{ name: OperationType.PAN, icon: 'dc-move', iconSize: 18 },
|
||||||
{ name: OperationType.DRAW, icon: 'dc-brush', iconSize: 18, disabled: ref(false) },
|
{ name: OperationType.DRAW, icon: 'dc-brush', iconSize: 18 },
|
||||||
{ name: OperationType.ERASER, icon: 'dc-eraser', iconSize: 18, disabled: ref(false) },
|
{ name: OperationType.ERASER, icon: 'dc-eraser', iconSize: 18 },
|
||||||
|
{ icon: 'dc-image', iconSize: 17, on: () => onImageClick() },
|
||||||
|
{ name: OperationType.SELECTBOX, icon: 'dc-selectbox', iconSize: 16 },
|
||||||
{
|
{
|
||||||
name: OperationType.IMAGE,
|
name: OperationType.RECTANGLE,
|
||||||
icon: 'dc-image',
|
icon: 'dc-rectangle',
|
||||||
iconSize: 17,
|
iconSize: 16,
|
||||||
disabled: ref(false),
|
showChild: false,
|
||||||
on: () => onImageClick()
|
child: [
|
||||||
|
{
|
||||||
|
name: OperationType.RECTANGLE,
|
||||||
|
label: 'Rectangle',
|
||||||
|
icon: 'dc-rectangle',
|
||||||
|
iconSize: 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: OperationType.LINE,
|
||||||
|
label: 'Line',
|
||||||
|
icon: 'dc-line',
|
||||||
|
iconSize: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: OperationType.ARROW,
|
||||||
|
label: 'Arrow',
|
||||||
|
icon: 'dc-arrow',
|
||||||
|
iconSize: 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: OperationType.ELLIPSE,
|
||||||
|
label: 'Ellipse',
|
||||||
|
icon: 'dc-ellipse',
|
||||||
|
iconSize: 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: OperationType.TRIANGLE,
|
||||||
|
label: 'Polygon',
|
||||||
|
icon: 'dc-triangle',
|
||||||
|
iconSize: 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: OperationType.STAR,
|
||||||
|
label: 'Star',
|
||||||
|
icon: 'dc-star',
|
||||||
|
iconSize: 15
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{ name: OperationType.SELECTBOX, icon: 'dc-selectbox', iconSize: 16, disabled: ref(false) },
|
|
||||||
{ name: OperationType.RECTANGLE, icon: 'dc-rectangle', iconSize: 16, disabled: ref(false) },
|
|
||||||
{ type: 'line' },
|
{ type: 'line' },
|
||||||
{
|
{
|
||||||
name: OperationType.UNDO,
|
|
||||||
icon: 'dc-undo',
|
icon: 'dc-undo',
|
||||||
iconSize: 18,
|
iconSize: 18,
|
||||||
disabled: isUndo,
|
disabled: isUndo,
|
||||||
on: () => stateManager.undoState()
|
on: () => stateManager.undoState()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: OperationType.REDO,
|
|
||||||
icon: 'dc-redo',
|
icon: 'dc-redo',
|
||||||
iconSize: 18,
|
iconSize: 18,
|
||||||
disabled: isRedo,
|
disabled: isRedo,
|
||||||
on: () => stateManager.redoState()
|
on: () => stateManager.redoState()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'copy',
|
||||||
|
icon: 'dc-copy',
|
||||||
|
iconSize: 16,
|
||||||
|
disabled: computed(() => !activeLayerId.value),
|
||||||
|
on: () => onCopyActiveLayer()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
icon: 'dc-delete',
|
||||||
|
iconSize: 18,
|
||||||
|
disabled: computed(() => !activeLayerId.value),
|
||||||
|
on: () => onDeleteActiveLayer()
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const onClickTool = (tool: any) => {
|
const onClickTool = (v: any, parent?: any) => {
|
||||||
if (tool.disabled?.value) return
|
if (v.disabled?.value) return
|
||||||
if (tool.on) {
|
if (parent) {
|
||||||
tool.on()
|
parent.name = v.name
|
||||||
} else {
|
parent.icon = v.icon
|
||||||
toolManager.setTool(tool.name)
|
parent.iconSize = v.iconSize * 1.23
|
||||||
}
|
}
|
||||||
|
tools.value.forEach((v) => v.hasOwnProperty('showChild') && (v.showChild = false))
|
||||||
|
v.on ? v.on() : toolManager.setTool(v.name)
|
||||||
|
}
|
||||||
|
const onClickMore = (v: any) => {
|
||||||
|
tools.value.forEach((item) => {
|
||||||
|
if (item.hasOwnProperty('showChild') && item.name !== v.name) item.showChild = false
|
||||||
|
})
|
||||||
|
v.showChild = !v.showChild
|
||||||
}
|
}
|
||||||
const onImageClick = async () => {
|
const onImageClick = async () => {
|
||||||
const layer = await importLocalImage(false)
|
const layer = await importLocalImage(false)
|
||||||
@@ -91,6 +168,14 @@
|
|||||||
objectManager.setBlendMode(id, BlendMode.MULTIPLY)
|
objectManager.setBlendMode(id, BlendMode.MULTIPLY)
|
||||||
objectManager.setFillRepeat(id)
|
objectManager.setFillRepeat(id)
|
||||||
}
|
}
|
||||||
|
const onCopyActiveLayer = () => {
|
||||||
|
if (!activeLayerId.value) return
|
||||||
|
layerManager.copyLayerById(activeLayerId.value)
|
||||||
|
}
|
||||||
|
const onDeleteActiveLayer = () => {
|
||||||
|
if (!activeLayerId.value) return
|
||||||
|
layerManager.deleteLayerById(activeLayerId.value)
|
||||||
|
}
|
||||||
const onWorkbench = async () => {
|
const onWorkbench = async () => {
|
||||||
exportCanvasToImage(canvasManager.canvas).then((url) => {
|
exportCanvasToImage(canvasManager.canvas).then((url) => {
|
||||||
emit('workbench', { url })
|
emit('workbench', { url })
|
||||||
@@ -123,22 +208,81 @@
|
|||||||
border-radius: 0.2rem;
|
border-radius: 0.2rem;
|
||||||
margin: 0 0.6rem;
|
margin: 0 0.6rem;
|
||||||
}
|
}
|
||||||
> .icon {
|
> .item {
|
||||||
cursor: pointer;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 3rem;
|
> span {
|
||||||
height: 3rem;
|
cursor: pointer;
|
||||||
--svg-icon-color: #000;
|
display: flex;
|
||||||
border-radius: 0.4rem;
|
align-items: center;
|
||||||
&:not(.disabled).active,
|
justify-content: center;
|
||||||
&:not(.disabled):hover {
|
--svg-icon-color: #000;
|
||||||
background-color: #ebebeb;
|
border-radius: 0.4rem;
|
||||||
|
height: 3rem;
|
||||||
|
&:not(.disabled).active,
|
||||||
|
&:not(.disabled):hover {
|
||||||
|
background-color: #ebebeb;
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.disabled {
|
> .icon {
|
||||||
opacity: 0.5;
|
width: 3rem;
|
||||||
cursor: not-allowed;
|
}
|
||||||
|
> .more {
|
||||||
|
width: 1.1rem;
|
||||||
|
}
|
||||||
|
> .child {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
min-width: 21rem;
|
||||||
|
top: calc(100% + 2rem);
|
||||||
|
left: 0;
|
||||||
|
padding: 0.6rem 0;
|
||||||
|
border: 0.1remx solid #d9d9d9;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 1.2rem;
|
||||||
|
box-shadow: 0 0.4rem 0.4rem 0 rgba(0, 0, 0, 0.25);
|
||||||
|
> div {
|
||||||
|
height: 3.2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 1.6rem;
|
||||||
|
> .dui {
|
||||||
|
position: absolute;
|
||||||
|
left: 1.5rem;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
> .icon {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
margin-left: 0.8rem;
|
||||||
|
}
|
||||||
|
> .label {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
> .tip {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
color: rgba(13, 13, 13, 0.65);
|
||||||
|
}
|
||||||
|
&:not(.disabled).active,
|
||||||
|
&:not(.disabled):hover {
|
||||||
|
background-color: #ebebeb;
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> button {
|
> button {
|
||||||
|
|||||||
@@ -33,6 +33,19 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="label">Opacity</div>
|
||||||
|
<div class="value">
|
||||||
|
<depth-slider
|
||||||
|
v-model="opacity"
|
||||||
|
:min="0"
|
||||||
|
:max="100"
|
||||||
|
:tipFormatter="(v) => v + '%'"
|
||||||
|
@input="inputOpacity"
|
||||||
|
@change="changeOpacity"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="label">Gap X</div>
|
<div class="label">Gap X</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
@@ -101,6 +114,7 @@
|
|||||||
const gapX = ref(0)
|
const gapX = ref(0)
|
||||||
const gapY = ref(0)
|
const gapY = ref(0)
|
||||||
const offset = ref({ x: 0, y: 0 })
|
const offset = ref({ x: 0, y: 0 })
|
||||||
|
const opacity = ref(100)
|
||||||
|
|
||||||
const updateData = async () => {
|
const updateData = async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
@@ -113,6 +127,7 @@
|
|||||||
x: Math.round((fill.offsetX / props.object.width) * 100),
|
x: Math.round((fill.offsetX / props.object.width) * 100),
|
||||||
y: Math.round((fill.offsetY / props.object.height) * 100)
|
y: Math.round((fill.offsetY / props.object.height) * 100)
|
||||||
}
|
}
|
||||||
|
opacity.value = Math.round(props.object.opacity * 100)
|
||||||
}
|
}
|
||||||
updateData()
|
updateData()
|
||||||
|
|
||||||
@@ -138,6 +153,15 @@
|
|||||||
objectManager.updateFillRepeatGap(id.value, options, isRecord)
|
objectManager.updateFillRepeatGap(id.value, options, isRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inputOpacity = () => setOpacity(false)
|
||||||
|
const changeOpacity = () => setOpacity(true)
|
||||||
|
const setOpacity = (isRecord: boolean) => {
|
||||||
|
const options = {
|
||||||
|
opacity: opacity.value / 100
|
||||||
|
}
|
||||||
|
objectManager.updateOpacity(id.value, options, isRecord)
|
||||||
|
}
|
||||||
|
|
||||||
stateManager.event.add('canvas:undo', updateData)
|
stateManager.event.add('canvas:undo', updateData)
|
||||||
stateManager.event.add('canvas:redo', updateData)
|
stateManager.event.add('canvas:redo', updateData)
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
><svg-icon :name="layer.visible ? 'dc-show' : 'dc-hide'" size="15"
|
><svg-icon :name="layer.visible ? 'dc-show' : 'dc-hide'" size="15"
|
||||||
/></span>
|
/></span>
|
||||||
<span @click.stop="onClickDelete"><svg-icon name="dc-delete" size="13" /></span>
|
<span @click.stop="onClickDelete"><svg-icon name="dc-delete" size="13" /></span>
|
||||||
<span><svg-icon name="dc-down_arrow" size="11" /></span>
|
<!-- <span><svg-icon name="dc-down_arrow" size="11" /></span> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
layerManager.dragSort(data.info.id, newIndex)
|
layerManager.dragSort(data.info.id, newIndex)
|
||||||
}
|
}
|
||||||
const addLayer = () => {
|
const addLayer = () => {
|
||||||
layerManager.createEmptyLayer()
|
layerManager.createEmptyLayer(true, true)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { fabric } from 'fabric-with-all'
|
|||||||
import { createId } from '../../tools/tools'
|
import { createId } from '../../tools/tools'
|
||||||
import { exportObjectsToImage, exportObjectToThumbnail } from '../tools/exportMethod'
|
import { exportObjectsToImage, exportObjectToThumbnail } from '../tools/exportMethod'
|
||||||
import { OperationType } from '../tools/layerHelper'
|
import { OperationType } from '../tools/layerHelper'
|
||||||
|
import { getArrowPath, cloneObjects, getStarArr } from '../tools/canvasMethod'
|
||||||
|
|
||||||
export class LayerManager {
|
export class LayerManager {
|
||||||
stateManager: any
|
stateManager: any
|
||||||
@@ -53,6 +54,7 @@ export class LayerManager {
|
|||||||
this.canvasManager.renderAll()
|
this.canvasManager.renderAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** 删除指定图层 */
|
||||||
deleteLayerById(id, isActive = true) {
|
deleteLayerById(id, isActive = true) {
|
||||||
this.canvasManager.deleteObjectById(id)
|
this.canvasManager.deleteObjectById(id)
|
||||||
if (id === this.activeID.value && isActive) {
|
if (id === this.activeID.value && isActive) {
|
||||||
@@ -60,6 +62,24 @@ export class LayerManager {
|
|||||||
}
|
}
|
||||||
if (isActive) this.stateManager.recordState()
|
if (isActive) this.stateManager.recordState()
|
||||||
}
|
}
|
||||||
|
/** 复制指定图层 */
|
||||||
|
copyLayerById(id) {
|
||||||
|
const object = this.canvasManager.getObjectById(id)
|
||||||
|
if (!object) return console.warn('复制图层失败,对象不存在ID:', id)
|
||||||
|
cloneObjects([object]).then(objects => {
|
||||||
|
const newObject = objects[0]
|
||||||
|
const info = JSON.parse(JSON.stringify(newObject.info))
|
||||||
|
info.id = createId("image")
|
||||||
|
// info.name = info.name
|
||||||
|
newObject.set({
|
||||||
|
top: newObject.top + 15,
|
||||||
|
left: newObject.left + 15,
|
||||||
|
info: info,
|
||||||
|
})
|
||||||
|
this.canvasManager.add(newObject)
|
||||||
|
this.setActiveID(newObject.info.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
// 拖拽排序
|
// 拖拽排序
|
||||||
dragSort(id, newIndex) {
|
dragSort(id, newIndex) {
|
||||||
const index = Math.abs(this.layers.value.length - newIndex - 1)
|
const index = Math.abs(this.layers.value.length - newIndex - 1)
|
||||||
@@ -90,7 +110,7 @@ export class LayerManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** 创建空图层 */
|
/** 创建空图层 */
|
||||||
createEmptyLayer(isRecord = true) {
|
createEmptyLayer(isRecord = true, isActive = false) {
|
||||||
const emptyObject = new fabric.Rect({
|
const emptyObject = new fabric.Rect({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@@ -102,6 +122,7 @@ export class LayerManager {
|
|||||||
})
|
})
|
||||||
this.setLayerPosition(emptyObject)
|
this.setLayerPosition(emptyObject)
|
||||||
this.canvasManager.add(emptyObject, isRecord)
|
this.canvasManager.add(emptyObject, isRecord)
|
||||||
|
if (isActive) this.setActiveID(emptyObject.info.id, false)
|
||||||
return emptyObject
|
return emptyObject
|
||||||
}
|
}
|
||||||
/** 创建文本图层 */
|
/** 创建文本图层 */
|
||||||
@@ -138,23 +159,109 @@ export class LayerManager {
|
|||||||
if (isActive) this.setActiveID(rectObject.info.id)
|
if (isActive) this.setActiveID(rectObject.info.id)
|
||||||
return rectObject
|
return rectObject
|
||||||
}
|
}
|
||||||
/** 创建圆形图层 */
|
/** 创建直线图层 */
|
||||||
async createCircleLayer(options?: any, isActive = false) {
|
async createLineLayer(options?: any, isActive = false) {
|
||||||
const circleObject = new fabric.Circle({
|
const line = [options?.x1 || 0, options?.y1 || 0, options?.x2 || 100, options?.y2 || 0]
|
||||||
|
delete options.x1
|
||||||
|
delete options.y1
|
||||||
|
delete options.x2
|
||||||
|
delete options.y2
|
||||||
|
const lineObject = new fabric.Line(line, {
|
||||||
|
stroke: 'black', // 线条颜色
|
||||||
|
strokeWidth: 2, // 线条粗细
|
||||||
|
...(options || {}),
|
||||||
|
info: {
|
||||||
|
id: createId("line"),
|
||||||
|
name: '直线图层',
|
||||||
|
...(options?.info || {}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.setLayerPosition(lineObject, options)
|
||||||
|
await this.canvasManager.add(lineObject)
|
||||||
|
if (isActive) this.setActiveID(lineObject.info.id)
|
||||||
|
return lineObject
|
||||||
|
}
|
||||||
|
/** 创建椭圆图层 */
|
||||||
|
async createEllipseLayer(options?: any, isActive = false) {
|
||||||
|
const ellipseObject = new fabric.Ellipse({
|
||||||
radius: 50,
|
radius: 50,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
info: {
|
info: {
|
||||||
id: createId("circle"),
|
id: createId("ellipse"),
|
||||||
name: '圆形图层',
|
name: '椭圆图层',
|
||||||
...(options?.info || {}),
|
...(options?.info || {}),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.setLayerPosition(circleObject, options)
|
this.setLayerPosition(ellipseObject, options)
|
||||||
await this.canvasManager.add(circleObject)
|
await this.canvasManager.add(ellipseObject)
|
||||||
if (isActive) this.setActiveID(circleObject.info.id)
|
if (isActive) this.setActiveID(ellipseObject.info.id)
|
||||||
return circleObject
|
return ellipseObject
|
||||||
}
|
}
|
||||||
|
/** 创建三角形图层 */
|
||||||
|
async createTriangleLayer(options?: any, isActive = false) {
|
||||||
|
const triangleObject = new fabric.Triangle({
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
fill: '#000',
|
||||||
|
...(options || {}),
|
||||||
|
info: {
|
||||||
|
id: createId("triangle"),
|
||||||
|
name: '三角形图层',
|
||||||
|
...(options?.info || {}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.setLayerPosition(triangleObject, options)
|
||||||
|
await this.canvasManager.add(triangleObject)
|
||||||
|
if (isActive) this.setActiveID(triangleObject.info.id)
|
||||||
|
return triangleObject
|
||||||
|
}
|
||||||
|
/** 创建五角星图层 */
|
||||||
|
async createStarLayer(options?: any, isActive = false) {
|
||||||
|
const width = options?.width || 100
|
||||||
|
const height = options?.height || 100
|
||||||
|
delete options.points
|
||||||
|
const starObject = new fabric.Polygon(getStarArr(width, height), {
|
||||||
|
fill: '#000',
|
||||||
|
...(options || {}),
|
||||||
|
info: {
|
||||||
|
id: createId("star"),
|
||||||
|
name: '五角星图层',
|
||||||
|
...(options?.info || {}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.setLayerPosition(starObject, options)
|
||||||
|
await this.canvasManager.add(starObject)
|
||||||
|
if (isActive) this.setActiveID(starObject.info.id)
|
||||||
|
return starObject
|
||||||
|
}
|
||||||
|
/** 创建箭头图层 */
|
||||||
|
async createArrowLayer(options?: any, isActive = false) {
|
||||||
|
const width = options?.width || 100
|
||||||
|
const height = options?.height || 10
|
||||||
|
delete options.width
|
||||||
|
delete options.height
|
||||||
|
const arrowObject = new fabric.Path(getArrowPath(width, height), {
|
||||||
|
stroke: '#000', // 只设置边框颜色
|
||||||
|
strokeWidth: 3, // 边框宽度
|
||||||
|
fill: 'transparent', // 不填充
|
||||||
|
strokeLineCap: 'round',
|
||||||
|
strokeLineJoin: 'round',
|
||||||
|
...(options || {}),
|
||||||
|
info: {
|
||||||
|
id: createId("star"),
|
||||||
|
name: '箭头图层',
|
||||||
|
...(options?.info || {}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setLayerPosition(arrowObject, options)
|
||||||
|
this.canvasManager.add(arrowObject)
|
||||||
|
if (isActive) this.setActiveID(arrowObject.info.id)
|
||||||
|
return arrowObject
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** 创建图片图层 */
|
/** 创建图片图层 */
|
||||||
async createImageLayer(imgOrUrl: string | HTMLImageElement, options?: any, isRecord = true) {
|
async createImageLayer(imgOrUrl: string | HTMLImageElement, options?: any, isRecord = true) {
|
||||||
const canvasWidth = this.canvasManager.canvasWidth
|
const canvasWidth = this.canvasManager.canvasWidth
|
||||||
|
|||||||
@@ -207,5 +207,24 @@ export class ObjectManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 修改透明度
|
||||||
|
* @param id 目标对象ID
|
||||||
|
* @param options 透明度参数
|
||||||
|
* @param options.opacity 透明度
|
||||||
|
* @param isRecord 是否记录
|
||||||
|
*/
|
||||||
|
async updateOpacity(id: string, options: any, isRecord: boolean) {
|
||||||
|
const object = this.getFillRepeatObject(id)
|
||||||
|
if (!object) return null
|
||||||
|
const opacity = options.opacity
|
||||||
|
object.set("opacity", opacity);
|
||||||
|
this.canvasManager.renderAll()
|
||||||
|
if (isRecord) {
|
||||||
|
this.stateManager.recordState()
|
||||||
|
this.layerManager.updateLayerThumbnailsById(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dispose() { }
|
dispose() { }
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { OperationType, OperationTypes } from "../tools/layerHelper";
|
||||||
import { fabric } from 'fabric-with-all'
|
import { fabric } from 'fabric-with-all'
|
||||||
/** 矩形工具管理器 */
|
/** 矩形工具管理器 */
|
||||||
export class RectToolManager {
|
export class RectToolManager {
|
||||||
@@ -10,6 +11,9 @@ export class RectToolManager {
|
|||||||
startX: number = 0
|
startX: number = 0
|
||||||
startY: number = 0
|
startY: number = 0
|
||||||
demoObject: any
|
demoObject: any
|
||||||
|
tools = [
|
||||||
|
OperationType.RECTANGLE
|
||||||
|
]
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.canvasManager = options.canvasManager
|
this.canvasManager = options.canvasManager
|
||||||
this.stateManager = options.stateManager
|
this.stateManager = options.stateManager
|
||||||
255
src/components/Canvas/DepthCanvas/manager/ShapeToolManager.ts
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
import { OperationType, OperationTypes } from "../tools/layerHelper";
|
||||||
|
import { getStarArr, getArrowPath, distance, angleBetweenPointsDegrees } from "../tools/canvasMethod";
|
||||||
|
import { fabric } from 'fabric-with-all'
|
||||||
|
/** 形状管理器 */
|
||||||
|
export class ShapeToolManager {
|
||||||
|
// 管理器
|
||||||
|
canvasManager: any
|
||||||
|
stateManager: any
|
||||||
|
layerManager: any
|
||||||
|
toolManager: any
|
||||||
|
|
||||||
|
isDragging: boolean = false
|
||||||
|
startX: number = 0
|
||||||
|
startY: number = 0
|
||||||
|
demoObject: any
|
||||||
|
tools = [
|
||||||
|
OperationType.RECTANGLE,
|
||||||
|
OperationType.LINE,
|
||||||
|
OperationType.ARROW,
|
||||||
|
OperationType.ELLIPSE,
|
||||||
|
OperationType.TRIANGLE,
|
||||||
|
OperationType.STAR,
|
||||||
|
]
|
||||||
|
constructor(options) {
|
||||||
|
this.canvasManager = options.canvasManager
|
||||||
|
this.stateManager = options.stateManager
|
||||||
|
this.layerManager = options.layerManager
|
||||||
|
this.toolManager = options.toolManager
|
||||||
|
}
|
||||||
|
mouseDownEvent(e) {
|
||||||
|
this.isDragging = false
|
||||||
|
this.demoObject = null
|
||||||
|
|
||||||
|
this.startX = e.absolutePointer.x
|
||||||
|
this.startY = e.absolutePointer.y
|
||||||
|
const currentTool = this.toolManager.currentTool.value
|
||||||
|
if (currentTool === OperationType.RECTANGLE) {
|
||||||
|
this.demoObject = this.downRectangle()
|
||||||
|
} else if (currentTool === OperationType.LINE) {
|
||||||
|
this.demoObject = this.downLine()
|
||||||
|
} else if (currentTool === OperationType.ELLIPSE) {
|
||||||
|
this.demoObject = this.downEllipse()
|
||||||
|
} else if (currentTool === OperationType.TRIANGLE) {
|
||||||
|
this.demoObject = this.downTriangle()
|
||||||
|
} else if (currentTool === OperationType.STAR) {
|
||||||
|
this.demoObject = this.downStar()
|
||||||
|
} else if (currentTool === OperationType.ARROW) {
|
||||||
|
this.demoObject = this.downArrow()
|
||||||
|
}
|
||||||
|
if (!this.demoObject) return;
|
||||||
|
this.demoObject.set({
|
||||||
|
evented: false,
|
||||||
|
})
|
||||||
|
this.demoObject.set
|
||||||
|
this.isDragging = true
|
||||||
|
this.canvasManager.canvas.add(this.demoObject)
|
||||||
|
this.canvasManager.canvas.renderAll()
|
||||||
|
}
|
||||||
|
mouseMoveEvent(e) {
|
||||||
|
if (!this.isDragging) return;
|
||||||
|
var width = e.absolutePointer.x - this.startX
|
||||||
|
var height = e.absolutePointer.y - this.startY
|
||||||
|
var left = this.startX
|
||||||
|
var top = this.startY
|
||||||
|
if (width < 0) {
|
||||||
|
left += width
|
||||||
|
width = -width
|
||||||
|
}
|
||||||
|
if (height < 0) {
|
||||||
|
top += height
|
||||||
|
height = -height
|
||||||
|
}
|
||||||
|
const currentTool = this.toolManager.currentTool.value
|
||||||
|
if (currentTool === OperationType.RECTANGLE) {
|
||||||
|
this.moveRectangle({ width, height, left, top })
|
||||||
|
} else if (currentTool === OperationType.LINE) {
|
||||||
|
this.moveLine(e.absolutePointer)
|
||||||
|
} else if (currentTool === OperationType.ELLIPSE) {
|
||||||
|
this.moveEllipse({ width, height, left, top })
|
||||||
|
} else if (currentTool === OperationType.TRIANGLE) {
|
||||||
|
this.moveTriangle({ width, height, left, top })
|
||||||
|
} else if (currentTool === OperationType.STAR) {
|
||||||
|
this.moveStar({ width, height, left, top })
|
||||||
|
} else if (currentTool === OperationType.ARROW) {
|
||||||
|
this.moveArrow(e.absolutePointer)
|
||||||
|
}
|
||||||
|
this.demoObject.set({
|
||||||
|
evented: false,
|
||||||
|
})
|
||||||
|
this.canvasManager.canvas.renderAll()
|
||||||
|
}
|
||||||
|
mouseUpEvent(e) {
|
||||||
|
if (!this.isDragging) return;
|
||||||
|
this.isDragging = false;
|
||||||
|
const object = this.demoObject.toJSON("evented")
|
||||||
|
const currentTool = this.toolManager.currentTool.value
|
||||||
|
if (currentTool === OperationType.RECTANGLE) {
|
||||||
|
this.upRectangle(object)
|
||||||
|
} else if (currentTool === OperationType.LINE) {
|
||||||
|
this.upLine(object)
|
||||||
|
} else if (currentTool === OperationType.ELLIPSE) {
|
||||||
|
this.upEllipse(object)
|
||||||
|
} else if (currentTool === OperationType.TRIANGLE) {
|
||||||
|
this.upTriangle(object)
|
||||||
|
} else if (currentTool === OperationType.STAR) {
|
||||||
|
this.upStar(object)
|
||||||
|
} else if (currentTool === OperationType.ARROW) {
|
||||||
|
this.upArrow(object)
|
||||||
|
}
|
||||||
|
this.canvasManager.canvas.remove(this.demoObject)
|
||||||
|
this.demoObject = null
|
||||||
|
this.canvasManager.canvas.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绘制矩形 */
|
||||||
|
downRectangle() {
|
||||||
|
const rect = new fabric.Rect({
|
||||||
|
left: this.startX,
|
||||||
|
top: this.startY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
fill: '#000',
|
||||||
|
})
|
||||||
|
return rect
|
||||||
|
}
|
||||||
|
moveRectangle({ width, height, left, top }) {
|
||||||
|
this.demoObject.set({ width, height, left, top })
|
||||||
|
|
||||||
|
}
|
||||||
|
upRectangle(object) {
|
||||||
|
if (object.width === 0) object.width = 100
|
||||||
|
if (object.height === 0) object.height = 100
|
||||||
|
this.layerManager.createRectLayer(object, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绘制直线 */
|
||||||
|
downLine() {
|
||||||
|
const line = new fabric.Line([this.startX, this.startY, this.startX, this.startY], {
|
||||||
|
stroke: 'black', // 线条颜色
|
||||||
|
strokeWidth: 2 // 线条粗细
|
||||||
|
})
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
moveLine({ x, y }) {
|
||||||
|
this.demoObject.set({
|
||||||
|
x1: this.startX,
|
||||||
|
y1: this.startY,
|
||||||
|
x2: x,
|
||||||
|
y2: y,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
upLine(object) {
|
||||||
|
this.layerManager.createLineLayer(object, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绘制椭圆 */
|
||||||
|
downEllipse() {
|
||||||
|
const circle = new fabric.Ellipse({
|
||||||
|
left: this.startX,
|
||||||
|
top: this.startY,
|
||||||
|
fill: '#000',
|
||||||
|
})
|
||||||
|
return circle
|
||||||
|
}
|
||||||
|
moveEllipse({ width, height, left, top }) {
|
||||||
|
this.demoObject.set({ rx: width / 2, ry: height / 2, left, top })
|
||||||
|
}
|
||||||
|
upEllipse(object) {
|
||||||
|
if (object.rx === 0) object.rx = 50
|
||||||
|
if (object.ry === 0) object.ry = 50
|
||||||
|
this.layerManager.createEllipseLayer(object, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 绘制三角形 */
|
||||||
|
downTriangle() {
|
||||||
|
const triangle = new fabric.Triangle({
|
||||||
|
left: this.startX,
|
||||||
|
top: this.startY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
fill: '#000',
|
||||||
|
})
|
||||||
|
return triangle
|
||||||
|
}
|
||||||
|
moveTriangle({ width, height, left, top }) {
|
||||||
|
this.demoObject.set({ width, height, left, top })
|
||||||
|
}
|
||||||
|
upTriangle(object) {
|
||||||
|
if (object.width === 0) object.width = 100
|
||||||
|
if (object.height === 0) object.height = 100
|
||||||
|
this.layerManager.createTriangleLayer(object, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 绘制五角星 */
|
||||||
|
downStar() {
|
||||||
|
const star = new fabric.Polygon(getStarArr(0, 0), {
|
||||||
|
left: this.startX,
|
||||||
|
top: this.startY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
fill: '#000',
|
||||||
|
strokeLineJoin: 'round', // 圆角连接
|
||||||
|
strokeLineCap: 'round', // 圆角端点
|
||||||
|
});
|
||||||
|
|
||||||
|
return star
|
||||||
|
}
|
||||||
|
moveStar({ width, height, left, top }) {
|
||||||
|
this.demoObject.set({ left, top, width, height, points: getStarArr(width, height) })
|
||||||
|
}
|
||||||
|
upStar(object) {
|
||||||
|
if (object.width === 0) object.width = 100
|
||||||
|
if (object.height === 0) object.height = 100
|
||||||
|
this.layerManager.createStarLayer(object, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绘制箭头 */
|
||||||
|
downArrow() {
|
||||||
|
return new fabric.Path();
|
||||||
|
}
|
||||||
|
moveArrow({ x, y }) {
|
||||||
|
const width = distance(this.startX, this.startY, x, y)
|
||||||
|
const angle = angleBetweenPointsDegrees(this.startX, this.startY, x, y)
|
||||||
|
this.canvasManager.canvas.remove(this.demoObject)
|
||||||
|
const arrow = new fabric.Path(getArrowPath(width, 10), {
|
||||||
|
left: this.startX,
|
||||||
|
top: this.startY,
|
||||||
|
stroke: '#000', // 只设置边框颜色
|
||||||
|
strokeWidth: 3, // 边框宽度
|
||||||
|
fill: 'transparent', // 不填充
|
||||||
|
strokeLineCap: 'round',
|
||||||
|
strokeLineJoin: 'round',
|
||||||
|
originY: 'center',
|
||||||
|
angle: angle,
|
||||||
|
});
|
||||||
|
this.canvasManager.canvas.add(arrow)
|
||||||
|
this.demoObject = arrow
|
||||||
|
}
|
||||||
|
upArrow(object) {
|
||||||
|
if (object.originY !== "center") {
|
||||||
|
this.layerManager.createArrowLayer({
|
||||||
|
left: this.startX,
|
||||||
|
top: this.startY,
|
||||||
|
}, true)
|
||||||
|
} else {
|
||||||
|
this.layerManager.createArrowLayer(object, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dispose() { }
|
||||||
|
}
|
||||||
@@ -54,6 +54,32 @@ export class ToolManager {
|
|||||||
name: OperationType.RECTANGLE,
|
name: OperationType.RECTANGLE,
|
||||||
cursor: "crosshair",
|
cursor: "crosshair",
|
||||||
},
|
},
|
||||||
|
/** 直线工具 */
|
||||||
|
{
|
||||||
|
name: OperationType.LINE,
|
||||||
|
cursor: "crosshair",
|
||||||
|
},
|
||||||
|
/** 箭头工具 */
|
||||||
|
{
|
||||||
|
name: OperationType.ARROW,
|
||||||
|
cursor: "crosshair",
|
||||||
|
},
|
||||||
|
/** 椭圆工具 */
|
||||||
|
{
|
||||||
|
name: OperationType.ELLIPSE,
|
||||||
|
cursor: "crosshair",
|
||||||
|
},
|
||||||
|
/** 三角形工具 */
|
||||||
|
{
|
||||||
|
name: OperationType.TRIANGLE,
|
||||||
|
cursor: "crosshair",
|
||||||
|
},
|
||||||
|
/** 五角星工具 */
|
||||||
|
{
|
||||||
|
name: OperationType.STAR,
|
||||||
|
cursor: "crosshair",
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
onMounted() {
|
onMounted() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { isBoolean } from "lodash-es";
|
import { isBoolean } from "lodash-es";
|
||||||
import { OperationType, OperationTypes } from "../../tools/layerHelper";
|
import { OperationType, OperationTypes } from "../../tools/layerHelper";
|
||||||
import { RectToolManager } from "../RectToolManager"
|
import { ShapeToolManager } from "../ShapeToolManager"
|
||||||
import { AISelectboxToolManager } from "../AISelectboxToolManager"
|
import { AISelectboxToolManager } from "../AISelectboxToolManager"
|
||||||
|
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export class CanvasEventManager {
|
|||||||
toolManager: this.toolManager,
|
toolManager: this.toolManager,
|
||||||
layerManager: this.layerManager,
|
layerManager: this.layerManager,
|
||||||
}
|
}
|
||||||
this.rectToolManager = new RectToolManager(managers)
|
this.shapeToolManager = new ShapeToolManager(managers)
|
||||||
this.aiSelectboxToolManager = new AISelectboxToolManager(managers)
|
this.aiSelectboxToolManager = new AISelectboxToolManager(managers)
|
||||||
|
|
||||||
// 初始化所有事件
|
// 初始化所有事件
|
||||||
@@ -209,9 +209,9 @@ export class CanvasEventManager {
|
|||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
this.aiSelectboxToolManager.mouseDownEvent(opt);
|
this.aiSelectboxToolManager.mouseDownEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||||
// 矩形模式
|
// 形状模式
|
||||||
this.rectToolManager.mouseDownEvent(opt);
|
this.shapeToolManager.mouseDownEvent(opt);
|
||||||
} else if (opt.e.altKey || opt.e.which === 2 || currentTool === OperationType.PAN) {
|
} else if (opt.e.altKey || opt.e.which === 2 || currentTool === OperationType.PAN) {
|
||||||
this.canvas.isDragging = true;
|
this.canvas.isDragging = true;
|
||||||
this.canvas.lastPosX = opt.e.clientX;
|
this.canvas.lastPosX = opt.e.clientX;
|
||||||
@@ -237,9 +237,9 @@ export class CanvasEventManager {
|
|||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||||
// 矩形模式
|
// 形状模式
|
||||||
this.rectToolManager.mouseMoveEvent(opt);
|
this.shapeToolManager.mouseMoveEvent(opt);
|
||||||
} else if (this.canvas.isDragging) {
|
} else if (this.canvas.isDragging) {
|
||||||
const vpt = this.canvas.viewportTransform;
|
const vpt = this.canvas.viewportTransform;
|
||||||
vpt[4] += opt.e.clientX - this.canvas.lastPosX;
|
vpt[4] += opt.e.clientX - this.canvas.lastPosX;
|
||||||
@@ -321,9 +321,9 @@ export class CanvasEventManager {
|
|||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||||
// 矩形模式
|
// 形状模式
|
||||||
this.rectToolManager.mouseDownEvent(opt);
|
this.shapeToolManager.mouseDownEvent(opt);
|
||||||
} else if (currentTool === OperationType.PAN) {
|
} else if (currentTool === OperationType.PAN) {
|
||||||
|
|
||||||
// 平滑停止任何正在进行的惯性动画
|
// 平滑停止任何正在进行的惯性动画
|
||||||
@@ -386,9 +386,9 @@ export class CanvasEventManager {
|
|||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||||
// 矩形模式
|
// 形状模式
|
||||||
this.rectToolManager.mouseMoveEvent(opt);
|
this.shapeToolManager.mouseMoveEvent(opt);
|
||||||
} else if (currentTool === OperationType.PAN) {
|
} else if (currentTool === OperationType.PAN) {
|
||||||
|
|
||||||
// 检查是否是触摸事件
|
// 检查是否是触摸事件
|
||||||
@@ -496,9 +496,9 @@ export class CanvasEventManager {
|
|||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||||
// 矩形模式
|
// 形状模式
|
||||||
this.rectToolManager.mouseUpEvent(opt);
|
this.shapeToolManager.mouseUpEvent(opt);
|
||||||
} else if (currentTool === OperationType.PAN) {
|
} else if (currentTool === OperationType.PAN) {
|
||||||
|
|
||||||
// 重置触摸状态
|
// 重置触摸状态
|
||||||
@@ -668,9 +668,9 @@ export class CanvasEventManager {
|
|||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||||
// 矩形模式
|
// 形状模式
|
||||||
this.rectToolManager.mouseUpEvent(opt);
|
this.shapeToolManager.mouseUpEvent(opt);
|
||||||
} else if (this.canvas.isDragging) {
|
} else if (this.canvas.isDragging) {
|
||||||
// if (this.lastMousePositions.length > 1 && opt && opt.e) {
|
// if (this.lastMousePositions.length > 1 && opt && opt.e) {
|
||||||
// this.animationManager.applyInertiaEffect(
|
// this.animationManager.applyInertiaEffect(
|
||||||
@@ -1074,7 +1074,7 @@ export class CanvasEventManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.rectToolManager?.dispose()
|
this.shapeToolManager?.dispose()
|
||||||
this.aiSelectboxToolManager?.dispose()
|
this.aiSelectboxToolManager?.dispose()
|
||||||
// 移除所有事件监听
|
// 移除所有事件监听
|
||||||
this.canvas.off();
|
this.canvas.off();
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ export class KeyEventManager {
|
|||||||
/** 处理键盘事件 */
|
/** 处理键盘事件 */
|
||||||
_handleKeyDown: any
|
_handleKeyDown: any
|
||||||
handleKeyDown(event: any) {
|
handleKeyDown(event: any) {
|
||||||
|
const activeID = this.stateManager.layerManager.activeID.value
|
||||||
const ctrl = event.ctrlKey ? 'ctrl-' : "";
|
const ctrl = event.ctrlKey ? 'ctrl-' : "";
|
||||||
const shift = event.shiftKey ? 'shift-' : "";
|
const shift = event.shiftKey ? 'shift-' : "";
|
||||||
const key = event.key;
|
const key = event.key;
|
||||||
const reg = new RegExp(`^${ctrl}${shift}${key}$`, 'i')
|
const reg = new RegExp(`^${ctrl}${shift}${key}$`, 'i')
|
||||||
const list = [
|
const list = [
|
||||||
// { key: "ctrl-c", handler: () => this.handleCopy(event) },
|
{ key: "ctrl-c", handler: () => this.stateManager.layerManager.copyLayerById(activeID) },
|
||||||
// { key: "delete", handler: () => this.handleDelete(event) },
|
{ key: "delete", handler: () => this.stateManager.layerManager.deleteLayerById(activeID) },
|
||||||
{ key: "ctrl-z", handler: () => this.stateManager.undoState() },
|
{ key: "ctrl-z", handler: () => this.stateManager.undoState() },
|
||||||
{ key: "ctrl-shift-z", handler: () => this.stateManager.redoState() },
|
{ key: "ctrl-shift-z", handler: () => this.stateManager.redoState() },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -40,3 +40,56 @@ export async function getObjectsBoundingBox(objects = []) {
|
|||||||
height: box2.y - box1.y,
|
height: box2.y - box1.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取五角星数组 */
|
||||||
|
export function getStarArr(width = 0, height = 0) {
|
||||||
|
const arr = [
|
||||||
|
{ x: 0, y: -0.5 }, // 顶点0 (上)
|
||||||
|
{ x: 0.15, y: -0.15 }, // 顶点1 (内)
|
||||||
|
{ x: 0.50, y: -0.15 }, // 顶点2 (右上外)
|
||||||
|
{ x: 0.20, y: 0.10 }, // 顶点3 (内)
|
||||||
|
{ x: 0.30, y: 0.50 }, // 顶点4 (右下外)
|
||||||
|
{ x: 0.0, y: 0.25 }, // 顶点5 (内)
|
||||||
|
{ x: -0.30, y: 0.50 }, // 顶点6 (左下外)
|
||||||
|
{ x: -0.20, y: 0.10 }, // 顶点7 (内)
|
||||||
|
{ x: -0.50, y: -0.15 }, // 顶点8 (左上外)
|
||||||
|
{ x: -0.15, y: -0.15 } // 顶点9 (内)
|
||||||
|
]
|
||||||
|
return arr.map(item => ({
|
||||||
|
x: item.x * width,
|
||||||
|
y: item.y * height,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/** 获取箭头路径 */
|
||||||
|
export function getArrowPath(width = 0, height = 0) {
|
||||||
|
const arr = [
|
||||||
|
["M", 0, height / 2],
|
||||||
|
["L", width, height / 2],
|
||||||
|
["M", width - 8, 0],
|
||||||
|
["L", width, height / 2],
|
||||||
|
["L", width - 8, height],
|
||||||
|
]
|
||||||
|
var path = ""
|
||||||
|
arr.forEach(item => {
|
||||||
|
path += item.join(" ") + " "
|
||||||
|
})
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 计算两点之间的距离 */
|
||||||
|
export function distance(x1, y1, x2, y2) {
|
||||||
|
const dx = x2 - x1;
|
||||||
|
const dy = y2 - y1;
|
||||||
|
return Math.sqrt(dx * dx + dy * dy);
|
||||||
|
}
|
||||||
|
/** 计算两点之间的角度(角度) */
|
||||||
|
export function angleBetweenPointsDegrees(x1, y1, x2, y2) {
|
||||||
|
const dx = x2 - x1;
|
||||||
|
const dy = y2 - y1;
|
||||||
|
|
||||||
|
// 计算弧度并转换为角度
|
||||||
|
const rad = Math.atan2(dy, dx);
|
||||||
|
const deg = rad * 180 / Math.PI;
|
||||||
|
|
||||||
|
return deg;
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,12 +29,16 @@ export const OperationType = {
|
|||||||
PAN: "pan", // 拖拽模式
|
PAN: "pan", // 拖拽模式
|
||||||
DRAW: "draw", // 绘画模式
|
DRAW: "draw", // 绘画模式
|
||||||
ERASER: "eraser", // 橡皮擦模式
|
ERASER: "eraser", // 橡皮擦模式
|
||||||
IMAGE: "image",// 图片工具模式
|
|
||||||
SELECTBOX: "selectbox",// 选择框工具模式
|
SELECTBOX: "selectbox",// 选择框工具模式
|
||||||
|
|
||||||
RECTANGLE: "rectangle",// 矩形工具模式
|
RECTANGLE: "rectangle",// 矩形工具模式
|
||||||
|
LINE: "line",// 直线工具模式
|
||||||
|
ARROW: "arrow",// 箭头工具模式
|
||||||
|
ELLIPSE: "ellipse",// 椭圆工具模式
|
||||||
|
TRIANGLE: "triangle",// 三角形工具模式
|
||||||
|
STAR: "star",// 五角星工具模式
|
||||||
|
|
||||||
TEXT: "text",// 文字工具模式
|
TEXT: "text",// 文字工具模式
|
||||||
UNDO: "undo",// 撤销工具模式
|
|
||||||
REDO: "redo",// 重做工具模式
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 所有操作模式类型列表
|
// 所有操作模式类型列表
|
||||||
|
|||||||
@@ -17,9 +17,12 @@ import 'element-plus/dist/index.css'
|
|||||||
|
|
||||||
import ignoredWarning from './ignoredWarning'
|
import ignoredWarning from './ignoredWarning'
|
||||||
|
|
||||||
|
import vEllipsis from './utils/ellispsis'
|
||||||
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
ignoredWarning(app)
|
ignoredWarning(app)
|
||||||
|
app.directive('ellipsis', vEllipsis)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
.use(ElementPlus)
|
.use(ElementPlus)
|
||||||
.use(store)
|
.use(store)
|
||||||
|
|||||||
52
src/utils/ellispsis.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import type { Directive } from 'vue'
|
||||||
|
/**
|
||||||
|
* 多行文本省略指令(悬浮显示完整内容)
|
||||||
|
* @directive v-ellipsis
|
||||||
|
* @param {number} [value=3] - 超过value行数时显示省略号,不传参数默认为3
|
||||||
|
*/
|
||||||
|
|
||||||
|
const applyStyles = (el: HTMLElement, binding: any) => {
|
||||||
|
const lines = typeof binding.value === 'number' && binding.value > 0 ? binding.value : 3
|
||||||
|
|
||||||
|
el.style.display = '-webkit-box'
|
||||||
|
el.style.webkitBoxOrient = 'vertical'
|
||||||
|
el.style.overflow = 'hidden'
|
||||||
|
el.style.webkitLineClamp = lines.toString()
|
||||||
|
|
||||||
|
el.style.maxHeight = `${lines}lh`
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkTruncated = (el: HTMLElement) => {
|
||||||
|
const isTruncated = el.scrollHeight > el.clientHeight + 1
|
||||||
|
if (isTruncated) {
|
||||||
|
el.title = el.textContent?.trim() || ''
|
||||||
|
} else {
|
||||||
|
el.removeAttribute('title')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const vEllipsis: Directive<HTMLElement> = {
|
||||||
|
mounted(el, binding) {
|
||||||
|
applyStyles(el, binding)
|
||||||
|
checkTruncated(el)
|
||||||
|
|
||||||
|
const ro = new ResizeObserver(() => checkTruncated(el))
|
||||||
|
ro.observe(el)
|
||||||
|
;(el as any)._ellipsisObserver = ro
|
||||||
|
},
|
||||||
|
|
||||||
|
updated(el, binding) {
|
||||||
|
applyStyles(el, binding)
|
||||||
|
checkTruncated(el)
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted(el) {
|
||||||
|
const ro = (el as any)._ellipsisObserver
|
||||||
|
if (ro) {
|
||||||
|
ro.disconnect()
|
||||||
|
delete (el as any)._ellipsisObserver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default vEllipsis
|
||||||
@@ -53,12 +53,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else-if="type === 'url'">
|
<template v-else-if="type === 'url'">
|
||||||
<div class="url-list flex">
|
<div class="url-list flex">
|
||||||
<div class="url-item" v-for="item in urlList" :key="item">
|
<div class="url-item flex flex-col" v-for="item in urlList" :key="item">
|
||||||
<div class="url-title" @click="handleClickUrl(item)">
|
<div class="url-title" v-ellipsis="3" @click="handleClickUrl(item)">
|
||||||
{{ item }}
|
{{ item }}
|
||||||
<img src="@/assets/images/link-outer.png" class="link-outer" />
|
<img src="@/assets/images/link-outer.png" class="link-outer" />
|
||||||
</div>
|
</div>
|
||||||
<div class="url-link">{{ item }}</div>
|
<div class="url-link" v-ellipsis="3">{{ item }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -328,16 +328,27 @@
|
|||||||
.url-item {
|
.url-item {
|
||||||
width: 24rem;
|
width: 24rem;
|
||||||
height: 28.7rem;
|
height: 28.7rem;
|
||||||
|
line-height: 2rem;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
background: url('@/assets/images/web-card.png') no-repeat;
|
background: url('@/assets/images/web-card.png') no-repeat;
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
padding: 5rem 1.5rem;
|
padding: 5rem 1.5rem;
|
||||||
|
row-gap: 0.6rem;
|
||||||
|
// .url-title,.url-link{
|
||||||
|
// // 两行省略
|
||||||
|
// display: -webkit-box;
|
||||||
|
// -webkit-line-clamp: 2;
|
||||||
|
// line-clamp: 2;
|
||||||
|
// -webkit-box-orient: vertical;
|
||||||
|
// overflow: hidden;
|
||||||
|
// text-overflow: ellipsis;
|
||||||
|
// }
|
||||||
.url-title {
|
.url-title {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: 'Medium';
|
font-family: 'Medium';
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
color: #232323;
|
color: #232323;
|
||||||
padding-bottom: 0.6rem;
|
max-height: 4rem;
|
||||||
.link-outer {
|
.link-outer {
|
||||||
width: 1.2rem;
|
width: 1.2rem;
|
||||||
height: 1.2rem;
|
height: 1.2rem;
|
||||||
|
|||||||
@@ -87,7 +87,8 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGetProjectInfoAndHistory = () => {
|
const handleGetProjectInfoAndHistory = () => {
|
||||||
|
handleOpenSketch()
|
||||||
getProjectInfo({ id: route.params.id }).then((res) => {
|
getProjectInfo({ id: route.params.id }).then((res) => {
|
||||||
if (res) agentRef.value.setChatInfo(res)
|
if (res) agentRef.value.setChatInfo(res)
|
||||||
let data = res?.project || res
|
let data = res?.project || res
|
||||||
|
|||||||