This commit is contained in:
lzp
2026-03-25 17:27:47 +08:00
17 changed files with 304 additions and 49 deletions

View File

@@ -44,7 +44,7 @@
const tools = ref([
{ name: TOOLS.SELECT, icon: 'c-select', iconSize: 16, disabled: ref(false) },
{ name: TOOLS.MOVE, icon: 'c-move', iconSize: 18, disabled: ref(false) },
{ name: TOOLS.TEXT, icon: 'c-text', iconSize: 18, disabled: ref(false) },
{ name: TOOLS.TEXT, icon: 'c-text', iconSize: 22, disabled: ref(false) },
{ type: 'line' },
{
name: TOOLS.UNDO,

View File

@@ -4,6 +4,7 @@
<div
tabindex="0"
class="input"
:style="data.textStyle"
ref="inputRef"
:contenteditable="active"
@input="onInput"
@@ -11,6 +12,9 @@
@paste.prevent
@keydown.stop
></div>
<div class="tools" @mousedown="onMouseDown" v-if="active">
<myTextTools :textStyle="data.textStyle"></myTextTools>
</div>
<span class="delete" @mousedown.stop @click="emit('delete-node')" v-show="active">
<svg-icon name="close" size="7" size-unit="px" />
</span>
@@ -19,6 +23,7 @@
<script setup lang="ts">
import { reactive, ref, onMounted, nextTick, watch } from 'vue'
import myTextTools from '@/components/Canvas/FlowCanvas/components/tools/my-textTools.vue'
const props = defineProps({
active: {
type: Boolean,
@@ -31,7 +36,15 @@
})
const emit = defineEmits(['update-data', 'delete-node'])
const data = reactive({
text: props.data?.text || '点击编辑文本'
text: props.data?.text || '点击编辑文本',
textStyle:{
'--font-size':props.data?.textStyle?.['--font-size'] || '16px',
'--font-color':props.data?.textStyle?.['--font-color'] || '#000',
'--font-style':props.data?.textStyle?.['--font-style'] || 'normal',//italic
'--font-family':props.data?.textStyle?.['--font-family'] || 'Medium',
'--font-weight':props.data?.textStyle?.['--font-weight'] || 'initial',//blod
'--font-decoration':props.data?.textStyle?.['--font-decoration'] || 'none',//underline
}
})
const time = ref(null)
const inputRef = ref<any>()
@@ -70,19 +83,25 @@
<style lang="less" scoped>
.text {
user-select: none;
border: 1px solid transparent;
padding: 2px;
position: relative;
&.active {
border-color: #000;
> .input {
border-color: #000;
cursor: text;
}
}
> .input {
border: 1px solid transparent;
padding: 2px;
outline: none;
min-width: 2px;
font-size: 16px;
color: var(--font-color);
font-style: var(--font-style);
font-weight: var(--font-weight);
font-size: var(--font-size);
text-decoration: var(--font-decoration);
font-family: var(--font-family);
}
> .delete {
position: absolute;
@@ -94,5 +113,15 @@
border-radius: 50%;
cursor: pointer;
}
> .tools{
position: absolute;
left: 50%;
transform: translate(-50%,-100%);
top: -10px;
height: 41px;
border: 2px solid #EBEBEB;
border-radius: 11px;
background-color: #fff;
}
}
</style>

View File

@@ -43,6 +43,7 @@
.el-select-dropdown {
li {
padding-left: 8px;
padding-right: 8px;
height: 30px;
line-height: 30px;
font-size: 12px;

View File

@@ -0,0 +1,141 @@
<template>
<div class="my-textTools">
<input class="color" type="color" ref="colorInput" @change="changeColor" />
<div class="interval"></div>
<div class="fontFamily">
<my-select v-model="textStyle['--font-family']" @change="changeFontFamily" :list="fontFamilyList[locale]" />
</div>
<div class="interval"></div>
<div class="size">
<div class="label" @click="changeFontSize(-1)">
-
</div>
<div class="value">{{ parseInt(props.textStyle?.['--font-size']) }}</div>
<div class="label" @click="changeFontSize(1)">
+
</div>
</div>
<div class="interval"></div>
<div class="fontStyle">
<div class="label" @click="changeFontStyle('--font-weight',['bold','normal'])" :class="{'active': props.textStyle['--font-weight'] === 'bold'}">
<svg-icon name="textToolsweight" size="12" size-unit="px" color="#000" />
</div>
<div class="label" @click="changeFontStyle('--font-style',['italic','normal'])" :class="{'active': props.textStyle['--font-style'] === 'italic'}">
<svg-icon name="textToolsStyle" size="12" size-unit="px" color="#000" />
</div>
<div class="label" @click="changeFontStyle('--font-decoration',['underline','none'])" :class="{'active': props.textStyle['--font-decoration'] === 'underline'}">
<svg-icon name="textToolsDecoration" size="12" size-unit="px" color="#000" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, markRaw, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
const emit = defineEmits(['update:textStyle'])
const { locale } = useI18n()
const fontFamilyList = ref({
ENGLISH: [
{ value:'Medium',label:'Medium' },
{ value:'Arial',label:'Arial' },
{ value:'Helvetica',label:'Helvetica' },
{ value:'Times New Roman',label:'Times New Roman' },
{ value:'Georgia',label:'Georgia' },
{ value:'Verdana',label:'Verdana' },
{ value:'Courier New',label:'Courier New' },
{ value:'Lucida Console',label:'Lucida Console' },
{ value:'General Sans',label:'General Sans' },
{ value:'Inter',label:'Inter' },
],
CHINESE_SIMPLIFIED: [
{ value:'Medium',label:'Medium' },
{ value:'微软雅黑',label:'微软雅黑' },
{ value:'宋体',label:'宋体' },
{ value:'黑体',label:'黑体' },
{ value:'仿宋',label:'仿宋' },
{ value:'楷体',label:'楷体' },
{ value:'苹方',label:'苹方' },
{ value:'华文黑体',label:'华文黑体' },
{ value:'华文宋体',label:'华文宋体' },
{ value:'华文楷体',label:'华文楷体' },
{ value:'思源黑体',label:'思源黑体' },
{ value:'MiSans',label:'MiSans' },
]
})
const props = defineProps({
textStyle: { type: Object },
})
const changeFontFamily = (val: string) => {
props.textStyle['--font-family'] = val
onChange()
}
const changeColor = (e: Event) => {
const target = e.target as HTMLInputElement
props.textStyle['--font-color'] = target.value
onChange()
}
const changeFontSize = (val: number) => {
if (parseInt(props.textStyle?.['--font-size']) + val <= 9 || parseInt(props.textStyle?.['--font-size']) + val >= 51) {
return
}
props.textStyle['--font-size'] = parseInt(props.textStyle?.['--font-size']) + val + 'px'
onChange()
}
const changeFontStyle = (key: string,value: string[]) => {
props.textStyle[key] = props.textStyle[key] === value[0] ? value[1] : value[0]
onChange()
}
const onChange = () => {
emit('update:textStyle', props.textStyle)
}
</script>
<style lang="less" scoped>
.my-textTools {
height: 100%;
display: flex;
padding: 6px 19px;
align-items: center;
.interval{
margin: 0 16px;
height: 25px;
width: 1px;
background-color: #EBEBEB;
}
.label{
height: 20px;
width: 20px;
border-radius: 2px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
&:hover{
background-color: #e5e5e5;
}
&.active{
background-color: #e5e5e5;
}
}
.fontFamily{
width: 103px;
--el-text-color-regular: #000
}
.color{
width: 55px;
height: 25px;
}
.size{
display: flex;
align-items: center;
gap: 16px;
}
.fontStyle{
display: flex;
gap: 16px;
}
}
</style>

View File

@@ -47,7 +47,7 @@ export class NodeManager {
//获取上级节点所生成的最后一个node设置位置为最后一个节点的xy 加上 节点间距
const superiorGenerateNodes = this.stateManager.getSubordNodes(superiorID)
const currentNode = superiorGenerateNodes.find((node) => {
return node.data.createIndexPosition === options?.data?.createIndexPosition
return (node.data.createIndexPosition === options?.data?.createIndexPosition && options?.data?.createIndexPosition)
})
const endGenerateNode = superiorGenerateNodes.reduce((max, current) => {
return current.data.createIndexPosition > max.data.createIndexPosition ? current : max

View File

@@ -127,7 +127,7 @@ export class StateManager {
getNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.id === id) }
/** 获取下级节点 */
getSubordNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.data.superiorID === id) }
getLastNode() { console.log(this.nodes.value); return this.nodes.value[this.nodes.value.length - 1] }
getLastNode() { return this.nodes.value[this.nodes.value.length - 1] }
/** 获取上级生成节点的图片 */
getSuperiorNodeImage(superiorID: string) {