Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front

This commit is contained in:
2026-02-26 16:55:27 +08:00
16 changed files with 270 additions and 108 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

View File

@@ -9,3 +9,4 @@ export * from './global'
export * from './userInfo' export * from './userInfo'
export * from './projectData' export * from './projectData'
export * from './agent' export * from './agent'
export * from './versionTree'

View File

@@ -3,11 +3,25 @@ import { ref, computed } from 'vue'
export const useVersionTreeStore = defineStore('versionTree', () => { export const useVersionTreeStore = defineStore('versionTree', () => {
const state = ref({ const state = ref({
nodeDetail: { nodeDetail: {
createTime:'',
userEndChat:{
content:'',
createTime:'',
image_url:'',
},
generateEndChat:{
content:'',
createTime:'',
image_url:'',
},
},// 节点详情 },// 节点详情
}) })
const setNodeDetail = (v: any) => { state.value.nodeDetail = v } const setNodeDetail = (v: any) => {
for(let key in v){
state.value.nodeDetail[key] = v[key]
}
}
return { return {
state, state,

View File

@@ -10,6 +10,7 @@
<div class="agent-body flex-1 flex flex-col"> <div class="agent-body flex-1 flex flex-col">
<List ref="listRef" :message-list="messageList" @regenerate="handleRegenerate" /> <List ref="listRef" :message-list="messageList" @regenerate="handleRegenerate" />
<Input <Input
ref="inputRef"
is-agent-mode is-agent-mode
:generating="isGenerating" :generating="isGenerating"
@send="handleSendMessage" @send="handleSendMessage"
@@ -45,6 +46,7 @@
const messageList = ref([]) const messageList = ref([])
const listRef = ref() const listRef = ref()
const inputRef = ref()
const isGenerating = ref(false) const isGenerating = ref(false)
const isPaused = ref(false) // 标记是否为主动暂停 const isPaused = ref(false) // 标记是否为主动暂停
const params = reactive<AgentParamsType>({ const params = reactive<AgentParamsType>({
@@ -62,11 +64,15 @@
}) })
const sketchList = ref([]) const sketchList = ref([])
watch(sketchList, (newVal) => { watch(
console.log('添加图片链接--------'); sketchList,
(newVal) => {
console.log('添加图片链接--------')
emits('update:sketchList', newVal) emits('update:sketchList', newVal)
}, { deep: true }) },
{ deep: true }
)
// 每次请求时创建新的 AbortController // 每次请求时创建新的 AbortController
let abort: AbortController let abort: AbortController
@@ -255,7 +261,7 @@ watch(sketchList, (newVal) => {
try { try {
const jsonData = JSON.parse(jsonText) const jsonData = JSON.parse(jsonText)
console.log('jsonData', jsonData); console.log('jsonData', jsonData)
// 赋值 project_id 和 version_id // 赋值 project_id 和 version_id
// if (jsonData.project_id) params.projectID = jsonData.project_id // if (jsonData.project_id) params.projectID = jsonData.project_id
@@ -354,6 +360,10 @@ watch(sketchList, (newVal) => {
true true
) )
} }
defineExpose({
inputRef
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@@ -215,3 +215,11 @@
} }
} }
</style> </style>
<style lang="less">
.message-txt {
ul {
list-style-position: inside;
}
}
</style>

View File

@@ -10,6 +10,14 @@ const props = defineProps({
type: String, type: String,
default: 'user' default: 'user'
}, },
chatDetail: {
type: Object,
default: () => ({
content:'',
creatTime:'',
image_url:'',
})
},
}) })
//const emit = defineEmits([ //const emit = defineEmits([
//]) //])
@@ -30,12 +38,13 @@ const {} = toRefs(data);
<span>{{ type == 'user'? $t('VersionTree.input'): $t('VersionTree.sketch') }}</span> <span>{{ type == 'user'? $t('VersionTree.input'): $t('VersionTree.sketch') }}</span>
</div> </div>
<div class="time"> <div class="time">
<span>12:00:00</span> <span>{{ new Date(chatDetail.createTime * 1000).toLocaleString() }}</span>
</div> </div>
</div> </div>
<div class="infoTitle">{{ type == 'user'? $t('VersionTree.userRequest'): $t('VersionTree.generateResult') }}</div> <div class="infoTitle">{{ type == 'user'? $t('VersionTree.userRequest'): $t('VersionTree.generateResult') }}</div>
<div class="history"> <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. <span>{{ chatDetail.content }}</span>
<img v-if="chatDetail.image_url" :src="chatDetail.image_url" alt="">
</div> </div>
</div> </div>
</template> </template>
@@ -90,6 +99,16 @@ const {} = toRefs(data);
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
margin-left: 3rem; margin-left: 3rem;
> span{
}
> img{
width: 100%;
height: auto;
margin-top: 1.2rem;
border-radius: 1.3rem;
border: 1px solid #C9C9C9;
}
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 4px; width: 4px;
} }

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, watch } from "vue"; import { ref, onMounted, onUnmounted, computed, watch } from "vue";
import VersionDetail from './versionDetail.vue' import VersionDetail from './versionDetail.vue'
import ChatDetail from './chatDetail.vue' import ChatDetail from './chatDetail.vue'
import { useProjectStore } from '@/stores' import { useProjectStore, useVersionTreeStore } from '@/stores'
import { getChatNodeDetail, getNodeAncestors } from '@/api/versitonTree' import { getChatNodeDetail, getNodeAncestors } from '@/api/versitonTree'
//const props = defineProps({ //const props = defineProps({
@@ -11,35 +11,57 @@ const emit = defineEmits([
]) ])
const projectStore = useProjectStore() const projectStore = useProjectStore()
const versiontreeStore = useVersionTreeStore()
const detailData = ref({ const detailData = ref({
id:1, chatEndDetail:computed(()=>{
versionDetail:{ return versiontreeStore.state.nodeDetail
version:'1.0.0', }),
versionTime:'2023-08-01 10:00:00', loading:false,
versionSketch:'Version 1 - Sketch',
versionSketchTime:'2023-08-01 10:00:00',
},
userChatDetail:{
}
}) })
watch(()=>projectStore.state.nodeId, (newVal, oldVal) => { watch(()=>projectStore.state.nodeId, (newVal, oldVal) => {
console.log(newVal)
if(newVal){ if(newVal){
detailData.value.loading = true
getChatNodeDetail({ getChatNodeDetail({
projectId: projectStore.state.id, projectId: projectStore.state.id,
id: newVal id: newVal
}).then(res => { }).then((res:any) => {
console.log(res) let userEndChat = {
content:'',
createTime:'',
image_url:'',
}
let generateEndChat = {
content:'',
createTime:'',
image_url:'',
}
res.dialogue.forEach(item => {
if(item.role == 'user'){
if(item.content){
userEndChat.content += item.content
userEndChat.createTime = item.createTime
}
}else if(item.role == "assistant"){
if(item.content){
generateEndChat.content += item.content
generateEndChat.createTime = item.createTime
if(item.image_url)generateEndChat.image_url = item.image_url
}
}
}) })
getNodeAncestors({ versiontreeStore.setNodeDetail({createTime:res.createTime,userEndChat,generateEndChat})
projectId: projectStore.state.id, detailData.value.loading = false
id: newVal
}).then(res => {
console.log(res)
}) })
// getNodeAncestors({
// projectId: projectStore.state.id,
// id: newVal
// }).then(res => {
// console.log(res)
// })
} }
},{ immediate: true }) },{ immediate: true })
onMounted(()=>{ onMounted(()=>{
@@ -53,15 +75,16 @@ defineExpose({})
<div class="detailBox"> <div class="detailBox">
<div class="versionDetail"> <div class="versionDetail">
<VersionDetail <VersionDetail
:versionDetail="detailData.versionDetail" :versionDetail="detailData.chatEndDetail"
></VersionDetail> ></VersionDetail>
</div> </div>
<div class="useInput"> <div class="useInput">
<ChatDetail type="user"></ChatDetail> <ChatDetail type="user" :chatDetail="detailData.chatEndDetail.userEndChat"></ChatDetail>
</div> </div>
<div class="systemInput"> <div class="systemInput">
<ChatDetail type="robot"></ChatDetail> <ChatDetail type="robot" :chatDetail="detailData.chatEndDetail.generateEndChat"></ChatDetail>
</div> </div>
<div class="loading" v-if="detailData.loading" v-loading="true"></div>
</div> </div>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
@@ -71,7 +94,15 @@ defineExpose({})
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
> div{ > .loading{
position: absolute;
z-index: 999999999;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
> .versionDetail,> .useInput,> .systemInput{
border-radius: var(--border-radius, 1rem); border-radius: var(--border-radius, 1rem);
border: 1px solid #D9D9D9; border: 1px solid #D9D9D9;
background-color: #f7f7f7; background-color: #f7f7f7;

View File

@@ -5,7 +5,12 @@ const { t: $t } = useI18n()
const props = defineProps({ const props = defineProps({
versionDetail: { versionDetail: {
type: Object, type: Object,
default: () => ({}) default: () => ({
version:'1.0.0',
createTime:'',
versionSketch:'Version 1 - Sketch',
versionSketchTime:'2023-08-01 10:00:00',
})
} }
}) })
const emit = defineEmits([ const emit = defineEmits([
@@ -29,10 +34,10 @@ const {} = toRefs(data);
{{ $t('VersionTree.versionInformation') }} {{ $t('VersionTree.versionInformation') }}
</span> </span>
</div> </div>
<div class="version">{{versionDetail.version}}</div> <div class="version">{{versionDetail.version || '1.0.0'}}</div>
<div class="time marBott1">{{versionDetail.versionTime}}</div> <div class="time marBott1">{{ new Date(versionDetail.createTime * 1000).toLocaleString() }}</div>
<div class="version gray">{{versionDetail.versionSketch}}</div> <div class="version gray">{{versionDetail.versionSketch || 'Version 1 - Sketch'}}</div>
<div class="time gray">{{versionDetail.versionSketchTime}}</div> <div class="time gray">{{versionDetail.versionSketchTime || '2023-08-01 10:00:00'}}</div>
</div> </div>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>

View File

@@ -19,8 +19,9 @@ const props = defineProps({
} }
}) })
//const emit = defineEmits([ const emit = defineEmits([
//]) 'restore'
])
const versionsList = ref([]) const versionsList = ref([])
@@ -47,7 +48,7 @@ const setVersionsList = (res)=>{
function traverseArray(items,father, callback) { function traverseArray(items,father, callback) {
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
const item = items[i] const item = items[i]
if(!item.url)continue // if(!item?.url)continue
callback(item, i,father) callback(item, i,father)
if (item.children && Array.isArray(item.children) && item.children.length > 0) { if (item.children && Array.isArray(item.children) && item.children.length > 0) {
traverseArray(item.children, item, callback) traverseArray(item.children, item, callback)
@@ -71,6 +72,7 @@ const openTree = (state)=>{
} }
const versionRestore = () => { const versionRestore = () => {
let id = '' let id = ''
if(selectItem.value?.children?.length > 0){ if(selectItem.value?.children?.length > 0){
function findMaxForYourFormat(items) { function findMaxForYourFormat(items) {
@@ -99,6 +101,7 @@ const versionRestore = ()=>{
findAndAddChild(versionsList.value, selectItem.value?.versionId, addObj) findAndAddChild(versionsList.value, selectItem.value?.versionId, addObj)
selectItem.value = {...addObj} selectItem.value = {...addObj}
treeKey.value++ treeKey.value++
emit('restore')
} }
const versionDelete = (versionDetail)=>{ const versionDelete = (versionDetail)=>{
if(!selectItem.value?.versionId)return if(!selectItem.value?.versionId)return

View File

@@ -73,7 +73,7 @@ const initialize = ()=>{
const setSelectItem = (item)=>{ const setSelectItem = (item)=>{
if(!item.versionId)return if(!item.versionId)return
console.log(item) console.log(item)
// projectStore.setProject({latestNodeId: item.id}) projectStore.setProject({nodeId: item.id})
emit('update:selectItem', {...item}) emit('update:selectItem', {...item})
} }

View File

@@ -198,7 +198,10 @@ defineExpose({push})
overflow: hidden; overflow: hidden;
&.active{ &.active{
// background-color: var(--treeItem-active-background); // background-color: var(--treeItem-active-background);
border: 2px solid #000; border: 2px solid #d9d9d9;
.mask{
display: none;
}
} }
&.start{ &.start{
background-color: #7A7A7A; background-color: #7A7A7A;

View File

@@ -25,7 +25,8 @@ const props = defineProps<{
<!-- <div>{{ props.data.id }}</div> --> <!-- <div>{{ props.data.id }}</div> -->
<div class="item"> <div class="item">
<!-- {{ props.data.url }} --> <!-- {{ props.data.url }} -->
<img :src="props.data.url" /> <img :src="props.data?.url" />
<div class="mask"></div>
</div> </div>
</div> </div>
</template> </template>
@@ -35,11 +36,20 @@ const props = defineProps<{
.item{ .item{
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative;
img{ img{
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
} }
} }
.mask{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3);
}
} }
</style> </style>

View File

@@ -4,12 +4,16 @@
<div class="btn" @click="versionTreeData.drawer = true">Version Tree</div> <div class="btn" @click="versionTreeData.drawer = true">Version Tree</div>
</div> </div>
<div class="content-wrapper"> <div class="content-wrapper">
<Agent :title="agentTitle" @update:sketchList="updateSketchList" /> <Agent ref="agentRef" :title="agentTitle" @update:sketchList="updateSketchList" />
<div class="preview-wrapper"> <div class="preview-wrapper">
<Preview :type="previewType" :sketchList="sketchList" /> <Preview :type="previewType" :sketchList="sketchList" />
</div> </div>
</div> </div>
<VersionTreeIndex ref="VersionTreeIndexRef" v-model:versionTreeData="versionTreeData" /> <VersionTreeIndex
ref="VersionTreeIndexRef"
v-model:versionTreeData="versionTreeData"
@restore="handleRestore"
/>
</div> </div>
</template> </template>
@@ -21,12 +25,11 @@
import { useProjectStore } from '@/stores' import { useProjectStore } from '@/stores'
import { getProjectInfo } from '@/api/agent' import { getProjectInfo } from '@/api/agent'
const agentTitle = ref('Retro Sofa Sketch') const agentTitle = ref('Retro Sofa Sketch')
const previewType = ref<'sketch' | 'report'>('sketch') const previewType = ref<'sketch' | 'report'>('sketch')
const VersionTreeIndexRef = ref() const VersionTreeIndexRef = ref()
const agentRef = ref()
const sketchList = ref([]) const sketchList = ref([])
const updateSketchList = (newVal) => { const updateSketchList = (newVal) => {
console.log('newVal', newVal) console.log('newVal', newVal)
@@ -35,21 +38,30 @@
} }
const versionTreeData = ref({ const versionTreeData = ref({
drawer: false, drawer: false
}) })
const handleRestore = () => {
console.log('-----------', agentRef.value.inputRef.addReportTag)
agentRef.value?.inputRef?.addReportTag('Restore')
}
const projectStore = useProjectStore() const projectStore = useProjectStore()
watch(()=>projectStore.state.id, (newVal, oldVal) => { watch(
() => projectStore.state.id,
(newVal, oldVal) => {
if (newVal) { if (newVal) {
getProjectInfo({ id: newVal }).then(res => { getProjectInfo({ id: newVal }).then((res) => {
projectStore.setProject(res.project) projectStore.setProject(res.project)
}) })
} }
}) }
)
onMounted(() => { onMounted(() => {
if (projectStore.state.id) { if (projectStore.state.id) {
getProjectInfo({ id: projectStore.state.id }).then(res => { getProjectInfo({ id: projectStore.state.id }).then((res) => {
projectStore.setProject(res.project) projectStore.setProject(res.project)
}) })
} }

View File

@@ -22,7 +22,7 @@
:placeholder="$t('Input.placeholder')" :placeholder="$t('Input.placeholder')"
@input="handleEditorInput" @input="handleEditorInput"
@paste="handleEditorPaste" @paste="handleEditorPaste"
@keypress="handleKeyPress" @keydown="handleKeyDown"
></div> ></div>
</div> </div>
<div class="operate flex align-center space-between"> <div class="operate flex align-center space-between">
@@ -184,6 +184,8 @@
import { useAgentStore, useProjectStore } from '@/stores' import { useAgentStore, useProjectStore } from '@/stores'
import lightIcon from '@/assets/images/light-icon.png' import lightIcon from '@/assets/images/light-icon.png'
import closeIcon from '@/assets/images/close-icon.png' import closeIcon from '@/assets/images/close-icon.png'
import restoreIcon from '@/assets/images/restore.png'
import restoreCloseIcon from '@/assets/images/tag-close.png'
import { createProject } from '@/api/agent' import { createProject } from '@/api/agent'
import { getStyleImage } from './style' import { getStyleImage } from './style'
// import Tag from './Tag.vue' // import Tag from './Tag.vue'
@@ -265,22 +267,35 @@
const inputValue = ref<string>('') const inputValue = ref<string>('')
const reportTags = ref([]) const reportTags = ref([])
const addReportTag = () => { // 导出给父组件调用的方法
const addReportTag = (text?: string) => {
// 使用传入的文本,如果没有传入则使用默认的翻译文本
const tagText = text || t('Input.trendingReport')
// create container matching static structure: <div class="editor-tag report-btn flex-center" contenteditable="false">... // create container matching static structure: <div class="editor-tag report-btn flex-center" contenteditable="false">...
const tag = document.createElement('div') const tag = document.createElement('div')
tag.className = 'editor-tag report-btn flex-center'
tag.contentEditable = 'false' tag.contentEditable = 'false'
const imgLeft = document.createElement('img') const imgLeft = document.createElement('img')
const imgClose = document.createElement('img')
const textSpan = document.createElement('span')
imgClose.className = 'close-icon'
if (text) {
tag.className = 'editor-tag restore flex-center'
imgLeft.className = 'restore-icon'
imgLeft.src = restoreIcon as unknown as string
imgClose.src = restoreCloseIcon as unknown as string
imgClose.className = 'close-icon restore'
textSpan.className = 'restore-text'
} else {
tag.className = 'editor-tag report-btn flex-center'
imgLeft.className = 'light-icon' imgLeft.className = 'light-icon'
imgLeft.src = lightIcon as unknown as string imgLeft.src = lightIcon as unknown as string
const textSpan = document.createElement('span')
textSpan.innerText = t('Input.trendingReport')
const imgClose = document.createElement('img')
imgClose.className = 'close-icon'
imgClose.src = closeIcon as unknown as string imgClose.src = closeIcon as unknown as string
}
textSpan.innerText = tagText
imgClose.addEventListener('click', (ev) => { imgClose.addEventListener('click', (ev) => {
ev.stopPropagation() ev.stopPropagation()
// remove tag when close clicked // remove tag when close clicked
@@ -398,11 +413,17 @@
} }
} }
const handleKeyPress = (e) => { const handleKeyDown = (e) => {
// 检测回车 // 检测回车
if (e.key === 'Enter') { if (e.key === 'Enter') {
console.log('11111111111')
e.preventDefault() e.preventDefault()
if (props.isAgentMode) {
handleSendAgent() handleSendAgent()
} else {
handleCreateProject()
}
return return
} }
if (e.key === 'Backspace') { if (e.key === 'Backspace') {
@@ -446,12 +467,20 @@
} }
const handleSendAgent = async () => { const handleSendAgent = async () => {
console.log('发送信息--------')
if (props.generating) { if (props.generating) {
emits('pause') emits('pause')
return return
} }
if (!inputValue.value.trim()) return if (!inputValue.value.trim()) return
emits('send', { text: inputValue.value.trim(), images: uploadedImages.value }) console.log('222222')
const payload = { text: inputValue.value.trim(), images: uploadedImages.value }
console.log('准备发送 send 事件', payload)
emits('send', payload)
console.log('send 事件已发送')
// 发送后清空输入框 // 发送后清空输入框
if (editorRef.value) { if (editorRef.value) {
editorRef.value.innerHTML = '' editorRef.value.innerHTML = ''
@@ -534,7 +563,7 @@
temperature: 0.7 temperature: 0.7
} }
const projectres = await createProject(params) const projectres = await createProject(params)
console.log('projectres', projectres) // console.log('projectres', projectres)
projectStore.setId(projectres) projectStore.setId(projectres)
// 保存初始数据到 store // 保存初始数据到 store
agentStore.setInitialProjectData({ agentStore.setInitialProjectData({
@@ -543,9 +572,14 @@
...params ...params
}) })
console.log('Create project with:', params) // console.log('Create project with:', params)
router.push('/home/agent', { query: params }) router.push('/home/agent', { query: params })
} }
// 暴露方法给父组件
defineExpose({
addReportTag
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@@ -983,9 +1017,32 @@
margin: 0 0.5rem; margin: 0 0.5rem;
vertical-align: middle; vertical-align: middle;
border-radius: 2.2rem; border-radius: 2.2rem;
&.restore {
width: auto;
max-width: 100%;
display: inline-flex;
border: none;
border-radius: 0.4rem;
background-color: rgba(0, 0, 0, 0.05);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
align-items: center;
justify-content: space-between;
padding: 0 0.9rem 0 0.7rem;
box-sizing: border-box;
}
span { span {
margin: 0 0.7rem 0 1.2rem; margin: 0 0.7rem 0 1.2rem;
&.restore-text {
flex: 1;
min-width: 0;
max-width: 52rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
} }
.light-icon { .light-icon {
@@ -999,6 +1056,15 @@
height: 1rem; height: 1rem;
cursor: pointer; cursor: pointer;
flex-shrink: 0; flex-shrink: 0;
&.restore{
width: 0.5rem;
height: 0.5rem;
} }
} }
.restore-icon{
width: 1.2rem;
height: 1.2rem;
}
}
</style> </style>

View File

@@ -25,35 +25,15 @@ function slugify(str) {
* @returns { string | null } 返回可直接用于 <img :src=""> 的 URL * @returns { string | null } 返回可直接用于 <img :src=""> 的 URL
*/ */
export function getStyleImage(types, style) { export function getStyleImage(types, style) {
if (!types) types = 'Sofa' if (!types) types = 'Sofa'
const type = types.toLowerCase() const type = types.toLowerCase()
const map = imagesMaps[type] const map = imagesMaps[type]
const fileName = `${slugify(style)}.png` const fileName = `${slugify(style)}.png`
const key = `/src/assets/images/${type}/${fileName}` const key = `/src/assets/images/${type}/${fileName}`
console.log('types',types, 'style',style, 'fileName',fileName, 'key',key);
if (map[key]) { if (map[key]) {
return map[key].default return map[key].default
} }
return '' return ''
} }
const styleList = [
'Venetian Modern',
'Coastal',
'Maximalism',
'Memphis',
'Verdant',
'Century Chrome',
'Modern Revival',
'Transitional',
"Tuscan 2000's",
'Kitsch-core',
'Bauhaus',
'Constructivism',
'Nordic Noir',
'Dopamine',
'Squiggle'
]