上传字体包 版本树页面
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"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, 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>
|
||||
</head>
|
||||
|
||||
|
||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -40,7 +40,6 @@
|
||||
"less": "^4.3.0",
|
||||
"lint-staged": "^13.2.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.4",
|
||||
"typescript": "~4.8.4",
|
||||
"unplugin-auto-import": "^0.15.3",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
@@ -6256,11 +6255,16 @@
|
||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-linter-helpers": {
|
||||
@@ -13280,7 +13284,8 @@
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"prettier-linter-helpers": {
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
"less": "^4.3.0",
|
||||
"lint-staged": "^13.2.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.4",
|
||||
"typescript": "~4.8.4",
|
||||
"unplugin-auto-import": "^0.15.3",
|
||||
"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;
|
||||
font-family: 'Medium';
|
||||
}
|
||||
html,
|
||||
body,
|
||||
@@ -33,32 +34,3 @@ body,
|
||||
background-image: url('@/assets/images/home-bg.png');
|
||||
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;
|
||||
font-family: 'Medium';
|
||||
}
|
||||
|
||||
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',
|
||||
canada: 'Canada',
|
||||
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">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
import Tree from './tree/index.vue'
|
||||
import Detail from './detail/index.vue'
|
||||
const props = defineProps({
|
||||
versionTreeData:{
|
||||
type:Object,
|
||||
@@ -16,6 +17,8 @@ const props = defineProps({
|
||||
//])
|
||||
const treeState = ref(true)//
|
||||
|
||||
const selectItem = ref({})
|
||||
|
||||
const openTree = ()=>{
|
||||
treeState.value = !treeState.value
|
||||
|
||||
@@ -47,16 +50,16 @@ const {} = toRefs(data);
|
||||
</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>
|
||||
</div>
|
||||
<div class="versionTreeBox">
|
||||
<div class="tree">
|
||||
<Tree :treeState="treeState"></Tree>
|
||||
<Tree :treeState="treeState" v-model:selectItem="selectItem"></Tree>
|
||||
</div>
|
||||
<div class="detail">
|
||||
|
||||
<Detail v-model:selectItem="selectItem"></Detail>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
@@ -83,17 +86,21 @@ const {} = toRefs(data);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 .8rem 0 1.2rem;
|
||||
padding: 0 .8rem 0 2.4rem;
|
||||
border-bottom: 1px solid #C9C9C9;
|
||||
> span{
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
font-family: 'SemiBold';
|
||||
}
|
||||
> .closeBtn{
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.expandBtnBox{
|
||||
|
||||
}
|
||||
.versionTreeBox{
|
||||
flex: 1;
|
||||
@@ -103,14 +110,13 @@ const {} = toRefs(data);
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 1.8rem 0 1.8rem 2.1rem;
|
||||
padding: 2.1rem 0 5.4rem 2.2rem;
|
||||
}
|
||||
> .detail{
|
||||
width: 35rem;
|
||||
margin: 1.4rem 3rem 0 3.4rem;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: #F5F5F5;
|
||||
margin: 2.1rem 3rem 5.4rem 3.4rem;
|
||||
height: calc(100% - 2.1rem - 5.4rem);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,18 @@ const props = defineProps({
|
||||
treeState:{
|
||||
default:false,
|
||||
},
|
||||
selectItem: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
} as any,
|
||||
})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
const emit = defineEmits([
|
||||
'update:selectItem'
|
||||
])
|
||||
let data = reactive({
|
||||
})
|
||||
|
||||
const isLoad = ref(false)
|
||||
const treeStateTime = ref(true)
|
||||
|
||||
watch(()=>props.treeState,(newVal,oldVal)=>{
|
||||
@@ -28,7 +34,7 @@ const pushView2Item = (item)=>{
|
||||
view2Ref.value.push(item)
|
||||
}
|
||||
|
||||
const view1List = ref([
|
||||
const treeList = ref([
|
||||
{
|
||||
name:'P1',
|
||||
},{
|
||||
@@ -37,9 +43,34 @@ const view1List = ref([
|
||||
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(()=>{
|
||||
// addView2Item()
|
||||
view2Ref.value.init(versionsList)
|
||||
initialize()
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
@@ -47,12 +78,17 @@ defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="tree" v-show="treeStateTime">
|
||||
<div class="tree" v-show="treeStateTime" v-if="isLoad">
|
||||
<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 v-show="treeState" class="box view2">
|
||||
<view2 ref="view2Ref"></view2>
|
||||
<view2
|
||||
ref="view2Ref"
|
||||
@setSelectItem="setSelectItem"
|
||||
:treeList="treeList"
|
||||
:selectItem="props.selectItem"
|
||||
></view2>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -67,7 +103,9 @@ const {} = toRefs(data);
|
||||
box-sizing: border-box;
|
||||
&.view1{
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.view2{
|
||||
border: 1px solid #D9D9D9;
|
||||
|
||||
@@ -13,11 +13,9 @@ const props = defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="node">
|
||||
<Handle type="target" id="top" :position="Position.Top" />
|
||||
<Handle type="source" id="bottom" :position="Position.Bottom" />
|
||||
<Handle type="source" id="right" :position="Position.Right" />
|
||||
<div>{{ props.data.id }}</div>
|
||||
<div class="node start">
|
||||
<Handle type="source" id="Bottom" :position="Position.Bottom" />
|
||||
<div>index</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
<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 { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||
import SpecialEdge from './speciaiEdge.vue'
|
||||
import PrimaryNode from './primaryNode.vue'//主
|
||||
import InputNode from './InputNode.vue'//主
|
||||
import SecondaryNode from './secondaryNode.vue'//分支
|
||||
import { useLayout } from './tools/tools'
|
||||
const props = defineProps({
|
||||
item: {
|
||||
selectItem: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
} as any,
|
||||
treeList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
|
||||
let selectId = ref(2)
|
||||
const isLoad = ref(false)
|
||||
const emit = defineEmits([
|
||||
'setSelectItem',
|
||||
])
|
||||
|
||||
// 节点类型:input、output、default、custom
|
||||
// input:开始点,output:结尾点,default:普通节点,custom:自定义节点
|
||||
const position = { x: 0, y: 0 }
|
||||
const nodes = ref<Node[]>([
|
||||
// { id: '1', type: 'input', label: 'Node 1', class: 'custom-node start', position, sourcePosition: 'bottom' },
|
||||
// { id: '2', type: 'PrimaryNode', class: 'custom-node', data: { id: '主 1' }, position },
|
||||
// { id: '2-1', type: 'SecondaryNode', class: 'custom-node', data: { id: '分 1-1' }, position },
|
||||
// { id: '3', type: 'PrimaryNode', class: 'custom-node', data: { id: '主 2' }, position },
|
||||
// { id: '1', type: 'input', label: 'Node 1', class: 'custom-node start', position },
|
||||
// { id: '2', type: 'SecondaryNode', class: 'custom-node', data: { id: '主 1' }, position },
|
||||
// { id: '2-1', type: 'SecondaryNode', class: 'custom-node', data: { id: '主 2' }, position },
|
||||
// { id: '3', type: 'SecondaryNode', class: 'custom-node', data: { id: '主 3' }, position },
|
||||
])
|
||||
|
||||
// 边类型:custom、default
|
||||
// custom:自定义边,default:普通边,step:直角边,smoothstep:平滑边
|
||||
const edges = ref<Edge[]>([
|
||||
// { id: 'e1-2', source: '1', target: '2', type: 'smoothstep' },
|
||||
// { id: 'e1-3', source: '2', target: '2-1', type: 'smoothstep', sourceHandle:'right',},
|
||||
// { id: 'e1-4', source: '2', target: '3', type: 'smoothstep',sourceHandle:'bottom', animated: true },
|
||||
// { id: 'e1-3', source: '2', target: '2-1', type: 'smoothstep', },
|
||||
// { id: 'e1-4', source: '2', target: '3', type: 'smoothstep', animated: true },
|
||||
])
|
||||
const { fitView } = useVueFlow()
|
||||
const { layout } = useLayout()
|
||||
@@ -47,37 +48,19 @@ async function layoutGraph(direction) {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
let elIndex = 1
|
||||
const push = (item)=>{
|
||||
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 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})
|
||||
edges.value.push({ id, source: id, target, type: 'smoothstep' })
|
||||
console.log()
|
||||
edges.value.push({ id, target:id, source, type: 'smoothstep' })
|
||||
}
|
||||
|
||||
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 init = (list)=>{
|
||||
isLoad.value = false
|
||||
traverseArray(list, (item, index) => {
|
||||
console.log()
|
||||
push(item)
|
||||
})
|
||||
isLoad.value = true
|
||||
console.log(nodes.value,edges.value)
|
||||
const initialized = ()=>{
|
||||
layoutGraph('TB')
|
||||
}
|
||||
|
||||
//是否可拖动节点
|
||||
@@ -86,29 +69,39 @@ const toggleNodesDraggable = () => {
|
||||
nodesDraggable.value = !nodesDraggable.value
|
||||
}
|
||||
|
||||
const handleVueFlowNodeClick = (node: Node) => {
|
||||
console.log(node)
|
||||
const handleVueFlowNodeClick = ({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(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({init,push})
|
||||
defineExpose({push})
|
||||
// const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="view2">
|
||||
<div class="vueFlowBox" v-if="isLoad">
|
||||
<div class="vueFlowBox">
|
||||
<div @click="toggleNodesDraggable">拖拽节点</div>
|
||||
<VueFlow :nodes="nodes" @nodes-initialized="layoutGraph('LR')" :edges="edges" @node-click="handleVueFlowNodeClick" :nodes-draggable="nodesDraggable">
|
||||
<template #node-PrimaryNode="nodeProps">
|
||||
<PrimaryNode v-bind="nodeProps" />
|
||||
<VueFlow :nodes="nodes" @nodes-initialized="initialized" :edges="edges" @node-click="handleVueFlowNodeClick" :nodes-draggable="nodesDraggable">
|
||||
<template #node-InputNode="nodeProps">
|
||||
<InputNode v-bind="nodeProps" />
|
||||
</template>
|
||||
<template #node-SecondaryNode="nodeProps">
|
||||
<SecondaryNode
|
||||
v-bind="nodeProps"
|
||||
:selectId="selectId"
|
||||
:selectItem="props.selectItem"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -135,6 +128,7 @@ defineExpose({init,push})
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.custom-node){
|
||||
.node{
|
||||
--vf-handle: #000;
|
||||
--vf-node-color: #000;
|
||||
--vf-box-shadow: #000;
|
||||
@@ -151,18 +145,6 @@ defineExpose({init,push})
|
||||
cursor: pointer;
|
||||
background-color: var(--treeItem-background);
|
||||
box-sizing: border-box;
|
||||
.vue-flow__handle-right{
|
||||
transform: translate(calc(50% + 2px), -50%);
|
||||
}
|
||||
.vue-flow__handle-left{
|
||||
transform: translate(calc(-50% - 2px), -50%);
|
||||
}
|
||||
.vue-flow__handle-top{
|
||||
transform: translate(-50%, calc(-50% - 2px));
|
||||
}
|
||||
.vue-flow__handle-bottom{
|
||||
transform: translate(-50%, calc(50% + 2px));
|
||||
}
|
||||
&.active{
|
||||
background-color: var(--treeItem-active-background);
|
||||
}
|
||||
@@ -173,4 +155,5 @@ defineExpose({init,push})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,15 +7,21 @@ const props = defineProps<{
|
||||
default: () => ({
|
||||
id: '',
|
||||
})
|
||||
}
|
||||
},
|
||||
selectItem: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
}>()
|
||||
|
||||
</script>
|
||||
|
||||
<!-- source输入,target输出 -->
|
||||
<template>
|
||||
<div class="node">
|
||||
<Handle type="target" id="left" :position="Position.Left" />
|
||||
<Handle type="source" id="right" :position="Position.Right" />
|
||||
<div class="node" :class="{active:props.selectItem.id == props.data.id}">
|
||||
<Handle type="target" id="Top" :position="Position.Top" />
|
||||
<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>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +14,11 @@ export function useLayout() {
|
||||
|
||||
const previousDirection = ref('LR')
|
||||
|
||||
function layout(nodes, edges, direction) {
|
||||
function layout(nodes, edges, direction = 'LR') {
|
||||
// 验证和规范化方向参数
|
||||
const validDirections = ['TB', 'BT', 'LR', 'RL']
|
||||
const layoutDirection = validDirections.includes(direction) ? 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()
|
||||
|
||||
@@ -22,10 +26,11 @@ export function useLayout() {
|
||||
|
||||
dagreGraph.setDefaultEdgeLabel(() => ({}))
|
||||
|
||||
const isHorizontal = direction === 'LR'
|
||||
dagreGraph.setGraph({ rankdir: direction })
|
||||
// 根据方向判断是否为水平布局
|
||||
const isHorizontal = layoutDirection === 'LR' || layoutDirection === 'RL'
|
||||
dagreGraph.setGraph({ rankdir: layoutDirection })
|
||||
|
||||
previousDirection.value = direction
|
||||
previousDirection.value = layoutDirection
|
||||
|
||||
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)
|
||||
@@ -44,10 +49,34 @@ export function useLayout() {
|
||||
return nodes.map((node) => {
|
||||
const nodeWithPosition = dagreGraph.node(node.id)
|
||||
|
||||
// 根据方向动态计算连接点位置
|
||||
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: isHorizontal ? Position.Left : Position.Top,
|
||||
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
|
||||
targetPosition,
|
||||
sourcePosition,
|
||||
position: { x: nodeWithPosition.x, y: nodeWithPosition.y },
|
||||
}
|
||||
})
|
||||
|
||||
@@ -12,7 +12,8 @@ export const versionsList = [
|
||||
name:'V1-1-1',
|
||||
}
|
||||
]
|
||||
},{
|
||||
},
|
||||
{
|
||||
id: '1-2',
|
||||
name:'V1-2',
|
||||
child:[
|
||||
@@ -24,7 +25,8 @@ export const versionsList = [
|
||||
name:'V1-2-2',
|
||||
}
|
||||
]
|
||||
},{
|
||||
},
|
||||
{
|
||||
id: '1-2',
|
||||
name:'V1-2',
|
||||
child:[
|
||||
@@ -51,7 +53,8 @@ export const versionsList = [
|
||||
]
|
||||
},
|
||||
]
|
||||
},{
|
||||
},
|
||||
{
|
||||
id: '1-3',
|
||||
name:'V1-3',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user