diff --git a/package.json b/package.json index 9e2ae9f7b..9b3286f97 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "element-sea": "^2.15.10-9", "file-loader": "^6.2.0", "highlight.js": "^11.7.0", + "html-to-md": "^0.8.8", "inquirer": "^8.2.0", "internal-ip": "^6.2.0", "jquery": "^3.6.4", diff --git a/resources/assets/js/components/AIAssistant.vue b/resources/assets/js/components/AIAssistant.vue index 2f66b1d5a..68fdc0190 100644 --- a/resources/assets/js/components/AIAssistant.vue +++ b/resources/assets/js/components/AIAssistant.vue @@ -514,6 +514,7 @@ export default { if (!responseEntry) { return; } + const stickToBottom = this.shouldStickToBottom(); const payload = this.parseStreamPayload(event); const chunk = this.resolveStreamContent(payload); if (type === 'replace') { @@ -523,7 +524,9 @@ export default { } this.updateResponseDisplayOutput(responseEntry); responseEntry.status = 'streaming'; - this.scrollResponsesToBottom(); + if (stickToBottom) { + this.scrollResponsesToBottom(); + } }, /** @@ -777,6 +780,22 @@ export default { } }); }, + + /** + * 判断是否需要保持滚动到底部 + */ + shouldStickToBottom(threshold = 20) { + const container = this.$refs.responseContainer; + if (!container) { + return true; + } + const currentBottom = container.scrollTop + container.clientHeight; + const distance = container.scrollHeight - currentBottom; + if (Number.isNaN(distance)) { + return true; + } + return distance <= threshold; + }, }, } diff --git a/resources/assets/js/pages/manage/components/ChatInput/index.vue b/resources/assets/js/pages/manage/components/ChatInput/index.vue index b841bedb4..27a9d33ed 100755 --- a/resources/assets/js/pages/manage/components/ChatInput/index.vue +++ b/resources/assets/js/pages/manage/components/ChatInput/index.vue @@ -343,7 +343,7 @@ import longpress from "../../../../directives/longpress"; import {inputLoadAdd, inputLoadIsLast, inputLoadRemove} from "./one"; import {languageList, languageName} from "../../../../language"; import {isMarkdownFormat, MarkdownConver} from "../../../../utils/markdown"; -import {extractPlainText} from "../../../../utils/text"; +import {cutText, extractPlainText} from "../../../../utils/text"; import {MESSAGE_AI_SYSTEM_PROMPT} from "../../../../utils/ai"; import emitter from "../../../../store/events"; import historyMixin from "./history"; @@ -1947,14 +1947,14 @@ export default { const sections = []; const infoLines = []; if (this.dialogData?.name) { - infoLines.push(`名称:${this.cutText(this.dialogData.name, 60)}`); + infoLines.push(`名称:${cutText(this.dialogData.name, 60)}`); } if (this.dialogData?.type) { const typeMap = {group: this.$L('群聊'), user: this.$L('单聊')}; infoLines.push(`类型:${typeMap[this.dialogData.type] || this.dialogData.type}`); } if (this.dialogData?.group_type) { - infoLines.push(`分类:${this.cutText(this.dialogData.group_type, 60)}`); + infoLines.push(`分类:${cutText(this.dialogData.group_type, 60)}`); } if (infoLines.length) { sections.push('## 会话信息'); @@ -1987,10 +1987,10 @@ export default { } } - const draftText = extractPlainText(this.value); + const draftText = extractPlainText(this.value, 500); if (draftText) { sections.push('## 当前草稿'); - sections.push(this.cutText(draftText, 200)); + sections.push(draftText); } return sections.join('\n'); @@ -2003,7 +2003,7 @@ export default { const result = []; const seen = new Set(); const pushName = (name) => { - const clean = this.cutText((name || '').trim(), 30); + const clean = cutText((name || '').trim(), 30); if (!clean || seen.has(clean)) { return; } @@ -2058,25 +2058,12 @@ export default { } try { const preview = $A.getMsgSimpleDesc(message); - const plain = extractPlainText(preview || ''); - return this.cutText(plain, 160); + return extractPlainText(preview || '', 300); } catch (error) { return ''; } }, - cutText(text, limit = 60) { - const value = (text || '').trim(); - if (!value) { - return ''; - } - const units = Array.from(value); - if (units.length <= limit) { - return value; - } - return units.slice(0, limit).join('') + '…'; - }, - resolveUserNickname(userid) { if (!userid) { return ''; diff --git a/resources/assets/js/pages/manage/components/ReportDetail.vue b/resources/assets/js/pages/manage/components/ReportDetail.vue index e39214daa..8a92e86f6 100644 --- a/resources/assets/js/pages/manage/components/ReportDetail.vue +++ b/resources/assets/js/pages/manage/components/ReportDetail.vue @@ -168,7 +168,7 @@ export default { $A.messageWarning("当前没有可分析的汇报"); return; } - const plain = extractPlainText(this.currentDetail.content || ''); + const plain = extractPlainText(this.currentDetail.content, null, true); if (!plain) { $A.messageWarning("汇报内容为空,无法分析"); return; @@ -178,6 +178,7 @@ export default { onBeforeSend: this.handleReportAnalysisBeforeSend, onApply: this.handleReportAnalysisApply, autoSubmit: true, + applyButtonText: this.$L('保存分析'), }); }, @@ -292,14 +293,10 @@ export default { viewerMeta.forEach(line => sections.push(`- ${line}`)); } - const bodyText = extractPlainText(detail.content || ''); + const bodyText = extractPlainText(detail.content, 8000, true); if (bodyText) { - const limit = 5000; - const trimmed = bodyText.length > limit - ? `${bodyText.slice(0, limit)}...` - : bodyText; sections.push('## 汇报正文'); - sections.push(trimmed); + sections.push(bodyText); } const previous = this.aiAnalysis?.text || detail.ai_analysis?.text; diff --git a/resources/assets/js/pages/manage/components/ReportEdit.vue b/resources/assets/js/pages/manage/components/ReportEdit.vue index a8c097110..779faf31f 100644 --- a/resources/assets/js/pages/manage/components/ReportEdit.vue +++ b/resources/assets/js/pages/manage/components/ReportEdit.vue @@ -291,12 +291,10 @@ export default { sections.push(...meta); } - const plain = extractPlainText(this.reportData.content || ''); + const plain = extractPlainText(this.reportData.content, 8000, true); if (plain) { - const limit = 3200; - const slice = plain.slice(0, limit); sections.push('## 当前汇报正文'); - sections.push(slice + (plain.length > limit ? '...' : '')); + sections.push(plain); } return sections.join('\n').trim(); diff --git a/resources/assets/js/pages/manage/components/TaskAdd.vue b/resources/assets/js/pages/manage/components/TaskAdd.vue index fe75de499..e6f7db568 100644 --- a/resources/assets/js/pages/manage/components/TaskAdd.vue +++ b/resources/assets/js/pages/manage/components/TaskAdd.vue @@ -639,16 +639,8 @@ export default { buildTaskAIContextData() { const prompts = []; - const plainText = (value, limit = 600) => { - const text = extractPlainText(value || ''); - if (!text) { - return ''; - } - return text.slice(0, limit).trim(); - }; - const currentTitle = (this.addData.name || '').trim(); - const currentContent = plainText(this.addData.content, 600); + const currentContent = extractPlainText(this.addData.content, 2000, true); if (currentTitle || currentContent) { prompts.push('## 当前任务信息'); if (currentTitle) { @@ -665,7 +657,7 @@ export default { : null; if (currentTemplate) { const templateName = (currentTemplate.name || currentTemplate.title || '').trim(); - const templateContent = plainText(nostyle(currentTemplate.content, {sanitize: false}), 800); + const templateContent = extractPlainText(nostyle(currentTemplate.content, {sanitize: false}), 1200, true); prompts.push('## 任务模板要求'); if (templateName) { prompts.push(`模板名称:${templateName}`); diff --git a/resources/assets/js/utils/text.js b/resources/assets/js/utils/text.js index 23b49fe91..846337e30 100644 --- a/resources/assets/js/utils/text.js +++ b/resources/assets/js/utils/text.js @@ -1,13 +1,34 @@ -export function extractPlainText(content) { +import html2md from 'html-to-md' + +const cutText = (text, limit = 60, ellipsis = '...') => { + const value = (text || '').trim(); + if (!value) { + return ''; + } + const units = Array.from(value); + if (units.length <= limit) { + return value; + } + return units.slice(0, limit).join('') + ellipsis; +} + +const extractPlainText = (content, cutLength = null, convertHtmlToMarkdownMode = false) => { if (!content) { return ''; } const value = typeof content === 'string' ? content : JSON.stringify(content); + if (convertHtmlToMarkdownMode) { + const newValue = html2md(value).trim(); + return cutLength ? cutText(newValue, cutLength) : newValue; + } if (typeof window === 'undefined' || !window.document) { - return value.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); + const newValue = value.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); + return cutLength ? cutText(newValue, cutLength) : newValue; } const div = document.createElement('div'); div.innerHTML = value; - return (div.textContent || div.innerText || '').replace(/\s+/g, ' ').trim(); + const newValue = (div.textContent || div.innerText || '').replace(/\s+/g, ' ').trim(); + return cutLength ? cutText(newValue, cutLength) : newValue; } +export {cutText, extractPlainText}