Compare commits
12 Commits
26cd16be09
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8f7d157f4 | ||
| 8acb5b4ce5 | |||
| 1dd36b1b8c | |||
| 2bca08ed97 | |||
| ba63d16d60 | |||
|
|
f24a9afe5c | ||
| 4d1d082fae | |||
| c2f0f82218 | |||
| 620962b9ee | |||
| 89aab7e960 | |||
| 1078961608 | |||
| d073008736 |
@@ -20,7 +20,7 @@
|
|||||||
const stateManager = inject('stateManager') as any
|
const stateManager = inject('stateManager') as any
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
url: stateManager.getSuperiorNodeImage(attrs.node?.data?.superiorID),
|
url: stateManager.getSuperiorNodeImage(attrs.node?.data?.superiorID),
|
||||||
mode: 'Advanced',
|
mode: 'Normal',
|
||||||
})
|
})
|
||||||
const modeList = ref([
|
const modeList = ref([
|
||||||
{ value: 'Advanced', label: t('FlowCanvas.advancedMode') },
|
{ value: 'Advanced', label: t('FlowCanvas.advancedMode') },
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ const captureView = ()=>{
|
|||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
})
|
})
|
||||||
onUnmounted(()=>{
|
onUnmounted(()=>{
|
||||||
|
console.log('onUnmounted')
|
||||||
|
threeModel.disposeModel()
|
||||||
|
threeModel = null
|
||||||
})
|
})
|
||||||
defineExpose({open,captureView})
|
defineExpose({open,captureView})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class ThreeManager {
|
|||||||
camera: THREE.PerspectiveCamera;//相机对象
|
camera: THREE.PerspectiveCamera;//相机对象
|
||||||
renderer: THREE.WebGLRenderer;//渲染器对象
|
renderer: THREE.WebGLRenderer;//渲染器对象
|
||||||
controls: OrbitControls;//轨道控制器对象
|
controls: OrbitControls;//轨道控制器对象
|
||||||
|
animationId: number | null = null;
|
||||||
|
|
||||||
pointLight: THREE.AmbientLight;//环境光对象
|
pointLight: THREE.AmbientLight;//环境光对象
|
||||||
studioLights: any;//工作室光对象数组
|
studioLights: any;//工作室光对象数组
|
||||||
@@ -316,15 +316,14 @@ export class ThreeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
operation(){
|
operation(){
|
||||||
let this_ = this
|
|
||||||
const animate = () => {
|
const animate = () => {
|
||||||
requestAnimationFrame(animate);
|
this.animationId = requestAnimationFrame(animate);
|
||||||
this.controls.update();
|
this.controls?.update();
|
||||||
this.updateStudioLighting();
|
this.updateStudioLighting();
|
||||||
this.updateImagePipeline();
|
this.updateImagePipeline();
|
||||||
this.renderer.render(this.scene, this.camera);
|
this.renderer?.render(this.scene, this.camera);
|
||||||
}
|
};
|
||||||
animate();
|
animate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 释放模型资源
|
// 释放模型资源
|
||||||
@@ -343,7 +342,91 @@ export class ThreeManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
disposeModel() {
|
||||||
|
console.log('开始销毁 ThreeManager...');
|
||||||
|
// 1. 停止动画
|
||||||
|
if (this.animationId) {
|
||||||
|
cancelAnimationFrame(this.animationId);
|
||||||
|
this.animationId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 销毁控制器
|
||||||
|
if (this.controls) {
|
||||||
|
this.controls.dispose();
|
||||||
|
this.controls = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 销毁模型
|
||||||
|
if (this.currentModel) {
|
||||||
|
this.dispose(this.currentModel);
|
||||||
|
this.scene?.remove(this.currentModel);
|
||||||
|
this.currentModel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 销毁灯光
|
||||||
|
if (this.studioLights?.length) {
|
||||||
|
this.studioLights.forEach(item => {
|
||||||
|
if (item.light) {
|
||||||
|
this.scene?.remove(item.light);
|
||||||
|
item.light.dispose?.();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.studioLights = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 清理环境光
|
||||||
|
if (this.pointLight) {
|
||||||
|
this.scene?.remove(this.pointLight);
|
||||||
|
this.pointLight = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 清理场景
|
||||||
|
if (this.scene) {
|
||||||
|
this.scene.traverse((obj) => {
|
||||||
|
if (obj.isMesh) {
|
||||||
|
const mesh = obj as THREE.Mesh;
|
||||||
|
mesh.geometry?.dispose();
|
||||||
|
if (mesh.material) {
|
||||||
|
if (Array.isArray(mesh.material)) {
|
||||||
|
mesh.material.forEach(m => m.dispose());
|
||||||
|
} else {
|
||||||
|
mesh.material.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
while (this.scene.children.length) {
|
||||||
|
this.scene.remove(this.scene.children[0]);
|
||||||
|
}
|
||||||
|
this.scene = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 销毁渲染器
|
||||||
|
if (this.renderer) {
|
||||||
|
this.renderer.dispose();
|
||||||
|
if (this.renderer.domElement?.parentNode) {
|
||||||
|
this.renderer.domElement.parentNode.removeChild(this.renderer.domElement);
|
||||||
|
}
|
||||||
|
this.renderer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 清空 DOM 容器
|
||||||
|
if (this.threeDom) {
|
||||||
|
this.threeDom.innerHTML = '';
|
||||||
|
this.threeDom = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 清空其他引用
|
||||||
|
this.camera = null;
|
||||||
|
this.v1 = null;
|
||||||
|
this.camDir = null;
|
||||||
|
this.camForward = null;
|
||||||
|
this.camToTarget = null;
|
||||||
|
this.modelInfo = null;
|
||||||
|
|
||||||
|
console.log('ThreeManager 已销毁');
|
||||||
|
}
|
||||||
|
|
||||||
exportAsImage(){
|
exportAsImage(){
|
||||||
return this.renderer.domElement.toDataURL('image/png');
|
return this.renderer.domElement.toDataURL('image/png');
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default {
|
|||||||
retrievePassword: 'Retrieve password'
|
retrievePassword: 'Retrieve password'
|
||||||
},
|
},
|
||||||
Nuic: {
|
Nuic: {
|
||||||
hiName: `Hi, {name}, I'm Fiphant.`,
|
hiName: `Hi {name}, I'm Fiphant.`,
|
||||||
nuic1Title: `Let’s reveal the creative paths waiting for you.`,
|
nuic1Title: `Let’s reveal the creative paths waiting for you.`,
|
||||||
nuic1Tip: `Let's set up your profile. A few quick details will help Fiphant understand<br />your needs and find exactly what you're looking for.`,
|
nuic1Tip: `Let's set up your profile. A few quick details will help Fiphant understand<br />your needs and find exactly what you're looking for.`,
|
||||||
letsGo: 'Let’s go, Fiphant!',
|
letsGo: 'Let’s go, Fiphant!',
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type InitialProjectData = {
|
|||||||
useReport:boolean
|
useReport:boolean
|
||||||
needSuggestion:boolean
|
needSuggestion:boolean
|
||||||
quoteList: Array<string>
|
quoteList: Array<string>
|
||||||
|
parameterTags?: Array<{ kind: string; label: string }>
|
||||||
tempImages: any[]
|
tempImages: any[]
|
||||||
}
|
}
|
||||||
export const useAgentStore = defineStore('agent', () => {
|
export const useAgentStore = defineStore('agent', () => {
|
||||||
|
|||||||
@@ -37,8 +37,12 @@
|
|||||||
import type { AgentParamsType } from '@/api/agent'
|
import type { AgentParamsType } from '@/api/agent'
|
||||||
import { useUserInfoStore, useProjectStore, useAgentStore } from '@/stores'
|
import { useUserInfoStore, useProjectStore, useAgentStore } from '@/stores'
|
||||||
import MyEvent from '@/utils/myEvent'
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
import { areaList } from '@/utils/area'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { createStyleOptions, createTypeOptions } from '../../components/input/options'
|
||||||
|
import type { OptionItem, ParameterTag } from '../../components/input/types'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -83,6 +87,59 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
const sketchList = ref([])
|
const sketchList = ref([])
|
||||||
|
const typeOptions = createTypeOptions()
|
||||||
|
const areaOptions = areaList
|
||||||
|
const styleOptions = createStyleOptions()
|
||||||
|
|
||||||
|
const getTranslatedOptionLabel = (options: OptionItem[], value?: string) => {
|
||||||
|
if (!value) return ''
|
||||||
|
const option = options.find((item) => item.value === value)
|
||||||
|
return option ? t(option.label) : value
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOptionLabel = (options: OptionItem[], value?: string) => {
|
||||||
|
if (!value) return ''
|
||||||
|
return options.find((item) => item.value === value)?.label || value
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProjectParameterTags = (project: any): ParameterTag[] => {
|
||||||
|
const type = project?.type || ''
|
||||||
|
const region = project?.region || project?.area || ''
|
||||||
|
const style = project?.style || ''
|
||||||
|
const tags: ParameterTag[] = []
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
tags.push({
|
||||||
|
kind: 'type',
|
||||||
|
label: getTranslatedOptionLabel(typeOptions, type)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (region) {
|
||||||
|
tags.push({
|
||||||
|
kind: 'area',
|
||||||
|
label: getTranslatedOptionLabel(areaOptions, region)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (style) {
|
||||||
|
tags.push({
|
||||||
|
kind: 'style',
|
||||||
|
label: getOptionLabel(styleOptions, style)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyProjectParameterTags = (messages: any[], project: any) => {
|
||||||
|
const parameterTags = getProjectParameterTags(project)
|
||||||
|
if (parameterTags.length === 0) return
|
||||||
|
|
||||||
|
const firstUserMessage = messages.find((item) => item.isUser || item.role === 'user')
|
||||||
|
if (firstUserMessage) {
|
||||||
|
firstUserMessage.parameterTags = parameterTags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
sketchList,
|
sketchList,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
@@ -143,7 +200,8 @@
|
|||||||
images: initialData.images,
|
images: initialData.images,
|
||||||
useReport: initialData.useReport,
|
useReport: initialData.useReport,
|
||||||
tempImages: initialData.tempImages,
|
tempImages: initialData.tempImages,
|
||||||
quoteList: initialData.quoteList
|
quoteList: initialData.quoteList,
|
||||||
|
parameterTags: initialData.parameterTags || []
|
||||||
})
|
})
|
||||||
// 更新 configParams
|
// 更新 configParams
|
||||||
|
|
||||||
@@ -181,6 +239,7 @@
|
|||||||
tempImages: any[]
|
tempImages: any[]
|
||||||
useReport: boolean
|
useReport: boolean
|
||||||
quoteList: Array<string>
|
quoteList: Array<string>
|
||||||
|
parameterTags?: Array<{ kind: string; label: string }>
|
||||||
},
|
},
|
||||||
skipUserMessage = false
|
skipUserMessage = false
|
||||||
) => {
|
) => {
|
||||||
@@ -199,7 +258,8 @@
|
|||||||
id: messageList.value.length + 1,
|
id: messageList.value.length + 1,
|
||||||
text: message.text,
|
text: message.text,
|
||||||
isUser: true,
|
isUser: true,
|
||||||
imageUrls: message.tempImages.concat(message.quoteList)
|
imageUrls: message.tempImages.concat(message.quoteList),
|
||||||
|
parameterTags: message.parameterTags || []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +688,7 @@
|
|||||||
const setChatInfo = (info) => {
|
const setChatInfo = (info) => {
|
||||||
const initialData = agentStore.getInitialProjectData
|
const initialData = agentStore.getInitialProjectData
|
||||||
if (isGenerating.value || initialData) return
|
if (isGenerating.value || initialData) return
|
||||||
|
console.log('---',info)
|
||||||
const data = info.conversation
|
const data = info.conversation
|
||||||
let project = info.project
|
let project = info.project
|
||||||
if (info.id) {
|
if (info.id) {
|
||||||
@@ -641,7 +701,7 @@
|
|||||||
|
|
||||||
if (project) {
|
if (project) {
|
||||||
params.configParams.type = project.type || ''
|
params.configParams.type = project.type || ''
|
||||||
params.configParams.region = project.area || ''
|
params.configParams.region = project.region || project.area || ''
|
||||||
params.configParams.style = project.style || ''
|
params.configParams.style = project.style || ''
|
||||||
params.configParams.temperature = project.temperature
|
params.configParams.temperature = project.temperature
|
||||||
params.projectID = project.id
|
params.projectID = project.id
|
||||||
@@ -668,6 +728,7 @@
|
|||||||
item.text += `<slot slot-name="sketch"></slot>`
|
item.text += `<slot slot-name="sketch"></slot>`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
applyProjectParameterTags(ancestorsList, project)
|
||||||
// console.log('ancestorslist', ancestorsList)
|
// console.log('ancestorslist', ancestorsList)
|
||||||
messageList.value = [...ancestorsList]
|
messageList.value = [...ancestorsList]
|
||||||
params.versionID = current?.id
|
params.versionID = current?.id
|
||||||
|
|||||||
@@ -52,20 +52,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="message-txt markdown-body flex flex-col">
|
<div class="message-txt markdown-body">
|
||||||
|
<span
|
||||||
|
v-if="parameterTags.length > 0"
|
||||||
|
class="message-parameter-tags"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-for="tag in parameterTags"
|
||||||
|
:key="`${tag.kind}-${tag.label}`"
|
||||||
|
class="message-parameter-tag"
|
||||||
|
>
|
||||||
|
<img :src="getParameterTagIcon(tag.kind)" class="parameter-tag-icon" />
|
||||||
|
<span>{{ tag.label }}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
<VueMarkdown
|
<VueMarkdown
|
||||||
|
class="message-markdown"
|
||||||
:custom-attrs="customAttrs"
|
:custom-attrs="customAttrs"
|
||||||
:markdown="content.text"
|
:markdown="content.text"
|
||||||
:rehype-plugins="[rehypeRaw]"
|
:rehype-plugins="[rehypeRaw]"
|
||||||
>
|
>
|
||||||
<template v-slot:s-card="{ children: children, ...attrs }">
|
<template v-slot:s-card="{ children: children, ...attrs }">
|
||||||
<Card title="Trend Report" @click.native="handleClickReport" />
|
<Card title="Trend Report" @click="handleClickReport" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:s-url="{ children: children }">
|
<template v-slot:s-url="{ children: children }">
|
||||||
<Url :list="content.webAddress" @click.native="handleClickUrls" />
|
<Url :list="content.webAddress" @click="handleClickUrls" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:s-sketch="{ children: children }">
|
<template v-slot:s-sketch="{ children: children }">
|
||||||
<Sketch @click.native="handleClickSketch" />
|
<Sketch @click="handleClickSketch" />
|
||||||
</template>
|
</template>
|
||||||
</VueMarkdown>
|
</VueMarkdown>
|
||||||
<div
|
<div
|
||||||
@@ -116,6 +130,9 @@
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import userThumb from '@/assets/images/user-thumb.jpg'
|
import userThumb from '@/assets/images/user-thumb.jpg'
|
||||||
import agentThumb from '@/assets/images/agent-thumb.png'
|
import agentThumb from '@/assets/images/agent-thumb.png'
|
||||||
|
import TypeIcon from '@/assets/icons/TypeIcon.svg'
|
||||||
|
import RegionIcon from '@/assets/icons/RegionIcon.svg'
|
||||||
|
import StyleIcon from '@/assets/icons/StyleIcon.svg'
|
||||||
import Card from './ReportCard.vue'
|
import Card from './ReportCard.vue'
|
||||||
import Url from './UrlCard.vue'
|
import Url from './UrlCard.vue'
|
||||||
import Sketch from './SketchCard.vue'
|
import Sketch from './SketchCard.vue'
|
||||||
@@ -126,6 +143,7 @@
|
|||||||
import rehypeRaw from 'rehype-raw'
|
import rehypeRaw from 'rehype-raw'
|
||||||
import MyEvent from '@/utils/myEvent'
|
import MyEvent from '@/utils/myEvent'
|
||||||
import { useUserInfoStore, useProjectStore } from '@/stores'
|
import { useUserInfoStore, useProjectStore } from '@/stores'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const userStore = useUserInfoStore()
|
const userStore = useUserInfoStore()
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
@@ -136,8 +154,14 @@
|
|||||||
return locale.value === 'CHINESE_SIMPLIFIED'
|
return locale.value === 'CHINESE_SIMPLIFIED'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type ParameterTagKind = 'type' | 'area' | 'style'
|
||||||
|
type ParameterTag = {
|
||||||
|
kind: ParameterTagKind
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
content: Object
|
content: any
|
||||||
isLast: Boolean
|
isLast: Boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -174,6 +198,16 @@
|
|||||||
return list
|
return list
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const parameterTags = computed<ParameterTag[]>(() => {
|
||||||
|
return Array.isArray(props.content?.parameterTags) ? props.content.parameterTags : []
|
||||||
|
})
|
||||||
|
|
||||||
|
const getParameterTagIcon = (kind: ParameterTagKind) => {
|
||||||
|
if (kind === 'type') return TypeIcon
|
||||||
|
if (kind === 'area') return RegionIcon
|
||||||
|
return StyleIcon
|
||||||
|
}
|
||||||
|
|
||||||
const customAttrs: CustomAttrs = {
|
const customAttrs: CustomAttrs = {
|
||||||
img: {
|
img: {
|
||||||
style: 'max-width: 100%;'
|
style: 'max-width: 100%;'
|
||||||
@@ -348,6 +382,39 @@
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
max-width: 82%;
|
max-width: 82%;
|
||||||
}
|
}
|
||||||
|
.message-parameter-tags {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
.message-parameter-tag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 3rem;
|
||||||
|
max-width: 24rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
margin: 0 0.8rem 0.4rem 0;
|
||||||
|
border: 0.1rem solid #0000001a;
|
||||||
|
border-radius: 2.2rem;
|
||||||
|
column-gap: 0.8rem;
|
||||||
|
font-family: 'Medium';
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #0d0d0d;
|
||||||
|
background-color: #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
.parameter-tag-icon {
|
||||||
|
width: 1.2rem;
|
||||||
|
height: 1.2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
.web-address {
|
.web-address {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
min-width: 22.5rem;
|
min-width: 22.5rem;
|
||||||
@@ -443,6 +510,18 @@
|
|||||||
<style lang="less">
|
<style lang="less">
|
||||||
.message-txt {
|
.message-txt {
|
||||||
user-select: text;
|
user-select: text;
|
||||||
|
|
||||||
|
.message-markdown,
|
||||||
|
> div:not(.web-address) {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-markdown > p:first-child,
|
||||||
|
> div:not(.web-address) > p:first-child {
|
||||||
|
display: inline;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,10 @@
|
|||||||
:setting-title="$t('Input.styleTitle')"
|
:setting-title="$t('Input.styleTitle')"
|
||||||
:confirm-text="$t('Input.confirm')"
|
:confirm-text="$t('Input.confirm')"
|
||||||
:create-text="$t('Input.createProject')"
|
:create-text="$t('Input.createProject')"
|
||||||
|
:parameters-disabled="isReportSelected"
|
||||||
:translate="translate"
|
:translate="translate"
|
||||||
@file-change="handleFileChange"
|
@file-change="handleFileChange"
|
||||||
@toggle-report="toggleReportTag()"
|
@toggle-report="handleToggleReportTag"
|
||||||
@create="handleCreateProject"
|
@create="handleCreateProject"
|
||||||
@send="handleSendAgent"
|
@send="handleSendAgent"
|
||||||
/>
|
/>
|
||||||
@@ -48,7 +49,7 @@
|
|||||||
v-if="!isAgentMode"
|
v-if="!isAgentMode"
|
||||||
:is-cn="isCn"
|
:is-cn="isCn"
|
||||||
:label="$t('Input.trendingReport')"
|
:label="$t('Input.trendingReport')"
|
||||||
@click="toggleReportTag()"
|
@click="handleToggleReportTag"
|
||||||
/>
|
/>
|
||||||
<Preview v-model="showPreview" :url="previewUrl" />
|
<Preview v-model="showPreview" :url="previewUrl" />
|
||||||
</div>
|
</div>
|
||||||
@@ -70,11 +71,12 @@
|
|||||||
import {
|
import {
|
||||||
createSettingOptions,
|
createSettingOptions,
|
||||||
createStyleOptions,
|
createStyleOptions,
|
||||||
createTypeOptions
|
createTypeOptions,
|
||||||
|
optionTagOrder
|
||||||
} from './input/options'
|
} from './input/options'
|
||||||
import { useInputEditor } from './input/useInputEditor'
|
import { useInputEditor } from './input/useInputEditor'
|
||||||
import { useInputImages } from './input/useInputImages'
|
import { useInputImages } from './input/useInputImages'
|
||||||
import type { OptionItem, SettingOption } from './input/types'
|
import type { OptionItem, OptionTagKind, ParameterTag, SettingOption } from './input/types'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const agentStore = useAgentStore()
|
const agentStore = useAgentStore()
|
||||||
@@ -112,6 +114,7 @@
|
|||||||
const {
|
const {
|
||||||
inputValue,
|
inputValue,
|
||||||
reportTags,
|
reportTags,
|
||||||
|
optionTagKinds,
|
||||||
showPlaceholder,
|
showPlaceholder,
|
||||||
setEditorElement,
|
setEditorElement,
|
||||||
focusEditor,
|
focusEditor,
|
||||||
@@ -156,6 +159,59 @@
|
|||||||
clearImages
|
clearImages
|
||||||
} = useInputImages(focusEditor)
|
} = useInputImages(focusEditor)
|
||||||
|
|
||||||
|
const isReportSelected = computed(() => {
|
||||||
|
return reportTags.value.some((tag) => tag.parentNode)
|
||||||
|
})
|
||||||
|
|
||||||
|
const clearParameterValues = () => {
|
||||||
|
typeValue.value = ''
|
||||||
|
areaValue.value = ''
|
||||||
|
styleValue.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleToggleReportTag = () => {
|
||||||
|
const shouldSelectReport = !isReportSelected.value
|
||||||
|
toggleReportTag()
|
||||||
|
if (shouldSelectReport) {
|
||||||
|
clearParameterValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTranslatedOptionLabel = (options: OptionItem[], value: string) => {
|
||||||
|
const option = options.find((item) => item.value === value)
|
||||||
|
return option ? translate(option.label) : value
|
||||||
|
}
|
||||||
|
|
||||||
|
const getParameterTagLabel = (kind: OptionTagKind) => {
|
||||||
|
if (kind === 'type') {
|
||||||
|
return getTranslatedOptionLabel(typeOptions.value, typeValue.value)
|
||||||
|
}
|
||||||
|
if (kind === 'area') {
|
||||||
|
return getTranslatedOptionLabel(areaOptions.value, areaValue.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = styleOptions.value.find((item) => item.value === styleValue.value)
|
||||||
|
return option?.label || styleValue.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasParameterValue = (kind: OptionTagKind) => {
|
||||||
|
if (kind === 'type') return Boolean(typeValue.value)
|
||||||
|
if (kind === 'area') return Boolean(areaValue.value)
|
||||||
|
return Boolean(styleValue.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedParameterTags = computed<ParameterTag[]>(() => {
|
||||||
|
const currentKinds = optionTagKinds.value.filter(hasParameterValue)
|
||||||
|
const missingKinds = optionTagOrder.filter((kind) => {
|
||||||
|
return hasParameterValue(kind) && !currentKinds.includes(kind)
|
||||||
|
})
|
||||||
|
|
||||||
|
return [...currentKinds, ...missingKinds].map((kind) => ({
|
||||||
|
kind,
|
||||||
|
label: getParameterTagLabel(kind)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
watch(inputValue, () => {
|
watch(inputValue, () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
autoResizeEditor()
|
autoResizeEditor()
|
||||||
@@ -199,12 +255,14 @@
|
|||||||
images: any[]
|
images: any[]
|
||||||
tempImages: typeof uploadedImages.value
|
tempImages: typeof uploadedImages.value
|
||||||
quoteList: string[]
|
quoteList: string[]
|
||||||
|
parameterTags: ParameterTag[]
|
||||||
useReport?: boolean
|
useReport?: boolean
|
||||||
} = {
|
} = {
|
||||||
text: inputValue.value.trim(),
|
text: inputValue.value.trim(),
|
||||||
images: imageUrlList,
|
images: imageUrlList,
|
||||||
tempImages: uploadedImages.value,
|
tempImages: uploadedImages.value,
|
||||||
quoteList: quoteList.value
|
quoteList: quoteList.value,
|
||||||
|
parameterTags: selectedParameterTags.value
|
||||||
}
|
}
|
||||||
if (reportTags.value.length > 0) {
|
if (reportTags.value.length > 0) {
|
||||||
payload.useReport = true
|
payload.useReport = true
|
||||||
@@ -238,6 +296,7 @@
|
|||||||
tempImages: uploadedImages.value,
|
tempImages: uploadedImages.value,
|
||||||
needSuggestion: false,
|
needSuggestion: false,
|
||||||
quoteList: quoteList.value,
|
quoteList: quoteList.value,
|
||||||
|
parameterTags: selectedParameterTags.value,
|
||||||
...params
|
...params
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -171,10 +171,15 @@
|
|||||||
column-gap: 0.8rem;
|
column-gap: 0.8rem;
|
||||||
color: #0d0d0d;
|
color: #0d0d0d;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.option-tag-icon {
|
.option-tag-icon {
|
||||||
width: 1.2rem;
|
width: 1.2rem;
|
||||||
height: 1.2rem;
|
height: 1.2rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-tag-text {
|
.option-tag-text {
|
||||||
@@ -185,10 +190,8 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-close {
|
.option-tag-close {
|
||||||
width: 0.7rem;
|
cursor: pointer;
|
||||||
height: 0.7rem;
|
|
||||||
margin-left: 0.2rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
v-if="!isAgentMode"
|
v-if="!isAgentMode"
|
||||||
v-model="typeModel"
|
v-model="typeModel"
|
||||||
:placeholder="typePlaceholder"
|
:placeholder="typePlaceholder"
|
||||||
|
:disabled="parametersDisabled"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in typeOptions"
|
v-for="item in typeOptions"
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
v-if="!isAgentMode"
|
v-if="!isAgentMode"
|
||||||
v-model="areaModel"
|
v-model="areaModel"
|
||||||
:placeholder="areaPlaceholder"
|
:placeholder="areaPlaceholder"
|
||||||
|
:disabled="parametersDisabled"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in areaOptions"
|
v-for="item in areaOptions"
|
||||||
@@ -54,12 +56,13 @@
|
|||||||
:placeholder="stylePlaceholder"
|
:placeholder="stylePlaceholder"
|
||||||
:title="styleTitle"
|
:title="styleTitle"
|
||||||
:confirm-text="confirmText"
|
:confirm-text="confirmText"
|
||||||
|
:disabled="parametersDisabled"
|
||||||
/>
|
/>
|
||||||
<SettingPopover
|
<!-- <SettingPopover
|
||||||
v-model:options="settingOptionsModel"
|
v-model:options="settingOptionsModel"
|
||||||
:title="settingTitle"
|
:title="settingTitle"
|
||||||
:translate="translate"
|
:translate="translate"
|
||||||
/>
|
/> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<div
|
<div
|
||||||
@@ -109,11 +112,13 @@
|
|||||||
settingTitle: string
|
settingTitle: string
|
||||||
confirmText: string
|
confirmText: string
|
||||||
createText: string
|
createText: string
|
||||||
|
parametersDisabled?: boolean
|
||||||
translate: (key: string) => string
|
translate: (key: string) => string
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
isAgentMode: false,
|
isAgentMode: false,
|
||||||
generating: false
|
generating: false,
|
||||||
|
parametersDisabled: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -198,6 +203,13 @@
|
|||||||
.el-select__icon {
|
.el-select__icon {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
.el-select__placeholder,
|
||||||
|
.el-select__selected-item {
|
||||||
|
color: #c9c9c9;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fida-style-select-wrapper">
|
<div class="fida-style-select-wrapper" :class="{ 'is-disabled': disabled }">
|
||||||
<el-select
|
<el-select
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
@focus="openStylePopup"
|
@focus="openStylePopup"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<el-popover
|
<el-popover
|
||||||
v-model:visible="stylePopupVisible"
|
v-model:visible="stylePopupVisible"
|
||||||
|
:disabled="disabled"
|
||||||
placement="top"
|
placement="top"
|
||||||
:width="342"
|
:width="342"
|
||||||
:show-arrow="false"
|
:show-arrow="false"
|
||||||
@@ -49,7 +51,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { getStyleImage } from '../style'
|
import { getStyleImage } from '../style'
|
||||||
import type { OptionItem } from './types'
|
import type { OptionItem } from './types'
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@
|
|||||||
placeholder: string
|
placeholder: string
|
||||||
title: string
|
title: string
|
||||||
confirmText: string
|
confirmText: string
|
||||||
|
disabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -69,12 +72,31 @@
|
|||||||
const tempSelectedValue = ref('')
|
const tempSelectedValue = ref('')
|
||||||
const stylePopupVisible = ref(false)
|
const stylePopupVisible = ref(false)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(value) => {
|
||||||
|
tempSelectedValue.value = value
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.disabled,
|
||||||
|
(disabled) => {
|
||||||
|
if (disabled) {
|
||||||
|
stylePopupVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const openStylePopup = () => {
|
const openStylePopup = () => {
|
||||||
|
if (props.disabled) return
|
||||||
tempSelectedValue.value = props.modelValue
|
tempSelectedValue.value = props.modelValue
|
||||||
stylePopupVisible.value = true
|
stylePopupVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectStyle = (value: string) => {
|
const selectStyle = (value: string) => {
|
||||||
|
if (props.disabled) return
|
||||||
tempSelectedValue.value = value
|
tempSelectedValue.value = value
|
||||||
confirmStyle()
|
confirmStyle()
|
||||||
}
|
}
|
||||||
@@ -111,6 +133,13 @@
|
|||||||
.el-select__icon {
|
.el-select__icon {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
.el-select__placeholder,
|
||||||
|
.el-select__selected-item {
|
||||||
|
color: #c9c9c9;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,6 +153,12 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.is-disabled {
|
||||||
|
.fida-style-select-trigger {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|||||||
@@ -17,3 +17,8 @@ export interface UploadedImage {
|
|||||||
export type PreviewImage = UploadedImage | string
|
export type PreviewImage = UploadedImage | string
|
||||||
|
|
||||||
export type OptionTagKind = 'type' | 'area' | 'style'
|
export type OptionTagKind = 'type' | 'area' | 'style'
|
||||||
|
|
||||||
|
export interface ParameterTag {
|
||||||
|
kind: OptionTagKind
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
|||||||
const inputValue = ref<string>('')
|
const inputValue = ref<string>('')
|
||||||
const reportTags = ref<HTMLElement[]>([])
|
const reportTags = ref<HTMLElement[]>([])
|
||||||
const optionTags = ref<Partial<Record<OptionTagKind, HTMLElement>>>({})
|
const optionTags = ref<Partial<Record<OptionTagKind, HTMLElement>>>({})
|
||||||
|
const optionTagKinds = ref<OptionTagKind[]>([])
|
||||||
const reportPromptText = ref<Text | null>(null)
|
const reportPromptText = ref<Text | null>(null)
|
||||||
let reportTypewriterTimeout: ReturnType<typeof setTimeout> | null = null
|
let reportTypewriterTimeout: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
@@ -114,6 +115,18 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
|||||||
return StyleIcon
|
return StyleIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refreshOptionTagKinds = () => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (!editor) {
|
||||||
|
optionTagKinds.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
optionTagKinds.value = Array.from(editor.children)
|
||||||
|
.map((child) => (child as HTMLElement).dataset.optionTagKind as OptionTagKind)
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
const getTranslatedOptionLabel = (items: OptionItem[], value: string) => {
|
const getTranslatedOptionLabel = (items: OptionItem[], value: string) => {
|
||||||
const option = items.find((item) => item.value === value)
|
const option = items.find((item) => item.value === value)
|
||||||
return option ? options.t(option.label) : value
|
return option ? options.t(option.label) : value
|
||||||
@@ -132,11 +145,13 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
|||||||
removeNodeWithNextSpacer(tag)
|
removeNodeWithNextSpacer(tag)
|
||||||
}
|
}
|
||||||
delete optionTags.value[kind]
|
delete optionTags.value[kind]
|
||||||
|
refreshOptionTagKinds()
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeAllOptionTags = () => {
|
const removeAllOptionTags = () => {
|
||||||
optionTagOrder.forEach(removeOptionTag)
|
optionTagOrder.forEach(removeOptionTag)
|
||||||
optionTags.value = {}
|
optionTags.value = {}
|
||||||
|
optionTagKinds.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearOptionTagValue = (kind: OptionTagKind) => {
|
const clearOptionTagValue = (kind: OptionTagKind) => {
|
||||||
@@ -172,26 +187,33 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
|||||||
tag.contentEditable = 'false'
|
tag.contentEditable = 'false'
|
||||||
tag.className = 'editor-tag option-tag flex-center'
|
tag.className = 'editor-tag option-tag flex-center'
|
||||||
tag.dataset.optionTagKind = kind
|
tag.dataset.optionTagKind = kind
|
||||||
|
const optionIcon = getOptionTagIcon(kind) as unknown as string
|
||||||
|
|
||||||
const icon = document.createElement('img')
|
const icon = document.createElement('img')
|
||||||
icon.className = 'option-tag-icon'
|
icon.className = 'option-tag-icon'
|
||||||
icon.src = getOptionTagIcon(kind) as unknown as string
|
icon.src = optionIcon
|
||||||
|
icon.addEventListener('click', (ev) => {
|
||||||
|
ev.stopPropagation()
|
||||||
|
clearOptionTagValue(kind)
|
||||||
|
})
|
||||||
|
|
||||||
const textSpan = document.createElement('span')
|
const textSpan = document.createElement('span')
|
||||||
textSpan.className = 'option-tag-text'
|
textSpan.className = 'option-tag-text'
|
||||||
textSpan.innerText = label
|
textSpan.innerText = label
|
||||||
|
|
||||||
const close = document.createElement('img')
|
tag.addEventListener('mouseenter', () => {
|
||||||
close.className = 'close-icon option-close'
|
icon.src = closeIcon as unknown as string
|
||||||
close.src = closeIcon as unknown as string
|
icon.classList.add('option-tag-close')
|
||||||
close.addEventListener('click', (ev) => {
|
tag.classList.add('is-hovered')
|
||||||
ev.stopPropagation()
|
})
|
||||||
clearOptionTagValue(kind)
|
tag.addEventListener('mouseleave', () => {
|
||||||
|
icon.src = optionIcon
|
||||||
|
icon.classList.remove('option-tag-close')
|
||||||
|
tag.classList.remove('is-hovered')
|
||||||
})
|
})
|
||||||
|
|
||||||
tag.appendChild(icon)
|
tag.appendChild(icon)
|
||||||
tag.appendChild(textSpan)
|
tag.appendChild(textSpan)
|
||||||
tag.appendChild(close)
|
|
||||||
|
|
||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
@@ -230,6 +252,7 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
optionTags.value[kind] = tag
|
optionTags.value[kind] = tag
|
||||||
|
refreshOptionTagKinds()
|
||||||
handleEditorInput()
|
handleEditorInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,12 +526,14 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
|||||||
|
|
||||||
const resetOptionTags = () => {
|
const resetOptionTags = () => {
|
||||||
optionTags.value = {}
|
optionTags.value = {}
|
||||||
|
optionTagKinds.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
editorRef,
|
editorRef,
|
||||||
inputValue,
|
inputValue,
|
||||||
reportTags,
|
reportTags,
|
||||||
|
optionTagKinds,
|
||||||
showPlaceholder,
|
showPlaceholder,
|
||||||
setEditorElement,
|
setEditorElement,
|
||||||
focusEditor,
|
focusEditor,
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
if (res) {
|
if (res) {
|
||||||
userInfoStore.setToken(res)
|
userInfoStore.setToken(res)
|
||||||
userInfoStore.setUserInfo({
|
userInfoStore.setUserInfo({
|
||||||
|
username: formData.name,
|
||||||
email: formData.email
|
email: formData.email
|
||||||
})
|
})
|
||||||
router.push({ name: 'nuic' })
|
router.push({ name: 'nuic' })
|
||||||
|
|||||||
Reference in New Issue
Block a user