This commit is contained in:
lzp
2026-03-03 15:39:54 +08:00
parent 76afe1022f
commit b3b7ce2f2a
15 changed files with 141 additions and 46 deletions

View File

@@ -55,7 +55,8 @@
const id = props.node.id const id = props.node.id
if (!id) return if (!id) return
stateManager.deleteNode(id) stateManager.deleteNode(id)
nodeManager.createCardNode({ data: { type: v.type } }) const superiorID = props.node.data.superiorID
nodeManager.createCardNode({ data: { type: v.type, superiorID } })
} }
defineExpose({}) defineExpose({})

View File

@@ -110,6 +110,7 @@
if (attrs.node.data) attrs.node.data.data = data if (attrs.node.data) attrs.node.data.data = data
nodeManager.createResultNode({ nodeManager.createResultNode({
data: { data: {
superiorID: attrs.node.id,
tier: currentComponent.value.tier, tier: currentComponent.value.tier,
data: { data: {
url: 'https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__' url: 'https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__'

View File

@@ -44,18 +44,19 @@
} }
}) })
const nodes = computed(() => props.stateManager.nodes.value) const nodes = computed(() => props.stateManager.nodes.value)
const firstNode = computed(() => nodes.value[0]) const isSubord = computed(() => nodes.value.some((v) => v.data.superiorID === props.node.id))
const lastNode = computed(() => nodes.value[nodes.value.length - 1])
const tier = computed(() => Number(props.node?.data?.tier || 0)) const tier = computed(() => Number(props.node?.data?.tier || 0))
const isAdd = computed( const isAdd = computed(
() => () =>
props.node.id === lastNode.value.id && !isSubord.value &&
NODE_DATATYPE.RESULT_IMAGE === props.node.data.type && NODE_DATATYPE.RESULT_IMAGE === props.node.data.type &&
!(tier.value === NODE_DATATIER.TO_3VIEW) !(tier.value === NODE_DATATIER.TO_3VIEW)
) )
const onAdd = () => { const onAdd = () => {
const tier_ = tier.value + 1 const tier_ = tier.value + 1
props.stateManager.nodeManager.createCardsSelect({ data: { tier: tier_ } }) props.stateManager.nodeManager.createCardsSelect({
data: { tier: tier_, superiorID: props.node.id }
})
} }
const posCenter = computed(() => { const posCenter = computed(() => {
const arr = [NODE_DATATYPE.RESULT_IMAGE] const arr = [NODE_DATATYPE.RESULT_IMAGE]

View File

@@ -37,13 +37,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onBeforeUnmount, useAttrs } from 'vue' import { reactive, ref, onBeforeUnmount, useAttrs, inject } from 'vue'
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
} }
}) })
const nodeManager = inject('nodeManager') as any
const attrs = useAttrs() const attrs = useAttrs()
const showHeader = ref(!!attrs.node?.data?.isHeader) const showHeader = ref(!!attrs.node?.data?.isHeader)
const showMenu = ref(false) const showMenu = ref(false)
@@ -55,7 +56,7 @@
{ label: 'Copy', tip: 'Ctrl+C', on: () => {} }, { label: 'Copy', tip: 'Ctrl+C', on: () => {} },
{ label: 'Paste', tip: 'Ctrl+V', on: () => {} }, { label: 'Paste', tip: 'Ctrl+V', on: () => {} },
{ label: 'Duplicate', tip: 'Ctrl+D', on: () => {} }, { label: 'Duplicate', tip: 'Ctrl+D', on: () => {} },
{ label: 'Delete', tip: 'Del', on: () => {} }, { label: 'Delete', tip: 'Del', on: () => nodeManager.deleteNode(attrs.node.id) },
{ isDivide: true }, { isDivide: true },
{ label: 'Bring to font', tip: 'Del', on: () => {} }, { label: 'Bring to font', tip: 'Del', on: () => {} },
{ label: 'Send to back', tip: 'Del', on: () => {} }, { label: 'Send to back', tip: 'Del', on: () => {} },

View File

@@ -27,8 +27,10 @@
<zoom <zoom
:zoom="stateManager.zoom.value" :zoom="stateManager.zoom.value"
:step="0.1" :step="0.1"
is-home
@add="(e) => flowManager.setZoom(e)" @add="(e) => flowManager.setZoom(e)"
@sub="(e) => flowManager.setZoom(e)" @sub="(e) => flowManager.setZoom(e)"
@home="() => fitView({ maxZoom: 1 })"
/> />
</template> </template>
@@ -87,7 +89,6 @@
provide('nodeManager', nodeManager) provide('nodeManager', nodeManager)
provide('nodeManager', nodeManager) provide('nodeManager', nodeManager)
const { fitView } = useVueFlow() const { fitView } = useVueFlow()
const { layout } = useLayout() const { layout } = useLayout()
const index = ref(0) const index = ref(0)

