|
@@ -55,6 +55,8 @@ const DEFAULT_AI_QA_CONFIG = Object.freeze({
|
|
|
const AI_QA_CONFIG = Object.freeze({ ...DEFAULT_AI_QA_CONFIG, ...props.config })
|
|
const AI_QA_CONFIG = Object.freeze({ ...DEFAULT_AI_QA_CONFIG, ...props.config })
|
|
|
const uploadEnabled = computed(() => AI_QA_CONFIG.enableFileUpload === true)
|
|
const uploadEnabled = computed(() => AI_QA_CONFIG.enableFileUpload === true)
|
|
|
const uploadUrl = computed(() => `${AI_QA_CONFIG.apiBaseUrl}${AI_QA_CONFIG.uploadPath}`)
|
|
const uploadUrl = computed(() => `${AI_QA_CONFIG.apiBaseUrl}${AI_QA_CONFIG.uploadPath}`)
|
|
|
|
|
+const hasInputLengthLimit = computed(() => Number.isFinite(AI_QA_CONFIG.maxInputLength) && AI_QA_CONFIG.maxInputLength >= 0)
|
|
|
|
|
+const conversationStorageKey = `ai-qa-conversation-id:${AI_QA_CONFIG.roleId}`
|
|
|
const input = ref('')
|
|
const input = ref('')
|
|
|
const textareaRef = ref(null)
|
|
const textareaRef = ref(null)
|
|
|
const messages = ref([])
|
|
const messages = ref([])
|
|
@@ -142,8 +144,38 @@ const switchQaPage = (path) => {
|
|
|
if (route.path !== path) router.push(path)
|
|
if (route.path !== path) router.push(path)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const getCachedConversationId = () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return localStorage.getItem(conversationStorageKey)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.warn('读取 AI 会话缓存失败:', error)
|
|
|
|
|
+ return null
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const cacheConversationId = (id) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ localStorage.setItem(conversationStorageKey, id)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.warn('保存 AI 会话缓存失败:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const clearCachedConversationId = () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ localStorage.removeItem(conversationStorageKey)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.warn('清除 AI 会话缓存失败:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const makeConversation = async () => {
|
|
const makeConversation = async () => {
|
|
|
if (conversationId.value || isCreatingConversation.value) return conversationId.value
|
|
if (conversationId.value || isCreatingConversation.value) return conversationId.value
|
|
|
|
|
+ const cachedConversationId = getCachedConversationId()
|
|
|
|
|
+ if (cachedConversationId) {
|
|
|
|
|
+ conversationId.value = cachedConversationId
|
|
|
|
|
+ return conversationId.value
|
|
|
|
|
+ }
|
|
|
isCreatingConversation.value = true
|
|
isCreatingConversation.value = true
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -154,6 +186,7 @@ const makeConversation = async () => {
|
|
|
const result = await props.createDialogueApi({ roleId: AI_QA_CONFIG.roleId })
|
|
const result = await props.createDialogueApi({ roleId: AI_QA_CONFIG.roleId })
|
|
|
conversationId.value = result?.data
|
|
conversationId.value = result?.data
|
|
|
if (!conversationId.value) throw new Error('未获取到会话标识')
|
|
if (!conversationId.value) throw new Error('未获取到会话标识')
|
|
|
|
|
+ cacheConversationId(conversationId.value)
|
|
|
return conversationId.value
|
|
return conversationId.value
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('创建 AI 会话失败:', error)
|
|
console.error('创建 AI 会话失败:', error)
|
|
@@ -165,7 +198,7 @@ const makeConversation = async () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const chooseSuggestion = (suggestion) => {
|
|
const chooseSuggestion = (suggestion) => {
|
|
|
- input.value = suggestion.slice(0, AI_QA_CONFIG.maxInputLength)
|
|
|
|
|
|
|
+ input.value = hasInputLengthLimit.value ? suggestion.slice(0, AI_QA_CONFIG.maxInputLength) : suggestion
|
|
|
nextTick(() => document.querySelector('.ai-qa-textarea')?.focus())
|
|
nextTick(() => document.querySelector('.ai-qa-textarea')?.focus())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -242,7 +275,8 @@ const handleKeydown = (event) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const onVoiceRecognized = ({ processedText }) => {
|
|
const onVoiceRecognized = ({ processedText }) => {
|
|
|
- input.value = (processedText || input.value).slice(0, AI_QA_CONFIG.maxInputLength)
|
|
|
|
|
|
|
+ const value = processedText || input.value
|
|
|
|
|
+ input.value = hasInputLengthLimit.value ? value.slice(0, AI_QA_CONFIG.maxInputLength) : value
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const onRecordingStatusChanged = (recording) => {
|
|
const onRecordingStatusChanged = (recording) => {
|
|
@@ -257,7 +291,7 @@ const formatSize = (size) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const getFileExtension = (fileName) => fileName.split('.').pop()?.toLowerCase() || ''
|
|
const getFileExtension = (fileName) => fileName.split('.').pop()?.toLowerCase() || ''
|
|
|
-const isSupportedFile = (file) => file.type.startsWith('image/') || AI_QA_CONFIG.acceptedFileExtensions.includes(getFileExtension(file.name))
|
|
|
|
|
|
|
+const isSupportedFile = (file) => file.type?.startsWith('image/') || AI_QA_CONFIG.acceptedFileExtensions.includes(getFileExtension(file.name))
|
|
|
const getAttachmentSize = () => attachments.value.reduce((total, file) => total + file.size, 0)
|
|
const getAttachmentSize = () => attachments.value.reduce((total, file) => total + file.size, 0)
|
|
|
|
|
|
|
|
const handleFileChange = async (event) => {
|
|
const handleFileChange = async (event) => {
|
|
@@ -308,7 +342,7 @@ const removeAttachment = (index) => attachments.value.splice(index, 1)
|
|
|
|
|
|
|
|
const sendMessage = async () => {
|
|
const sendMessage = async () => {
|
|
|
if (!canSend.value) return
|
|
if (!canSend.value) return
|
|
|
- if (input.value.length > AI_QA_CONFIG.maxInputLength) {
|
|
|
|
|
|
|
+ if (hasInputLengthLimit.value && input.value.length > AI_QA_CONFIG.maxInputLength) {
|
|
|
Message().warning(`单次提问最多输入 ${AI_QA_CONFIG.maxInputLength.toLocaleString()} 个字符`, true)
|
|
Message().warning(`单次提问最多输入 ${AI_QA_CONFIG.maxInputLength.toLocaleString()} 个字符`, true)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -448,13 +482,15 @@ const toggleDigitalHuman = () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const startNewConversation = () => {
|
|
|
|
|
|
|
+const startNewConversation = async () => {
|
|
|
stopResponse()
|
|
stopResponse()
|
|
|
messages.value = []
|
|
messages.value = []
|
|
|
attachments.value = []
|
|
attachments.value = []
|
|
|
input.value = ''
|
|
input.value = ''
|
|
|
conversationId.value = null
|
|
conversationId.value = null
|
|
|
|
|
+ clearCachedConversationId()
|
|
|
userHasScrolled.value = false
|
|
userHasScrolled.value = false
|
|
|
|
|
+ await makeConversation()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
watch(messages, () => scrollToBottom(), { deep: true })
|
|
watch(messages, () => scrollToBottom(), { deep: true })
|
|
@@ -587,7 +623,7 @@ onUnmounted(() => {
|
|
|
v-model="input"
|
|
v-model="input"
|
|
|
class="ai-qa-textarea"
|
|
class="ai-qa-textarea"
|
|
|
rows="1"
|
|
rows="1"
|
|
|
- :maxlength="AI_QA_CONFIG.maxInputLength"
|
|
|
|
|
|
|
+ :maxlength="hasInputLengthLimit ? AI_QA_CONFIG.maxInputLength : undefined"
|
|
|
:placeholder="AI_QA_CONFIG.inputHint"
|
|
:placeholder="AI_QA_CONFIG.inputHint"
|
|
|
:disabled="isSending"
|
|
:disabled="isSending"
|
|
|
@keydown="handleKeydown"
|
|
@keydown="handleKeydown"
|