上传字体包 版本树页面
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/prettierrc",
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"tabWidth": 2,
|
"tabWidth": 4,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"trailingComma": "none"
|
"trailingComma": "none"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
|
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
|
||||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> -->
|
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> -->
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
|
||||||
<link rel="stylesheet" href="/css/woff/fontFamily.css">
|
<link rel="stylesheet" href="/fonts/general/css/general-sans.css">
|
||||||
<title>FiDA</title>
|
<title>FiDA</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -40,7 +40,6 @@
|
|||||||
"less": "^4.3.0",
|
"less": "^4.3.0",
|
||||||
"lint-staged": "^13.2.1",
|
"lint-staged": "^13.2.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^2.8.4",
|
|
||||||
"typescript": "~4.8.4",
|
"typescript": "~4.8.4",
|
||||||
"unplugin-auto-import": "^0.15.3",
|
"unplugin-auto-import": "^0.15.3",
|
||||||
"unplugin-vue-components": "^0.24.1",
|
"unplugin-vue-components": "^0.24.1",
|
||||||
@@ -6256,11 +6255,16 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
|
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
|
||||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin-prettier.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier-linter-helpers": {
|
"node_modules/prettier-linter-helpers": {
|
||||||
@@ -13280,7 +13284,8 @@
|
|||||||
"version": "2.8.8",
|
"version": "2.8.8",
|
||||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
|
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
|
||||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"prettier-linter-helpers": {
|
"prettier-linter-helpers": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"less": "^4.3.0",
|
"less": "^4.3.0",
|
||||||
"lint-staged": "^13.2.1",
|
"lint-staged": "^13.2.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^2.8.4",
|
|
||||||
"typescript": "~4.8.4",
|
"typescript": "~4.8.4",
|
||||||
"unplugin-auto-import": "^0.15.3",
|
"unplugin-auto-import": "^0.15.3",
|
||||||
"unplugin-vue-components": "^0.24.1",
|
"unplugin-vue-components": "^0.24.1",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,50 +0,0 @@
|
|||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'satoshiRegular';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: url("./Satoshi/Satoshi-Regular.ttf") format('woff2'), url("./Satoshi/Satoshi-Regular.woff") format('woff2');
|
|
||||||
/* unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; */
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'satoshiBold';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: url("./Satoshi/Satoshi-Bold.ttf") format('woff2'), url("./Satoshi/Satoshi-Bold.woff") format('woff2');
|
|
||||||
/* unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; */
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'satoshiMedium';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: url("./Satoshi/Satoshi-Medium.ttf") format('woff2'), url("./Satoshi/Satoshi-Medium.woff") format('woff2');
|
|
||||||
/* unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; */
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'mazzardHRegular';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: url("./Mazzard/MazzardH-Regular.otf") format('opentype');
|
|
||||||
/* unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; */
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'robotoBold';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: url("./Roboto/Roboto-Bold.ttf") format('woff2');
|
|
||||||
/* unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; */
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'robotoRegular';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: url("./Roboto/Roboto-Regular.ttf") format('woff2');
|
|
||||||
/* unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; */
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'boskaRegular';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: url("./Boska/Boska-Regular.ttf") format('woff2'), url("./Boska/Boska-Regular.woff") format('woff2');
|
|
||||||
/* unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; */
|
|
||||||
}
|
|
||||||
70
public/fonts/general/css/general-sans.css
Normal file
70
public/fonts/general/css/general-sans.css
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
*
|
||||||
|
* Font Family: General Sans
|
||||||
|
* Designed by: Frode Helland
|
||||||
|
* URL: https://www.fontshare.com/fonts/general-sans
|
||||||
|
* © 2026 Indian Type Foundry
|
||||||
|
*
|
||||||
|
* General Sans Extralight
|
||||||
|
* General Sans ExtralightItalic
|
||||||
|
* General Sans Light
|
||||||
|
* General Sans LightItalic
|
||||||
|
* General Sans Regular
|
||||||
|
* General Sans Italic
|
||||||
|
* General Sans Medium
|
||||||
|
* General Sans MediumItalic
|
||||||
|
* General Sans Semibold
|
||||||
|
* General Sans SemiboldItalic
|
||||||
|
* General Sans Bold
|
||||||
|
* General Sans BoldItalic
|
||||||
|
* General Sans Variable (Variable font)
|
||||||
|
* General Sans VariableItalic (Variable font)
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Regular';
|
||||||
|
src:url('../fonts/GeneralSans-Regular.otf') format('opentype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Medium';
|
||||||
|
src:url('../fonts/GeneralSans-Medium.otf') format('opentype'),
|
||||||
|
url('../fonts/GeneralSans-MediumItalic.otf') format('opentype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SemiBold';
|
||||||
|
src:url('../fonts/GeneralSans-Semibold.otf') format('opentype'),
|
||||||
|
url('../fonts/GeneralSans-SemiboldItalic.otf') format('opentype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bold';
|
||||||
|
src:url('../fonts/GeneralSans-Bold.otf') format('opentype'),
|
||||||
|
url('../fonts/GeneralSans-BoldItalic.otf') format('opentype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Migra-Extrabold';
|
||||||
|
src:url('../fonts/Migra-Extrabold.otf') format('opentype'),
|
||||||
|
url('../fonts/Migra-Extrabold.ttf') format('truetype'),
|
||||||
|
url('../fonts/Migra-Extrabold.woff') format('woff'),
|
||||||
|
url('../fonts/Migra-Extrabold.woff2') format('woff2');
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
BIN
public/fonts/general/fonts/GeneralSans-Bold.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-Bold.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-BoldItalic.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-BoldItalic.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-Extralight.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-Extralight.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-ExtralightItalic.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-ExtralightItalic.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-Italic.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-Italic.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-Light.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-Light.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-LightItalic.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-LightItalic.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-Medium.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-Medium.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-MediumItalic.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-MediumItalic.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-Regular.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-Regular.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-Semibold.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-Semibold.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/GeneralSans-SemiboldItalic.otf
Normal file
BIN
public/fonts/general/fonts/GeneralSans-SemiboldItalic.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/Migra-Extrabold.otf
Normal file
BIN
public/fonts/general/fonts/Migra-Extrabold.otf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/Migra-Extrabold.ttf
Normal file
BIN
public/fonts/general/fonts/Migra-Extrabold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/Migra-Extrabold.woff
Normal file
BIN
public/fonts/general/fonts/Migra-Extrabold.woff
Normal file
Binary file not shown.
BIN
public/fonts/general/fonts/Migra-Extrabold.woff2
Normal file
BIN
public/fonts/general/fonts/Migra-Extrabold.woff2
Normal file
Binary file not shown.
@@ -12,6 +12,7 @@ p {
|
|||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-family: 'Medium';
|
||||||
}
|
}
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
@@ -33,32 +34,3 @@ body,
|
|||||||
background-image: url('@/assets/images/home-bg.png');
|
background-image: url('@/assets/images/home-bg.png');
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-col {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-center {
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-1 {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.justify-center {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.space-between {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ p {
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-family: 'Medium';
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
|
|||||||
BIN
src/assets/images/chatVersion/robotHead.png
Normal file
BIN
src/assets/images/chatVersion/robotHead.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 910 B |
BIN
src/assets/images/chatVersion/userHead.png
Normal file
BIN
src/assets/images/chatVersion/userHead.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 843 B |
@@ -97,5 +97,14 @@ export default {
|
|||||||
japan: 'Japan',
|
japan: 'Japan',
|
||||||
canada: 'Canada',
|
canada: 'Canada',
|
||||||
germany: 'Germany'
|
germany: 'Germany'
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// Version Tree
|
||||||
|
VersionTree: {
|
||||||
|
versionInformation: 'Version Information',
|
||||||
|
input: 'Input',
|
||||||
|
userRequest: 'User Request',
|
||||||
|
sketch: 'Sketch',
|
||||||
|
generateResult: 'Generate Result',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
103
src/views/home/versionTree/detail/ChatHistory.vue
Normal file
103
src/views/home/versionTree/detail/ChatHistory.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||||
|
import userHead from '@/assets/images/chatVersion/userHead.png'
|
||||||
|
import robotHead from '@/assets/images/chatVersion/robotHead.png'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
const { t: $t } = useI18n()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'user'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
//const emit = defineEmits([
|
||||||
|
//])
|
||||||
|
let data = reactive({
|
||||||
|
})
|
||||||
|
onMounted(()=>{
|
||||||
|
})
|
||||||
|
onUnmounted(()=>{
|
||||||
|
})
|
||||||
|
defineExpose({})
|
||||||
|
const {} = toRefs(data);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="ChatHistory">
|
||||||
|
<div class="titleInfo">
|
||||||
|
<div class="userInfo">
|
||||||
|
<img :src="type == 'user'? userHead:robotHead" alt="">
|
||||||
|
<span>{{ type == 'user'? $t('VersionTree.input'): $t('VersionTree.sketch') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="time">
|
||||||
|
<span>12:00:00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="infoTitle">{{ type == 'user'? $t('VersionTree.userRequest'): $t('VersionTree.generateResult') }}</div>
|
||||||
|
<div class="history">
|
||||||
|
Design a modern yellow sofa that combines comfort, elegance, and contemporary aesthetics.
The sofa features a warm, soft yellow tone (mustard or light ochre), with a minimalist silhouette and clean lines.
Upholstered in high-quality fabric with a subtle texture, offering a cozy and inviting feel.
The seat is deep and plush, with generous cushioning for relaxation, while the backrest and armrests are softly rounded to enhance comfort.
|
||||||
|
Design a modern yellow sofa that combines comfort, elegance, and contemporary aesthetics.
The sofa features a warm, soft yellow tone (mustard or light ochre), with a minimalist silhouette and clean lines.
Upholstered in high-quality fabric with a subtle texture, offering a cozy and inviting feel.
The seat is deep and plush, with generous cushioning for relaxation, while the backrest and armrests are softly rounded to enhance comfort.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ChatHistory{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
> .titleInfo{
|
||||||
|
height: 2.4rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
> .userInfo{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
> img{
|
||||||
|
margin-right: .2rem;
|
||||||
|
}
|
||||||
|
> span{
|
||||||
|
font-family: 'SemiBold';
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
color: #000;
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .time{
|
||||||
|
font-family: 'SemiBold';
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .infoTitle{
|
||||||
|
margin-top: .6rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
margin-left: 3rem;
|
||||||
|
}
|
||||||
|
> .history{
|
||||||
|
margin-top: .8rem;
|
||||||
|
font-family: 'Regular';
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1.4rem;
|
||||||
|
letter-spacing: Letter Spacing;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-left: 3rem;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #ababab #f1f5f9;
|
||||||
|
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: #f1f5f9;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
71
src/views/home/versionTree/detail/index.vue
Normal file
71
src/views/home/versionTree/detail/index.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||||
|
import VersionDetail from './versionDetail.vue'
|
||||||
|
import ChatHistory from './chatHistory.vue'
|
||||||
|
//const props = defineProps({
|
||||||
|
//})
|
||||||
|
//const emit = defineEmits([
|
||||||
|
//])
|
||||||
|
const detailData = ref({
|
||||||
|
id:1,
|
||||||
|
versionDetail:{
|
||||||
|
version:'1.0.0',
|
||||||
|
versionTime:'2023-08-01 10:00:00',
|
||||||
|
versionSketch:'Version 1 - Sketch',
|
||||||
|
versionSketchTime:'2023-08-01 10:00:00',
|
||||||
|
},
|
||||||
|
userChatHistory:{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
onMounted(()=>{
|
||||||
|
})
|
||||||
|
onUnmounted(()=>{
|
||||||
|
})
|
||||||
|
defineExpose({})
|
||||||
|
// const {} = toRefs(data);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="detailBox">
|
||||||
|
<div class="versionDetail">
|
||||||
|
<VersionDetail :versionDetail="detailData.versionDetail"></VersionDetail>
|
||||||
|
</div>
|
||||||
|
<div class="useInput">
|
||||||
|
<ChatHistory type="user"></ChatHistory>
|
||||||
|
</div>
|
||||||
|
<div class="systemInput">
|
||||||
|
<ChatHistory type="robot"></ChatHistory>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.detailBox{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
> div{
|
||||||
|
border-radius: var(--border-radius, 1rem);
|
||||||
|
border: 1px solid #D9D9D9;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
margin-bottom: 3.6rem;
|
||||||
|
padding: 1.5rem 1.4rem;
|
||||||
|
width: 100%;
|
||||||
|
&.versionDetail{
|
||||||
|
height: 21rem;
|
||||||
|
}
|
||||||
|
&.useInput{
|
||||||
|
height: 21.5rem;
|
||||||
|
}
|
||||||
|
&.systemInput{
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
&:last-child{
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
77
src/views/home/versionTree/detail/versionDetail.vue
Normal file
77
src/views/home/versionTree/detail/versionDetail.vue
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
const { t: $t } = useI18n()
|
||||||
|
const props = defineProps({
|
||||||
|
versionDetail: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//const emit = defineEmits([
|
||||||
|
//])
|
||||||
|
let data = reactive({
|
||||||
|
})
|
||||||
|
onMounted(()=>{
|
||||||
|
})
|
||||||
|
onUnmounted(()=>{
|
||||||
|
})
|
||||||
|
defineExpose({})
|
||||||
|
const {} = toRefs(data);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="versionDetail">
|
||||||
|
<div class="title">
|
||||||
|
<span class="titleText">
|
||||||
|
{{ $t('VersionTree.versionInformation') }}
|
||||||
|
</span>
|
||||||
|
<span class="icon"><svg-icon name="more" size="25" /></span>
|
||||||
|
</div>
|
||||||
|
<div class="version">{{versionDetail.version}}</div>
|
||||||
|
<div class="time marBott1">{{versionDetail.versionTime}}</div>
|
||||||
|
<div class="version gray">{{versionDetail.versionSketch}}</div>
|
||||||
|
<div class="time gray">{{versionDetail.versionSketchTime}}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.versionDetail{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
> .title{
|
||||||
|
line-height: 2rem;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: #000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 2rem;
|
||||||
|
margin-bottom: 2.8rem;
|
||||||
|
> .titleText{
|
||||||
|
opacity: .5;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: 'SemiBold';
|
||||||
|
}
|
||||||
|
> .icon{
|
||||||
|
color: #5a5a5a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .version{
|
||||||
|
font-family: 'SemiBold';
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
margin-bottom: .2rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
> .marBott1{
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
}
|
||||||
|
> .time{
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
> .gray{
|
||||||
|
color: #C1C1C1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||||
import Tree from './tree/index.vue'
|
import Tree from './tree/index.vue'
|
||||||
|
import Detail from './detail/index.vue'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
versionTreeData:{
|
versionTreeData:{
|
||||||
type:Object,
|
type:Object,
|
||||||
@@ -16,6 +17,8 @@ const props = defineProps({
|
|||||||
//])
|
//])
|
||||||
const treeState = ref(true)//
|
const treeState = ref(true)//
|
||||||
|
|
||||||
|
const selectItem = ref({})
|
||||||
|
|
||||||
const openTree = ()=>{
|
const openTree = ()=>{
|
||||||
treeState.value = !treeState.value
|
treeState.value = !treeState.value
|
||||||
|
|
||||||
@@ -47,16 +50,16 @@ const {} = toRefs(data);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex;">
|
<div style="display: flex;" class="expandBtnBox">
|
||||||
<el-button class="expandBtn" @click="openTree" style="width: 5rem;">+</el-button>
|
<el-button class="expandBtn" @click="openTree" style="width: 5rem;">+</el-button>
|
||||||
<el-button class="expandBtn" @click="openTree" style="width: 5rem;">-</el-button>
|
<el-button class="expandBtn" @click="openTree" style="width: 5rem;">-</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="versionTreeBox">
|
<div class="versionTreeBox">
|
||||||
<div class="tree">
|
<div class="tree">
|
||||||
<Tree :treeState="treeState"></Tree>
|
<Tree :treeState="treeState" v-model:selectItem="selectItem"></Tree>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
|
<Detail v-model:selectItem="selectItem"></Detail>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
@@ -83,17 +86,21 @@ const {} = toRefs(data);
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 .8rem 0 1.2rem;
|
padding: 0 .8rem 0 2.4rem;
|
||||||
border-bottom: 1px solid #C9C9C9;
|
border-bottom: 1px solid #C9C9C9;
|
||||||
> span{
|
> span{
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
font-family: 'SemiBold';
|
||||||
}
|
}
|
||||||
> .closeBtn{
|
> .closeBtn{
|
||||||
width: 2.4rem;
|
width: 2.4rem;
|
||||||
height: 2.4rem;
|
height: 2.4rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.expandBtnBox{
|
||||||
|
|
||||||
}
|
}
|
||||||
.versionTreeBox{
|
.versionTreeBox{
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -103,14 +110,13 @@ const {} = toRefs(data);
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 1.8rem 0 1.8rem 2.1rem;
|
padding: 2.1rem 0 5.4rem 2.2rem;
|
||||||
}
|
}
|
||||||
> .detail{
|
> .detail{
|
||||||
width: 35rem;
|
width: 35rem;
|
||||||
margin: 1.4rem 3rem 0 3.4rem;
|
margin: 2.1rem 3rem 5.4rem 3.4rem;
|
||||||
height: 100%;
|
height: calc(100% - 2.1rem - 5.4rem);
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
background-color: #F5F5F5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ const props = defineProps({
|
|||||||
treeState:{
|
treeState:{
|
||||||
default:false,
|
default:false,
|
||||||
},
|
},
|
||||||
|
selectItem: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
} as any,
|
||||||
})
|
})
|
||||||
//const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
//])
|
'update:selectItem'
|
||||||
|
])
|
||||||
let data = reactive({
|
let data = reactive({
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const isLoad = ref(false)
|
||||||
const treeStateTime = ref(true)
|
const treeStateTime = ref(true)
|
||||||
|
|
||||||
watch(()=>props.treeState,(newVal,oldVal)=>{
|
watch(()=>props.treeState,(newVal,oldVal)=>{
|
||||||
@@ -28,7 +34,7 @@ const pushView2Item = (item)=>{
|
|||||||
view2Ref.value.push(item)
|
view2Ref.value.push(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const view1List = ref([
|
const treeList = ref([
|
||||||
{
|
{
|
||||||
name:'P1',
|
name:'P1',
|
||||||
},{
|
},{
|
||||||
@@ -37,9 +43,34 @@ const view1List = ref([
|
|||||||
name:'V1-1',
|
name:'V1-1',
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function traverseArray(items, callback) {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i]
|
||||||
|
callback(item, i)
|
||||||
|
if (item.child && Array.isArray(item.child) && item.child.length > 0) {
|
||||||
|
traverseArray(item.child, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialize = ()=>{
|
||||||
|
isLoad.value = false
|
||||||
|
setSelectItem(versionsList[0])
|
||||||
|
treeList.value = []
|
||||||
|
traverseArray(versionsList, (item, index) => {
|
||||||
|
treeList.value.push(item)
|
||||||
|
})
|
||||||
|
isLoad.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const setSelectItem = (item)=>{
|
||||||
|
console.log(item[0])
|
||||||
|
emit('update:selectItem', {...item})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
// addView2Item()
|
initialize()
|
||||||
view2Ref.value.init(versionsList)
|
|
||||||
})
|
})
|
||||||
onUnmounted(()=>{
|
onUnmounted(()=>{
|
||||||
})
|
})
|
||||||
@@ -47,12 +78,17 @@ defineExpose({})
|
|||||||
const {} = toRefs(data);
|
const {} = toRefs(data);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="tree" v-show="treeStateTime">
|
<div class="tree" v-show="treeStateTime" v-if="isLoad">
|
||||||
<div v-show="!treeState" class="box view1">
|
<div v-show="!treeState" class="box view1">
|
||||||
<view1Item v-for="item in view1List" :key="item.name" :item="item"></view1Item>
|
<view1Item v-for="item in treeList" :key="item.name" :item="item" @click="emit('selectItem', item)"></view1Item>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="treeState" class="box view2">
|
<div v-show="treeState" class="box view2">
|
||||||
<view2 ref="view2Ref"></view2>
|
<view2
|
||||||
|
ref="view2Ref"
|
||||||
|
@setSelectItem="setSelectItem"
|
||||||
|
:treeList="treeList"
|
||||||
|
:selectItem="props.selectItem"
|
||||||
|
></view2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -67,7 +103,9 @@ const {} = toRefs(data);
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
&.view1{
|
&.view1{
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.view2{
|
&.view2{
|
||||||
border: 1px solid #D9D9D9;
|
border: 1px solid #D9D9D9;
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="node">
|
<div class="node start">
|
||||||
<Handle type="target" id="top" :position="Position.Top" />
|
<Handle type="source" id="Bottom" :position="Position.Bottom" />
|
||||||
<Handle type="source" id="bottom" :position="Position.Bottom" />
|
<div>index</div>
|
||||||
<Handle type="source" id="right" :position="Position.Right" />
|
|
||||||
<div>{{ props.data.id }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -1,39 +1,40 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted, reactive, nextTick } from "vue";
|
import { ref, onMounted, onUnmounted, reactive, nextTick, watch } from "vue";
|
||||||
import type { Node, Edge } from '@vue-flow/core'
|
import type { Node, Edge } from '@vue-flow/core'
|
||||||
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
import SpecialEdge from './speciaiEdge.vue'
|
import SpecialEdge from './speciaiEdge.vue'
|
||||||
import PrimaryNode from './primaryNode.vue'//主
|
import InputNode from './InputNode.vue'//主
|
||||||
import SecondaryNode from './secondaryNode.vue'//分支
|
import SecondaryNode from './secondaryNode.vue'//分支
|
||||||
import { useLayout } from './tools/tools'
|
import { useLayout } from './tools/tools'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
selectItem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
} as any,
|
||||||
|
treeList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
//const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
//])
|
'setSelectItem',
|
||||||
|
])
|
||||||
let selectId = ref(2)
|
|
||||||
const isLoad = ref(false)
|
|
||||||
|
|
||||||
// 节点类型:input、output、default、custom
|
// 节点类型:input、output、default、custom
|
||||||
// input:开始点,output:结尾点,default:普通节点,custom:自定义节点
|
// input:开始点,output:结尾点,default:普通节点,custom:自定义节点
|
||||||
const position = { x: 0, y: 0 }
|
const position = { x: 0, y: 0 }
|
||||||
const nodes = ref<Node[]>([
|
const nodes = ref<Node[]>([
|
||||||
// { id: '1', type: 'input', label: 'Node 1', class: 'custom-node start', position, sourcePosition: 'bottom' },
|
// { id: '1', type: 'input', label: 'Node 1', class: 'custom-node start', position },
|
||||||
// { id: '2', type: 'PrimaryNode', class: 'custom-node', data: { id: '主 1' }, position },
|
// { id: '2', type: 'SecondaryNode', class: 'custom-node', data: { id: '主 1' }, position },
|
||||||
// { id: '2-1', type: 'SecondaryNode', class: 'custom-node', data: { id: '分 1-1' }, position },
|
// { id: '2-1', type: 'SecondaryNode', class: 'custom-node', data: { id: '主 2' }, position },
|
||||||
// { id: '3', type: 'PrimaryNode', class: 'custom-node', data: { id: '主 2' }, position },
|
// { id: '3', type: 'SecondaryNode', class: 'custom-node', data: { id: '主 3' }, position },
|
||||||
])
|
])
|
||||||
|
|
||||||
// 边类型:custom、default
|
// 边类型:custom、default
|
||||||
// custom:自定义边,default:普通边,step:直角边,smoothstep:平滑边
|
// custom:自定义边,default:普通边,step:直角边,smoothstep:平滑边
|
||||||
const edges = ref<Edge[]>([
|
const edges = ref<Edge[]>([
|
||||||
// { id: 'e1-2', source: '1', target: '2', type: 'smoothstep' },
|
// { id: 'e1-3', source: '2', target: '2-1', type: 'smoothstep', },
|
||||||
// { id: 'e1-3', source: '2', target: '2-1', type: 'smoothstep', sourceHandle:'right',},
|
// { id: 'e1-4', source: '2', target: '3', type: 'smoothstep', animated: true },
|
||||||
// { id: 'e1-4', source: '2', target: '3', type: 'smoothstep',sourceHandle:'bottom', animated: true },
|
|
||||||
])
|
])
|
||||||
const { fitView } = useVueFlow()
|
const { fitView } = useVueFlow()
|
||||||
const { layout } = useLayout()
|
const { layout } = useLayout()
|
||||||
@@ -47,37 +48,19 @@ async function layoutGraph(direction) {
|
|||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
let elIndex = 1
|
|
||||||
const push = (item)=>{
|
const push = (item)=>{
|
||||||
if(nodes.value.length == 0){
|
if(nodes.value.length == 0){
|
||||||
nodes.value.push({ id: '0', type: 'input', label: 'Node 1', class: 'custom-node start', position, sourcePosition: 'bottom' })
|
nodes.value.push({ id: '0', type: 'InputNode', class: 'custom-node', position })
|
||||||
}
|
}
|
||||||
let className = 'custom-node'
|
let className = `custom-node item${item.id.replace(/-/g, "_")}`
|
||||||
let id = item.id
|
let id = item.id
|
||||||
let target = edges.value.length == 0?'0':item.id.slice(0, -2)
|
let source = edges.value.length == 0?'0':item.id.slice(0, -2)
|
||||||
nodes.value.push({id,type:'SecondaryNode',class:className,position,data:item})
|
nodes.value.push({id,type:'SecondaryNode',class:className,position,data:item})
|
||||||
edges.value.push({ id, source: id, target, type: 'smoothstep' })
|
edges.value.push({ id, target:id, source, type: 'smoothstep' })
|
||||||
console.log()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function traverseArray(items, callback) {
|
const initialized = ()=>{
|
||||||
for (let i = 0; i < items.length; i++) {
|
layoutGraph('TB')
|
||||||
const item = items[i]
|
|
||||||
callback(item, i)
|
|
||||||
if (item.child && Array.isArray(item.child) && item.child.length > 0) {
|
|
||||||
traverseArray(item.child, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const init = (list)=>{
|
|
||||||
isLoad.value = false
|
|
||||||
traverseArray(list, (item, index) => {
|
|
||||||
console.log()
|
|
||||||
push(item)
|
|
||||||
})
|
|
||||||
isLoad.value = true
|
|
||||||
console.log(nodes.value,edges.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//是否可拖动节点
|
//是否可拖动节点
|
||||||
@@ -86,29 +69,39 @@ const toggleNodesDraggable = () => {
|
|||||||
nodesDraggable.value = !nodesDraggable.value
|
nodesDraggable.value = !nodesDraggable.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleVueFlowNodeClick = (node: Node) => {
|
const handleVueFlowNodeClick = ({node}) => {
|
||||||
console.log(node)
|
if(node.data.id)emit('setSelectItem', node.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(()=>props.treeList.length, (newVal, oldVal) => {
|
||||||
|
nodes.value = []
|
||||||
|
edges.value = []
|
||||||
|
props.treeList.forEach(item=>{
|
||||||
|
push(item)
|
||||||
|
})
|
||||||
|
},{immediate:true})
|
||||||
|
watch(()=>props.selectItem.id, (newVal, oldVal) => {
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
})
|
})
|
||||||
onUnmounted(()=>{
|
onUnmounted(()=>{
|
||||||
})
|
})
|
||||||
defineExpose({init,push})
|
defineExpose({push})
|
||||||
// const {} = toRefs(data);
|
// const {} = toRefs(data);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="view2">
|
<div class="view2">
|
||||||
<div class="vueFlowBox" v-if="isLoad">
|
<div class="vueFlowBox">
|
||||||
<div @click="toggleNodesDraggable">拖拽节点</div>
|
<div @click="toggleNodesDraggable">拖拽节点</div>
|
||||||
<VueFlow :nodes="nodes" @nodes-initialized="layoutGraph('LR')" :edges="edges" @node-click="handleVueFlowNodeClick" :nodes-draggable="nodesDraggable">
|
<VueFlow :nodes="nodes" @nodes-initialized="initialized" :edges="edges" @node-click="handleVueFlowNodeClick" :nodes-draggable="nodesDraggable">
|
||||||
<template #node-PrimaryNode="nodeProps">
|
<template #node-InputNode="nodeProps">
|
||||||
<PrimaryNode v-bind="nodeProps" />
|
<InputNode v-bind="nodeProps" />
|
||||||
</template>
|
</template>
|
||||||
<template #node-SecondaryNode="nodeProps">
|
<template #node-SecondaryNode="nodeProps">
|
||||||
<SecondaryNode
|
<SecondaryNode
|
||||||
v-bind="nodeProps"
|
v-bind="nodeProps"
|
||||||
:selectId="selectId"
|
:selectItem="props.selectItem"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -135,41 +128,31 @@ defineExpose({init,push})
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
:deep(.custom-node){
|
:deep(.custom-node){
|
||||||
--vf-handle: #000;
|
.node{
|
||||||
--vf-node-color: #000;
|
--vf-handle: #000;
|
||||||
--vf-box-shadow: #000;
|
--vf-node-color: #000;
|
||||||
font-size: 1.2rem;
|
--vf-box-shadow: #000;
|
||||||
width: var(--treeItem-width);
|
font-size: 1.2rem;
|
||||||
height: var(--treeItem-height);
|
width: var(--treeItem-width);
|
||||||
font-weight: 500;
|
height: var(--treeItem-height);
|
||||||
display: flex;
|
font-weight: 500;
|
||||||
align-items: center;
|
display: flex;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
border-radius: var(--treeItem-raduis);
|
justify-content: center;
|
||||||
border: var(--treeItem-border);
|
border-radius: var(--treeItem-raduis);
|
||||||
color: #000;
|
border: var(--treeItem-border);
|
||||||
cursor: pointer;
|
color: #000;
|
||||||
background-color: var(--treeItem-background);
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
background-color: var(--treeItem-background);
|
||||||
.vue-flow__handle-right{
|
box-sizing: border-box;
|
||||||
transform: translate(calc(50% + 2px), -50%);
|
&.active{
|
||||||
}
|
background-color: var(--treeItem-active-background);
|
||||||
.vue-flow__handle-left{
|
}
|
||||||
transform: translate(calc(-50% - 2px), -50%);
|
&.start{
|
||||||
}
|
background-color: #7A7A7A;
|
||||||
.vue-flow__handle-top{
|
color: #FFF;
|
||||||
transform: translate(-50%, calc(-50% - 2px));
|
border: 2px solid #7A7A7A;
|
||||||
}
|
}
|
||||||
.vue-flow__handle-bottom{
|
|
||||||
transform: translate(-50%, calc(50% + 2px));
|
|
||||||
}
|
|
||||||
&.active{
|
|
||||||
background-color: var(--treeItem-active-background);
|
|
||||||
}
|
|
||||||
&.start{
|
|
||||||
background-color: #7A7A7A;
|
|
||||||
color: #FFF;
|
|
||||||
border: 2px solid #7A7A7A;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,15 +7,21 @@ const props = defineProps<{
|
|||||||
default: () => ({
|
default: () => ({
|
||||||
id: '',
|
id: '',
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
|
selectItem: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
<!-- source输入,target输出 -->
|
||||||
<template>
|
<template>
|
||||||
<div class="node">
|
<div class="node" :class="{active:props.selectItem.id == props.data.id}">
|
||||||
<Handle type="target" id="left" :position="Position.Left" />
|
<Handle type="target" id="Top" :position="Position.Top" />
|
||||||
<Handle type="source" id="right" :position="Position.Right" />
|
<Handle type="source" id="Bottom" :position="Position.Bottom" />
|
||||||
|
<!-- <Handle type="target" id="Bottom" :position="Position.Bottom" />
|
||||||
|
<Handle type="source" id="Top" :position="Position.Top" /> -->
|
||||||
<div>{{ props.data.id }}</div>
|
<div>{{ props.data.id }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,50 +8,79 @@ import { ref } from 'vue'
|
|||||||
* It uses the `dagre` library to calculate the layout of the nodes and edges.
|
* It uses the `dagre` library to calculate the layout of the nodes and edges.
|
||||||
*/
|
*/
|
||||||
export function useLayout() {
|
export function useLayout() {
|
||||||
const { findNode } = useVueFlow()
|
const { findNode } = useVueFlow()
|
||||||
|
|
||||||
const graph = ref(new dagre.graphlib.Graph())
|
const graph = ref(new dagre.graphlib.Graph())
|
||||||
|
|
||||||
const previousDirection = ref('LR')
|
const previousDirection = ref('LR')
|
||||||
|
|
||||||
function layout(nodes, edges, direction) {
|
function layout(nodes, edges, direction = 'LR') {
|
||||||
// we create a new graph instance, in case some nodes/edges were removed, otherwise dagre would act as if they were still there
|
// 验证和规范化方向参数
|
||||||
const dagreGraph = new dagre.graphlib.Graph()
|
const validDirections = ['TB', 'BT', 'LR', 'RL']
|
||||||
|
const layoutDirection = validDirections.includes(direction) ? direction : 'LR'
|
||||||
|
|
||||||
graph.value = dagreGraph
|
// we create a new graph instance, in case some nodes/edges were removed, otherwise dagre would act as if they were still there
|
||||||
|
const dagreGraph = new dagre.graphlib.Graph()
|
||||||
|
|
||||||
dagreGraph.setDefaultEdgeLabel(() => ({}))
|
graph.value = dagreGraph
|
||||||
|
|
||||||
const isHorizontal = direction === 'LR'
|
dagreGraph.setDefaultEdgeLabel(() => ({}))
|
||||||
dagreGraph.setGraph({ rankdir: direction })
|
|
||||||
|
|
||||||
previousDirection.value = direction
|
// 根据方向判断是否为水平布局
|
||||||
|
const isHorizontal = layoutDirection === 'LR' || layoutDirection === 'RL'
|
||||||
|
dagreGraph.setGraph({ rankdir: layoutDirection })
|
||||||
|
|
||||||
for (const node of nodes) {
|
previousDirection.value = layoutDirection
|
||||||
// if you need width+height of nodes for your layout, you can use the dimensions property of the internal node (`GraphNode` type)
|
|
||||||
const graphNode = findNode(node.id)
|
|
||||||
|
|
||||||
dagreGraph.setNode(node.id, { width: graphNode.dimensions.width || 150, height: graphNode.dimensions.height || 50 })
|
for (const node of nodes) {
|
||||||
}
|
// if you need width+height of nodes for your layout, you can use the dimensions property of the internal node (`GraphNode` type)
|
||||||
|
const graphNode = findNode(node.id)
|
||||||
|
|
||||||
for (const edge of edges) {
|
dagreGraph.setNode(node.id, { width: graphNode.dimensions.width || 150, height: graphNode.dimensions.height || 50 })
|
||||||
dagreGraph.setEdge(edge.source, edge.target)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
dagre.layout(dagreGraph)
|
for (const edge of edges) {
|
||||||
|
dagreGraph.setEdge(edge.source, edge.target)
|
||||||
|
}
|
||||||
|
|
||||||
// set nodes with updated positions
|
dagre.layout(dagreGraph)
|
||||||
return nodes.map((node) => {
|
|
||||||
const nodeWithPosition = dagreGraph.node(node.id)
|
|
||||||
|
|
||||||
return {
|
// set nodes with updated positions
|
||||||
...node,
|
return nodes.map((node) => {
|
||||||
targetPosition: isHorizontal ? Position.Left : Position.Top,
|
const nodeWithPosition = dagreGraph.node(node.id)
|
||||||
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
|
|
||||||
position: { x: nodeWithPosition.x, y: nodeWithPosition.y },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return { graph, layout, previousDirection }
|
// 根据方向动态计算连接点位置
|
||||||
}
|
let targetPosition, sourcePosition
|
||||||
|
switch (layoutDirection) {
|
||||||
|
case 'BT': // 从上到下 (Top to Bottom)
|
||||||
|
targetPosition = Position.Bottom // 目标节点连接点在下方
|
||||||
|
sourcePosition = Position.Top // 源节点连接点在上方
|
||||||
|
break
|
||||||
|
case 'TB': // 从下到上 (Bottom to Top)
|
||||||
|
targetPosition = Position.Top // 目标节点连接点在上方
|
||||||
|
sourcePosition = Position.Bottom // 源节点连接点在下方
|
||||||
|
break
|
||||||
|
case 'LR': // 从左到右 (Left to Right)
|
||||||
|
targetPosition = Position.Left
|
||||||
|
sourcePosition = Position.Right
|
||||||
|
break
|
||||||
|
case 'RL': // 从右到左 (Right to Left)
|
||||||
|
targetPosition = Position.Right
|
||||||
|
sourcePosition = Position.Left
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
targetPosition = Position.Top
|
||||||
|
sourcePosition = Position.Bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
targetPosition,
|
||||||
|
sourcePosition,
|
||||||
|
position: { x: nodeWithPosition.x, y: nodeWithPosition.y },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { graph, layout, previousDirection }
|
||||||
|
}
|
||||||
@@ -4,27 +4,29 @@ export const versionsList = [
|
|||||||
name:'V1',
|
name:'V1',
|
||||||
child:[
|
child:[
|
||||||
{
|
{
|
||||||
id: '1-1',
|
id: '1-1',
|
||||||
name:'V1-1',
|
name:'V1-1',
|
||||||
child:[
|
child:[
|
||||||
{
|
{
|
||||||
id: '1-1-1',
|
id: '1-1-1',
|
||||||
name:'V1-1-1',
|
name:'V1-1-1',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},{
|
},
|
||||||
|
{
|
||||||
id: '1-2',
|
id: '1-2',
|
||||||
name:'V1-2',
|
name:'V1-2',
|
||||||
child:[
|
child:[
|
||||||
{
|
{
|
||||||
id: '1-2-1',
|
id: '1-2-1',
|
||||||
name:'V1-2-1',
|
name:'V1-2-1',
|
||||||
},{
|
},{
|
||||||
id: '1-2-2',
|
id: '1-2-2',
|
||||||
name:'V1-2-2',
|
name:'V1-2-2',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},{
|
},
|
||||||
|
{
|
||||||
id: '1-2',
|
id: '1-2',
|
||||||
name:'V1-2',
|
name:'V1-2',
|
||||||
child:[
|
child:[
|
||||||
@@ -51,7 +53,8 @@ export const versionsList = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},{
|
},
|
||||||
|
{
|
||||||
id: '1-3',
|
id: '1-3',
|
||||||
name:'V1-3',
|
name:'V1-3',
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user