View File

@@ -9,6 +9,9 @@ export class FlowManager {
this.stateManager.zoom.value = zoom this.stateManager.zoom.value = zoom
this.vueFlow.value.zoomTo(zoom) this.vueFlow.value.zoomTo(zoom)
} }
getNodeById(id: string) {
return this.vueFlow.value.getNode(id)
}
getLastNode() { getLastNode() {
const lastNode = this.stateManager.getLastNode() const lastNode = this.stateManager.getLastNode()
if (lastNode?.id) { if (lastNode?.id) {

View File

@@ -28,13 +28,17 @@ export class NodeManager {
/** 创建节点 */ /** 创建节点 */
createNode(options: NodeOptions) { createNode(options: NodeOptions) {
const lastNode = this.stateManager.flowManager.getLastNode(); const superiorID = options?.data?.superiorID
const snode = superiorID ? this.stateManager.flowManager.getNodeById(superiorID) : this.stateManager.flowManager.getLastNode();
const id = options.id || createId() const id = options.id || createId()
const positionX = options.positionX || 0 const positionX = options.positionX || 0
const positionY = options.positionY || 0 const positionY = options.positionY || 0
const position = options.position || const position = options.position ||
!lastNode ? { x: positionX, y: positionY } : !snode ? { x: positionX, y: positionY } :
{ x: lastNode.position.x + lastNode.dimensions.width + 50 + positionX, y: lastNode.position.y + positionY } {
x: snode.position.x + snode.dimensions.width + 50 + positionX,
y: snode.position.y + positionY
}
const data = options?.data || {} const data = options?.data || {}
data['component'] = options.component data['component'] = options.component
const options_ = { const options_ = {

View File

@@ -6,7 +6,7 @@ export interface NodesItem {
type: string type: string
class: string class: string
position: { x: number, y: number } position: { x: number, y: number }
data: { component: any, type: string } data: { component: any, type: string, superiorID?: string }
} }
export class StateManager { export class StateManager {
vueFlow: any vueFlow: any
@@ -30,15 +30,25 @@ export class StateManager {
this.nodes = ref<NodesItem[]>([]); this.nodes = ref<NodesItem[]>([]);
this.nodes_ = computed(() => { this.nodes_ = computed(() => {
return this.nodes.value.map((node, index) => { return this.nodes.value.map((node, index) => {
const obj = { const obj = node;
...node, // if (index === 0) {
} // obj.type = NODE_TYPE.INPUT;
if (index === 0) { // } else if (index === this.nodes.value.length - 1) {
// obj.type = NODE_TYPE.OUTPUT;
// } else {
// obj.type = NODE_TYPE.SECONDARY;
// }
const superiorID = node.data.superiorID;
const isSuperior = this.nodes.value.some((v) => v.id === superiorID)
const isSubord = this.nodes.value.some((v) => v.data.superiorID === node.id)
if (!superiorID) {// 没有上级ID
obj.type = NODE_TYPE.INPUT; obj.type = NODE_TYPE.INPUT;
} else if (index === this.nodes.value.length - 1) { } else if (isSuperior && isSubord) {// 有上级ID并找到下级
obj.type = NODE_TYPE.OUTPUT;
} else {
obj.type = NODE_TYPE.SECONDARY; obj.type = NODE_TYPE.SECONDARY;
} else if (isSuperior && !isSubord) {// 有上级ID但未找到下级
obj.type = NODE_TYPE.OUTPUT;
} else {// 其他情况-有上级ID未找到上级
obj.type = NODE_TYPE.INPUT;
} }
return obj return obj
}) })
@@ -47,12 +57,25 @@ export class StateManager {
this.edges = computed(() => { this.edges = computed(() => {
const arr = [] const arr = []
this.nodes.value.forEach((node, index) => { this.nodes.value.forEach((node, index) => {
if (index < this.nodes.value.length - 1) { // if (index < this.nodes.value.length - 1) {
const id = node.id // const source = node.id
const target = this.nodes.value[index + 1].id // const target = this.nodes.value[index + 1].id
// arr.push({
// id: `el-${source}-${target}`,
// source: source,
// target: target,
// selectable: false,
// type: 'default'
// })
// }
const superiorID = node.data.superiorID;
const isSuperior = this.nodes.value.some((v) => v.id === superiorID)
if (superiorID && isSuperior) {
const source = node.data.superiorID
const target = node.id
arr.push({ arr.push({
id: `el-${id}-${target}`, id: `el-${source}-${target}`,
source: id, source: source,
target: target, target: target,
selectable: false, selectable: false,
type: 'default' type: 'default'

View File

@@ -1,5 +1,6 @@
<template> <template>
<div class="zoom"> <div class="zoom">
<span class="icon" @click="onHome" v-if="isHome"><svg-icon name="home" size="14" /></span>
<span class="icon" @click="zoomIn"><svg-icon name="add" size="14" /></span> <span class="icon" @click="zoomIn"><svg-icon name="add" size="14" /></span>
<span class="value">{{ Math.round(zoom * 100) }}%</span> <span class="value">{{ Math.round(zoom * 100) }}%</span>
<span class="icon" @click="zoomOut"><svg-icon name="sub" size="14" /></span> <span class="icon" @click="zoomOut"><svg-icon name="sub" size="14" /></span>
@@ -10,9 +11,13 @@
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
const props = defineProps({ const props = defineProps({
zoom: { default: 1, type: Number }, zoom: { default: 1, type: Number },
step: { default: 0.1, type: Number } step: { default: 0.1, type: Number },
isHome: { default: false, type: Boolean }
}) })
const emit = defineEmits(['add', 'sub']) const emit = defineEmits(['home', 'add', 'sub'])
const onHome = () => {
emit('home')
}
const zoomIn = () => { const zoomIn = () => {
emit('add', Number(props.zoom) + props.step) emit('add', Number(props.zoom) + props.step)
} }
@@ -27,7 +32,6 @@
bottom: 4rem; bottom: 4rem;
right: 3rem; right: 3rem;
padding: 0.7rem 1rem; padding: 0.7rem 1rem;
min-width: 12.8rem;
height: 4.2rem; height: 4.2rem;
border-radius: 0.8rem; border-radius: 0.8rem;
border: 0.1rem solid #ffcf90; border: 0.1rem solid #ffcf90;
@@ -48,7 +52,7 @@
--svg-icon-color: #0d0d0d; --svg-icon-color: #0d0d0d;
} }
> .value { > .value {
flex: 1; min-width: 5rem;
text-align: center; text-align: center;
font-family: Medium; font-family: Medium;
font-size: 1.6rem; font-size: 1.6rem;

View File

@@ -17,22 +17,26 @@ const router = createRouter({
{ {
path: '/index', path: '/index',
name: 'index', name: 'index',
component: () => import('../views/login/index.vue') component: () => import('../views/login/index.vue'),
meta: { notToken: true }
}, },
{ {
path: '/login', path: '/login',
name: 'login', name: 'login',
component: () => import('../views/login/login.vue') component: () => import('../views/login/login.vue'),
meta: { notToken: true }
}, },
{ {
path: '/register', path: '/register',
name: 'register', name: 'register',
component: () => import('../views/login/register.vue') component: () => import('../views/login/register.vue'),
meta: { notToken: true }
}, },
{ {
path: '/retrievepass', path: '/retrievepass',
name: 'retrievepass', name: 'retrievepass',
component: () => import('../views/login/retrieve-password.vue') component: () => import('../views/login/retrieve-password.vue'),
meta: { notToken: true }
}, },
{ {
path: '/nuic', path: '/nuic',
@@ -71,13 +75,15 @@ const router = createRouter({
{ {
path: '/canvastest', path: '/canvastest',
name: 'canvastest', name: 'canvastest',
component: () => import('../components/Canvas/CanvasTest.vue') component: () => import('../components/Canvas/CanvasTest.vue'),
meta: { notToken: true }
}, },
{ {
path: '/:pathMatch(.*)', path: '/:pathMatch(.*)',
name: '404', name: '404',
component: () => import('../views/404.vue') component: () => import('../views/404.vue'),
meta: { notToken: true }
} }
] ]
}) })

View File

@@ -1,7 +1,11 @@
import router from './index' import router from './index'
import { useGlobalStore } from '@/stores/global' import { useGlobalStore, useUserInfoStore } from '@/stores'
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
if (!to.meta.notToken && !useUserInfoStore().state.token) {
return next({ name: 'login' })
}
useGlobalStore().setViewLoading(true) useGlobalStore().setViewLoading(true)
next() next()
}) })

View File

@@ -5,16 +5,19 @@ export const useGlobalStore = defineStore('global', () => {
loading: false,// 全局loading loading: false,// 全局loading
view_loading: false,// 页面跳转loading view_loading: false,// 页面跳转loading
homeLeftNavCollapse: false,// 首页左侧导航是否折叠 homeLeftNavCollapse: false,// 首页左侧导航是否折叠
homeAnimation: false,// 首页动画
}) })
const setLoading = (v: boolean) => { state.value.loading = v } const setLoading = (v: boolean) => { state.value.loading = v }
const setViewLoading = (v: boolean) => { state.value.view_loading = v } const setViewLoading = (v: boolean) => { state.value.view_loading = v }
const setHomeLeftNavCollapse = (v: boolean) => { state.value.homeLeftNavCollapse = v } const setHomeLeftNavCollapse = (v: boolean) => { state.value.homeLeftNavCollapse = v }
const setHomeAnimation = (v: boolean) => { state.value.homeAnimation = v }
return { return {
state, state,
setLoading, setLoading,
setViewLoading, setViewLoading,
setHomeLeftNavCollapse, setHomeLeftNavCollapse,
setHomeAnimation,
} }
}) })

View File

@@ -1,9 +1,9 @@
<template> <template>
<div class="bg bg-1"> <div class="bg bg-1" :class="{ animation: isAnimation }">
<div class="topright"></div> <div class="topright"></div>
<div class="bottomleft"></div> <div class="bottomleft"></div>
</div> </div>
<div class="bg bg-2"> <div class="bg bg-2" :class="{ animation: isAnimation }">
<div class="bottom-1"></div> <div class="bottom-1"></div>
<div class="bottom-2"></div> <div class="bottom-2"></div>
<div class="bottom-3"></div> <div class="bottom-3"></div>
@@ -24,17 +24,23 @@
import LeftNav from './left-nav.vue' import LeftNav from './left-nav.vue'
import TopNav from './top-nav.vue' import TopNav from './top-nav.vue'
import setting from './setting/index.vue' import setting from './setting/index.vue'
import { useUserInfoStore } from '@/stores' import { useUserInfoStore, useGlobalStore } from '@/stores'
import FlowCanvas from '@/components/Canvas/FlowCanvas/index.vue' import FlowCanvas from '@/components/Canvas/FlowCanvas/index.vue'
import myEvent from '@/utils/myEvent' import myEvent from '@/utils/myEvent'
const userInfoStore = useUserInfoStore() const userInfoStore = useUserInfoStore()
const globalStore = useGlobalStore()
userInfoStore.getUserInfo() userInfoStore.getUserInfo()
const isAnimation = computed(() => globalStore.state.homeAnimation)
const flowCanvasRef = ref(null) const flowCanvasRef = ref(null)
const openFlowCanvas = (config) => { const openFlowCanvas = (config) => {
flowCanvasRef.value.open(config) flowCanvasRef.value.open(config)
} }
myEvent.add('openFlowCanvas', openFlowCanvas) myEvent.add('openFlowCanvas', openFlowCanvas)
onMounted(() => {
setTimeout(() => {
globalStore.setHomeAnimation(false)
}, 1000)
})
onUnmounted(() => { onUnmounted(() => {
myEvent.remove('openFlowCanvas', openFlowCanvas) myEvent.remove('openFlowCanvas', openFlowCanvas)
}) })
@@ -66,10 +72,15 @@
.bg-1 { .bg-1 {
z-index: -1; z-index: -1;
background: #f8f7f5; background: #f8f7f5;
animation: opacity-in 0.5s ease-in-out 1 both; &.animation {
animation: opacity-in 0.5s ease-in-out 1 both;
}
} }
.bg-2 { .bg-2 {
animation: z-index-10to-1 0.5s ease-in-out 1 both; z-index: -1;
&.animation {
animation: z-index-10to-1 0.5s ease-in-out 1 both;
}
} }
.bg { .bg {
position: absolute; position: absolute;
@@ -115,7 +126,6 @@
transform: rotate(-25.36deg); transform: rotate(-25.36deg);
} }
> .bottom-1 { > .bottom-1 {
animation: bottom-1 0.5s ease-in-out 1 both;
background: linear-gradient( background: linear-gradient(
87.58deg, 87.58deg,
rgba(241, 193, 145, 0.8) 23.02%, rgba(241, 193, 145, 0.8) 23.02%,
@@ -126,10 +136,14 @@
); );
filter: blur(13.17rem); filter: blur(13.17rem);
transform: matrix(-1, 0.03, -0.05, -1, 0, 0); transform: matrix(-1, 0.03, -0.05, -1, 0, 0);
width: 138.014rem;
height: 29.323rem;
left: 32.123rem;
bottom: -21rem;
transform: translate(0, 0);
} }
> .bottom-2 { > .bottom-2 {
animation: bottom-2 0.5s ease-in-out 1 both;
background: conic-gradient( background: conic-gradient(
from 94.36deg at 71.77% 41.01%, from 94.36deg at 71.77% 41.01%,
rgba(242, 171, 180, 0.2) 0deg, rgba(242, 171, 180, 0.2) 0deg,
@@ -140,9 +154,14 @@
); );
filter: blur(12.927rem); filter: blur(12.927rem);
transform: matrix(-0.05, 1, -1, -0.03, 0, 0); transform: matrix(-0.05, 1, -1, -0.03, 0, 0);
width: 42.215rem;
height: 98.009rem;
left: 150rem;
bottom: -65rem;
transform: translate(0, 0);
} }
> .bottom-3 { > .bottom-3 {
animation: bottom-3 0.5s ease-in-out 1 both;
background: linear-gradient( background: linear-gradient(
130.72deg, 130.72deg,
rgba(242, 171, 180, 0.24) 29.52%, rgba(242, 171, 180, 0.24) 29.52%,
@@ -153,7 +172,25 @@
); );
filter: blur(11.5411rem); filter: blur(11.5411rem);
transform: matrix(-0.26, -0.97, 0.99, -0.15, 0, 0); transform: matrix(-0.26, -0.97, 0.99, -0.15, 0, 0);
width: 51.936rem;
height: 97.139rem;
left: 40rem;
bottom: -65rem;
transform: translate(0, 0);
} }
&.animation {
> .bottom-1 {
animation: bottom-1 0.5s ease-in-out 1 both;
}
> .bottom-2 {
animation: bottom-2 0.5s ease-in-out 1 both;
}
> .bottom-3 {
animation: bottom-3 0.5s ease-in-out 1 both;
}
}
@keyframes bottom-1 { @keyframes bottom-1 {
0% { 0% {
width: 15rem; width: 15rem;

View File

@@ -32,11 +32,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, markRaw } from 'vue' import { computed, ref, markRaw } from 'vue'
import { useGlobalStore } from '@/stores'
import { UpdateUserProfile } from '@/api/user' import { UpdateUserProfile } from '@/api/user'
import nuic1 from './nuic-1.vue' import nuic1 from './nuic-1.vue'
import nuic2 from './nuic-2.vue' import nuic2 from './nuic-2.vue'
import nuic3 from './nuic-3.vue' import nuic3 from './nuic-3.vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
const globalStore = useGlobalStore()
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const active = computed(() => Number(route.query.index || 0)) const active = computed(() => Number(route.query.index || 0))
@@ -69,6 +71,7 @@
UpdateUserProfile(data) UpdateUserProfile(data)
.then((res) => { .then((res) => {
if (!res) return (loading.value = false) if (!res) return (loading.value = false)
globalStore.setHomeAnimation(true)
const time = stime - Date.now() + 3000 const time = stime - Date.now() + 3000
setTimeout(() => { setTimeout(() => {
router.push({ name: 'mainInput' }) router.push({ name: 'mainInput' })

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="nuic-1"> <div class="nuic-1">
<img src="@/assets/images/nuic/nuic-1-bg.png" /> <img src="@/assets/images/nuic/nuic-1-bg.png" />
<p class="hi">{{ $t('Nuic.hiName', { name: 'Aaa' }) }}</p> <p class="hi">{{ $t('Nuic.hiName', { name }) }}</p>
<p class="title" v-html="$t('Nuic.nuic1Title')"></p> <p class="title" v-html="$t('Nuic.nuic1Title')"></p>
<p class="tip" v-html="$t('Nuic.nuic1Tip')"></p> <p class="tip" v-html="$t('Nuic.nuic1Tip')"></p>
<div class="btns"> <div class="btns">
@@ -13,9 +13,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useUserInfoStore } from '@/stores'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const userInfoStore = useUserInfoStore()
const router = useRouter() const router = useRouter()
const emit = defineEmits(['next']) const emit = defineEmits(['next'])
const name = computed(() => userInfoStore.state.userInfo?.username || '')
const onSkip = () => { const onSkip = () => {
router.push({ name: 'mainInput' }) router.push({ name: 'mainInput' })
} }