396 lines
9.3 KiB
Vue
396 lines
9.3 KiB
Vue
<template>
|
|
<div class="assist-input-wrapper flex flex-col" :class="{ agent: isAgentMode }">
|
|
<div class="animate-container flex-1 flex flex-col">
|
|
<div class="scroll-content flex-col">
|
|
<InputImagePreviewList
|
|
:uploaded-images="uploadedImages"
|
|
:quote-list="quoteList"
|
|
@preview="previewImage"
|
|
@remove="removeImage"
|
|
/>
|
|
<InputEditor
|
|
:show-placeholder="showPlaceholder"
|
|
:placeholder="$t('Input.placeholder')"
|
|
:is-agent-mode="isAgentMode"
|
|
@ready="setEditorElement"
|
|
@input="handleEditorInput"
|
|
@paste="handleEditorPaste"
|
|
@keydown="handleKeyDown"
|
|
/>
|
|
</div>
|
|
|
|
<InputToolbar
|
|
v-model:type-value="typeValue"
|
|
v-model:area-value="areaValue"
|
|
v-model:style-value="styleValue"
|
|
v-model:setting-options="settingOptions"
|
|
:is-agent-mode="isAgentMode"
|
|
:generating="generating"
|
|
:type-options="typeOptions"
|
|
:area-options="areaOptions"
|
|
:style-options="styleOptions"
|
|
:type-placeholder="$t('Input.typePlaceholder')"
|
|
:area-placeholder="$t('Input.areaPlaceholder')"
|
|
:style-placeholder="$t('Input.stylePlaceholder')"
|
|
:style-title="$t('Input.chooseStyle')"
|
|
:setting-title="$t('Input.styleTitle')"
|
|
:confirm-text="$t('Input.confirm')"
|
|
:create-text="$t('Input.createProject')"
|
|
:parameters-disabled="isReportSelected"
|
|
:translate="translate"
|
|
@file-change="handleFileChange"
|
|
@toggle-report="handleToggleReportTag"
|
|
@create="handleCreateProject"
|
|
@send="handleSendAgent"
|
|
/>
|
|
</div>
|
|
|
|
<ReportShortcutButton
|
|
v-if="!isAgentMode"
|
|
:is-cn="isCn"
|
|
:label="$t('Input.trendingReport')"
|
|
@click="handleToggleReportTag"
|
|
/>
|
|
<Preview v-model="showPreview" :url="previewUrl" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
import { areaList } from '@/utils/area'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useRouter } from 'vue-router'
|
|
import { useAgentStore, useProjectStore } from '@/stores'
|
|
import { createProject } from '@/api/agent'
|
|
import MyEvent from '@/utils/myEvent'
|
|
import Preview from '@/components/Preview/Preview.vue'
|
|
import InputEditor from './input/InputEditor.vue'
|
|
import InputImagePreviewList from './input/InputImagePreviewList.vue'
|
|
import InputToolbar from './input/InputToolbar.vue'
|
|
import ReportShortcutButton from './input/ReportShortcutButton.vue'
|
|
import {
|
|
createSettingOptions,
|
|
createStyleOptions,
|
|
createTypeOptions,
|
|
optionTagOrder
|
|
} from './input/options'
|
|
import { useInputEditor } from './input/useInputEditor'
|
|
import { useInputImages } from './input/useInputImages'
|
|
import type { OptionItem, OptionTagKind, ParameterTag, SettingOption } from './input/types'
|
|
|
|
const router = useRouter()
|
|
const agentStore = useAgentStore()
|
|
const projectStore = useProjectStore()
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
isAgentMode?: boolean
|
|
generating?: boolean
|
|
}>(),
|
|
{
|
|
isAgentMode: false,
|
|
generating: false
|
|
}
|
|
)
|
|
|
|
const emits = defineEmits(['send', 'pause'])
|
|
|
|
const { t, locale } = useI18n()
|
|
const translate = (key: string) => t(key)
|
|
|
|
const isCn = computed(() => {
|
|
return locale.value === 'CHINESE_SIMPLIFIED'
|
|
})
|
|
const isAgentModeRef = computed(() => props.isAgentMode)
|
|
|
|
const typeValue = ref<string>('')
|
|
const areaValue = ref<string>('')
|
|
const styleValue = ref<string>('')
|
|
const settingOptions = ref<SettingOption[]>(createSettingOptions())
|
|
const typeOptions = ref<OptionItem[]>(createTypeOptions())
|
|
const areaOptions = ref<OptionItem[]>(areaList)
|
|
const styleOptions = ref<OptionItem[]>(createStyleOptions())
|
|
|
|
const {
|
|
inputValue,
|
|
reportTags,
|
|
optionTagKinds,
|
|
showPlaceholder,
|
|
setEditorElement,
|
|
focusEditor,
|
|
stopReportTypewriter,
|
|
addReportTag,
|
|
toggleReportTag,
|
|
handleEditorInput,
|
|
handleEditorPaste,
|
|
handleKeyDown,
|
|
autoResizeEditor,
|
|
syncOptionTag,
|
|
syncAllOptionTags,
|
|
clearEditorText,
|
|
resetOptionTags
|
|
} = useInputEditor({
|
|
isAgentMode: isAgentModeRef,
|
|
t: translate,
|
|
typeValue,
|
|
areaValue,
|
|
styleValue,
|
|
typeOptions,
|
|
areaOptions,
|
|
styleOptions,
|
|
onSubmit: () => {
|
|
if (props.isAgentMode) {
|
|
handleSendAgent()
|
|
return
|
|
}
|
|
handleCreateProject()
|
|
}
|
|
})
|
|
|
|
const {
|
|
uploadedImages,
|
|
quoteList,
|
|
showPreview,
|
|
previewUrl,
|
|
handleFileChange,
|
|
removeImage,
|
|
previewImage,
|
|
handleQuote,
|
|
clearImages
|
|
} = 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, () => {
|
|
nextTick(() => {
|
|
autoResizeEditor()
|
|
})
|
|
})
|
|
|
|
watch(typeValue, () => {
|
|
nextTick(() => {
|
|
syncOptionTag('type')
|
|
})
|
|
})
|
|
|
|
watch(areaValue, () => {
|
|
nextTick(() => {
|
|
syncOptionTag('area')
|
|
})
|
|
})
|
|
|
|
watch(styleValue, () => {
|
|
nextTick(() => {
|
|
syncOptionTag('style')
|
|
})
|
|
})
|
|
|
|
watch(locale, () => {
|
|
nextTick(() => {
|
|
syncAllOptionTags()
|
|
})
|
|
})
|
|
|
|
const handleSendAgent = async () => {
|
|
if (props.generating) {
|
|
emits('pause')
|
|
return
|
|
}
|
|
if (!inputValue.value.trim()) return
|
|
const imageUrlList = uploadedImages.value.map((item) => item.path)
|
|
|
|
const payload: {
|
|
text: string
|
|
images: any[]
|
|
tempImages: typeof uploadedImages.value
|
|
quoteList: string[]
|
|
parameterTags: ParameterTag[]
|
|
useReport?: boolean
|
|
} = {
|
|
text: inputValue.value.trim(),
|
|
images: imageUrlList,
|
|
tempImages: uploadedImages.value,
|
|
quoteList: quoteList.value,
|
|
parameterTags: selectedParameterTags.value
|
|
}
|
|
if (reportTags.value.length > 0) {
|
|
payload.useReport = true
|
|
}
|
|
emits('send', payload)
|
|
clearImages()
|
|
clearEditorText()
|
|
}
|
|
|
|
const handleCreateProject = async () => {
|
|
if (!inputValue.value.trim()) {
|
|
return
|
|
}
|
|
|
|
const params = {
|
|
type: typeValue.value || '',
|
|
area: areaValue.value || '',
|
|
style: styleValue.value || '',
|
|
useReport: reportTags.value.length > 0,
|
|
temperature: 0.7
|
|
}
|
|
const projectres = await createProject({
|
|
...params,
|
|
region: params.area
|
|
} as any)
|
|
projectStore.setId(projectres)
|
|
MyEvent.emit('updateProjectList')
|
|
agentStore.setInitialProjectData({
|
|
text: inputValue.value.trim(),
|
|
images: uploadedImages.value.map((item) => item.path),
|
|
tempImages: uploadedImages.value,
|
|
needSuggestion: false,
|
|
quoteList: quoteList.value,
|
|
parameterTags: selectedParameterTags.value,
|
|
...params
|
|
})
|
|
|
|
router.push({
|
|
path: `/home/agent/${projectres}`,
|
|
query: {
|
|
type: params.type,
|
|
area: params.area,
|
|
style: params.style,
|
|
useReport: String(params.useReport),
|
|
temperature: String(params.temperature)
|
|
}
|
|
})
|
|
uploadedImages.value = []
|
|
}
|
|
|
|
const handleInitInput = () => {
|
|
inputValue.value = ''
|
|
uploadedImages.value = []
|
|
quoteList.value = []
|
|
toggleReportTag(true)
|
|
clearEditorText()
|
|
resetOptionTags()
|
|
nextTick(() => {
|
|
syncAllOptionTags()
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
MyEvent.add('quote', handleQuote)
|
|
MyEvent.add('projectChange', handleInitInput)
|
|
nextTick(() => {
|
|
syncAllOptionTags()
|
|
autoResizeEditor()
|
|
})
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
stopReportTypewriter()
|
|
MyEvent.remove('quote', handleQuote)
|
|
MyEvent.remove('projectChange', handleInitInput)
|
|
})
|
|
|
|
defineExpose({
|
|
addReportTag
|
|
})
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.assist-input-wrapper {
|
|
min-height: 23.5rem;
|
|
max-height: 43.5rem;
|
|
width: 106.3rem;
|
|
margin: 0 auto;
|
|
padding: 0;
|
|
position: relative;
|
|
|
|
.animate-container {
|
|
overflow-y: hidden;
|
|
}
|
|
|
|
&:not(.agent) .animate-container {
|
|
box-shadow: 0px 0.5rem 1.4rem 0px #0000001a;
|
|
transition: all 0.3s ease;
|
|
border-radius: 2.8rem;
|
|
background-color: #fff;
|
|
border: 0.1rem solid #00000005;
|
|
|
|
&:hover {
|
|
box-shadow: 0px 0.5rem 3.36rem 2.2rem #f1ede999;
|
|
transform: translateY(-1rem);
|
|
}
|
|
}
|
|
|
|
.scroll-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 3.4rem 1.7rem 1.7rem;
|
|
}
|
|
|
|
&.agent {
|
|
padding: 1.2rem;
|
|
box-shadow: none;
|
|
border-radius: 1.5rem;
|
|
border: 0.1rem solid #0000001a;
|
|
|
|
.scroll-content {
|
|
padding: 0;
|
|
flex: 1;
|
|
overflow: auto;
|
|
}
|
|
}
|
|
}
|
|
</style>
|