feat: 参数标签逻辑修改&对话列表显示标签
This commit is contained in:
@@ -16,6 +16,7 @@ type InitialProjectData = {
|
||||
useReport:boolean
|
||||
needSuggestion:boolean
|
||||
quoteList: Array<string>
|
||||
parameterTags?: Array<{ kind: string; label: string }>
|
||||
tempImages: any[]
|
||||
}
|
||||
export const useAgentStore = defineStore('agent', () => {
|
||||
|
||||
@@ -37,8 +37,12 @@
|
||||
import type { AgentParamsType } from '@/api/agent'
|
||||
import { useUserInfoStore, useProjectStore, useAgentStore } from '@/stores'
|
||||
import MyEvent from '@/utils/myEvent'
|
||||
import { areaList } from '@/utils/area'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
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 route = useRoute()
|
||||
@@ -83,6 +87,59 @@
|
||||
})
|
||||
|
||||
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(
|
||||
sketchList,
|
||||
(newVal) => {
|
||||
@@ -143,7 +200,8 @@
|
||||
images: initialData.images,
|
||||
useReport: initialData.useReport,
|
||||
tempImages: initialData.tempImages,
|
||||
quoteList: initialData.quoteList
|
||||
quoteList: initialData.quoteList,
|
||||
parameterTags: initialData.parameterTags || []
|
||||
})
|
||||
// 更新 configParams
|
||||
|
||||
@@ -181,6 +239,7 @@
|
||||
tempImages: any[]
|
||||
useReport: boolean
|
||||
quoteList: Array<string>
|
||||
parameterTags?: Array<{ kind: string; label: string }>
|
||||
},
|
||||
skipUserMessage = false
|
||||
) => {
|
||||
@@ -199,7 +258,8 @@
|
||||
id: messageList.value.length + 1,
|
||||
text: message.text,
|
||||
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 initialData = agentStore.getInitialProjectData
|
||||
if (isGenerating.value || initialData) return
|
||||
|
||||
console.log('---',info)
|
||||
const data = info.conversation
|
||||
let project = info.project
|
||||
if (info.id) {
|
||||
@@ -641,7 +701,7 @@
|
||||
|
||||
if (project) {
|
||||
params.configParams.type = project.type || ''
|
||||
params.configParams.region = project.area || ''
|
||||
params.configParams.region = project.region || project.area || ''
|
||||
params.configParams.style = project.style || ''
|
||||
params.configParams.temperature = project.temperature
|
||||
params.projectID = project.id
|
||||
@@ -668,6 +728,7 @@
|
||||
item.text += `<slot slot-name="sketch"></slot>`
|
||||
}
|
||||
})
|
||||
applyProjectParameterTags(ancestorsList, project)
|
||||
// console.log('ancestorslist', ancestorsList)
|
||||
messageList.value = [...ancestorsList]
|
||||
params.versionID = current?.id
|
||||
|
||||
@@ -52,20 +52,36 @@
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="message-txt markdown-body flex flex-col">
|
||||
<div
|
||||
class="message-txt markdown-body flex"
|
||||
:class="{ 'has-parameter-tags': parameterTags.length > 0 }"
|
||||
>
|
||||
<div
|
||||
v-if="parameterTags.length > 0"
|
||||
class="message-parameter-tags flex align-center"
|
||||
>
|
||||
<div
|
||||
v-for="tag in parameterTags"
|
||||
:key="`${tag.kind}-${tag.label}`"
|
||||
class="message-parameter-tag flex align-center"
|
||||
>
|
||||
<img :src="getParameterTagIcon(tag.kind)" class="parameter-tag-icon" />
|
||||
<span>{{ tag.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<VueMarkdown
|
||||
:custom-attrs="customAttrs"
|
||||
:markdown="content.text"
|
||||
:rehype-plugins="[rehypeRaw]"
|
||||
>
|
||||
<template v-slot:s-card="{ children: children, ...attrs }">
|
||||
<Card title="Trend Report" @click.native="handleClickReport" />
|
||||
<Card title="Trend Report" @click="handleClickReport" />
|
||||
</template>
|
||||
<template v-slot:s-url="{ children: children }">
|
||||
<Url :list="content.webAddress" @click.native="handleClickUrls" />
|
||||
<Url :list="content.webAddress" @click="handleClickUrls" />
|
||||
</template>
|
||||
<template v-slot:s-sketch="{ children: children }">
|
||||
<Sketch @click.native="handleClickSketch" />
|
||||
<Sketch @click="handleClickSketch" />
|
||||
</template>
|
||||
</VueMarkdown>
|
||||
<div
|
||||
@@ -116,6 +132,9 @@
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import userThumb from '@/assets/images/user-thumb.jpg'
|
||||
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 Url from './UrlCard.vue'
|
||||
import Sketch from './SketchCard.vue'
|
||||
@@ -126,6 +145,7 @@
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
import MyEvent from '@/utils/myEvent'
|
||||
import { useUserInfoStore, useProjectStore } from '@/stores'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const userStore = useUserInfoStore()
|
||||
const projectStore = useProjectStore()
|
||||
@@ -136,8 +156,14 @@
|
||||
return locale.value === 'CHINESE_SIMPLIFIED'
|
||||
})
|
||||
|
||||
type ParameterTagKind = 'type' | 'area' | 'style'
|
||||
type ParameterTag = {
|
||||
kind: ParameterTagKind
|
||||
label: string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
content: Object
|
||||
content: any
|
||||
isLast: Boolean
|
||||
}>()
|
||||
|
||||
@@ -174,6 +200,16 @@
|
||||
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 = {
|
||||
img: {
|
||||
style: 'max-width: 100%;'
|
||||
@@ -348,6 +384,38 @@
|
||||
width: fit-content;
|
||||
max-width: 82%;
|
||||
}
|
||||
.message-parameter-tags {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
gap: 0.8rem;
|
||||
margin-right: 0.8rem;
|
||||
}
|
||||
.message-parameter-tag {
|
||||
height: 3rem;
|
||||
max-width: 24rem;
|
||||
padding: 0 1rem;
|
||||
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 {
|
||||
width: fit-content;
|
||||
min-width: 22.5rem;
|
||||
@@ -443,6 +511,18 @@
|
||||
<style lang="less">
|
||||
.message-txt {
|
||||
user-select: text;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 0.4rem;
|
||||
|
||||
&.has-parameter-tags {
|
||||
align-items: center;
|
||||
> div:not(.message-parameter-tags) {
|
||||
flex: 1;
|
||||
min-width: 18rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
@@ -71,11 +71,12 @@
|
||||
import {
|
||||
createSettingOptions,
|
||||
createStyleOptions,
|
||||
createTypeOptions
|
||||
createTypeOptions,
|
||||
optionTagOrder
|
||||
} from './input/options'
|
||||
import { useInputEditor } from './input/useInputEditor'
|
||||
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 agentStore = useAgentStore()
|
||||
@@ -113,6 +114,7 @@
|
||||
const {
|
||||
inputValue,
|
||||
reportTags,
|
||||
optionTagKinds,
|
||||
showPlaceholder,
|
||||
setEditorElement,
|
||||
focusEditor,
|
||||
@@ -175,6 +177,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
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, () => {
|
||||
nextTick(() => {
|
||||
autoResizeEditor()
|
||||
@@ -218,12 +255,14 @@
|
||||
images: any[]
|
||||
tempImages: typeof uploadedImages.value
|
||||
quoteList: string[]
|
||||
parameterTags: ParameterTag[]
|
||||
useReport?: boolean
|
||||
} = {
|
||||
text: inputValue.value.trim(),
|
||||
images: imageUrlList,
|
||||
tempImages: uploadedImages.value,
|
||||
quoteList: quoteList.value
|
||||
quoteList: quoteList.value,
|
||||
parameterTags: selectedParameterTags.value
|
||||
}
|
||||
if (reportTags.value.length > 0) {
|
||||
payload.useReport = true
|
||||
@@ -257,6 +296,7 @@
|
||||
tempImages: uploadedImages.value,
|
||||
needSuggestion: false,
|
||||
quoteList: quoteList.value,
|
||||
parameterTags: selectedParameterTags.value,
|
||||
...params
|
||||
})
|
||||
|
||||
|
||||
@@ -171,10 +171,15 @@
|
||||
column-gap: 0.8rem;
|
||||
color: #0d0d0d;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.option-tag-icon {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
flex-shrink: 0;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.option-tag-text {
|
||||
@@ -185,10 +190,8 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.option-close {
|
||||
width: 0.7rem;
|
||||
height: 0.7rem;
|
||||
margin-left: 0.2rem;
|
||||
.option-tag-close {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,8 @@ export interface UploadedImage {
|
||||
export type PreviewImage = UploadedImage | string
|
||||
|
||||
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 reportTags = ref<HTMLElement[]>([])
|
||||
const optionTags = ref<Partial<Record<OptionTagKind, HTMLElement>>>({})
|
||||
const optionTagKinds = ref<OptionTagKind[]>([])
|
||||
const reportPromptText = ref<Text | null>(null)
|
||||
let reportTypewriterTimeout: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
@@ -114,6 +115,18 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
||||
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 option = items.find((item) => item.value === value)
|
||||
return option ? options.t(option.label) : value
|
||||
@@ -132,11 +145,13 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
||||
removeNodeWithNextSpacer(tag)
|
||||
}
|
||||
delete optionTags.value[kind]
|
||||
refreshOptionTagKinds()
|
||||
}
|
||||
|
||||
const removeAllOptionTags = () => {
|
||||
optionTagOrder.forEach(removeOptionTag)
|
||||
optionTags.value = {}
|
||||
optionTagKinds.value = []
|
||||
}
|
||||
|
||||
const clearOptionTagValue = (kind: OptionTagKind) => {
|
||||
@@ -172,26 +187,33 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
||||
tag.contentEditable = 'false'
|
||||
tag.className = 'editor-tag option-tag flex-center'
|
||||
tag.dataset.optionTagKind = kind
|
||||
const optionIcon = getOptionTagIcon(kind) as unknown as string
|
||||
|
||||
const icon = document.createElement('img')
|
||||
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')
|
||||
textSpan.className = 'option-tag-text'
|
||||
textSpan.innerText = label
|
||||
|
||||
const close = document.createElement('img')
|
||||
close.className = 'close-icon option-close'
|
||||
close.src = closeIcon as unknown as string
|
||||
close.addEventListener('click', (ev) => {
|
||||
ev.stopPropagation()
|
||||
clearOptionTagValue(kind)
|
||||
tag.addEventListener('mouseenter', () => {
|
||||
icon.src = closeIcon as unknown as string
|
||||
icon.classList.add('option-tag-close')
|
||||
tag.classList.add('is-hovered')
|
||||
})
|
||||
tag.addEventListener('mouseleave', () => {
|
||||
icon.src = optionIcon
|
||||
icon.classList.remove('option-tag-close')
|
||||
tag.classList.remove('is-hovered')
|
||||
})
|
||||
|
||||
tag.appendChild(icon)
|
||||
tag.appendChild(textSpan)
|
||||
tag.appendChild(close)
|
||||
|
||||
return tag
|
||||
}
|
||||
@@ -230,6 +252,7 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
||||
}
|
||||
|
||||
optionTags.value[kind] = tag
|
||||
refreshOptionTagKinds()
|
||||
handleEditorInput()
|
||||
}
|
||||
|
||||
@@ -503,12 +526,14 @@ export function useInputEditor(options: UseInputEditorOptions) {
|
||||
|
||||
const resetOptionTags = () => {
|
||||
optionTags.value = {}
|
||||
optionTagKinds.value = []
|
||||
}
|
||||
|
||||
return {
|
||||
editorRef,
|
||||
inputValue,
|
||||
reportTags,
|
||||
optionTagKinds,
|
||||
showPlaceholder,
|
||||
setEditorElement,
|
||||
focusEditor,
|
||||
|
||||
Reference in New Issue
Block a user