zj-plugin-intelligent 1.2.26-beta.2 → 1.2.26-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1012,7 +1012,7 @@ eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@
|
|
|
1012
1012
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1013
1013
|
|
|
1014
1014
|
"use strict";
|
|
1015
|
-
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _logo = _interopRequireDefault(__webpack_require__(/*! @/assets/logo.png */ \"./src/assets/logo.png\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.mjs\"));\n\nvar _kbChat = __webpack_require__(/*! ./api/kbChat */ \"./src/lib/knowledgeBase/api/kbChat.js\");\n\nvar _knowledgeBase = __webpack_require__(/*! ./api/knowledgeBase */ \"./src/lib/knowledgeBase/api/knowledgeBase.js\");\n\nvar _native = __webpack_require__(/*! ./native.js */ \"./src/lib/knowledgeBase/native.js\");\n\nvar _request = __webpack_require__(/*! @lib/utils/request.js */ \"./src/lib/utils/request.js\");\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar AI_AVATAR_URL = 'https://qiguanbang.obs.cn-east-3.myhuaweicloud.com/production/image/AlAgent/doc_query.png'; // 与 ChatDetail 使用相同的 Markdown 配置\n\nvar md = new _markdownIt.default({\n breaks: true,\n html: false,\n linkify: true,\n typographer: true\n});\nmd.enable('table');\nvar _default = {\n name: 'KbChatMain',\n props: {\n companyId: {\n type: String,\n default: ''\n },\n userno: {\n type: String,\n default: ''\n },\n imAccount: {\n type: [String, Object],\n default: ''\n },\n userName: {\n type: String,\n default: ''\n },\n // 是否移动端:由父级(KnowledgeBaseMain)传入;未传时组件自身按窗口宽度判断\n isMobile: {\n type: Boolean,\n default: null\n }\n },\n data: function data() {\n // 首帧前计算是否移动端,避免 H5 先显示 PC 布局再切换的闪烁(与 AI 助理一致)\n // 宿主没有 Element UI 时(H5/平板横屏)一律走自绘 UI 分支,避免 el-* 未知元素报错\n var _isMobile = typeof window !== 'undefined' && (window.innerWidth <= 768 || !(0, _native.hasElementUI)(this));\n\n return {\n initDone: false,\n activeSessionId: '',\n sessionList: [],\n // 默认折叠侧边栏:避免新用户进入时先展开再合住的闪烁,\n // 加载出会话后再按规则决定是否展开\n sidebarCollapsed: true,\n messages: [],\n inputText: '',\n streaming: false,\n streamMsg: null,\n showToolInfo: false,\n contentCache: {},\n eventSource: null,\n streamRequestId: 0,\n suggestedQuestions: [],\n // 消息多选模式(豆包式):PC 由消息操作区「删除/多选」进入,移动端长按气泡弹出菜单进入\n selectMode: false,\n // 已选中的消息 id 列表(后端主键)\n selectedMsgIds: [],\n // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: null,\n // 移动端长按气泡弹出的消息操作单(复制 / 删除 / 多选)\n msgAction: null,\n // 移动端侧滑手势(参考 AI 助理):记录起手坐标与是否已触发\n _swipeStartX: 0,\n _swipeStartY: 0,\n _swipeHandled: false,\n _swipeFromLeftEdge: false,\n _swipeFromRightEdge: false,\n _swipeFromEdge: false\n };\n },\n computed: {\n /** 实际是否移动端:优先用父级 prop,未传则用自身窗口判断 */\n isMobileActive: function isMobileActive() {\n return this.isMobile !== null ? this.isMobile : this.localIsMobile;\n },\n aiAvatar: function aiAvatar() {\n return AI_AVATAR_URL;\n },\n userAvatar: function userAvatar() {\n var account = this.imAccount;\n console.log('KbChatMain - imAccount 原始值:', account); // 尝试解析字符串为对象\n\n if (typeof account === 'string') {\n try {\n account = JSON.parse(account);\n console.log('KbChatMain - imAccount 解析后:', account);\n } catch (e) {\n // 如果解析失败,检查是否是纯 URL 字符串\n if (account.trim() && (account.includes('http://') || account.includes('https://'))) {\n console.log('KbChatMain - imAccount 是 URL:', account.trim());\n return account.trim();\n }\n }\n } // 如果是对象,尝试多个可能的头像字段(优先使用 headImg)\n\n\n if (account && (0, _typeof2.default)(account) === 'object') {\n var avatarFields = ['headImg', 'headPortrait', 'avatar', 'avatarUrl', 'headImage', 'photoUrl', 'portrait', 'headUrl', 'userHeadUrl', 'userAvatar'];\n\n for (var _i = 0, _avatarFields = avatarFields; _i < _avatarFields.length; _i++) {\n var field = _avatarFields[_i];\n\n if (account[field] && String(account[field]).trim()) {\n console.log(\"KbChatMain - \\u627E\\u5230\\u5934\\u50CF\\u5B57\\u6BB5 \".concat(field, \":\"), account[field]);\n return String(account[field]).trim();\n }\n }\n }\n\n console.log('KbChatMain - 使用默认头像');\n return _logo.default;\n },\n // 是否有真实头像(区别于默认 fallback 图片)\n hasUserAvatar: function hasUserAvatar() {\n return this.userAvatar && this.userAvatar !== _logo.default;\n },\n // 无头像时的文字头像(与 zjkjAvatar 一致:取名字最后两个字)\n userAvatarText: function userAvatarText() {\n var name = this.userName || '';\n return name ? name.substring(name.length - 2) : '';\n },\n // 可删除的消息 id(仅后端已落库、带回 id 的消息可删)\n deletableMsgIds: function deletableMsgIds() {\n return this.messages.filter(function (m) {\n return m.id;\n }).map(function (m) {\n return m.id;\n });\n },\n // 是否已全选\n isAllSelected: function isAllSelected() {\n return this.deletableMsgIds.length > 0 && this.selectedMsgIds.length === this.deletableMsgIds.length;\n }\n },\n watch: {\n companyId: function companyId() {\n this.initDone = false;\n this.finishStream();\n this.loadSuggestedQuestions();\n this.checkKbDocuments();\n this.initSession();\n },\n userno: function userno() {\n this.initDone = false;\n this.finishStream();\n this.activeSessionId = '';\n this.messages = [];\n this.initSession();\n },\n // 移动端进入时默认先显示会话列表页(仿 AI 助理)\n isMobileActive: function isMobileActive(val) {\n if (val) {\n // 无资料时遮罩只在详情页可见(列表页 .kb-chat-main 被隐藏),故保持停在详情页\n this.showH5List = !this.noDocument;\n this.mobileAction = null;\n }\n }\n },\n mounted: function mounted() {\n var _this = this;\n\n // 独立使用(父级未传 isMobile)时自检窗口宽度\n this._onResize = this.handleResize;\n window.addEventListener('resize', this._onResize, {\n passive: true\n }); // Android WebView 兼容:在 document 级别监听 touchmove/touchend,\n // 避免可滚动子元素消费事件后不冒泡到父级(参考 AI 助理)\n\n this._swipeMoveHandler = this._onSwipeMove;\n this._swipeEndHandler = this._onSwipeEnd;\n this._popStateHandler = this._onPopState;\n document.addEventListener('touchmove', this._swipeMoveHandler, {\n passive: true\n });\n document.addEventListener('touchend', this._swipeEndHandler, {\n passive: true\n }); // 拦截 Android 系统返回手势:通过 history.pushState + popstate\n\n window.addEventListener('popstate', this._popStateHandler); // 移动端首屏为会话列表页,推入一条历史状态供系统返回拦截\n\n if (this.isMobileActive) {\n history.pushState({\n page: 'kbChatList'\n }, '');\n }\n\n this.initSession();\n this.checkKbDocuments(); // v-show 隐藏期间加载的历史消息无法滚动(scrollHeight 为 0),\n // 监听组件由隐藏变为可见时,滚动到最新问答;\n // 同时每次变为可见时刷新\"猜您想问\"(管理端可能刚更新过)\n\n if (typeof IntersectionObserver !== 'undefined' && this.$el) {\n this._visibilityObserver = new IntersectionObserver(function (entries) {\n if (entries.some(function (e) {\n return e.isIntersecting;\n })) {\n _this.loadSuggestedQuestions(); // 管理端可能刚上传/删除了文档,重新校验遮罩状态\n\n\n _this.checkKbDocuments();\n\n if (_this.messages.length) {\n _this.$nextTick(function () {\n return _this.scrollToBottom();\n });\n }\n }\n });\n\n this._visibilityObserver.observe(this.$el);\n } else {\n this.loadSuggestedQuestions();\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (this._visibilityObserver) {\n this._visibilityObserver.disconnect();\n\n this._visibilityObserver = null;\n }\n\n if (this._onResize) {\n window.removeEventListener('resize', this._onResize);\n }\n\n if (this._swipeMoveHandler) {\n document.removeEventListener('touchmove', this._swipeMoveHandler);\n }\n\n if (this._swipeEndHandler) {\n document.removeEventListener('touchend', this._swipeEndHandler);\n }\n\n if (this._popStateHandler) {\n window.removeEventListener('popstate', this._popStateHandler);\n }\n\n this.streamRequestId++;\n this.closeEventSource();\n this.clearTypeTimer();\n this.onMsgTouchEnd();\n this.contentCache = {};\n },\n methods: {\n // 独立使用时自检移动端(父级已传 isMobile 时忽略)\n handleResize: function handleResize() {\n var now = typeof window !== 'undefined' && (window.innerWidth <= 768 || !(0, _native.hasElementUI)(this));\n this.localIsMobile = now;\n },\n renderMarkdown: function renderMarkdown(text) {\n if (!text) return '';\n\n try {\n return md.render(text);\n } catch (err) {\n return this.escapeHtml(text);\n }\n },\n // 与 ChatDetail 一致的增量渲染:完整行走 markdown、未完成尾行转义拼接,\n // 借助 markdown-it breaks:true 实现逐行浮现效果。\n renderStreaming: function renderStreaming(text, msgKey) {\n if (!text) return '';\n var cache = this.contentCache[msgKey];\n var lines = text.split('\\n');\n var lastLineIdx = lines.length - 1;\n\n if (!cache) {\n try {\n var html = md.render(text);\n this.$set(this.contentCache, msgKey, {\n renderedLines: lastLineIdx,\n html: html,\n fullText: text\n });\n return html;\n } catch (_unused) {\n return this.escapeHtml(text);\n }\n }\n\n if (text === cache.fullText) return cache.html;\n cache.fullText = text; // 行数没变,仅末尾追加字符,直接拼接尾行\n\n if (lastLineIdx === cache.renderedLines) {\n return cache.html + this.escapeHtml(lines[lastLineIdx]);\n } // 行数变少/文本回退截断,全量重渲染\n\n\n if (lastLineIdx < cache.renderedLines) {\n try {\n var fullLines = lines.slice(0, lastLineIdx);\n var newBaseHtml = '';\n\n if (fullLines.length > 0) {\n newBaseHtml = md.render(fullLines.join('\\n'));\n }\n\n cache.html = newBaseHtml;\n cache.renderedLines = lastLineIdx;\n\n var _lastLine = this.escapeHtml(lines[lastLineIdx]);\n\n return cache.html + (_lastLine || '');\n } catch (_unused2) {\n return this.escapeHtml(text);\n }\n } // 新增完整行\n\n\n if (cache.renderedLines < lastLineIdx) {\n var allFullLines = lines.slice(0, lastLineIdx);\n var _newBaseHtml = '';\n\n if (allFullLines.length > 0) {\n _newBaseHtml = md.render(allFullLines.join('\\n'));\n }\n\n cache.html = _newBaseHtml;\n cache.renderedLines = lastLineIdx;\n }\n\n var lastLine = this.escapeHtml(lines[lastLineIdx]);\n cache.renderedLines = lastLineIdx;\n return cache.html + (lastLine || '');\n },\n escapeHtml: function escapeHtml(text) {\n if (!text) return '';\n var map = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n };\n return String(text).replace(/[&<>\"']/g, function (c) {\n return map[c];\n });\n },\n autoResizeTextarea: function autoResizeTextarea() {\n var ta = this.$refs.taRef;\n if (!ta) return; // 移动端从单行起增高(与 38px 最小高度一致),PC 保持 100px 基准\n\n var baseH = this.isMobileActive ? 38 : 100;\n var maxH = this.isMobileActive ? 120 : 180;\n ta.style.height = \"\".concat(baseH, \"px\");\n var realH = ta.scrollHeight;\n\n if (realH > baseH) {\n ta.style.height = \"\".concat(Math.min(realH, maxH), \"px\");\n }\n },\n newLine: function newLine(e) {\n var _this2 = this;\n\n e.preventDefault();\n var ta = this.$refs.taRef;\n var start = ta.selectionStart;\n var end = ta.selectionEnd;\n this.inputText = this.inputText.slice(0, start) + '\\n' + this.inputText.slice(end);\n this.$nextTick(function () {\n ta.selectionStart = ta.selectionEnd = start + 1;\n\n _this2.autoResizeTextarea();\n });\n },\n\n /**\n * 加载\"猜您想问\"问题(按公司,所有用户可见)\n */\n loadSuggestedQuestions: function loadSuggestedQuestions() {\n var _this3 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var list;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n if (_this3.companyId) {\n _context.next = 3;\n break;\n }\n\n _this3.suggestedQuestions = [];\n return _context.abrupt(\"return\");\n\n case 3:\n _context.prev = 3;\n _context.next = 6;\n return (0, _knowledgeBase.getSuggestedQuestions)(_this3.companyId);\n\n case 6:\n _context.t0 = _context.sent;\n\n if (_context.t0) {\n _context.next = 9;\n break;\n }\n\n _context.t0 = [];\n\n case 9:\n list = _context.t0;\n _this3.suggestedQuestions = list.map(function (q) {\n return q.questionText;\n });\n _context.next = 16;\n break;\n\n case 13:\n _context.prev = 13;\n _context.t1 = _context[\"catch\"](3);\n console.error('加载猜您想问失败', _context.t1);\n\n case 16:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee, null, [[3, 13]]);\n }))();\n },\n\n /**\n * 检查当前公司是否有「已索引」的知识库文档,无可用资料时展示遮罩提示\n * 仅已索引(indexStatus === 1)的文档才能被检索,未索引/索引中/索引失败都不算\n */\n checkKbDocuments: function checkKbDocuments() {\n var _this4 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var data, isEmpty;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n if (_this4.companyId) {\n _context2.next = 3;\n break;\n }\n\n _this4.noDocument = false;\n return _context2.abrupt(\"return\");\n\n case 3:\n _context2.prev = 3;\n _context2.next = 6;\n return (0, _knowledgeBase.getFileList)({\n companyId: _this4.companyId,\n indexStatus: 1,\n pageNum: 1,\n pageSize: 1\n });\n\n case 6:\n data = _context2.sent;\n isEmpty = (data && data.total || 0) === 0;\n _this4.noDocument = isEmpty; // 无资料时把会话固定回第一个\n\n if (isEmpty) {\n _this4.lockFirstSession(); // 无资料遮罩位于聊天详情页的 .kb-chat-main 内,而移动端首屏是会话列表页\n // (h5-list-mode 下 .kb-chat-main 被 display:none 隐藏),若停留在列表页\n // 则遮罩永远不可见;故无资料时强制切到详情页把遮罩展示出来\n\n\n if (_this4.isMobileActive) {\n _this4.showH5List = false;\n history.pushState({\n page: 'kbChatDetail'\n }, '');\n }\n }\n\n _context2.next = 16;\n break;\n\n case 12:\n _context2.prev = 12;\n _context2.t0 = _context2[\"catch\"](3);\n // 查询失败(如无管理权限)时不展示遮罩,避免误拦截\n console.error('检查知识库文档失败', _context2.t0);\n _this4.noDocument = false;\n\n case 16:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2, null, [[3, 12]]);\n }))();\n },\n initSession: function initSession() {\n var _this5 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var list;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (_this5.companyId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\");\n\n case 2:\n if (!(_this5.initDone && (_this5.messages.length || _this5.streaming || _this5.inputText))) {\n _context3.next = 4;\n break;\n }\n\n return _context3.abrupt(\"return\");\n\n case 4:\n _this5.initDone = false;\n _context3.prev = 5;\n _context3.next = 8;\n return (0, _kbChat.getKbSessionList)(_this5.companyId);\n\n case 8:\n _context3.t0 = _context3.sent;\n\n if (_context3.t0) {\n _context3.next = 11;\n break;\n }\n\n _context3.t0 = [];\n\n case 11:\n list = _context3.t0;\n _this5.sessionList = list;\n\n if (!(list.length > 0)) {\n _context3.next = 19;\n break;\n }\n\n // 只有 1 个会话且无消息(新用户首次进入自动生成的空会话)时,\n // 默认折叠侧边栏只显示聊天主界面;在 selectSession 前同步设置,\n // 避免「先展开再合住」的闪烁\n _this5.sidebarCollapsed = list.length === 1 && !list[0].lastContent;\n _context3.next = 17;\n return _this5.selectSession(list[0].sessionId);\n\n case 17:\n _context3.next = 21;\n break;\n\n case 19:\n _context3.next = 21;\n return _this5.autoCreateSession();\n\n case 21:\n _context3.next = 28;\n break;\n\n case 23:\n _context3.prev = 23;\n _context3.t1 = _context3[\"catch\"](5);\n console.error('获取会话列表失败,尝试自动创建', _context3.t1);\n _context3.next = 28;\n return _this5.autoCreateSession();\n\n case 28:\n _context3.prev = 28;\n _this5.initDone = true;\n return _context3.finish(28);\n\n case 31:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3, null, [[5, 23, 28, 31]]);\n }))();\n },\n autoCreateSession: function autoCreateSession() {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var data;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this6.companyId) {\n _context4.next = 3;\n break;\n }\n\n console.warn('[KbChat] 无法创建会话:companyId 为空');\n return _context4.abrupt(\"return\");\n\n case 3:\n console.log('[KbChat] 创建会话,companyId:', _this6.companyId);\n _context4.prev = 4;\n _context4.next = 7;\n return (0, _kbChat.createKbSession)(_this6.companyId);\n\n case 7:\n data = _context4.sent;\n console.log('[KbChat] 会话创建成功,sessionId:', data.sessionId);\n\n _this6.finishStream();\n\n _this6.activeSessionId = data.sessionId;\n _this6.streamRequestId++;\n _this6.messages = [];\n _this6.sessionList = [{\n sessionId: data.sessionId,\n title: '',\n lastContent: ''\n }].concat(_this6.sessionList || []); // 新建会话时保持侧边栏展开,方便继续切换会话\n\n _this6.sidebarCollapsed = false;\n\n _this6.$nextTick(function () {\n return _this6.scrollToBottom();\n });\n\n _context4.next = 21;\n break;\n\n case 18:\n _context4.prev = 18;\n _context4.t0 = _context4[\"catch\"](4);\n console.error('自动创建会话失败', _context4.t0);\n\n case 21:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[4, 18]]);\n }))();\n },\n\n /** 新建会话(豆包式「新对话」) */\n handleNewSession: function handleNewSession() {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() {\n return regeneratorRuntime.wrap(function _callee5$(_context5) {\n while (1) {\n switch (_context5.prev = _context5.next) {\n case 0:\n if (!_this7.noDocument) {\n _context5.next = 2;\n break;\n }\n\n return _context5.abrupt(\"return\");\n\n case 2:\n if (_this7.streaming) _this7.handleStop();\n _context5.next = 5;\n return _this7.autoCreateSession();\n\n case 5:\n case \"end\":\n return _context5.stop();\n }\n }\n }, _callee5);\n }))();\n },\n\n /** 切换会话 */\n handleSelectSession: function handleSelectSession(item) {\n var _this8 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() {\n return regeneratorRuntime.wrap(function _callee6$(_context6) {\n while (1) {\n switch (_context6.prev = _context6.next) {\n case 0:\n if (!(!item || !item.sessionId)) {\n _context6.next = 2;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 2:\n if (!_this8.noDocument) {\n _context6.next = 5;\n break;\n }\n\n _this8.lockFirstSession();\n\n return _context6.abrupt(\"return\");\n\n case 5:\n if (!(item.sessionId === _this8.activeSessionId && _this8.messages.length)) {\n _context6.next = 8;\n break;\n }\n\n if (_this8.isMobileActive) {\n _this8.showH5List = false;\n history.pushState({\n page: 'kbChatDetail'\n }, '');\n\n _this8.$nextTick(function () {\n return _this8.scrollToBottom();\n });\n }\n\n return _context6.abrupt(\"return\");\n\n case 8:\n _context6.next = 10;\n return _this8.selectSession(item.sessionId);\n\n case 10:\n // 移动端:选中后跳到聊天详情页\n if (_this8.isMobileActive) {\n _this8.showH5List = false; // 推入历史状态,使 Android 系统返回手势先触发 popstate 被拦截\n\n history.pushState({\n page: 'kbChatDetail'\n }, ''); // 列表页时聊天区被 display:none,scrollHeight 为 0 导致上面的滚动无效;\n // 切到详情页、容器可见后需再滚一次,默认定位到最新消息\n\n _this8.$nextTick(function () {\n return _this8.scrollToBottom();\n });\n }\n\n case 11:\n case \"end\":\n return _context6.stop();\n }\n }\n }, _callee6);\n }))();\n },\n\n /** 返回宿主 App 原生页面(参考 AI 助理 handleNativeBack) */\n handleNativeBack: function handleNativeBack() {\n if (this.$hybrid && typeof this.$hybrid.closePage === 'function') {\n this.$hybrid.closePage();\n } else if (window.history.length > 1) {\n window.history.back();\n }\n },\n\n /** 会话列表页返回按钮:返回原生页面 */\n handleSidebarBack: function handleSidebarBack() {\n this.handleNativeBack();\n },\n // ── H5 侧滑手势(参考 AI 助理)──\n // 从左/右屏幕边缘起手横滑触发返回,避免页面内部横向滚动被误判\n onSwipeTouchStart: function onSwipeTouchStart(e) {\n if (!this.isMobileActive) return;\n var t = e.touches[0];\n this._swipeStartX = t.clientX;\n this._swipeStartY = t.clientY;\n this._swipeHandled = false;\n var edge = Math.min(56, Math.round(window.innerWidth * 0.09));\n this._swipeFromLeftEdge = t.clientX <= edge;\n this._swipeFromRightEdge = t.clientX >= window.innerWidth - edge;\n this._swipeFromEdge = this._swipeFromLeftEdge || this._swipeFromRightEdge;\n },\n _swipeShouldTrigger: function _swipeShouldTrigger(diffX, diffY) {\n if (!this._swipeFromEdge) return false; // 从左边缘 → 右滑;从右边缘 → 左滑\n\n var directionOk = this._swipeFromLeftEdge && diffX >= 60 || this._swipeFromRightEdge && diffX <= -60;\n if (!directionOk) return false; // 水平距离明显大于垂直距离(排除上下滚动干扰)\n\n return Math.abs(diffX) >= Math.abs(diffY) * 2;\n },\n _onSwipeMove: function _onSwipeMove(e) {\n if (!this.isMobileActive || this._swipeHandled) return;\n if (!e.touches || !e.touches.length) return;\n var diffX = e.touches[0].clientX - this._swipeStartX;\n var diffY = e.touches[0].clientY - this._swipeStartY;\n if (!this._swipeShouldTrigger(diffX, diffY)) return;\n this._swipeHandled = true;\n\n this._triggerSwipeAction();\n },\n // touchend 兜底,兼容不支持 touchmove 的场景\n _onSwipeEnd: function _onSwipeEnd(e) {\n if (!this.isMobileActive || this._swipeHandled) return;\n if (!e.changedTouches || !e.changedTouches.length) return;\n var diffX = e.changedTouches[0].clientX - this._swipeStartX;\n var diffY = e.changedTouches[0].clientY - this._swipeStartY;\n if (!this._swipeShouldTrigger(diffX, diffY)) return;\n this._swipeHandled = true;\n\n this._triggerSwipeAction();\n },\n\n /** 侧滑触发:列表页 → 返回原生页面;详情页 → 回会话列表页 */\n _triggerSwipeAction: function _triggerSwipeAction() {\n if (this.showH5List) {\n this.handleNativeBack();\n } else {\n this.handleMobileBack();\n }\n },\n\n /**\n * 拦截 Android 系统返回手势(history.pushState + popstate)\n * WebView 先回退历史栈触发 popstate,这里拦截后导航到列表页,避免 Activity 被直接关闭\n */\n _onPopState: function _onPopState() {\n if (!this.isMobileActive) return;\n\n if (!this.showH5List) {\n // 详情页 → 返回会话列表页\n this.showH5List = true;\n history.pushState({\n page: 'kbChatList'\n }, '');\n } else {\n // 列表页 → 直接关闭 WebView 返回原生页面\n this.handleNativeBack();\n }\n },\n\n /** 移动端详情页返回:无资料遮罩时返回原生页面;否则回到会话列表页 */\n handleMobileBack: function handleMobileBack() {\n // 无资料遮罩期间无法进入会话,返回到宿主 App 原生页面\n if (this.noDocument) {\n this.handleNativeBack();\n return;\n } // 正常情况:回到会话列表页\n\n\n this.showH5List = true;\n },\n\n /** 无资料遮罩期间:把当前会话固定回列表中的第一个 */\n lockFirstSession: function lockFirstSession() {\n var first = this.sessionList[0];\n\n if (first && first.sessionId && first.sessionId !== this.activeSessionId) {\n this.selectSession(first.sessionId);\n }\n },\n\n /** 会话项「…」菜单:重命名 / 删除 */\n handleSessionMenu: function handleSessionMenu(cmd, item) {\n if (!item || !item.sessionId) return;\n\n if (cmd === 'rename') {\n this.handleRenameSession(item);\n } else if (cmd === 'delete') {\n this.handleDeleteSession(item);\n }\n },\n // ==================== 移动端会话操作单 ====================\n openMobileAction: function openMobileAction(item) {\n if (this.noDocument) return;\n this.mobileAction = item;\n },\n doMobileRename: function doMobileRename(item) {\n this.mobileAction = null;\n this.handleRenameSession(item);\n },\n doMobileDelete: function doMobileDelete(item) {\n this.mobileAction = null;\n this.handleDeleteSession(item);\n },\n\n /** 重命名会话 */\n handleRenameSession: function handleRenameSession(item) {\n var _this9 = this;\n\n if (!item || !item.sessionId) return;\n this.$prompt('请输入新的会话名称', '重命名会话', {\n confirmButtonText: '确定',\n cancelButtonText: '取消',\n inputValue: item.title || '',\n inputPattern: /\\S+/,\n inputErrorMessage: '名称不能为空'\n }).then(function (_ref) {\n var value = _ref.value;\n var title = (value || '').trim();\n if (!title) return;\n return (0, _kbChat.renameKbSession)(item.sessionId, title).then(function () {\n item.title = title;\n\n _this9.$message.success('重命名成功');\n });\n }).catch(function (err) {\n // 取消或校验失败,不提示错误\n if (err && err !== 'cancel' && err !== 'close') {\n console.error('重命名失败', err);\n\n _this9.$message.error('重命名失败');\n }\n });\n },\n\n /** 删除会话 */\n handleDeleteSession: function handleDeleteSession(item) {\n var _this10 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee7() {\n return regeneratorRuntime.wrap(function _callee7$(_context7) {\n while (1) {\n switch (_context7.prev = _context7.next) {\n case 0:\n if (!(!item || !item.sessionId)) {\n _context7.next = 2;\n break;\n }\n\n return _context7.abrupt(\"return\");\n\n case 2:\n if (!(_this10.sessionList.length <= 1)) {\n _context7.next = 5;\n break;\n }\n\n _this10.$message.warning('至少保留一个会话');\n\n return _context7.abrupt(\"return\");\n\n case 5:\n _context7.prev = 5;\n _context7.next = 8;\n return _this10.$confirm('删除后无法恢复,确定删除该会话吗?', '删除会话', {\n confirmButtonText: '确定',\n cancelButtonText: '取消',\n type: 'warning'\n });\n\n case 8:\n _context7.next = 13;\n break;\n\n case 10:\n _context7.prev = 10;\n _context7.t0 = _context7[\"catch\"](5);\n return _context7.abrupt(\"return\");\n\n case 13:\n _context7.prev = 13;\n _context7.next = 16;\n return (0, _kbChat.deleteKbSession)(item.sessionId);\n\n case 16:\n _this10.sessionList = (_this10.sessionList || []).filter(function (s) {\n return s.sessionId !== item.sessionId;\n });\n\n if (!(_this10.activeSessionId === item.sessionId)) {\n _context7.next = 28;\n break;\n }\n\n _this10.finishStream();\n\n _this10.activeSessionId = '';\n _this10.messages = [];\n\n if (!(_this10.sessionList.length > 0)) {\n _context7.next = 26;\n break;\n }\n\n _context7.next = 24;\n return _this10.selectSession(_this10.sessionList[0].sessionId);\n\n case 24:\n _context7.next = 28;\n break;\n\n case 26:\n _context7.next = 28;\n return _this10.autoCreateSession();\n\n case 28:\n _this10.$message.success('删除成功');\n\n _context7.next = 35;\n break;\n\n case 31:\n _context7.prev = 31;\n _context7.t1 = _context7[\"catch\"](13);\n console.error('删除会话失败', _context7.t1);\n\n _this10.$message.error('删除失败');\n\n case 35:\n case \"end\":\n return _context7.stop();\n }\n }\n }, _callee7, null, [[5, 10], [13, 31]]);\n }))();\n },\n\n /** 会话列表时间格式化:今天显示时分,更早显示月日 */\n formatTime: function formatTime(time) {\n if (!time) return '';\n var d = new Date(time);\n if (isNaN(d.getTime())) return '';\n var now = new Date();\n var sameDay = d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth() && d.getDate() === now.getDate();\n\n var pad = function pad(n) {\n return n < 10 ? '0' + n : '' + n;\n };\n\n if (sameDay) {\n return pad(d.getHours()) + ':' + pad(d.getMinutes());\n }\n\n return pad(d.getMonth() + 1) + '-' + pad(d.getDate());\n },\n\n /** 后端消息 → 前端消息对象(保留 id/msgKey,删除功能需要) */\n mapMessage: function mapMessage(m, i) {\n var thinking = m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '';\n return {\n id: m.id,\n msgKey: m.msgKey || m.msg_key || '',\n role: m.role,\n content: m.content || '',\n thinking: thinking,\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n },\n selectSession: function selectSession(sessionId) {\n var _this11 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() {\n var raw, msgs;\n return regeneratorRuntime.wrap(function _callee8$(_context8) {\n while (1) {\n switch (_context8.prev = _context8.next) {\n case 0:\n if (_this11.streaming) _this11.handleStop();\n\n _this11.exitSelectMode();\n\n _this11.msgAction = null;\n _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 6;\n _context8.next = 9;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 9:\n raw = _context8.sent;\n msgs = Array.isArray(raw) ? raw : raw.messages || raw.records || raw.list || [];\n _this11.messages = msgs.map(function (m, i) {\n return _this11.mapMessage(m, i);\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 18;\n break;\n\n case 15:\n _context8.prev = 15;\n _context8.t0 = _context8[\"catch\"](6);\n console.error('加载消息失败', _context8.t0);\n\n case 18:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[6, 15]]);\n }))();\n },\n\n /**\n * 回读当前会话消息,补全后端持久化后生成的 id。\n * 流式回答期间消息只存在于本地数组(无 id),无法按 id 删除;\n * 流式结束后调用一次即可拿到 id。已全部有 id 时跳过,避免多余请求。\n *\n * 注意:不能直接用后端数组整体覆盖 this.messages。后端回读可能包含重复/多余记录\n * (如同一会话里用户消息被落库两次),若整体覆盖会把重复消息直接渲染出来。\n * 这里以本地数组为基准,只用后端记录按「角色 + 内容」顺序回填 id,保证界面条数不变。\n */\n syncMessageIds: function syncMessageIds() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n var raw, backendMsgs, cursor;\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (!(!_this12.activeSessionId || _this12.streaming)) {\n _context9.next = 2;\n break;\n }\n\n return _context9.abrupt(\"return\");\n\n case 2:\n if (!(_this12.messages.length && _this12.messages.every(function (m) {\n return m.id;\n }))) {\n _context9.next = 4;\n break;\n }\n\n return _context9.abrupt(\"return\");\n\n case 4:\n _context9.prev = 4;\n _context9.next = 7;\n return (0, _kbChat.getKbSessionMessages)(_this12.activeSessionId);\n\n case 7:\n raw = _context9.sent;\n backendMsgs = Array.isArray(raw) ? raw : raw.messages || raw.records || raw.list || []; // 按顺序把本地消息与后端记录配对(角色一致且内容匹配),只补 id/msgKey\n\n cursor = 0;\n\n _this12.messages.forEach(function (m) {\n if (m.id) return;\n\n var role = _this12.normRole(m.role);\n\n for (var i = cursor; i < backendMsgs.length; i++) {\n var b = backendMsgs[i];\n\n if (_this12.normRole(b.role) === role && String(b.content || '').trim() === String(m.content || '').trim()) {\n m.id = b.id;\n if (!m.msgKey) m.msgKey = b.msgKey || b.msg_key || '';\n cursor = i + 1;\n return;\n }\n }\n });\n\n _context9.next = 16;\n break;\n\n case 13:\n _context9.prev = 13;\n _context9.t0 = _context9[\"catch\"](4);\n console.error('同步消息 id 失败', _context9.t0);\n\n case 16:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[4, 13]]);\n }))();\n },\n // ==================== 消息多选 / 删除 ====================\n\n /** 进入多选模式(preselectMsg 传入时默认勾选该条所在的整组问答,对应豆包式「删除」入口) */\n enterSelectMode: function enterSelectMode(preselectMsg) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var preselectId, idx;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n if (!(_this13.streaming || !_this13.messages.length)) {\n _context10.next = 2;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 2:\n // 点击条的后端持久化 id(模板上「删除」按钮仅在 msg.id 存在时渲染,故必有值)\n preselectId = preselectMsg ? preselectMsg.id : null; // 先回读消息确保每条都有 id(刚流式完成的消息本地没有 id)。\n // 回读只补 id、不重建数组,定位可用 id 或对象引用。\n\n _context10.next = 5;\n return _this13.syncMessageIds();\n\n case 5:\n _this13.selectMode = true;\n _this13.selectedMsgIds = [];\n idx = preselectId != null ? _this13.messages.findIndex(function (m) {\n return m.id === preselectId;\n }) : -1;\n if (idx === -1 && preselectMsg) idx = _this13.messages.indexOf(preselectMsg);\n\n if (idx !== -1) {\n // 默认勾选整组问答(用户问 + AI 答),便于一键删除\n _this13.selectedMsgIds = _this13.getQaPairIdsAt(idx);\n } // 滚动定位:让默认勾选的整组问答都进入视野(长回答会把用户问顶出屏幕)\n\n\n _this13.scrollSelectedIntoView();\n\n case 11:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\n }))();\n },\n\n /**\n * 把首个被选中的消息行滚动到容器内可见。\n * 选中行是「用户问 + AI 答」中的靠前一条,滚到它即能同时看到整组。\n * 若靠前那条已在视野内则不动,避免无谓跳动。\n */\n scrollSelectedIntoView: function scrollSelectedIntoView() {\n var _this14 = this;\n\n this.$nextTick(function () {\n var container = _this14.$refs.messagesRef;\n if (!container) return;\n var rows = container.querySelectorAll('.msg-row');\n if (!rows.length) return; // 找到第一个处于选中态、且带 id 的行(与 selectedMsgIds 顺序无关,取 DOM 顺序)\n\n var target = null;\n\n for (var i = 0; i < rows.length; i++) {\n var box = rows[i].querySelector('.msg-check-box.checked');\n\n if (box) {\n target = rows[i];\n break;\n }\n }\n\n if (!target) return;\n var cRect = container.getBoundingClientRect();\n var tRect = target.getBoundingClientRect(); // 目标行顶部已在容器可视区内则无需滚动\n\n if (tRect.top >= cRect.top && tRect.top < cRect.bottom) return; // 滚动到目标行顶部(留 8px 间距)\n\n container.scrollTop += tRect.top - cRect.top - 8;\n });\n },\n exitSelectMode: function exitSelectMode() {\n this.selectMode = false;\n this.selectedMsgIds = [];\n },\n isMsgSelected: function isMsgSelected(msg) {\n return !!msg.id && this.selectedMsgIds.indexOf(msg.id) !== -1;\n },\n\n /** 归一化 role,兼容后端可能返回的大小写/空白 */\n normRole: function normRole(role) {\n return String(role == null ? '' : role).trim().toLowerCase();\n },\n\n /**\n * 取与下标 idx 处消息成对的另一条:用户问 ↔ AI 答视为一组问答。\n * 只认紧邻的一条(问答对在列表里必然相邻),相邻非同组则不配对。\n * 不依赖 role 的具体拼写(user/assistant 的大小写、空白均容错)。\n */\n getQaPartnerAt: function getQaPartnerAt(idx) {\n if (idx < 0 || idx >= this.messages.length) return null;\n var cur = this.messages[idx];\n if (!cur) return null;\n var role = this.normRole(cur.role);\n if (role !== 'user' && role !== 'assistant') return null; // 只认紧邻的那一条:问答对在列表里必然相邻。\n // 若相邻消息不是异角色(例如回答已被删除),说明当前消息已无配对,\n // 不能继续向前/向后扫描,否则会误勾到上一个问答的回答。\n\n var neighbor = role === 'user' ? this.messages[idx + 1] : this.messages[idx - 1];\n if (!neighbor) return null;\n var neighborRole = this.normRole(neighbor.role);\n var expect = role === 'user' ? 'assistant' : 'user';\n return neighborRole === expect ? neighbor : null;\n },\n\n /** 下标 idx 处消息所在问答对的可删 id 列表(用户问 + AI 答) */\n getQaPairIdsAt: function getQaPairIdsAt(idx) {\n var ids = [];\n var cur = this.messages[idx];\n if (cur && cur.id) ids.push(cur.id);\n var partner = this.getQaPartnerAt(idx);\n if (partner && partner.id && ids.indexOf(partner.id) === -1) ids.push(partner.id);\n return ids;\n },\n\n /**\n * 勾选单条消息:勾上时连带把问答对里另一条一起勾上;\n * 取消时只取消当前这条,从而支持「先整组勾选、再单独取消一条」的单条选择。\n */\n toggleSelectMessage: function toggleSelectMessage(msg) {\n var _this15 = this;\n\n if (!msg.id) return;\n var i = this.selectedMsgIds.indexOf(msg.id);\n\n if (i === -1) {\n var idx = this.messages.indexOf(msg);\n var pairIds = idx === -1 ? [msg.id] : this.getQaPairIdsAt(idx);\n pairIds.forEach(function (id) {\n if (_this15.selectedMsgIds.indexOf(id) === -1) _this15.selectedMsgIds.push(id);\n });\n } else {\n this.selectedMsgIds.splice(i, 1);\n }\n },\n toggleSelectAll: function toggleSelectAll() {\n var ids = this.deletableMsgIds;\n this.selectedMsgIds = this.selectedMsgIds.length === ids.length ? [] : ids.slice();\n },\n\n /** 多选模式下点击整行即切换选中 */\n onMsgRowClick: function onMsgRowClick(msg) {\n // 长按弹出菜单后浏览器还会补一次 click,短暂屏蔽避免误触\n if (Date.now() < this._ignoreClickUntil) return;\n if (this.selectMode) this.toggleSelectMessage(msg);\n },\n\n /** 移动端长按气泡弹出消息操作单(豆包式:复制 / 删除 / 多选) */\n onMsgTouchStart: function onMsgTouchStart(msg) {\n var _this16 = this;\n\n if (!this.isMobileActive || this.selectMode) return;\n this._longPressMsg = msg;\n clearTimeout(this._longPressTimer);\n this._longPressTimer = setTimeout(function () {\n _this16._longPressTimer = null; // 用户正在长按选中文本(复制用),此时不弹菜单,避免抢走选择行为\n\n var sel = window.getSelection && window.getSelection();\n if (sel && String(sel).trim()) return;\n\n _this16.openMsgAction(_this16._longPressMsg);\n }, 500);\n },\n onMsgTouchEnd: function onMsgTouchEnd() {\n if (this._longPressTimer) {\n clearTimeout(this._longPressTimer);\n this._longPressTimer = null;\n }\n },\n openMsgAction: function openMsgAction(msg) {\n this.msgAction = msg; // 长按抬手后浏览器可能补发 click,短暂屏蔽,避免刚弹出的菜单被立即关闭\n\n this._ignoreClickUntil = Date.now() + 350;\n },\n closeMsgAction: function closeMsgAction() {\n if (Date.now() < this._ignoreClickUntil) return;\n this.msgAction = null;\n },\n\n /** 移动端消息操作单:复制 / 删除(删除进入多选模式,与豆包一致) */\n doMsgAction: function doMsgAction(action) {\n var msg = this.msgAction;\n this.msgAction = null;\n if (!msg) return;\n\n if (action === 'copy') {\n this.copyText(msg.content);\n } else if (action === 'delete') {\n this.enterSelectMode(msg);\n }\n },\n\n /** 批量删除选中的消息 */\n handleDeleteSelected: function handleDeleteSelected() {\n var _this17 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var ids;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (!(!_this17.selectedMsgIds.length || !_this17.activeSessionId)) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n ids = _this17.selectedMsgIds.slice();\n _context11.prev = 3;\n _context11.next = 6;\n return _this17.$confirm(\"\\u5220\\u9664\\u540E\\u65E0\\u6CD5\\u6062\\u590D\\uFF0C\\u786E\\u5B9A\\u5220\\u9664\\u9009\\u4E2D\\u7684 \".concat(ids.length, \" \\u6761\\u6D88\\u606F\\u5417\\uFF1F\"), '删除消息', {\n confirmButtonText: '确定',\n cancelButtonText: '取消',\n type: 'warning'\n });\n\n case 6:\n _context11.next = 11;\n break;\n\n case 8:\n _context11.prev = 8;\n _context11.t0 = _context11[\"catch\"](3);\n return _context11.abrupt(\"return\");\n\n case 11:\n _context11.prev = 11;\n _context11.next = 14;\n return (0, _kbChat.deleteKbMessages)(_this17.activeSessionId, ids);\n\n case 14:\n _this17.messages = _this17.messages.filter(function (m) {\n return ids.indexOf(m.id) === -1;\n });\n\n _this17.exitSelectMode();\n\n _this17.$message.success('删除成功');\n\n _this17.refreshSessionList();\n\n _context11.next = 24;\n break;\n\n case 20:\n _context11.prev = 20;\n _context11.t1 = _context11[\"catch\"](11);\n console.error('批量删除消息失败', _context11.t1);\n\n _this17.$message.error('删除失败');\n\n case 24:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, null, [[3, 8], [11, 20]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this18 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee12() {\n return regeneratorRuntime.wrap(function _callee12$(_context12) {\n while (1) {\n switch (_context12.prev = _context12.next) {\n case 0:\n if (!_this18.activeSessionId) {\n _context12.next = 8;\n break;\n }\n\n _context12.prev = 1;\n _context12.next = 4;\n return (0, _kbChat.closeKbSession)(_this18.activeSessionId);\n\n case 4:\n _context12.next = 8;\n break;\n\n case 6:\n _context12.prev = 6;\n _context12.t0 = _context12[\"catch\"](1);\n\n case 8:\n _context12.next = 10;\n return _this18.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context12.stop();\n }\n }\n }, _callee12, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this19 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee13() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee13$(_context13) {\n while (1) {\n switch (_context13.prev = _context13.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this19.inputText.trim();\n\n if (!(!text || !_this19.activeSessionId || _this19.streaming)) {\n _context13.next = 3;\n break;\n }\n\n return _context13.abrupt(\"return\");\n\n case 3:\n _this19.exitSelectMode();\n\n _this19.inputText = '';\n requestSessionId = _this19.activeSessionId;\n requestId = ++_this19.streamRequestId;\n\n _this19.clearTypeTimer();\n\n _this19.messages.push({\n role: 'user',\n content: text\n });\n\n _this19.$nextTick(function () {\n return _this19.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this19.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this19.messages[msgIdx];\n _this19.streamMsg = streamMsg;\n _this19.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this19.streamRequestId && requestSessionId === _this19.activeSessionId && _this19.streamMsg === streamMsg;\n }; // SSE 只把数据累积到 answerBuffer。typeTimer(30ms) 节流按「完整换行」逐行把内容\n // 推进到 msg.content:每个 tick 最多只推进「到下一个 \\n 为止」的一行,未完成的尾行\n // 保持不动;配合 renderStreaming(breaks:true) 实现「一行显示一行」。\n // 不是打字机模式(不逐字)、也不是一次性整段渲染(不会一出就是全文)。\n\n\n answerBuffer = '';\n finalContent = '';\n streamDone = false;\n _this19._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this19.messages[msgIdx];\n var source = finalContent || answerBuffer;\n var curLen = msg._prevReveal || 0;\n var nextLen = curLen; // 找到「当前已揭示进度之后」的第一条完整换行,仅推进这一行\n\n var nl = source.indexOf('\\n', curLen);\n\n if (nl !== -1) {\n nextLen = nl + 1;\n } else if (streamDone) {\n // 流结束且没有更多换行:最后一段未以换行结尾的内容一次性露出(此时即完整一行)\n nextLen = source.length;\n }\n\n if (nextLen > curLen) {\n msg._prevReveal = nextLen;\n msg.content = source.substring(0, nextLen);\n\n _this19.$nextTick(function () {\n return _this19.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this19.finishStream(); // 后端已在此刻落库,回读一次补全消息 id(单条删除需要)\n\n\n _this19.syncMessageIds();\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this19.activeSessionId, text);\n _this19.eventSource = new EventSource(url);\n\n _this19.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this19.messages[msgIdx]) {\n if (!_this19.messages[msgIdx].toolCalls) _this19.messages[msgIdx].toolCalls = [];\n\n _this19.messages[msgIdx].toolCalls.push(e.data);\n\n _this19.$nextTick(function () {\n return _this19.scrollToBottom();\n });\n }\n });\n\n _this19.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this19.eventSource.addEventListener('agent_result', function (e) {\n if (!isCurrentStream() || streamDone) return; // 与档案君(AiChat.vue)对齐:AGENT_RESULT 为最终真相源。\n // 只记录 finalContent 并结束流,不打断已在逐行推进的 content,交给 typeTimer 以 target 推进/校正。\n\n if (e.data) finalContent = e.data;\n streamDone = true;\n\n _this19.closeEventSource();\n });\n\n _this19.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this19.messages[msgIdx]) {\n _this19.messages[msgIdx].msgKey = e.data;\n }\n });\n\n _this19.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this19.$nextTick(function () {\n return _this19.scrollToBottom();\n });\n });\n\n _this19.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this19.$nextTick(function () {\n return _this19.scrollToBottom();\n });\n });\n\n _this19.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this19.closeEventSource();\n });\n\n case 29:\n case \"end\":\n return _context13.stop();\n }\n }\n }, _callee13);\n }))();\n },\n handleStop: function handleStop() {\n this.streamRequestId++;\n\n if (this.activeSessionId) {\n (0, _kbChat.stopKbStream)(this.activeSessionId).catch(function () {});\n }\n\n this.finishStream();\n },\n finishStream: function finishStream() {\n var _this20 = this;\n\n this.clearTypeTimer();\n\n if (this.streamMsg) {\n this.$delete(this.contentCache, this.streamMsg._key);\n this.streamMsg.loading = false;\n this.streamMsg = null;\n }\n\n this.closeEventSource();\n this.streaming = false;\n this.refreshSessionList();\n this.$nextTick(function () {\n return _this20.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this21 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee14() {\n var list;\n return regeneratorRuntime.wrap(function _callee14$(_context14) {\n while (1) {\n switch (_context14.prev = _context14.next) {\n case 0:\n if (_this21.companyId) {\n _context14.next = 2;\n break;\n }\n\n return _context14.abrupt(\"return\");\n\n case 2:\n _context14.prev = 2;\n _context14.next = 5;\n return (0, _kbChat.getKbSessionList)(_this21.companyId);\n\n case 5:\n _context14.t0 = _context14.sent;\n\n if (_context14.t0) {\n _context14.next = 8;\n break;\n }\n\n _context14.t0 = [];\n\n case 8:\n list = _context14.t0;\n _this21.sessionList = list;\n _context14.next = 15;\n break;\n\n case 12:\n _context14.prev = 12;\n _context14.t1 = _context14[\"catch\"](2);\n console.error('刷新会话列表失败', _context14.t1);\n\n case 15:\n case \"end\":\n return _context14.stop();\n }\n }\n }, _callee14, null, [[2, 12]]);\n }))();\n },\n clearTypeTimer: function clearTypeTimer() {\n if (this._typeTimer) {\n clearInterval(this._typeTimer);\n this._typeTimer = null;\n }\n },\n closeEventSource: function closeEventSource() {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = null;\n }\n },\n copyText: function copyText(text) {\n if (navigator.clipboard) {\n navigator.clipboard.writeText(text);\n }\n },\n scrollToBottom: function scrollToBottom() {\n var el = this.$refs.messagesRef;\n\n if (el) {\n el.scrollTop = el.scrollHeight;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KbChatMain.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1015
|
+
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _logo = _interopRequireDefault(__webpack_require__(/*! @/assets/logo.png */ \"./src/assets/logo.png\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.mjs\"));\n\nvar _kbChat = __webpack_require__(/*! ./api/kbChat */ \"./src/lib/knowledgeBase/api/kbChat.js\");\n\nvar _knowledgeBase = __webpack_require__(/*! ./api/knowledgeBase */ \"./src/lib/knowledgeBase/api/knowledgeBase.js\");\n\nvar _native = __webpack_require__(/*! ./native.js */ \"./src/lib/knowledgeBase/native.js\");\n\nvar _request = __webpack_require__(/*! @lib/utils/request.js */ \"./src/lib/utils/request.js\");\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar AI_AVATAR_URL = 'https://qiguanbang.obs.cn-east-3.myhuaweicloud.com/production/image/AlAgent/doc_query.png'; // 与 ChatDetail 使用相同的 Markdown 配置\n\nvar md = new _markdownIt.default({\n breaks: true,\n html: false,\n linkify: true,\n typographer: true\n});\nmd.enable('table');\nvar _default = {\n name: 'KbChatMain',\n props: {\n companyId: {\n type: String,\n default: ''\n },\n userno: {\n type: String,\n default: ''\n },\n imAccount: {\n type: [String, Object],\n default: ''\n },\n userName: {\n type: String,\n default: ''\n },\n // 是否移动端:由父级(KnowledgeBaseMain)传入;未传时组件自身按窗口宽度判断\n isMobile: {\n type: Boolean,\n default: null\n }\n },\n data: function data() {\n // 首帧前计算是否移动端,避免 H5 先显示 PC 布局再切换的闪烁(与 AI 助理一致)\n // 宿主没有 Element UI 时(H5/平板横屏)一律走自绘 UI 分支,避免 el-* 未知元素报错\n var _isMobile = typeof window !== 'undefined' && (window.innerWidth <= 768 || !(0, _native.hasElementUI)(this));\n\n return {\n initDone: false,\n activeSessionId: '',\n sessionList: [],\n // 默认折叠侧边栏:避免新用户进入时先展开再合住的闪烁,\n // 加载出会话后再按规则决定是否展开\n sidebarCollapsed: true,\n messages: [],\n inputText: '',\n streaming: false,\n streamMsg: null,\n showToolInfo: false,\n contentCache: {},\n eventSource: null,\n streamRequestId: 0,\n suggestedQuestions: [],\n // 消息多选模式(豆包式):PC 由消息操作区「删除/多选」进入,移动端长按气泡弹出菜单进入\n selectMode: false,\n // 已选中的消息 id 列表(后端主键)\n selectedMsgIds: [],\n // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: null,\n // 移动端长按气泡弹出的消息操作单(复制 / 删除 / 多选)\n msgAction: null,\n // 移动端侧滑手势(参考 AI 助理):记录起手坐标与是否已触发\n _swipeStartX: 0,\n _swipeStartY: 0,\n _swipeHandled: false,\n _swipeFromLeftEdge: false,\n _swipeFromRightEdge: false,\n _swipeFromEdge: false\n };\n },\n computed: {\n /** 实际是否移动端:优先用父级 prop,未传则用自身窗口判断 */\n isMobileActive: function isMobileActive() {\n return this.isMobile !== null ? this.isMobile : this.localIsMobile;\n },\n aiAvatar: function aiAvatar() {\n return AI_AVATAR_URL;\n },\n userAvatar: function userAvatar() {\n var account = this.imAccount;\n console.log('KbChatMain - imAccount 原始值:', account); // 尝试解析字符串为对象\n\n if (typeof account === 'string') {\n try {\n account = JSON.parse(account);\n console.log('KbChatMain - imAccount 解析后:', account);\n } catch (e) {\n // 如果解析失败,检查是否是纯 URL 字符串\n if (account.trim() && (account.includes('http://') || account.includes('https://'))) {\n console.log('KbChatMain - imAccount 是 URL:', account.trim());\n return account.trim();\n }\n }\n } // 如果是对象,尝试多个可能的头像字段(优先使用 headImg)\n\n\n if (account && (0, _typeof2.default)(account) === 'object') {\n var avatarFields = ['headImg', 'headPortrait', 'avatar', 'avatarUrl', 'headImage', 'photoUrl', 'portrait', 'headUrl', 'userHeadUrl', 'userAvatar'];\n\n for (var _i = 0, _avatarFields = avatarFields; _i < _avatarFields.length; _i++) {\n var field = _avatarFields[_i];\n\n if (account[field] && String(account[field]).trim()) {\n console.log(\"KbChatMain - \\u627E\\u5230\\u5934\\u50CF\\u5B57\\u6BB5 \".concat(field, \":\"), account[field]);\n return String(account[field]).trim();\n }\n }\n }\n\n console.log('KbChatMain - 使用默认头像');\n return _logo.default;\n },\n // 是否有真实头像(区别于默认 fallback 图片)\n hasUserAvatar: function hasUserAvatar() {\n return this.userAvatar && this.userAvatar !== _logo.default;\n },\n // 无头像时的文字头像(与 zjkjAvatar 一致:取名字最后两个字)\n userAvatarText: function userAvatarText() {\n var name = this.userName || '';\n return name ? name.substring(name.length - 2) : '';\n },\n // 可删除的消息 id(仅后端已落库、带回 id 的消息可删)\n deletableMsgIds: function deletableMsgIds() {\n return this.messages.filter(function (m) {\n return m.id;\n }).map(function (m) {\n return m.id;\n });\n },\n // 是否已全选\n isAllSelected: function isAllSelected() {\n return this.deletableMsgIds.length > 0 && this.selectedMsgIds.length === this.deletableMsgIds.length;\n }\n },\n watch: {\n companyId: function companyId() {\n this.initDone = false;\n this.finishStream();\n this.loadSuggestedQuestions();\n this.checkKbDocuments();\n this.initSession();\n },\n userno: function userno() {\n this.initDone = false;\n this.finishStream();\n this.activeSessionId = '';\n this.messages = [];\n this.initSession();\n },\n // 移动端进入时默认先显示会话列表页(仿 AI 助理)\n isMobileActive: function isMobileActive(val) {\n if (val) {\n // 无资料时遮罩只在详情页可见(列表页 .kb-chat-main 被隐藏),故保持停在详情页\n this.showH5List = !this.noDocument;\n this.mobileAction = null;\n }\n }\n },\n mounted: function mounted() {\n var _this = this;\n\n // 独立使用(父级未传 isMobile)时自检窗口宽度\n this._onResize = this.handleResize;\n window.addEventListener('resize', this._onResize, {\n passive: true\n }); // Android WebView 兼容:在 document 级别监听 touchmove/touchend,\n // 避免可滚动子元素消费事件后不冒泡到父级(参考 AI 助理)\n\n this._swipeMoveHandler = this._onSwipeMove;\n this._swipeEndHandler = this._onSwipeEnd;\n this._popStateHandler = this._onPopState;\n document.addEventListener('touchmove', this._swipeMoveHandler, {\n passive: true\n });\n document.addEventListener('touchend', this._swipeEndHandler, {\n passive: true\n }); // 拦截 Android 系统返回手势:通过 history.pushState + popstate\n\n window.addEventListener('popstate', this._popStateHandler); // 移动端首屏为会话列表页,推入一条历史状态供系统返回拦截\n\n if (this.isMobileActive) {\n history.pushState({\n page: 'kbChatList'\n }, '');\n }\n\n this.initSession();\n this.checkKbDocuments(); // v-show 隐藏期间加载的历史消息无法滚动(scrollHeight 为 0),\n // 监听组件由隐藏变为可见时,滚动到最新问答;\n // 同时每次变为可见时刷新\"猜您想问\"(管理端可能刚更新过)\n\n if (typeof IntersectionObserver !== 'undefined' && this.$el) {\n this._visibilityObserver = new IntersectionObserver(function (entries) {\n if (entries.some(function (e) {\n return e.isIntersecting;\n })) {\n _this.loadSuggestedQuestions(); // 管理端可能刚上传/删除了文档,重新校验遮罩状态\n\n\n _this.checkKbDocuments();\n\n if (_this.messages.length) {\n _this.$nextTick(function () {\n return _this.scrollToBottom();\n });\n }\n }\n });\n\n this._visibilityObserver.observe(this.$el);\n } else {\n this.loadSuggestedQuestions();\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (this._visibilityObserver) {\n this._visibilityObserver.disconnect();\n\n this._visibilityObserver = null;\n }\n\n if (this._onResize) {\n window.removeEventListener('resize', this._onResize);\n }\n\n if (this._swipeMoveHandler) {\n document.removeEventListener('touchmove', this._swipeMoveHandler);\n }\n\n if (this._swipeEndHandler) {\n document.removeEventListener('touchend', this._swipeEndHandler);\n }\n\n if (this._popStateHandler) {\n window.removeEventListener('popstate', this._popStateHandler);\n }\n\n this.streamRequestId++;\n this.closeEventSource();\n this.clearTypeTimer();\n this.onMsgTouchEnd();\n this.contentCache = {};\n },\n methods: {\n // 独立使用时自检移动端(父级已传 isMobile 时忽略)\n handleResize: function handleResize() {\n var now = typeof window !== 'undefined' && (window.innerWidth <= 768 || !(0, _native.hasElementUI)(this));\n this.localIsMobile = now;\n },\n\n /**\n * 用户消息按纯文本渲染(转义 HTML + 保留换行)。\n * 不能用 markdown 渲染用户输入:行首的「33.」「1.」会被当作有序列表标记、\n * 「- 」「* 」当无序列表标记、「---」当分隔线,标记本身不显示,\n * 导致仅输入「33.」时气泡内一片空白(内容看起来\"消失\")。\n * 用户原文需原样可见,故不做任何 markdown 语义解析。\n */\n renderUserContent: function renderUserContent(text) {\n if (!text) return '';\n return this.escapeHtml(text).replace(/\\n/g, '<br>');\n },\n renderMarkdown: function renderMarkdown(text) {\n if (!text) return '';\n\n try {\n return md.render(text);\n } catch (err) {\n return this.escapeHtml(text);\n }\n },\n // 与 ChatDetail 一致的增量渲染:完整行走 markdown、未完成尾行转义拼接,\n // 借助 markdown-it breaks:true 实现逐行浮现效果。\n renderStreaming: function renderStreaming(text, msgKey) {\n if (!text) return '';\n var cache = this.contentCache[msgKey];\n var lines = text.split('\\n');\n var lastLineIdx = lines.length - 1;\n\n if (!cache) {\n try {\n var html = md.render(text);\n this.$set(this.contentCache, msgKey, {\n renderedLines: lastLineIdx,\n html: html,\n fullText: text\n });\n return html;\n } catch (_unused) {\n return this.escapeHtml(text);\n }\n }\n\n if (text === cache.fullText) return cache.html;\n cache.fullText = text; // 行数没变,仅末尾追加字符,直接拼接尾行\n\n if (lastLineIdx === cache.renderedLines) {\n return cache.html + this.escapeHtml(lines[lastLineIdx]);\n } // 行数变少/文本回退截断,全量重渲染\n\n\n if (lastLineIdx < cache.renderedLines) {\n try {\n var fullLines = lines.slice(0, lastLineIdx);\n var newBaseHtml = '';\n\n if (fullLines.length > 0) {\n newBaseHtml = md.render(fullLines.join('\\n'));\n }\n\n cache.html = newBaseHtml;\n cache.renderedLines = lastLineIdx;\n\n var _lastLine = this.escapeHtml(lines[lastLineIdx]);\n\n return cache.html + (_lastLine || '');\n } catch (_unused2) {\n return this.escapeHtml(text);\n }\n } // 新增完整行\n\n\n if (cache.renderedLines < lastLineIdx) {\n var allFullLines = lines.slice(0, lastLineIdx);\n var _newBaseHtml = '';\n\n if (allFullLines.length > 0) {\n _newBaseHtml = md.render(allFullLines.join('\\n'));\n }\n\n cache.html = _newBaseHtml;\n cache.renderedLines = lastLineIdx;\n }\n\n var lastLine = this.escapeHtml(lines[lastLineIdx]);\n cache.renderedLines = lastLineIdx;\n return cache.html + (lastLine || '');\n },\n escapeHtml: function escapeHtml(text) {\n if (!text) return '';\n var map = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n };\n return String(text).replace(/[&<>\"']/g, function (c) {\n return map[c];\n });\n },\n autoResizeTextarea: function autoResizeTextarea() {\n var ta = this.$refs.taRef;\n if (!ta) return; // 移动端从单行起增高(与 38px 最小高度一致),PC 保持 100px 基准\n\n var baseH = this.isMobileActive ? 38 : 100;\n var maxH = this.isMobileActive ? 120 : 180;\n ta.style.height = \"\".concat(baseH, \"px\");\n var realH = ta.scrollHeight;\n\n if (realH > baseH) {\n ta.style.height = \"\".concat(Math.min(realH, maxH), \"px\");\n }\n },\n newLine: function newLine(e) {\n var _this2 = this;\n\n e.preventDefault();\n var ta = this.$refs.taRef;\n var start = ta.selectionStart;\n var end = ta.selectionEnd;\n this.inputText = this.inputText.slice(0, start) + '\\n' + this.inputText.slice(end);\n this.$nextTick(function () {\n ta.selectionStart = ta.selectionEnd = start + 1;\n\n _this2.autoResizeTextarea();\n });\n },\n\n /**\n * 加载\"猜您想问\"问题(按公司,所有用户可见)\n */\n loadSuggestedQuestions: function loadSuggestedQuestions() {\n var _this3 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var list;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n if (_this3.companyId) {\n _context.next = 3;\n break;\n }\n\n _this3.suggestedQuestions = [];\n return _context.abrupt(\"return\");\n\n case 3:\n _context.prev = 3;\n _context.next = 6;\n return (0, _knowledgeBase.getSuggestedQuestions)(_this3.companyId);\n\n case 6:\n _context.t0 = _context.sent;\n\n if (_context.t0) {\n _context.next = 9;\n break;\n }\n\n _context.t0 = [];\n\n case 9:\n list = _context.t0;\n _this3.suggestedQuestions = list.map(function (q) {\n return q.questionText;\n });\n _context.next = 16;\n break;\n\n case 13:\n _context.prev = 13;\n _context.t1 = _context[\"catch\"](3);\n console.error('加载猜您想问失败', _context.t1);\n\n case 16:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee, null, [[3, 13]]);\n }))();\n },\n\n /**\n * 检查当前公司是否有「已索引」的知识库文档,无可用资料时展示遮罩提示\n * 仅已索引(indexStatus === 1)的文档才能被检索,未索引/索引中/索引失败都不算\n */\n checkKbDocuments: function checkKbDocuments() {\n var _this4 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var data, isEmpty;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n if (_this4.companyId) {\n _context2.next = 3;\n break;\n }\n\n _this4.noDocument = false;\n return _context2.abrupt(\"return\");\n\n case 3:\n _context2.prev = 3;\n _context2.next = 6;\n return (0, _knowledgeBase.getFileList)({\n companyId: _this4.companyId,\n indexStatus: 1,\n pageNum: 1,\n pageSize: 1\n });\n\n case 6:\n data = _context2.sent;\n isEmpty = (data && data.total || 0) === 0;\n _this4.noDocument = isEmpty; // 无资料时把会话固定回第一个\n\n if (isEmpty) {\n _this4.lockFirstSession(); // 无资料遮罩位于聊天详情页的 .kb-chat-main 内,而移动端首屏是会话列表页\n // (h5-list-mode 下 .kb-chat-main 被 display:none 隐藏),若停留在列表页\n // 则遮罩永远不可见;故无资料时强制切到详情页把遮罩展示出来\n\n\n if (_this4.isMobileActive) {\n _this4.showH5List = false;\n history.pushState({\n page: 'kbChatDetail'\n }, '');\n }\n }\n\n _context2.next = 16;\n break;\n\n case 12:\n _context2.prev = 12;\n _context2.t0 = _context2[\"catch\"](3);\n // 查询失败(如无管理权限)时不展示遮罩,避免误拦截\n console.error('检查知识库文档失败', _context2.t0);\n _this4.noDocument = false;\n\n case 16:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2, null, [[3, 12]]);\n }))();\n },\n initSession: function initSession() {\n var _this5 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var list;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (_this5.companyId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\");\n\n case 2:\n if (!(_this5.initDone && (_this5.messages.length || _this5.streaming || _this5.inputText))) {\n _context3.next = 4;\n break;\n }\n\n return _context3.abrupt(\"return\");\n\n case 4:\n _this5.initDone = false;\n _context3.prev = 5;\n _context3.next = 8;\n return (0, _kbChat.getKbSessionList)(_this5.companyId);\n\n case 8:\n _context3.t0 = _context3.sent;\n\n if (_context3.t0) {\n _context3.next = 11;\n break;\n }\n\n _context3.t0 = [];\n\n case 11:\n list = _context3.t0;\n _this5.sessionList = list;\n\n if (!(list.length > 0)) {\n _context3.next = 19;\n break;\n }\n\n // 只有 1 个会话且无消息(新用户首次进入自动生成的空会话)时,\n // 默认折叠侧边栏只显示聊天主界面;在 selectSession 前同步设置,\n // 避免「先展开再合住」的闪烁\n _this5.sidebarCollapsed = list.length === 1 && !list[0].lastContent;\n _context3.next = 17;\n return _this5.selectSession(list[0].sessionId);\n\n case 17:\n _context3.next = 21;\n break;\n\n case 19:\n _context3.next = 21;\n return _this5.autoCreateSession();\n\n case 21:\n _context3.next = 28;\n break;\n\n case 23:\n _context3.prev = 23;\n _context3.t1 = _context3[\"catch\"](5);\n console.error('获取会话列表失败,尝试自动创建', _context3.t1);\n _context3.next = 28;\n return _this5.autoCreateSession();\n\n case 28:\n _context3.prev = 28;\n _this5.initDone = true;\n return _context3.finish(28);\n\n case 31:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3, null, [[5, 23, 28, 31]]);\n }))();\n },\n autoCreateSession: function autoCreateSession() {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var data;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this6.companyId) {\n _context4.next = 3;\n break;\n }\n\n console.warn('[KbChat] 无法创建会话:companyId 为空');\n return _context4.abrupt(\"return\");\n\n case 3:\n console.log('[KbChat] 创建会话,companyId:', _this6.companyId);\n _context4.prev = 4;\n _context4.next = 7;\n return (0, _kbChat.createKbSession)(_this6.companyId);\n\n case 7:\n data = _context4.sent;\n console.log('[KbChat] 会话创建成功,sessionId:', data.sessionId);\n\n _this6.finishStream();\n\n _this6.activeSessionId = data.sessionId;\n _this6.streamRequestId++;\n _this6.messages = [];\n _this6.sessionList = [{\n sessionId: data.sessionId,\n title: '',\n lastContent: ''\n }].concat(_this6.sessionList || []); // 新建会话时保持侧边栏展开,方便继续切换会话\n\n _this6.sidebarCollapsed = false;\n\n _this6.$nextTick(function () {\n return _this6.scrollToBottom();\n });\n\n _context4.next = 21;\n break;\n\n case 18:\n _context4.prev = 18;\n _context4.t0 = _context4[\"catch\"](4);\n console.error('自动创建会话失败', _context4.t0);\n\n case 21:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[4, 18]]);\n }))();\n },\n\n /** 新建会话(豆包式「新对话」) */\n handleNewSession: function handleNewSession() {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() {\n return regeneratorRuntime.wrap(function _callee5$(_context5) {\n while (1) {\n switch (_context5.prev = _context5.next) {\n case 0:\n if (!_this7.noDocument) {\n _context5.next = 2;\n break;\n }\n\n return _context5.abrupt(\"return\");\n\n case 2:\n if (_this7.streaming) _this7.handleStop();\n _context5.next = 5;\n return _this7.autoCreateSession();\n\n case 5:\n case \"end\":\n return _context5.stop();\n }\n }\n }, _callee5);\n }))();\n },\n\n /** 切换会话 */\n handleSelectSession: function handleSelectSession(item) {\n var _this8 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() {\n return regeneratorRuntime.wrap(function _callee6$(_context6) {\n while (1) {\n switch (_context6.prev = _context6.next) {\n case 0:\n if (!(!item || !item.sessionId)) {\n _context6.next = 2;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 2:\n if (!_this8.noDocument) {\n _context6.next = 5;\n break;\n }\n\n _this8.lockFirstSession();\n\n return _context6.abrupt(\"return\");\n\n case 5:\n if (!(item.sessionId === _this8.activeSessionId && _this8.messages.length)) {\n _context6.next = 8;\n break;\n }\n\n if (_this8.isMobileActive) {\n _this8.showH5List = false;\n history.pushState({\n page: 'kbChatDetail'\n }, '');\n\n _this8.$nextTick(function () {\n return _this8.scrollToBottom();\n });\n }\n\n return _context6.abrupt(\"return\");\n\n case 8:\n _context6.next = 10;\n return _this8.selectSession(item.sessionId);\n\n case 10:\n // 移动端:选中后跳到聊天详情页\n if (_this8.isMobileActive) {\n _this8.showH5List = false; // 推入历史状态,使 Android 系统返回手势先触发 popstate 被拦截\n\n history.pushState({\n page: 'kbChatDetail'\n }, ''); // 列表页时聊天区被 display:none,scrollHeight 为 0 导致上面的滚动无效;\n // 切到详情页、容器可见后需再滚一次,默认定位到最新消息\n\n _this8.$nextTick(function () {\n return _this8.scrollToBottom();\n });\n }\n\n case 11:\n case \"end\":\n return _context6.stop();\n }\n }\n }, _callee6);\n }))();\n },\n // 企管版本菜单排序联调开发完成提测\n // \n\n /** 返回宿主 App 原生页面(参考 AI 助理 handleNativeBack) */\n handleNativeBack: function handleNativeBack() {\n if (this.$hybrid && typeof this.$hybrid.closePage === 'function') {\n this.$hybrid.closePage();\n } else if (window.history.length > 1) {\n window.history.back();\n }\n },\n\n /** 会话列表页返回按钮:返回原生页面 */\n handleSidebarBack: function handleSidebarBack() {\n this.handleNativeBack();\n },\n // ── H5 侧滑手势(参考 AI 助理)──\n // 从左/右屏幕边缘起手横滑触发返回,避免页面内部横向滚动被误判\n onSwipeTouchStart: function onSwipeTouchStart(e) {\n if (!this.isMobileActive) return;\n var t = e.touches[0];\n this._swipeStartX = t.clientX;\n this._swipeStartY = t.clientY;\n this._swipeHandled = false;\n var edge = Math.min(56, Math.round(window.innerWidth * 0.09));\n this._swipeFromLeftEdge = t.clientX <= edge;\n this._swipeFromRightEdge = t.clientX >= window.innerWidth - edge;\n this._swipeFromEdge = this._swipeFromLeftEdge || this._swipeFromRightEdge;\n },\n _swipeShouldTrigger: function _swipeShouldTrigger(diffX, diffY) {\n if (!this._swipeFromEdge) return false; // 从左边缘 → 右滑;从右边缘 → 左滑\n\n var directionOk = this._swipeFromLeftEdge && diffX >= 60 || this._swipeFromRightEdge && diffX <= -60;\n if (!directionOk) return false; // 水平距离明显大于垂直距离(排除上下滚动干扰)\n\n return Math.abs(diffX) >= Math.abs(diffY) * 2;\n },\n _onSwipeMove: function _onSwipeMove(e) {\n if (!this.isMobileActive || this._swipeHandled) return;\n if (!e.touches || !e.touches.length) return;\n var diffX = e.touches[0].clientX - this._swipeStartX;\n var diffY = e.touches[0].clientY - this._swipeStartY;\n if (!this._swipeShouldTrigger(diffX, diffY)) return;\n this._swipeHandled = true;\n\n this._triggerSwipeAction();\n },\n // touchend 兜底,兼容不支持 touchmove 的场景\n _onSwipeEnd: function _onSwipeEnd(e) {\n if (!this.isMobileActive || this._swipeHandled) return;\n if (!e.changedTouches || !e.changedTouches.length) return;\n var diffX = e.changedTouches[0].clientX - this._swipeStartX;\n var diffY = e.changedTouches[0].clientY - this._swipeStartY;\n if (!this._swipeShouldTrigger(diffX, diffY)) return;\n this._swipeHandled = true;\n\n this._triggerSwipeAction();\n },\n\n /** 侧滑触发:列表页 → 返回原生页面;详情页 → 回会话列表页 */\n _triggerSwipeAction: function _triggerSwipeAction() {\n if (this.showH5List) {\n this.handleNativeBack();\n } else {\n this.handleMobileBack();\n }\n },\n\n /**\n * 拦截 Android 系统返回手势(history.pushState + popstate)\n * WebView 先回退历史栈触发 popstate,这里拦截后导航到列表页,避免 Activity 被直接关闭\n */\n _onPopState: function _onPopState() {\n if (!this.isMobileActive) return;\n\n if (!this.showH5List) {\n // 详情页 → 返回会话列表页\n this.showH5List = true;\n history.pushState({\n page: 'kbChatList'\n }, '');\n } else {\n // 列表页 → 直接关闭 WebView 返回原生页面\n this.handleNativeBack();\n }\n },\n\n /** 移动端详情页返回:无资料遮罩时返回原生页面;否则回到会话列表页 */\n handleMobileBack: function handleMobileBack() {\n // 无资料遮罩期间无法进入会话,返回到宿主 App 原生页面\n if (this.noDocument) {\n this.handleNativeBack();\n return;\n } // 正常情况:回到会话列表页\n\n\n this.showH5List = true;\n },\n\n /** 无资料遮罩期间:把当前会话固定回列表中的第一个 */\n lockFirstSession: function lockFirstSession() {\n var first = this.sessionList[0];\n\n if (first && first.sessionId && first.sessionId !== this.activeSessionId) {\n this.selectSession(first.sessionId);\n }\n },\n\n /** 会话项「…」菜单:重命名 / 删除 */\n handleSessionMenu: function handleSessionMenu(cmd, item) {\n if (!item || !item.sessionId) return;\n\n if (cmd === 'rename') {\n this.handleRenameSession(item);\n } else if (cmd === 'delete') {\n this.handleDeleteSession(item);\n }\n },\n // ==================== 移动端会话操作单 ====================\n openMobileAction: function openMobileAction(item) {\n if (this.noDocument) return;\n this.mobileAction = item;\n },\n doMobileRename: function doMobileRename(item) {\n this.mobileAction = null;\n this.handleRenameSession(item);\n },\n doMobileDelete: function doMobileDelete(item) {\n this.mobileAction = null;\n this.handleDeleteSession(item);\n },\n\n /** 重命名会话 */\n handleRenameSession: function handleRenameSession(item) {\n var _this9 = this;\n\n if (!item || !item.sessionId) return;\n this.$prompt('请输入新的会话名称', '重命名会话', {\n confirmButtonText: '确定',\n cancelButtonText: '取消',\n inputValue: item.title || '',\n inputPattern: /\\S+/,\n inputErrorMessage: '名称不能为空'\n }).then(function (_ref) {\n var value = _ref.value;\n var title = (value || '').trim();\n if (!title) return;\n return (0, _kbChat.renameKbSession)(item.sessionId, title).then(function () {\n item.title = title;\n\n _this9.$message.success('重命名成功');\n });\n }).catch(function (err) {\n // 取消或校验失败,不提示错误\n if (err && err !== 'cancel' && err !== 'close') {\n console.error('重命名失败', err);\n\n _this9.$message.error('重命名失败');\n }\n });\n },\n\n /** 删除会话 */\n handleDeleteSession: function handleDeleteSession(item) {\n var _this10 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee7() {\n return regeneratorRuntime.wrap(function _callee7$(_context7) {\n while (1) {\n switch (_context7.prev = _context7.next) {\n case 0:\n if (!(!item || !item.sessionId)) {\n _context7.next = 2;\n break;\n }\n\n return _context7.abrupt(\"return\");\n\n case 2:\n if (!(_this10.sessionList.length <= 1)) {\n _context7.next = 5;\n break;\n }\n\n _this10.$message.warning('至少保留一个会话');\n\n return _context7.abrupt(\"return\");\n\n case 5:\n _context7.prev = 5;\n _context7.next = 8;\n return _this10.$confirm('删除后无法恢复,确定删除该会话吗?', '删除会话', {\n confirmButtonText: '确定',\n cancelButtonText: '取消',\n type: 'warning'\n });\n\n case 8:\n _context7.next = 13;\n break;\n\n case 10:\n _context7.prev = 10;\n _context7.t0 = _context7[\"catch\"](5);\n return _context7.abrupt(\"return\");\n\n case 13:\n _context7.prev = 13;\n _context7.next = 16;\n return (0, _kbChat.deleteKbSession)(item.sessionId);\n\n case 16:\n _this10.sessionList = (_this10.sessionList || []).filter(function (s) {\n return s.sessionId !== item.sessionId;\n });\n\n if (!(_this10.activeSessionId === item.sessionId)) {\n _context7.next = 28;\n break;\n }\n\n _this10.finishStream();\n\n _this10.activeSessionId = '';\n _this10.messages = [];\n\n if (!(_this10.sessionList.length > 0)) {\n _context7.next = 26;\n break;\n }\n\n _context7.next = 24;\n return _this10.selectSession(_this10.sessionList[0].sessionId);\n\n case 24:\n _context7.next = 28;\n break;\n\n case 26:\n _context7.next = 28;\n return _this10.autoCreateSession();\n\n case 28:\n _this10.$message.success('删除成功');\n\n _context7.next = 35;\n break;\n\n case 31:\n _context7.prev = 31;\n _context7.t1 = _context7[\"catch\"](13);\n console.error('删除会话失败', _context7.t1);\n\n _this10.$message.error('删除失败');\n\n case 35:\n case \"end\":\n return _context7.stop();\n }\n }\n }, _callee7, null, [[5, 10], [13, 31]]);\n }))();\n },\n\n /** 会话列表时间格式化:今天显示时分,更早显示月日 */\n formatTime: function formatTime(time) {\n if (!time) return '';\n var d = new Date(time);\n if (isNaN(d.getTime())) return '';\n var now = new Date();\n var sameDay = d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth() && d.getDate() === now.getDate();\n\n var pad = function pad(n) {\n return n < 10 ? '0' + n : '' + n;\n };\n\n if (sameDay) {\n return pad(d.getHours()) + ':' + pad(d.getMinutes());\n }\n\n return pad(d.getMonth() + 1) + '-' + pad(d.getDate());\n },\n\n /** 后端消息 → 前端消息对象(保留 id/msgKey,删除功能需要) */\n mapMessage: function mapMessage(m, i) {\n var thinking = m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '';\n return {\n id: m.id,\n msgKey: m.msgKey || m.msg_key || '',\n role: m.role,\n content: m.content || '',\n thinking: thinking,\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n },\n selectSession: function selectSession(sessionId) {\n var _this11 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() {\n var raw, msgs;\n return regeneratorRuntime.wrap(function _callee8$(_context8) {\n while (1) {\n switch (_context8.prev = _context8.next) {\n case 0:\n if (_this11.streaming) _this11.handleStop();\n\n _this11.exitSelectMode();\n\n _this11.msgAction = null;\n _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 6;\n _context8.next = 9;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 9:\n raw = _context8.sent;\n msgs = Array.isArray(raw) ? raw : raw.messages || raw.records || raw.list || [];\n console.log('[KbChat] selectSession 回读', {\n sessionId: sessionId,\n rawIsArray: Array.isArray(raw),\n rawKeys: raw && !Array.isArray(raw) ? Object.keys(raw) : null,\n count: msgs.length,\n list: msgs.map(function (m) {\n return {\n id: m.id,\n role: m.role,\n content: String(m.content || '').slice(0, 25)\n };\n })\n });\n _this11.messages = _this11.dedupeMessages(msgs).map(function (m, i) {\n return _this11.mapMessage(m, i);\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 19;\n break;\n\n case 16:\n _context8.prev = 16;\n _context8.t0 = _context8[\"catch\"](6);\n console.error('加载消息失败', _context8.t0);\n\n case 19:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[6, 16]]);\n }))();\n },\n\n /**\n * 后端回读消息去重。\n * 后端存在同一问答对被重复落库的情况(同一会话里出现两套相同的「问+答」),\n * 直接渲染会让用户看到重复问答。这里按「问答对」为单位去重:\n * 把消息切成组(用户问 + 其后的 AI 答为一组),整组内容完全相同的只保留第一次出现的。\n * 相比「只和上一条比」的相邻去重,能覆盖组与组之间被隔开、或 content 有空白差异的情况。\n */\n dedupeMessages: function dedupeMessages(msgs) {\n var _this12 = this;\n\n // 先切组:以 user 消息为一个新组的起点,其后连续的 assistant 归入同组\n var groups = [];\n msgs.forEach(function (m) {\n var role = _this12.normRole(m.role);\n\n if (role === 'user' || !groups.length) {\n groups.push([m]);\n } else {\n groups[groups.length - 1].push(m);\n }\n });\n var seen = {};\n var out = [];\n groups.forEach(function (group) {\n var signature = group.map(function (m) {\n return _this12.normRole(m.role) + \"\\x01\" + String(m.content || '').replace(/\\s+/g, '');\n }).join(\"\\x02\");\n if (signature && seen[signature]) return;\n if (signature) seen[signature] = true;\n out.push.apply(out, group);\n });\n return out;\n },\n\n /**\n * 内容比对用的归一化:压掉所有空白(含换行)差异。\n * 后端落库时常会把换行/多空格规范化(或存成 CRLF),只 trim 首尾的严格相等会漏匹配,\n * 漏匹配 = 消息拿不到 id = 「删除」按钮偶现不出现。\n */\n normContent: function normContent(text) {\n return String(text == null ? '' : text).replace(/\\s+/g, ' ').trim();\n },\n\n /**\n * 回读当前会话消息,补全后端持久化后生成的 id。\n * 流式回答期间消息只存在于本地数组(无 id),无法按 id 删除;\n * 流式结束后调用一次即可拿到 id。已全部有 id 时跳过,避免多余请求。\n *\n * 注意:不能直接用后端数组整体覆盖 this.messages。后端回读可能包含重复/多余记录\n * (如同一会话里用户消息被落库两次),若整体覆盖会把重复消息直接渲染出来。\n * 这里以本地数组为基准,只用后端记录按「角色 + 内容」顺序回填 id,保证界面条数不变。\n *\n * 「删除」按钮偶现丢失(id 补不全)的易失点,本函数逐一兜住:\n * 1) **AI 回答条的内容与后端存储对不上**:本地内容是流式拼接/agent_result 的结果,\n * 后端会做规范化甚至改写,靠「内容相等」匹配回答条天然不可靠 →\n * 改为优先用 SSE 下发的 `msgKey`(与后端记录一一对应)做精确匹配,\n * 这是「回答出来了却没有删除按钮」的主因(提问条没有 msgKey,仍走内容匹配);\n * 2) 后端落库延迟:有限次重试;重试期间**不能**因为开了新流(streaming)就直接 return,\n * 否则重试预算被静默吞掉,消息永远拿不到 id;\n * 3) 历史消息已带 id 时 cursor 必须同步前进,且同一条后端记录不能重复分配给多条本地消息:\n * 否则会拿到**别的消息的 id**(删错消息)或彻底匹配不上。\n */\n syncMessageIds: function syncMessageIds() {\n var _arguments = arguments,\n _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n var retries, sessionId, backendMsgs, scheduleRetry, raw, usedIds, isUsed, assign, cursor;\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n retries = _arguments.length > 0 && _arguments[0] !== undefined ? _arguments[0] : 0;\n\n if (_this13.activeSessionId) {\n _context9.next = 3;\n break;\n }\n\n return _context9.abrupt(\"return\");\n\n case 3:\n if (!(_this13.messages.length && _this13.messages.every(function (m) {\n return m.id;\n }))) {\n _context9.next = 5;\n break;\n }\n\n return _context9.abrupt(\"return\");\n\n case 5:\n sessionId = _this13.activeSessionId;\n backendMsgs = []; // 重试排期:仅在仍属于同一会话且仍有消息缺 id 时继续,避免污染其它会话\n\n scheduleRetry = function scheduleRetry() {\n var missing = _this13.messages.filter(function (m) {\n return !m.id;\n });\n\n if (!missing.length || sessionId !== _this13.activeSessionId) return;\n\n if (retries > 0) {\n setTimeout(function () {\n return _this13.syncMessageIds(retries - 1);\n }, 800);\n return;\n } // 重试耗尽仍有缺 id(表现为该条没有「删除」按钮)→ 留一条诊断日志,便于对照\n // 本地内容与后端内容到底差在哪(内容差异 / 后端未返回 msgKey / 落库仍没完成)\n\n\n console.warn('[KbChat] 重试耗尽仍有消息缺 id(该条不显示删除按钮)', {\n sessionId: sessionId,\n missing: missing.map(function (m) {\n return {\n role: m.role,\n msgKey: m.msgKey || '',\n snippet: String(m.content || '').slice(0, 60)\n };\n }),\n backend: backendMsgs.map(function (b) {\n return {\n id: b.id,\n role: b.role,\n msgKey: b.msgKey || b.msg_key || '',\n snippet: String(b.content || '').slice(0, 60)\n };\n })\n });\n };\n\n _context9.prev = 8;\n _context9.next = 11;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 11:\n raw = _context9.sent;\n\n if (!(sessionId !== _this13.activeSessionId)) {\n _context9.next = 14;\n break;\n }\n\n return _context9.abrupt(\"return\");\n\n case 14:\n backendMsgs = Array.isArray(raw) ? raw : raw.messages || raw.records || raw.list || []; // 已占用的后端 id:同一条后端记录不能分配给多条本地消息\n\n usedIds = _this13.messages.filter(function (m) {\n return m.id;\n }).map(function (m) {\n return String(m.id);\n });\n\n isUsed = function isUsed(b) {\n return usedIds.indexOf(String(b.id)) !== -1;\n };\n\n assign = function assign(m, b) {\n // 本地消息对象在 handleSend 中创建时未声明 id/msgKey,\n // 直接赋值 Vue 2 无法侦测新增属性,模板不会重渲染(删除按钮仍不出现);\n // 必须用 $set 才能让「删除」等依赖 msg.id 的 UI 即时刷新。\n _this13.$set(m, 'id', b.id);\n\n if (!m.msgKey) _this13.$set(m, 'msgKey', b.msgKey || b.msg_key || '');\n usedIds.push(String(b.id));\n }; // Pass 1:msgKey 精确匹配(AI 回答条)。SSE 的 msgKey 与后端记录一一对应,\n // 不受\"后端改写/规范化回答内容\"影响,是回答条最可靠的配对方式。\n\n\n _this13.messages.forEach(function (m) {\n if (m.id || !m.msgKey) return;\n var b = backendMsgs.find(function (x) {\n return x && x.id != null && !isUsed(x) && String(x.msgKey || x.msg_key || '') === String(m.msgKey);\n });\n if (b) assign(m, b);\n }); // Pass 2:角色 + 归一化内容 + cursor 顺序匹配(提问条无 msgKey,走内容匹配)\n\n\n cursor = 0;\n\n _this13.messages.forEach(function (m) {\n if (m.id) {\n // 已补全的消息:按 id 定位并推进 cursor(不做则 cursor 停留在 0)\n var idx = backendMsgs.findIndex(function (b, i) {\n return i >= cursor && String(b.id) === String(m.id);\n });\n if (idx >= 0) cursor = idx + 1;\n return;\n }\n\n var role = _this13.normRole(m.role);\n\n var key = _this13.normContent(m.content);\n\n for (var i = cursor; i < backendMsgs.length; i++) {\n var b = backendMsgs[i];\n if (isUsed(b)) continue;\n\n if (_this13.normRole(b.role) === role && _this13.normContent(b.content) === key) {\n assign(m, b);\n cursor = i + 1;\n return;\n }\n }\n });\n\n _context9.next = 26;\n break;\n\n case 23:\n _context9.prev = 23;\n _context9.t0 = _context9[\"catch\"](8);\n console.error('同步消息 id 失败', _context9.t0);\n\n case 26:\n scheduleRetry();\n\n case 27:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[8, 23]]);\n }))();\n },\n // ==================== 消息多选 / 删除 ====================\n\n /** 进入多选模式(preselectMsg 传入时默认勾选该条所在的整组问答,对应豆包式「删除」入口) */\n enterSelectMode: function enterSelectMode(preselectMsg) {\n var _this14 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var preselectId, idx;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n if (!(_this14.streaming || !_this14.messages.length)) {\n _context10.next = 2;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 2:\n // 点击条的后端持久化 id(模板上「删除」按钮仅在 msg.id 存在时渲染,故必有值)\n preselectId = preselectMsg ? preselectMsg.id : null; // 先回读消息确保每条都有 id(刚流式完成的消息本地没有 id)。\n // 回读只补 id、不重建数组,定位可用 id 或对象引用。\n\n _context10.next = 5;\n return _this14.syncMessageIds();\n\n case 5:\n _this14.selectMode = true;\n _this14.selectedMsgIds = [];\n idx = preselectId != null ? _this14.messages.findIndex(function (m) {\n return m.id === preselectId;\n }) : -1;\n if (idx === -1 && preselectMsg) idx = _this14.messages.indexOf(preselectMsg);\n\n if (idx !== -1) {\n // 默认勾选整组问答(用户问 + AI 答),便于一键删除\n _this14.selectedMsgIds = _this14.getQaPairIdsAt(idx);\n } // 滚动定位:让默认勾选的整组问答都进入视野(长回答会把用户问顶出屏幕)\n\n\n _this14.scrollSelectedIntoView();\n\n case 11:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\n }))();\n },\n\n /**\n * 把首个被选中的消息行滚动到容器内可见。\n * 选中行是「用户问 + AI 答」中的靠前一条,滚到它即能同时看到整组。\n * 若靠前那条已在视野内则不动,避免无谓跳动。\n */\n scrollSelectedIntoView: function scrollSelectedIntoView() {\n var _this15 = this;\n\n this.$nextTick(function () {\n var container = _this15.$refs.messagesRef;\n if (!container) return;\n var rows = container.querySelectorAll('.msg-row');\n if (!rows.length) return; // 找到第一个处于选中态、且带 id 的行(与 selectedMsgIds 顺序无关,取 DOM 顺序)\n\n var target = null;\n\n for (var i = 0; i < rows.length; i++) {\n var box = rows[i].querySelector('.msg-check-box.checked');\n\n if (box) {\n target = rows[i];\n break;\n }\n }\n\n if (!target) return;\n var cRect = container.getBoundingClientRect();\n var tRect = target.getBoundingClientRect(); // 目标行顶部已在容器可视区内则无需滚动\n\n if (tRect.top >= cRect.top && tRect.top < cRect.bottom) return; // 滚动到目标行顶部(留 8px 间距)\n\n container.scrollTop += tRect.top - cRect.top - 8;\n });\n },\n exitSelectMode: function exitSelectMode() {\n this.selectMode = false;\n this.selectedMsgIds = [];\n },\n isMsgSelected: function isMsgSelected(msg) {\n return !!msg.id && this.selectedMsgIds.indexOf(msg.id) !== -1;\n },\n\n /** 归一化 role,兼容后端可能返回的大小写/空白 */\n normRole: function normRole(role) {\n return String(role == null ? '' : role).trim().toLowerCase();\n },\n\n /**\n * 取与下标 idx 处消息成对的另一条:用户问 ↔ AI 答视为一组问答。\n * 只认紧邻的一条(问答对在列表里必然相邻),相邻非同组则不配对。\n * 不依赖 role 的具体拼写(user/assistant 的大小写、空白均容错)。\n */\n getQaPartnerAt: function getQaPartnerAt(idx) {\n if (idx < 0 || idx >= this.messages.length) return null;\n var cur = this.messages[idx];\n if (!cur) return null;\n var role = this.normRole(cur.role);\n if (role !== 'user' && role !== 'assistant') return null; // 只认紧邻的那一条:问答对在列表里必然相邻。\n // 若相邻消息不是异角色(例如回答已被删除),说明当前消息已无配对,\n // 不能继续向前/向后扫描,否则会误勾到上一个问答的回答。\n\n var neighbor = role === 'user' ? this.messages[idx + 1] : this.messages[idx - 1];\n if (!neighbor) return null;\n var neighborRole = this.normRole(neighbor.role);\n var expect = role === 'user' ? 'assistant' : 'user';\n return neighborRole === expect ? neighbor : null;\n },\n\n /** 下标 idx 处消息所在问答对的可删 id 列表(用户问 + AI 答) */\n getQaPairIdsAt: function getQaPairIdsAt(idx) {\n var ids = [];\n var cur = this.messages[idx];\n if (cur && cur.id) ids.push(cur.id);\n var partner = this.getQaPartnerAt(idx);\n if (partner && partner.id && ids.indexOf(partner.id) === -1) ids.push(partner.id);\n return ids;\n },\n\n /**\n * 勾选单条消息:勾上时连带把问答对里另一条一起勾上;\n * 取消时只取消当前这条,从而支持「先整组勾选、再单独取消一条」的单条选择。\n */\n toggleSelectMessage: function toggleSelectMessage(msg) {\n var _this16 = this;\n\n if (!msg.id) return;\n var i = this.selectedMsgIds.indexOf(msg.id);\n\n if (i === -1) {\n var idx = this.messages.indexOf(msg);\n var pairIds = idx === -1 ? [msg.id] : this.getQaPairIdsAt(idx);\n pairIds.forEach(function (id) {\n if (_this16.selectedMsgIds.indexOf(id) === -1) _this16.selectedMsgIds.push(id);\n });\n } else {\n this.selectedMsgIds.splice(i, 1);\n }\n },\n toggleSelectAll: function toggleSelectAll() {\n var ids = this.deletableMsgIds;\n this.selectedMsgIds = this.selectedMsgIds.length === ids.length ? [] : ids.slice();\n },\n\n /** 多选模式下点击整行即切换选中 */\n onMsgRowClick: function onMsgRowClick(msg) {\n // 长按弹出菜单后浏览器还会补一次 click,短暂屏蔽避免误触\n if (Date.now() < this._ignoreClickUntil) return;\n if (this.selectMode) this.toggleSelectMessage(msg);\n },\n\n /** 移动端长按气泡弹出消息操作单(豆包式:复制 / 删除 / 多选) */\n onMsgTouchStart: function onMsgTouchStart(msg) {\n var _this17 = this;\n\n if (!this.isMobileActive || this.selectMode) return;\n this._longPressMsg = msg;\n clearTimeout(this._longPressTimer);\n this._longPressTimer = setTimeout(function () {\n _this17._longPressTimer = null; // 用户正在长按选中文本(复制用),此时不弹菜单,避免抢走选择行为\n\n var sel = window.getSelection && window.getSelection();\n if (sel && String(sel).trim()) return;\n\n _this17.openMsgAction(_this17._longPressMsg);\n }, 500);\n },\n onMsgTouchEnd: function onMsgTouchEnd() {\n if (this._longPressTimer) {\n clearTimeout(this._longPressTimer);\n this._longPressTimer = null;\n }\n },\n openMsgAction: function openMsgAction(msg) {\n this.msgAction = msg; // 长按抬手后浏览器可能补发 click,短暂屏蔽,避免刚弹出的菜单被立即关闭\n\n this._ignoreClickUntil = Date.now() + 350;\n },\n closeMsgAction: function closeMsgAction() {\n if (Date.now() < this._ignoreClickUntil) return;\n this.msgAction = null;\n },\n\n /** 移动端消息操作单:复制 / 删除(删除进入多选模式,与豆包一致) */\n doMsgAction: function doMsgAction(action) {\n var msg = this.msgAction;\n this.msgAction = null;\n if (!msg) return;\n\n if (action === 'copy') {\n this.copyText(msg.content);\n } else if (action === 'delete') {\n this.enterSelectMode(msg);\n }\n },\n\n /** 批量删除选中的消息 */\n handleDeleteSelected: function handleDeleteSelected() {\n var _this18 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var ids;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (!(!_this18.selectedMsgIds.length || !_this18.activeSessionId)) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n ids = _this18.selectedMsgIds.slice();\n _context11.prev = 3;\n _context11.next = 6;\n return _this18.$confirm(\"\\u5220\\u9664\\u540E\\u65E0\\u6CD5\\u6062\\u590D\\uFF0C\\u786E\\u5B9A\\u5220\\u9664\\u9009\\u4E2D\\u7684 \".concat(ids.length, \" \\u6761\\u6D88\\u606F\\u5417\\uFF1F\"), '删除消息', {\n confirmButtonText: '确定',\n cancelButtonText: '取消',\n type: 'warning'\n });\n\n case 6:\n _context11.next = 11;\n break;\n\n case 8:\n _context11.prev = 8;\n _context11.t0 = _context11[\"catch\"](3);\n return _context11.abrupt(\"return\");\n\n case 11:\n _context11.prev = 11;\n _context11.next = 14;\n return (0, _kbChat.deleteKbMessages)(_this18.activeSessionId, ids);\n\n case 14:\n _this18.messages = _this18.messages.filter(function (m) {\n return ids.indexOf(m.id) === -1;\n });\n\n _this18.exitSelectMode();\n\n _this18.$message.success('删除成功');\n\n _this18.refreshSessionList();\n\n _context11.next = 24;\n break;\n\n case 20:\n _context11.prev = 20;\n _context11.t1 = _context11[\"catch\"](11);\n console.error('批量删除消息失败', _context11.t1);\n\n _this18.$message.error('删除失败');\n\n case 24:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, null, [[3, 8], [11, 20]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this19 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee12() {\n return regeneratorRuntime.wrap(function _callee12$(_context12) {\n while (1) {\n switch (_context12.prev = _context12.next) {\n case 0:\n if (!_this19.activeSessionId) {\n _context12.next = 8;\n break;\n }\n\n _context12.prev = 1;\n _context12.next = 4;\n return (0, _kbChat.closeKbSession)(_this19.activeSessionId);\n\n case 4:\n _context12.next = 8;\n break;\n\n case 6:\n _context12.prev = 6;\n _context12.t0 = _context12[\"catch\"](1);\n\n case 8:\n _context12.next = 10;\n return _this19.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context12.stop();\n }\n }\n }, _callee12, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this20 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee13() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee13$(_context13) {\n while (1) {\n switch (_context13.prev = _context13.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this20.inputText.trim();\n\n if (!(!text || !_this20.activeSessionId || _this20.streaming)) {\n _context13.next = 3;\n break;\n }\n\n return _context13.abrupt(\"return\");\n\n case 3:\n _this20.exitSelectMode();\n\n _this20.inputText = '';\n requestSessionId = _this20.activeSessionId;\n requestId = ++_this20.streamRequestId;\n\n _this20.clearTypeTimer();\n\n _this20.messages.push({\n role: 'user',\n content: text\n });\n\n _this20.$nextTick(function () {\n return _this20.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this20.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this20.messages[msgIdx];\n _this20.streamMsg = streamMsg;\n _this20.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this20.streamRequestId && requestSessionId === _this20.activeSessionId && _this20.streamMsg === streamMsg;\n }; // SSE 只把数据累积到 answerBuffer。typeTimer(30ms) 节流按「完整换行」逐行把内容\n // 推进到 msg.content:每个 tick 最多只推进「到下一个 \\n 为止」的一行,未完成的尾行\n // 保持不动;配合 renderStreaming(breaks:true) 实现「一行显示一行」。\n // 不是打字机模式(不逐字)、也不是一次性整段渲染(不会一出就是全文)。\n\n\n answerBuffer = '';\n finalContent = '';\n streamDone = false;\n _this20._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this20.messages[msgIdx];\n var source = finalContent || answerBuffer;\n var curLen = msg._prevReveal || 0;\n var nextLen = curLen; // 找到「当前已揭示进度之后」的第一条完整换行,仅推进这一行\n\n var nl = source.indexOf('\\n', curLen);\n\n if (nl !== -1) {\n nextLen = nl + 1;\n } else if (streamDone) {\n // 流结束且没有更多换行:最后一段未以换行结尾的内容一次性露出(此时即完整一行)\n nextLen = source.length;\n }\n\n if (nextLen > curLen) {\n msg._prevReveal = nextLen;\n msg.content = source.substring(0, nextLen);\n\n _this20.$nextTick(function () {\n return _this20.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this20.finishStream(); // 后端已在此刻落库,回读一次补全消息 id(单条删除需要)。\n // 落库延迟偶发较大(10 × 800ms ≈ 8s),给足重试次数,\n // 避免「删除」按钮偶现不出现、必须重新进入会话才恢复(见 syncMessageIds 注释)。\n\n\n _this20.syncMessageIds(10);\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this20.activeSessionId, text);\n _this20.eventSource = new EventSource(url);\n\n _this20.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this20.messages[msgIdx]) {\n if (!_this20.messages[msgIdx].toolCalls) _this20.messages[msgIdx].toolCalls = [];\n\n _this20.messages[msgIdx].toolCalls.push(e.data);\n\n _this20.$nextTick(function () {\n return _this20.scrollToBottom();\n });\n }\n });\n\n _this20.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this20.eventSource.addEventListener('agent_result', function (e) {\n if (!isCurrentStream() || streamDone) return; // 与档案君(AiChat.vue)对齐:AGENT_RESULT 为最终真相源。\n // 只记录 finalContent 并结束流,不打断已在逐行推进的 content,交给 typeTimer 以 target 推进/校正。\n\n if (e.data) finalContent = e.data;\n streamDone = true;\n\n _this20.closeEventSource();\n });\n\n _this20.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this20.messages[msgIdx]) {\n _this20.$set(_this20.messages[msgIdx], 'msgKey', e.data);\n }\n });\n\n _this20.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this20.$nextTick(function () {\n return _this20.scrollToBottom();\n });\n });\n\n _this20.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this20.$nextTick(function () {\n return _this20.scrollToBottom();\n });\n });\n\n _this20.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this20.closeEventSource();\n });\n\n case 29:\n case \"end\":\n return _context13.stop();\n }\n }\n }, _callee13);\n }))();\n },\n handleStop: function handleStop() {\n this.streamRequestId++;\n\n if (this.activeSessionId) {\n (0, _kbChat.stopKbStream)(this.activeSessionId).catch(function () {});\n }\n\n this.finishStream(); // 手动停止后 typeTimer 被清掉,收尾处的 syncMessageIds 永远不会执行,\n // 该条回答就一直没有 id →「删除」按钮不出现(偶现的典型来源)。\n // 这里主动补一次(带重试,等后端把已生成内容落库)。\n\n this.syncMessageIds(10);\n },\n finishStream: function finishStream() {\n var _this21 = this;\n\n this.clearTypeTimer();\n\n if (this.streamMsg) {\n this.$delete(this.contentCache, this.streamMsg._key);\n this.streamMsg.loading = false;\n this.streamMsg = null;\n }\n\n this.closeEventSource();\n this.streaming = false;\n this.refreshSessionList();\n this.$nextTick(function () {\n return _this21.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this22 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee14() {\n var list;\n return regeneratorRuntime.wrap(function _callee14$(_context14) {\n while (1) {\n switch (_context14.prev = _context14.next) {\n case 0:\n if (_this22.companyId) {\n _context14.next = 2;\n break;\n }\n\n return _context14.abrupt(\"return\");\n\n case 2:\n _context14.prev = 2;\n _context14.next = 5;\n return (0, _kbChat.getKbSessionList)(_this22.companyId);\n\n case 5:\n _context14.t0 = _context14.sent;\n\n if (_context14.t0) {\n _context14.next = 8;\n break;\n }\n\n _context14.t0 = [];\n\n case 8:\n list = _context14.t0;\n _this22.sessionList = list;\n _context14.next = 15;\n break;\n\n case 12:\n _context14.prev = 12;\n _context14.t1 = _context14[\"catch\"](2);\n console.error('刷新会话列表失败', _context14.t1);\n\n case 15:\n case \"end\":\n return _context14.stop();\n }\n }\n }, _callee14, null, [[2, 12]]);\n }))();\n },\n clearTypeTimer: function clearTypeTimer() {\n if (this._typeTimer) {\n clearInterval(this._typeTimer);\n this._typeTimer = null;\n }\n },\n closeEventSource: function closeEventSource() {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = null;\n }\n },\n copyText: function copyText(text) {\n if (navigator.clipboard) {\n navigator.clipboard.writeText(text);\n }\n },\n scrollToBottom: function scrollToBottom() {\n var el = this.$refs.messagesRef;\n\n if (el) {\n el.scrollTop = el.scrollHeight;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KbChatMain.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1016
1016
|
|
|
1017
1017
|
/***/ }),
|
|
1018
1018
|
|
|
@@ -1024,7 +1024,7 @@ eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@b
|
|
|
1024
1024
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1025
1025
|
|
|
1026
1026
|
"use strict";
|
|
1027
|
-
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _CategoryManager = _interopRequireDefault(__webpack_require__(/*! ./CategoryManager */ \"./src/lib/knowledgeBase/CategoryManager.vue\"));\n\nvar _DocumentList = _interopRequireDefault(__webpack_require__(/*! ./DocumentList */ \"./src/lib/knowledgeBase/DocumentList.vue\"));\n\nvar _KbChatMain = _interopRequireDefault(__webpack_require__(/*! ./KbChatMain */ \"./src/lib/knowledgeBase/KbChatMain.vue\"));\n\nvar _native = __webpack_require__(/*! ./native.js */ \"./src/lib/knowledgeBase/native.js\");\n\nvar _knowledgeBase = __webpack_require__(/*! ./api/knowledgeBase */ \"./src/lib/knowledgeBase/api/knowledgeBase.js\");\n\nvar _request = __webpack_require__(/*! @lib/utils/request.js */ \"./src/lib/utils/request.js\");\n\nvar _guideHelper = __webpack_require__(/*! @lib/utils/guideHelper.js */ \"./src/lib/utils/guideHelper.js\");\n\nvar _guideConfig = __webpack_require__(/*! @lib/components/guide/guideConfig.js */ \"./src/lib/components/guide/guideConfig.js\");\n\nvar _NewFeatureGuide = _interopRequireDefault(__webpack_require__(/*! @lib/components/guide/NewFeatureGuide.vue */ \"./src/lib/components/guide/NewFeatureGuide.vue\"));\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: 'ZjKnowledgeBase',\n components: {\n CategoryManager: _CategoryManager.default,\n DocumentList: _DocumentList.default,\n KbChatMain: _KbChatMain.default,\n NewFeatureGuide: _NewFeatureGuide.default\n },\n props: {\n title: {\n type: String,\n default: '知识库'\n },\n Ak: {\n type: String,\n default: ''\n },\n Sk: {\n type: String,\n default: ''\n },\n imAccount: {\n type: [String, Object],\n default: ''\n },\n // 用户名:无头像时用于渲染文字头像(与 zjkjAvatar 一致)\n userName: {\n type: String,\n default: ''\n },\n companyNo: {\n type: String,\n default: ''\n },\n chatImToken: {\n type: String,\n default: ''\n },\n userno: {\n type: String,\n default: ''\n },\n // 公司知识库固定公司ID(公司编号)。不传时自动使用第一家公司的ID\n companyId: {\n type: String,\n default: ''\n },\n\n /**\r\n * 【已废弃】是否授予知识库管理界面。\r\n * 知识库管理页现已对所有成员开放(入口不再受本 prop 控制),保留仅为向后兼容、不再使用。\r\n * 能否操作改为由 isAdmin 决定:isAdmin=true 完整管理,false 只读(仅预览)。\r\n */\n hasPermission: {\n type: Boolean,\n default: true\n },\n\n /**\r\n * 是否管理员\r\n * false(普通成员)→ 知识库管理页对所有成员开放,但隐藏全部可操作按钮,仅保留预览查看\r\n * true (管理员) → 管理页所有功能不变\r\n * 默认 true,保证老宿主(未传该 prop)行为不变\r\n */\n isAdmin: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n // 首帧前计算是否移动端,避免 H5 先显示 PC 布局再切换的闪烁(与 AI 助理一致)\n // 宿主没有 Element UI 时(H5/平板横屏)一律走自绘 UI 分支,避免 el-* 未知元素报错\n var _isMobile = typeof window !== 'undefined' && (window.innerWidth <= 768 || !(0, _native.hasElementUI)(this));\n\n return {\n // H5
|
|
1027
|
+
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _CategoryManager = _interopRequireDefault(__webpack_require__(/*! ./CategoryManager */ \"./src/lib/knowledgeBase/CategoryManager.vue\"));\n\nvar _DocumentList = _interopRequireDefault(__webpack_require__(/*! ./DocumentList */ \"./src/lib/knowledgeBase/DocumentList.vue\"));\n\nvar _KbChatMain = _interopRequireDefault(__webpack_require__(/*! ./KbChatMain */ \"./src/lib/knowledgeBase/KbChatMain.vue\"));\n\nvar _native = __webpack_require__(/*! ./native.js */ \"./src/lib/knowledgeBase/native.js\");\n\nvar _knowledgeBase = __webpack_require__(/*! ./api/knowledgeBase */ \"./src/lib/knowledgeBase/api/knowledgeBase.js\");\n\nvar _request = __webpack_require__(/*! @lib/utils/request.js */ \"./src/lib/utils/request.js\");\n\nvar _guideHelper = __webpack_require__(/*! @lib/utils/guideHelper.js */ \"./src/lib/utils/guideHelper.js\");\n\nvar _guideConfig = __webpack_require__(/*! @lib/components/guide/guideConfig.js */ \"./src/lib/components/guide/guideConfig.js\");\n\nvar _NewFeatureGuide = _interopRequireDefault(__webpack_require__(/*! @lib/components/guide/NewFeatureGuide.vue */ \"./src/lib/components/guide/NewFeatureGuide.vue\"));\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: 'ZjKnowledgeBase',\n components: {\n CategoryManager: _CategoryManager.default,\n DocumentList: _DocumentList.default,\n KbChatMain: _KbChatMain.default,\n NewFeatureGuide: _NewFeatureGuide.default\n },\n props: {\n title: {\n type: String,\n default: '知识库'\n },\n Ak: {\n type: String,\n default: ''\n },\n Sk: {\n type: String,\n default: ''\n },\n imAccount: {\n type: [String, Object],\n default: ''\n },\n // 用户名:无头像时用于渲染文字头像(与 zjkjAvatar 一致)\n userName: {\n type: String,\n default: ''\n },\n companyNo: {\n type: String,\n default: ''\n },\n chatImToken: {\n type: String,\n default: ''\n },\n userno: {\n type: String,\n default: ''\n },\n // 公司知识库固定公司ID(公司编号)。不传时自动使用第一家公司的ID\n companyId: {\n type: String,\n default: ''\n },\n\n /**\r\n * 【已废弃】是否授予知识库管理界面。\r\n * 知识库管理页现已对所有成员开放(入口不再受本 prop 控制),保留仅为向后兼容、不再使用。\r\n * 能否操作改为由 isAdmin 决定:isAdmin=true 完整管理,false 只读(仅预览)。\r\n */\n hasPermission: {\n type: Boolean,\n default: true\n },\n\n /**\r\n * 是否管理员\r\n * false(普通成员)→ 知识库管理页对所有成员开放,但隐藏全部可操作按钮,仅保留预览查看\r\n * true (管理员) → 管理页所有功能不变\r\n * 默认 true,保证老宿主(未传该 prop)行为不变\r\n */\n isAdmin: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n // 首帧前计算是否移动端,避免 H5 先显示 PC 布局再切换的闪烁(与 AI 助理一致)\n // 宿主没有 Element UI 时(H5/平板横屏)一律走自绘 UI 分支,避免 el-* 未知元素报错\n var _isMobile = typeof window !== 'undefined' && (window.innerWidth <= 768 || !(0, _native.hasElementUI)(this));\n\n return {\n // H5 与 PC 均默认进入知识库管理页(H5 管理页为只读预览,不展示编辑/删除)\n viewMode: 'manage',\n // 'manage' | 'chat'\n currentCategory: null,\n categoryList: [],\n currentCompanyId: this.companyId || this.companyNo || '',\n // 移动端:管理页内「目录 ⇄ 文档列表」导航\n isMobile: _isMobile,\n manageView: 'categories',\n // 'categories' | 'docs'(仅移动端管理用)\n // 新手引导状态\n showGuide: false,\n guideSteps: [],\n isGuideFirstStep: true,\n isGuideLastStep: false\n };\n },\n computed: {\n /**\r\n * 实际生效的管理权限:\r\n * - H5(isMobile)端强制只读(false)——只开放「预览」,不展示编辑/删除等操作,且不展示新手引导;\r\n * - PC 端沿用宿主传入的 isAdmin,行为完全不变。\r\n */\n effectiveIsAdmin: function effectiveIsAdmin() {\n if (this.isMobile) return false;\n return this.isAdmin;\n }\n },\n watch: {\n companyId: function companyId(val) {\n if (val) {\n this.currentCompanyId = val;\n this.refreshAuth();\n }\n },\n companyNo: function companyNo(val) {\n if (val) {\n this.currentCompanyId = val;\n }\n\n this.refreshAuth();\n },\n userno: function userno() {\n this.refreshAuth();\n }\n },\n created: function created() {\n // 关闭原生 App 启动 loading(与 layout/main.vue 一致,走 $hybrid 原生桥)\n if (this.$hybrid && typeof this.$hybrid.closeLoading === 'function') {\n this.$hybrid.closeLoading();\n }\n\n this.refreshAuth();\n\n if (!this.currentCompanyId) {\n this.loadCompanies();\n }\n },\n mounted: function mounted() {\n this.initGuide();\n this._onResize = this.handleResize;\n window.addEventListener('resize', this._onResize, {\n passive: true\n });\n },\n beforeDestroy: function beforeDestroy() {\n if (this._onResize) {\n window.removeEventListener('resize', this._onResize);\n }\n },\n methods: {\n /**\r\n * 窗口宽度变化切换移动端状态(与 AI 助理一致)\r\n */\n handleResize: function handleResize() {\n var now = typeof window !== 'undefined' && (window.innerWidth <= 768 || !(0, _native.hasElementUI)(this));\n\n if (now !== this.isMobile) {\n this.isMobile = now;\n }\n },\n\n /**\r\n * 移动端:点击某个目录/「显示全部文件」进入文档列表页\r\n */\n handleMobileGoDocs: function handleMobileGoDocs() {\n if (!this.isMobile) return;\n this.manageView = 'docs';\n },\n\n /**\r\n * 移动端:文档列表返回目录列表\r\n */\n mobileManageBack: function mobileManageBack() {\n if (this.manageView === 'docs') {\n this.manageView = 'categories';\n }\n },\n\n /**\r\n * 返回宿主 App 原生页面(参考 KbChatMain 的 handleNativeBack)\r\n * 优先走 $hybrid 原生桥关闭当前页,无桥时回退浏览器历史\r\n */\n handleNativeBack: function handleNativeBack() {\n if (this.$hybrid && typeof this.$hybrid.closePage === 'function') {\n this.$hybrid.closePage();\n } else if (window.history.length > 1) {\n window.history.back();\n }\n },\n refreshAuth: function refreshAuth() {\n if (this.Ak || this.Sk) {\n (0, _request.setKbAuth)(this.Ak, this.Sk, this.userno, this.currentCompanyId || this.companyNo, this.chatImToken, this.userno);\n }\n },\n loadCompanies: function loadCompanies() {\n var _this = this;\n\n (0, _knowledgeBase.getCompanyList)().then(function (list) {\n var companies = list || [];\n\n if (companies.length > 0 && !_this.currentCompanyId) {\n _this.currentCompanyId = companies[0];\n }\n }).catch(function () {});\n },\n handleCategorySelect: function handleCategorySelect(category) {\n this.currentCategory = category;\n },\n setCategoryList: function setCategoryList(list) {\n this.categoryList = list;\n },\n // 批量移动成功:左侧树定位并高亮到目标文件夹\n handleMoveTarget: function handleMoveTarget(folder) {\n if (folder && folder.id) {\n this.currentCategory = folder;\n\n if (this.$refs.categoryRef) {\n this.$refs.categoryRef.setCurrentFolder(folder);\n }\n }\n },\n handleRefresh: function handleRefresh() {\n if (!this.currentCompanyId) {\n this.loadCompanies();\n return;\n }\n\n if (this.$refs.categoryRef) {\n this.$refs.categoryRef.loadList();\n }\n\n if (this.$refs.docListRef) {\n this.$refs.docListRef.loadList();\n }\n },\n\n /**\r\n * 新手引导:仅管理员首次进入知识库管理页展示\r\n * (普通成员无可操作按钮,引导无目标元素,不引导)\r\n * H5(effectiveIsAdmin 恒为 false)不展示引导\r\n * 展示状态持久化在 guideHelper 的 ZJKJ_KB_MANAGE_GUIDE,二次进入不再展示\r\n */\n initGuide: function initGuide() {\n var _this2 = this;\n\n if (!this.effectiveIsAdmin) return;\n if (!(0, _guideHelper.shouldShowKbManageGuide)()) return; // 移动端为自绘 UI(MobileDialog + .kbm-*)且「目录/文档」分两页,步骤与选择器单独配置\n\n this.guideSteps = this.isMobile ? (0, _guideConfig.generateKbManageGuideStepsMobile)() : (0, _guideConfig.generateKbManageGuideSteps)();\n if (!this.guideSteps.length) return; // 等页面渲染稳定后再展示,避免引导定位到未布局完成的目标元素\n\n setTimeout(function () {\n _this2.showGuide = true; // 展示即视为已引导过,保证\"首次进入展示、二次进入不再展示\"\n\n (0, _guideHelper.markKbManageGuideCompleted)();\n }, 600);\n },\n onGuideSkip: function onGuideSkip() {\n this.showGuide = false;\n },\n onGuideComplete: function onGuideComplete() {\n this.showGuide = false;\n },\n handleGuideClosed: function handleGuideClosed() {\n this.showGuide = false;\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KnowledgeBaseMain.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1028
1028
|
|
|
1029
1029
|
/***/ }),
|
|
1030
1030
|
|
|
@@ -1192,7 +1192,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
|
|
|
1192
1192
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1193
1193
|
|
|
1194
1194
|
"use strict";
|
|
1195
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n {\n staticClass: \"kb-chat-root\",\n class: {\n \"is-mobile\": _vm.isMobileActive,\n \"h5-list-mode\": _vm.isMobileActive && _vm.showH5List\n },\n on: {\n \"&touchstart\": function($event) {\n return _vm.onSwipeTouchStart.apply(null, arguments)\n }\n }\n },\n [\n _c(\n \"div\",\n {\n staticClass: \"kb-chat-sidebar\",\n class: {\n collapsed: _vm.sidebarCollapsed || _vm.sessionList.length === 0\n }\n },\n [\n _c(\"div\", { staticClass: \"sidebar-header\" }, [\n _vm.isMobileActive\n ? _c(\n \"button\",\n {\n staticClass: \"kb-sidebar-back-btn\",\n attrs: { title: \"返回\" },\n on: { click: _vm.handleSidebarBack }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"22\",\n height: \"22\",\n fill: \"none\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15 18l-6-6 6-6\",\n stroke: \"currentColor\",\n \"stroke-width\": \"2\",\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _c(\n \"div\",\n { staticClass: \"sidebar-title\", attrs: { title: \"会话列表\" } },\n [_vm._v(\"会话列表\")]\n ),\n _c(\n \"button\",\n {\n staticClass: \"new-chat-btn\",\n attrs: { title: \"新建会话\", disabled: _vm.noDocument },\n on: { click: _vm.handleNewSession }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"18\",\n height: \"18\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z\"\n }\n })\n ]\n )\n ]\n )\n ]),\n _c(\n \"div\",\n { staticClass: \"sidebar-list\" },\n [\n _vm._l(_vm.sessionList, function(item) {\n return _c(\n \"div\",\n {\n key: item.sessionId,\n staticClass: \"session-item\",\n class: {\n active: item.sessionId === _vm.activeSessionId,\n \"is-locked\": _vm.noDocument\n },\n on: {\n click: function($event) {\n return _vm.handleSelectSession(item)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"session-item-main\" }, [\n _c(\n \"div\",\n {\n staticClass: \"session-item-title\",\n attrs: { title: item.title }\n },\n [_vm._v(_vm._s(item.title || \"新对话\"))]\n ),\n _c(\n \"div\",\n {\n staticClass: \"session-item-sub\",\n attrs: { title: item.lastContent }\n },\n [_vm._v(_vm._s(item.lastContent || \"暂无消息\"))]\n )\n ]),\n _c(\n \"div\",\n { staticClass: \"session-item-right\" },\n [\n _c(\"div\", { staticClass: \"session-item-time\" }, [\n _vm._v(\n _vm._s(\n _vm.formatTime(\n item.lastTime ||\n item.updateTime ||\n item.createTime\n )\n )\n )\n ]),\n !_vm.isMobileActive\n ? [\n _c(\n \"el-dropdown\",\n {\n staticClass: \"session-more-menu\",\n attrs: { trigger: \"click\" },\n on: {\n command: function(cmd) {\n return _vm.handleSessionMenu(cmd, item)\n }\n }\n },\n [\n _c(\n \"span\",\n {\n staticClass: \"session-more-btn\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\"\n }\n })\n ]\n )\n ]\n ),\n _c(\n \"el-dropdown-menu\",\n {\n attrs: { slot: \"dropdown\" },\n slot: \"dropdown\"\n },\n [\n _c(\n \"el-dropdown-item\",\n {\n attrs: {\n command: \"rename\",\n icon: \"el-icon-edit-outline\"\n }\n },\n [_vm._v(\"重命名\")]\n ),\n _vm.sessionList.length > 1\n ? _c(\n \"el-dropdown-item\",\n {\n attrs: {\n command: \"delete\",\n icon: \"el-icon-delete\",\n divided: \"\"\n }\n },\n [_vm._v(\"删除\")]\n )\n : _vm._e()\n ],\n 1\n )\n ],\n 1\n )\n ]\n : _c(\n \"button\",\n {\n staticClass:\n \"session-more-btn session-more-btn--m\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.openMobileAction(item)\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\"\n }\n })\n ]\n )\n ]\n )\n ],\n 2\n )\n ]\n )\n }),\n _vm.sessionList.length === 0\n ? _c(\"div\", { staticClass: \"sidebar-empty\" }, [\n _vm._v(\"暂无会话,点击「新对话」开始\")\n ])\n : _vm._e()\n ],\n 2\n )\n ]\n ),\n _c(\"div\", { staticClass: \"kb-chat-main\" }, [\n !_vm.isMobileActive && _vm.sessionList.length > 0\n ? _c(\n \"button\",\n {\n staticClass: \"sidebar-toggle-btn\",\n class: { collapsed: _vm.sidebarCollapsed },\n attrs: { title: \"收起/展开会话列表\" },\n on: {\n click: function($event) {\n _vm.sidebarCollapsed = !_vm.sidebarCollapsed\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"18\",\n height: \"18\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _vm.isMobileActive\n ? _c(\n \"button\",\n {\n staticClass: \"kb-m-chat-back\",\n attrs: { title: \"返回\" },\n on: { click: _vm.handleMobileBack }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"20\",\n height: \"20\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _c(\"div\", { staticClass: \"chat-header\" }, [\n _c(\"div\", { staticClass: \"header-left\" }, [\n _c(\"div\", { staticClass: \"agent-info\" }, [\n _c(\"div\", { staticClass: \"agent-avatar\" }, [\n _c(\"img\", {\n staticClass: \"avatar-image\",\n attrs: { src: _vm.aiAvatar, alt: \"AI\" }\n })\n ]),\n _vm._m(0)\n ])\n ])\n ]),\n _c(\n \"div\",\n { ref: \"messagesRef\", staticClass: \"chat-body\" },\n [\n !_vm.activeSessionId\n ? _c(\"div\", { staticClass: \"welcome welcome-loading\" }, [\n _c(\"div\", { staticClass: \"welcome-title\" }, [\n _vm._v(\"知识库 AI 助手\")\n ]),\n _c(\"div\", { staticClass: \"welcome-text\" }, [\n _vm._v(\"正在初始化会话...\")\n ])\n ])\n : _vm.messages.length === 0\n ? _c(\"div\", { staticClass: \"chat-empty-welcome\" }, [\n _c(\"div\", { staticClass: \"welcome-card\" }, [\n _c(\"div\", { staticClass: \"welcome-avatar-box\" }, [\n _c(\"div\", {\n staticClass: \"welcome-avatar has-chat-agent-img\",\n style: { backgroundImage: \"url(\" + _vm.aiAvatar + \")\" }\n })\n ]),\n _vm._m(1)\n ]),\n _c(\"div\", { staticClass: \"guess-box\" }, [\n _c(\"div\", { staticClass: \"guess-title\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n width: \"16\",\n height: \"16\",\n viewBox: \"0 0 24 24\",\n fill: \"#1677ff\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z\"\n }\n })\n ]\n ),\n _c(\"span\", [_vm._v(\"猜您想问\")])\n ]),\n _c(\n \"div\",\n { staticClass: \"tag-wrap\" },\n _vm._l(_vm.suggestedQuestions, function(question) {\n return _c(\n \"div\",\n {\n key: question,\n staticClass: \"quick-tag\",\n on: {\n click: function($event) {\n return _vm.handleSend(question)\n }\n }\n },\n [_vm._v(\" \" + _vm._s(question) + \" \")]\n )\n }),\n 0\n )\n ]),\n _vm.isMobileActive\n ? _c(\"div\", { staticClass: \"kb-m-guide-mask\" }, [\n _c(\"div\", { staticClass: \"kb-m-guide-line\" }),\n _c(\"div\", { staticClass: \"kb-m-guide-tip\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"15\",\n height: \"15\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z\"\n }\n })\n ]\n ),\n _c(\"span\", [\n _vm._v(\"在下方输入框输入问题,立即开始对话\")\n ])\n ])\n ])\n : _vm._e()\n ])\n : _vm._l(_vm.messages, function(msg, idx) {\n return _c(\n \"div\",\n {\n key: msg._key || \"m-\" + idx,\n staticClass: \"msg-row\",\n class: [\n msg.role,\n {\n \"select-mode\": _vm.selectMode,\n selected: _vm.selectMode && _vm.isMsgSelected(msg)\n }\n ],\n on: {\n click: function($event) {\n return _vm.onMsgRowClick(msg)\n },\n \"&touchstart\": function($event) {\n return _vm.onMsgTouchStart(msg)\n },\n \"&touchmove\": function($event) {\n return _vm.onMsgTouchEnd.apply(null, arguments)\n },\n touchend: _vm.onMsgTouchEnd\n }\n },\n [\n _vm.selectMode\n ? _c(\n \"div\",\n {\n staticClass: \"msg-check\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.toggleSelectMessage(msg)\n }\n }\n },\n [\n _c(\n \"span\",\n {\n staticClass: \"msg-check-box\",\n class: { checked: _vm.isMsgSelected(msg) }\n },\n [\n _vm.isMsgSelected(msg)\n ? _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"13\",\n height: \"13\",\n fill: \"none\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M4 12.5l5 5L20 6.5\",\n stroke: \"#fff\",\n \"stroke-width\": \"3.4\",\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\"\n }\n })\n ]\n )\n : _vm._e()\n ]\n )\n ]\n )\n : _vm._e(),\n _c(\"div\", { staticClass: \"msg-main\" }, [\n msg.role === \"assistant\"\n ? _c(\"img\", {\n staticClass: \"message-avatar\",\n attrs: { src: _vm.aiAvatar, alt: \"AI\" }\n })\n : _vm.hasUserAvatar\n ? _c(\"img\", {\n staticClass: \"message-avatar\",\n attrs: { src: _vm.userAvatar, alt: \"用户\" }\n })\n : _c(\n \"div\",\n {\n staticClass: \"message-avatar user-text-avatar\"\n },\n [_vm._v(_vm._s(_vm.userAvatarText))]\n ),\n _c(\"div\", { staticClass: \"msg-content\" }, [\n _c(\n \"div\",\n { staticClass: \"bubble\", class: msg.role },\n [\n msg.role === \"assistant\" && msg.thinking\n ? _c(\n \"details\",\n {\n staticClass: \"thinking-details\",\n attrs: { open: msg.loading }\n },\n [\n _c(\n \"summary\",\n { staticClass: \"thinking-summary\" },\n [_vm._v(\"💭 思考过程\")]\n ),\n _c(\"div\", {\n staticClass: \"thinking-body\",\n domProps: {\n innerHTML: _vm._s(\n _vm.escapeHtml(msg.thinking)\n )\n }\n })\n ]\n )\n : _vm._e(),\n msg.loading && msg.content\n ? _c(\"div\", {\n staticClass: \"final-content\",\n attrs: { \"aria-live\": \"polite\" },\n domProps: {\n innerHTML: _vm._s(\n _vm.renderStreaming(\n msg.content,\n msg._key\n )\n )\n }\n })\n : msg.content\n ? _c(\"div\", {\n staticClass: \"final-content\",\n domProps: {\n innerHTML: _vm._s(\n _vm.renderMarkdown(msg.content)\n )\n }\n })\n : _vm._e(),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value:\n _vm.showToolInfo &&\n msg.toolCalls &&\n msg.toolCalls.length,\n expression:\n \"showToolInfo && msg.toolCalls && msg.toolCalls.length\"\n }\n ],\n staticClass: \"tool-calls\"\n },\n _vm._l(msg.toolCalls, function(tc, ti) {\n return _c(\n \"div\",\n { key: ti, staticClass: \"tool-item\" },\n [\n _c(\n \"span\",\n { staticClass: \"tool-badge\" },\n [_vm._v(\"工具返回\")]\n ),\n _c(\"pre\", [\n _vm._v(\n _vm._s(tc.substring(0, 300)) +\n _vm._s(tc.length > 300 ? \"...\" : \"\")\n )\n ])\n ]\n )\n }),\n 0\n ),\n msg.loading && !msg.content\n ? _c(\"div\", { staticClass: \"typing\" }, [\n _c(\"span\", { staticClass: \"phrase\" }, [\n _vm._v(\"思考中\")\n ]),\n _vm._m(2, true)\n ])\n : _vm._e()\n ]\n ),\n !_vm.selectMode &&\n !_vm.isMobileActive &&\n msg.content &&\n !msg.loading\n ? _c(\"div\", { staticClass: \"msg-actions\" }, [\n _c(\n \"span\",\n {\n staticClass: \"action-btn\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.copyText(msg.content)\n }\n }\n },\n [_vm._v(\"复制\")]\n ),\n msg.id\n ? _c(\n \"span\",\n {\n staticClass:\n \"action-btn action-btn--danger\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.enterSelectMode(msg)\n }\n }\n },\n [_vm._v(\"删除\")]\n )\n : _vm._e(),\n msg.role === \"assistant\"\n ? _c(\"span\", { staticClass: \"ai-badge\" }, [\n _vm._v(\"AI 生成(仅供参考)\")\n ])\n : _vm._e()\n ])\n : _vm._e()\n ])\n ])\n ]\n )\n })\n ],\n 2\n ),\n _vm.selectMode\n ? _c(\"div\", { staticClass: \"kb-select-bar\" }, [\n _c(\"div\", { staticClass: \"select-bar-left\" }, [\n _c(\"span\", { staticClass: \"select-bar-count\" }, [\n _vm._v(\"已选 \" + _vm._s(_vm.selectedMsgIds.length) + \" 条\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"select-bar-all\",\n on: { click: _vm.toggleSelectAll }\n },\n [_vm._v(_vm._s(_vm.isAllSelected ? \"取消全选\" : \"全选\"))]\n )\n ]),\n _c(\"div\", { staticClass: \"select-bar-right\" }, [\n _c(\n \"button\",\n {\n staticClass: \"select-bar-cancel\",\n on: { click: _vm.exitSelectMode }\n },\n [_vm._v(\"取消\")]\n ),\n _c(\n \"button\",\n {\n staticClass: \"select-bar-delete\",\n attrs: { disabled: !_vm.selectedMsgIds.length },\n on: { click: _vm.handleDeleteSelected }\n },\n [_vm._v(\"删除\")]\n )\n ])\n ])\n : _c(\"div\", { staticClass: \"chat-footer\" }, [\n _c(\"div\", { staticClass: \"input-area\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"taRef\",\n staticClass: \"chat-textarea\",\n attrs: {\n placeholder: \"请输入消息\",\n maxlength: \"300\",\n disabled: !_vm.activeSessionId || _vm.streaming\n },\n domProps: { value: _vm.inputText },\n on: {\n input: [\n function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n },\n _vm.autoResizeTextarea\n ],\n keydown: [\n function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.handleSend.apply(null, arguments)\n },\n function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (!$event.ctrlKey) {\n return null\n }\n return _vm.newLine.apply(null, arguments)\n }\n ]\n }\n }),\n !_vm.streaming\n ? _c(\n \"button\",\n {\n staticClass: \"send-btn\",\n class: { active: _vm.inputText.trim() },\n attrs: {\n disabled:\n !_vm.activeSessionId || !_vm.inputText.trim()\n },\n on: { click: _vm.handleSend }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"20\",\n height: \"20\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _vm.streaming\n ? _c(\n \"button\",\n {\n staticClass: \"stop-btn\",\n on: { click: _vm.handleStop }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"18\",\n height: \"18\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"rect\", {\n attrs: {\n x: \"6\",\n y: \"6\",\n width: \"12\",\n height: \"12\",\n rx: \"2\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e()\n ])\n ]),\n _vm.noDocument\n ? _c(\"div\", { staticClass: \"kb-no-doc-mask\" }, [\n _c(\"div\", { staticClass: \"kb-no-doc-box\" }, [\n _c(\"div\", { staticClass: \"kb-no-doc-icon\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"34\",\n height: \"34\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM9 8V6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9z\"\n }\n })\n ]\n )\n ]),\n _c(\"div\", { staticClass: \"kb-no-doc-text\" }, [\n _vm._v(\"当前知识库没有资料,请联系管理员上传\")\n ]),\n _vm.isMobileActive\n ? _c(\"div\", { staticClass: \"kb-no-doc-mobile-tip\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"14\",\n height: \"14\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"\n }\n })\n ]\n ),\n _c(\"span\", [_vm._v(\"上传并完成索引后,即可开始智能问答\")])\n ])\n : _vm._e()\n ])\n ])\n : _vm._e()\n ]),\n _vm.isMobileActive && _vm.mobileAction\n ? _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet\",\n on: {\n click: function($event) {\n if ($event.target !== $event.currentTarget) {\n return null\n }\n _vm.mobileAction = null\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"kb-m-actionsheet-box\" }, [\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-title\",\n attrs: { title: _vm.mobileAction.title || \"新对话\" }\n },\n [\n _vm._v(\n \" \" + _vm._s(_vm.mobileAction.title || \"新对话\") + \" \"\n )\n ]\n ),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item\",\n on: {\n click: function($event) {\n return _vm.doMobileRename(_vm.mobileAction)\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 0 0 0-1.41l-2.34-2.34a.996.996 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z\"\n }\n })\n ]\n ),\n _vm._v(\"重命名 \")\n ]\n ),\n _vm.sessionList.length > 1\n ? _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item danger\",\n on: {\n click: function($event) {\n return _vm.doMobileDelete(_vm.mobileAction)\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\"\n }\n })\n ]\n ),\n _vm._v(\"删除 \")\n ]\n )\n : _vm._e(),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-cancel\",\n on: {\n click: function($event) {\n _vm.mobileAction = null\n }\n }\n },\n [_vm._v(\"取消\")]\n )\n ])\n ]\n )\n : _vm._e(),\n _vm.isMobileActive && _vm.msgAction\n ? _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet\",\n on: {\n click: function($event) {\n if ($event.target !== $event.currentTarget) {\n return null\n }\n return _vm.closeMsgAction.apply(null, arguments)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"kb-m-actionsheet-box\" }, [\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item\",\n on: {\n click: function($event) {\n return _vm.doMsgAction(\"copy\")\n }\n }\n },\n [_vm._v(\"复制\")]\n ),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item danger\",\n on: {\n click: function($event) {\n return _vm.doMsgAction(\"delete\")\n }\n }\n },\n [_vm._v(\"删除\")]\n ),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-cancel\",\n on: { click: _vm.closeMsgAction }\n },\n [_vm._v(\"取消\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]\n )\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"agent-text\" }, [\n _c(\"div\", { staticClass: \"agent-name\" }, [_vm._v(\"知识库 AI\")]),\n _c(\"div\", { staticClass: \"agent-desc\" }, [\n _vm._v(\"基于企业知识库智能问答\")\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"welcome-text-group\" }, [\n _c(\"div\", { staticClass: \"text-sm\" }, [_vm._v(\"您好,我是知识库 AI\")]),\n _c(\"div\", { staticClass: \"text-lg\" }, [\n _vm._v(\"我可以帮您快速查找和解读企业知识\")\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"dots\" }, [\n _c(\"span\"),\n _c(\"span\"),\n _c(\"span\")\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KbChatMain.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2268609640-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1195
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n {\n staticClass: \"kb-chat-root\",\n class: {\n \"is-mobile\": _vm.isMobileActive,\n \"h5-list-mode\": _vm.isMobileActive && _vm.showH5List\n },\n on: {\n \"&touchstart\": function($event) {\n return _vm.onSwipeTouchStart.apply(null, arguments)\n }\n }\n },\n [\n _c(\n \"div\",\n {\n staticClass: \"kb-chat-sidebar\",\n class: {\n collapsed: _vm.sidebarCollapsed || _vm.sessionList.length === 0\n }\n },\n [\n _c(\"div\", { staticClass: \"sidebar-header\" }, [\n _vm.isMobileActive\n ? _c(\n \"button\",\n {\n staticClass: \"kb-sidebar-back-btn\",\n attrs: { title: \"返回\" },\n on: { click: _vm.handleSidebarBack }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"22\",\n height: \"22\",\n fill: \"none\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15 18l-6-6 6-6\",\n stroke: \"currentColor\",\n \"stroke-width\": \"2\",\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _c(\n \"div\",\n { staticClass: \"sidebar-title\", attrs: { title: \"会话列表\" } },\n [_vm._v(\"会话列表\")]\n ),\n _c(\n \"button\",\n {\n staticClass: \"new-chat-btn\",\n attrs: { title: \"新建会话\", disabled: _vm.noDocument },\n on: { click: _vm.handleNewSession }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"18\",\n height: \"18\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z\"\n }\n })\n ]\n )\n ]\n )\n ]),\n _c(\n \"div\",\n { staticClass: \"sidebar-list\" },\n [\n _vm._l(_vm.sessionList, function(item) {\n return _c(\n \"div\",\n {\n key: item.sessionId,\n staticClass: \"session-item\",\n class: {\n active: item.sessionId === _vm.activeSessionId,\n \"is-locked\": _vm.noDocument\n },\n on: {\n click: function($event) {\n return _vm.handleSelectSession(item)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"session-item-main\" }, [\n _c(\n \"div\",\n {\n staticClass: \"session-item-title\",\n attrs: { title: item.title }\n },\n [_vm._v(_vm._s(item.title || \"新对话\"))]\n ),\n _c(\n \"div\",\n {\n staticClass: \"session-item-sub\",\n attrs: { title: item.lastContent }\n },\n [_vm._v(_vm._s(item.lastContent || \"暂无消息\"))]\n )\n ]),\n _c(\n \"div\",\n { staticClass: \"session-item-right\" },\n [\n _c(\"div\", { staticClass: \"session-item-time\" }, [\n _vm._v(\n _vm._s(\n _vm.formatTime(\n item.lastTime ||\n item.updateTime ||\n item.createTime\n )\n )\n )\n ]),\n !_vm.isMobileActive\n ? [\n _c(\n \"el-dropdown\",\n {\n staticClass: \"session-more-menu\",\n attrs: { trigger: \"click\" },\n on: {\n command: function(cmd) {\n return _vm.handleSessionMenu(cmd, item)\n }\n }\n },\n [\n _c(\n \"span\",\n {\n staticClass: \"session-more-btn\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\"\n }\n })\n ]\n )\n ]\n ),\n _c(\n \"el-dropdown-menu\",\n {\n attrs: { slot: \"dropdown\" },\n slot: \"dropdown\"\n },\n [\n _c(\n \"el-dropdown-item\",\n {\n attrs: {\n command: \"rename\",\n icon: \"el-icon-edit-outline\"\n }\n },\n [_vm._v(\"重命名\")]\n ),\n _vm.sessionList.length > 1\n ? _c(\n \"el-dropdown-item\",\n {\n attrs: {\n command: \"delete\",\n icon: \"el-icon-delete\",\n divided: \"\"\n }\n },\n [_vm._v(\"删除\")]\n )\n : _vm._e()\n ],\n 1\n )\n ],\n 1\n )\n ]\n : _c(\n \"button\",\n {\n staticClass:\n \"session-more-btn session-more-btn--m\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.openMobileAction(item)\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\"\n }\n })\n ]\n )\n ]\n )\n ],\n 2\n )\n ]\n )\n }),\n _vm.sessionList.length === 0\n ? _c(\"div\", { staticClass: \"sidebar-empty\" }, [\n _vm._v(\"暂无会话,点击「新对话」开始\")\n ])\n : _vm._e()\n ],\n 2\n )\n ]\n ),\n _c(\"div\", { staticClass: \"kb-chat-main\" }, [\n !_vm.isMobileActive && _vm.sessionList.length > 0\n ? _c(\n \"button\",\n {\n staticClass: \"sidebar-toggle-btn\",\n class: { collapsed: _vm.sidebarCollapsed },\n attrs: { title: \"收起/展开会话列表\" },\n on: {\n click: function($event) {\n _vm.sidebarCollapsed = !_vm.sidebarCollapsed\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"18\",\n height: \"18\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _vm.isMobileActive\n ? _c(\n \"button\",\n {\n staticClass: \"kb-m-chat-back\",\n attrs: { title: \"返回\" },\n on: { click: _vm.handleMobileBack }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"20\",\n height: \"20\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _c(\"div\", { staticClass: \"chat-header\" }, [\n _c(\"div\", { staticClass: \"header-left\" }, [\n _c(\"div\", { staticClass: \"agent-info\" }, [\n _c(\"div\", { staticClass: \"agent-avatar\" }, [\n _c(\"img\", {\n staticClass: \"avatar-image\",\n attrs: { src: _vm.aiAvatar, alt: \"AI\" }\n })\n ]),\n _vm._m(0)\n ])\n ])\n ]),\n _c(\n \"div\",\n { ref: \"messagesRef\", staticClass: \"chat-body\" },\n [\n !_vm.activeSessionId\n ? _c(\"div\", { staticClass: \"welcome welcome-loading\" }, [\n _c(\"div\", { staticClass: \"welcome-title\" }, [\n _vm._v(\"知识库 AI 助手\")\n ]),\n _c(\"div\", { staticClass: \"welcome-text\" }, [\n _vm._v(\"正在初始化会话...\")\n ])\n ])\n : _vm.messages.length === 0\n ? _c(\"div\", { staticClass: \"chat-empty-welcome\" }, [\n _c(\"div\", { staticClass: \"welcome-card\" }, [\n _c(\"div\", { staticClass: \"welcome-avatar-box\" }, [\n _c(\"div\", {\n staticClass: \"welcome-avatar has-chat-agent-img\",\n style: { backgroundImage: \"url(\" + _vm.aiAvatar + \")\" }\n })\n ]),\n _vm._m(1)\n ]),\n _c(\"div\", { staticClass: \"guess-box\" }, [\n _c(\"div\", { staticClass: \"guess-title\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n width: \"16\",\n height: \"16\",\n viewBox: \"0 0 24 24\",\n fill: \"#1677ff\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z\"\n }\n })\n ]\n ),\n _c(\"span\", [_vm._v(\"猜您想问\")])\n ]),\n _c(\n \"div\",\n { staticClass: \"tag-wrap\" },\n _vm._l(_vm.suggestedQuestions, function(question) {\n return _c(\n \"div\",\n {\n key: question,\n staticClass: \"quick-tag\",\n on: {\n click: function($event) {\n return _vm.handleSend(question)\n }\n }\n },\n [_vm._v(\" \" + _vm._s(question) + \" \")]\n )\n }),\n 0\n )\n ]),\n _vm.isMobileActive\n ? _c(\"div\", { staticClass: \"kb-m-guide-mask\" }, [\n _c(\"div\", { staticClass: \"kb-m-guide-line\" }),\n _c(\"div\", { staticClass: \"kb-m-guide-tip\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"15\",\n height: \"15\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z\"\n }\n })\n ]\n ),\n _c(\"span\", [\n _vm._v(\"在下方输入框输入问题,立即开始对话\")\n ])\n ])\n ])\n : _vm._e()\n ])\n : _vm._l(_vm.messages, function(msg, idx) {\n return _c(\n \"div\",\n {\n key: msg._key || \"m-\" + idx,\n staticClass: \"msg-row\",\n class: [\n msg.role,\n {\n \"select-mode\": _vm.selectMode,\n selected: _vm.selectMode && _vm.isMsgSelected(msg)\n }\n ],\n on: {\n click: function($event) {\n return _vm.onMsgRowClick(msg)\n },\n \"&touchstart\": function($event) {\n return _vm.onMsgTouchStart(msg)\n },\n \"&touchmove\": function($event) {\n return _vm.onMsgTouchEnd.apply(null, arguments)\n },\n touchend: _vm.onMsgTouchEnd\n }\n },\n [\n _vm.selectMode\n ? _c(\n \"div\",\n {\n staticClass: \"msg-check\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.toggleSelectMessage(msg)\n }\n }\n },\n [\n _c(\n \"span\",\n {\n staticClass: \"msg-check-box\",\n class: { checked: _vm.isMsgSelected(msg) }\n },\n [\n _vm.isMsgSelected(msg)\n ? _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"13\",\n height: \"13\",\n fill: \"none\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M4 12.5l5 5L20 6.5\",\n stroke: \"#fff\",\n \"stroke-width\": \"3.4\",\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\"\n }\n })\n ]\n )\n : _vm._e()\n ]\n )\n ]\n )\n : _vm._e(),\n _c(\"div\", { staticClass: \"msg-main\" }, [\n msg.role === \"assistant\"\n ? _c(\"img\", {\n staticClass: \"message-avatar\",\n attrs: { src: _vm.aiAvatar, alt: \"AI\" }\n })\n : _vm.hasUserAvatar\n ? _c(\"img\", {\n staticClass: \"message-avatar\",\n attrs: { src: _vm.userAvatar, alt: \"用户\" }\n })\n : _c(\n \"div\",\n {\n staticClass: \"message-avatar user-text-avatar\"\n },\n [_vm._v(_vm._s(_vm.userAvatarText))]\n ),\n _c(\"div\", { staticClass: \"msg-content\" }, [\n _c(\n \"div\",\n { staticClass: \"bubble\", class: msg.role },\n [\n msg.role === \"assistant\" && msg.thinking\n ? _c(\n \"details\",\n {\n staticClass: \"thinking-details\",\n attrs: { open: msg.loading }\n },\n [\n _c(\n \"summary\",\n { staticClass: \"thinking-summary\" },\n [_vm._v(\"💭 思考过程\")]\n ),\n _c(\"div\", {\n staticClass: \"thinking-body\",\n domProps: {\n innerHTML: _vm._s(\n _vm.escapeHtml(msg.thinking)\n )\n }\n })\n ]\n )\n : _vm._e(),\n msg.role === \"user\" && msg.content\n ? _c(\"div\", {\n staticClass: \"final-content\",\n domProps: {\n innerHTML: _vm._s(\n _vm.renderUserContent(msg.content)\n )\n }\n })\n : msg.loading && msg.content\n ? _c(\"div\", {\n staticClass: \"final-content\",\n attrs: { \"aria-live\": \"polite\" },\n domProps: {\n innerHTML: _vm._s(\n _vm.renderStreaming(\n msg.content,\n msg._key\n )\n )\n }\n })\n : msg.content\n ? _c(\"div\", {\n staticClass: \"final-content\",\n domProps: {\n innerHTML: _vm._s(\n _vm.renderMarkdown(msg.content)\n )\n }\n })\n : _vm._e(),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value:\n _vm.showToolInfo &&\n msg.toolCalls &&\n msg.toolCalls.length,\n expression:\n \"showToolInfo && msg.toolCalls && msg.toolCalls.length\"\n }\n ],\n staticClass: \"tool-calls\"\n },\n _vm._l(msg.toolCalls, function(tc, ti) {\n return _c(\n \"div\",\n { key: ti, staticClass: \"tool-item\" },\n [\n _c(\n \"span\",\n { staticClass: \"tool-badge\" },\n [_vm._v(\"工具返回\")]\n ),\n _c(\"pre\", [\n _vm._v(\n _vm._s(tc.substring(0, 300)) +\n _vm._s(tc.length > 300 ? \"...\" : \"\")\n )\n ])\n ]\n )\n }),\n 0\n ),\n msg.loading && !msg.content\n ? _c(\"div\", { staticClass: \"typing\" }, [\n _c(\"span\", { staticClass: \"phrase\" }, [\n _vm._v(\"思考中\")\n ]),\n _vm._m(2, true)\n ])\n : _vm._e()\n ]\n ),\n !_vm.selectMode &&\n !_vm.isMobileActive &&\n msg.content &&\n !msg.loading\n ? _c(\"div\", { staticClass: \"msg-actions\" }, [\n _c(\n \"span\",\n {\n staticClass: \"action-btn\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.copyText(msg.content)\n }\n }\n },\n [_vm._v(\"复制\")]\n ),\n msg.id\n ? _c(\n \"span\",\n {\n staticClass:\n \"action-btn action-btn--danger\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.enterSelectMode(msg)\n }\n }\n },\n [_vm._v(\"删除\")]\n )\n : _vm._e(),\n msg.role === \"assistant\"\n ? _c(\"span\", { staticClass: \"ai-badge\" }, [\n _vm._v(\"AI 生成(仅供参考)\")\n ])\n : _vm._e()\n ])\n : _vm._e()\n ])\n ])\n ]\n )\n })\n ],\n 2\n ),\n _vm.selectMode\n ? _c(\"div\", { staticClass: \"kb-select-bar\" }, [\n _c(\"div\", { staticClass: \"select-bar-left\" }, [\n _c(\"span\", { staticClass: \"select-bar-count\" }, [\n _vm._v(\"已选 \" + _vm._s(_vm.selectedMsgIds.length) + \" 条\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"select-bar-all\",\n on: { click: _vm.toggleSelectAll }\n },\n [_vm._v(_vm._s(_vm.isAllSelected ? \"取消全选\" : \"全选\"))]\n )\n ]),\n _c(\"div\", { staticClass: \"select-bar-right\" }, [\n _c(\n \"button\",\n {\n staticClass: \"select-bar-cancel\",\n on: { click: _vm.exitSelectMode }\n },\n [_vm._v(\"取消\")]\n ),\n _c(\n \"button\",\n {\n staticClass: \"select-bar-delete\",\n attrs: { disabled: !_vm.selectedMsgIds.length },\n on: { click: _vm.handleDeleteSelected }\n },\n [_vm._v(\"删除\")]\n )\n ])\n ])\n : _c(\"div\", { staticClass: \"chat-footer\" }, [\n _c(\"div\", { staticClass: \"input-area\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"taRef\",\n staticClass: \"chat-textarea\",\n attrs: {\n placeholder: \"请输入消息\",\n maxlength: \"300\",\n disabled: !_vm.activeSessionId || _vm.streaming\n },\n domProps: { value: _vm.inputText },\n on: {\n input: [\n function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n },\n _vm.autoResizeTextarea\n ],\n keydown: [\n function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.handleSend.apply(null, arguments)\n },\n function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (!$event.ctrlKey) {\n return null\n }\n return _vm.newLine.apply(null, arguments)\n }\n ]\n }\n }),\n !_vm.streaming\n ? _c(\n \"button\",\n {\n staticClass: \"send-btn\",\n class: { active: _vm.inputText.trim() },\n attrs: {\n disabled:\n !_vm.activeSessionId || !_vm.inputText.trim()\n },\n on: { click: _vm.handleSend }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"20\",\n height: \"20\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _vm.streaming\n ? _c(\n \"button\",\n {\n staticClass: \"stop-btn\",\n on: { click: _vm.handleStop }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"18\",\n height: \"18\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"rect\", {\n attrs: {\n x: \"6\",\n y: \"6\",\n width: \"12\",\n height: \"12\",\n rx: \"2\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e()\n ])\n ]),\n _vm.noDocument\n ? _c(\"div\", { staticClass: \"kb-no-doc-mask\" }, [\n _c(\"div\", { staticClass: \"kb-no-doc-box\" }, [\n _c(\"div\", { staticClass: \"kb-no-doc-icon\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"34\",\n height: \"34\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM9 8V6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9z\"\n }\n })\n ]\n )\n ]),\n _c(\"div\", { staticClass: \"kb-no-doc-text\" }, [\n _vm._v(\"当前知识库没有资料,请联系管理员上传\")\n ]),\n _vm.isMobileActive\n ? _c(\"div\", { staticClass: \"kb-no-doc-mobile-tip\" }, [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"14\",\n height: \"14\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"\n }\n })\n ]\n ),\n _c(\"span\", [_vm._v(\"上传并完成索引后,即可开始智能问答\")])\n ])\n : _vm._e()\n ])\n ])\n : _vm._e()\n ]),\n _vm.isMobileActive && _vm.mobileAction\n ? _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet\",\n on: {\n click: function($event) {\n if ($event.target !== $event.currentTarget) {\n return null\n }\n _vm.mobileAction = null\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"kb-m-actionsheet-box\" }, [\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-title\",\n attrs: { title: _vm.mobileAction.title || \"新对话\" }\n },\n [\n _vm._v(\n \" \" + _vm._s(_vm.mobileAction.title || \"新对话\") + \" \"\n )\n ]\n ),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item\",\n on: {\n click: function($event) {\n return _vm.doMobileRename(_vm.mobileAction)\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 0 0 0-1.41l-2.34-2.34a.996.996 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z\"\n }\n })\n ]\n ),\n _vm._v(\"重命名 \")\n ]\n ),\n _vm.sessionList.length > 1\n ? _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item danger\",\n on: {\n click: function($event) {\n return _vm.doMobileDelete(_vm.mobileAction)\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\"\n }\n })\n ]\n ),\n _vm._v(\"删除 \")\n ]\n )\n : _vm._e(),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-cancel\",\n on: {\n click: function($event) {\n _vm.mobileAction = null\n }\n }\n },\n [_vm._v(\"取消\")]\n )\n ])\n ]\n )\n : _vm._e(),\n _vm.isMobileActive && _vm.msgAction\n ? _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet\",\n on: {\n click: function($event) {\n if ($event.target !== $event.currentTarget) {\n return null\n }\n return _vm.closeMsgAction.apply(null, arguments)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"kb-m-actionsheet-box\" }, [\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item\",\n on: {\n click: function($event) {\n return _vm.doMsgAction(\"copy\")\n }\n }\n },\n [_vm._v(\"复制\")]\n ),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-item danger\",\n on: {\n click: function($event) {\n return _vm.doMsgAction(\"delete\")\n }\n }\n },\n [_vm._v(\"删除\")]\n ),\n _c(\n \"div\",\n {\n staticClass: \"kb-m-actionsheet-cancel\",\n on: { click: _vm.closeMsgAction }\n },\n [_vm._v(\"取消\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]\n )\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"agent-text\" }, [\n _c(\"div\", { staticClass: \"agent-name\" }, [_vm._v(\"知识库 AI\")]),\n _c(\"div\", { staticClass: \"agent-desc\" }, [\n _vm._v(\"基于企业知识库智能问答\")\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"welcome-text-group\" }, [\n _c(\"div\", { staticClass: \"text-sm\" }, [_vm._v(\"您好,我是知识库 AI\")]),\n _c(\"div\", { staticClass: \"text-lg\" }, [\n _vm._v(\"我可以帮您快速查找和解读企业知识\")\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"dots\" }, [\n _c(\"span\"),\n _c(\"span\"),\n _c(\"span\")\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KbChatMain.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2268609640-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1196
1196
|
|
|
1197
1197
|
/***/ }),
|
|
1198
1198
|
|
|
@@ -1204,7 +1204,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
|
|
|
1204
1204
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1205
1205
|
|
|
1206
1206
|
"use strict";
|
|
1207
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"kb-root\" },\n [\n !_vm.isMobile\n ? _c(\"div\", { staticClass: \"kb-tabs\" }, [\n _c(\n \"button\",\n {\n staticClass: \"kb-tab\",\n class: { active: _vm.viewMode === \"manage\" },\n on: {\n click: function($event) {\n _vm.viewMode = \"manage\"\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13zM6 20V4h5v7h7v9H6z\"\n }\n })\n ]\n ),\n _vm._v(\" 知识库管理 \")\n ]\n ),\n _c(\n \"button\",\n {\n staticClass: \"kb-tab\",\n class: { active: _vm.viewMode === \"chat\" },\n on: {\n click: function($event) {\n _vm.viewMode = \"chat\"\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z\"\n }\n })\n ]\n ),\n _vm._v(\" 知识库 AI \")\n ]\n )\n ])\n : _vm._e(),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.viewMode === \"manage\" && !_vm.isMobile,\n expression: \"viewMode === 'manage' && !isMobile\"\n }\n ],\n staticClass: \"kb-layout\"\n },\n [\n _c(\n \"div\",\n { staticClass: \"kb-sidebar\" },\n [\n _c(\"CategoryManager\", {\n ref: \"categoryRef\",\n attrs: {\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": false,\n \"is-admin\": _vm.isAdmin\n },\n on: {\n select: _vm.handleCategorySelect,\n list: _vm.setCategoryList\n }\n })\n ],\n 1\n ),\n _c(\n \"div\",\n { staticClass: \"kb-main\" },\n [\n _c(\"DocumentList\", {\n ref: \"docListRef\",\n attrs: {\n \"selected-category\": _vm.currentCategory,\n \"category-list\": _vm.categoryList,\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": false,\n \"is-admin\": _vm.isAdmin\n },\n on: { \"move-target\": _vm.handleMoveTarget }\n })\n ],\n 1\n )\n ]\n ),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.viewMode === \"manage\" && _vm.isMobile,\n expression: \"viewMode === 'manage' && isMobile\"\n }\n ],\n staticClass: \"kb-m-layout\"\n },\n [\n _c(\"div\", { staticClass: \"kb-m-navbar\" }, [\n _c(\n \"button\",\n {\n staticClass: \"kb-m-back\",\n class: { ghost: _vm.manageView === \"categories\" },\n on: { click: _vm.mobileManageBack }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"20\",\n height: \"20\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z\"\n }\n })\n ]\n )\n ]\n ),\n _c(\"span\", { staticClass: \"kb-m-nav-title\" }, [\n _vm._v(\n _vm._s(\n _vm.manageView === \"docs\" && _vm.currentCategory\n ? _vm.currentCategory.name || \"全部文件\"\n : _vm.manageView === \"docs\"\n ? \"全部文件\"\n : \"知识库管理\"\n )\n )\n ])\n ]),\n _c(\n \"div\",\n { staticClass: \"kb-m-page\" },\n [\n _vm.manageView === \"categories\"\n ? _c(\"CategoryManager\", {\n ref: \"categoryRef\",\n staticClass: \"kb-m-full\",\n attrs: {\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": true,\n \"is-admin\": _vm.isAdmin\n },\n on: {\n select: _vm.handleCategorySelect,\n list: _vm.setCategoryList,\n \"docs-entry\": _vm.handleMobileGoDocs\n }\n })\n : _c(\"DocumentList\", {\n ref: \"docListRef\",\n staticClass: \"kb-m-full\",\n attrs: {\n \"selected-category\": _vm.currentCategory,\n \"category-list\": _vm.categoryList,\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": true,\n \"is-admin\": _vm.isAdmin\n },\n on: { \"move-target\": _vm.handleMoveTarget }\n })\n ],\n 1\n )\n ]\n ),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.viewMode === \"chat\",\n expression: \"viewMode === 'chat'\"\n }\n ],\n staticClass: \"kb-chat-view\",\n class: { \"kb-chat-view--mobile\": _vm.isMobile }\n },\n [\n _c(\"KbChatMain\", {\n key: \"kb-chat-\" + _vm.currentCompanyId + \"-\" + _vm.userno,\n attrs: {\n \"company-id\": _vm.currentCompanyId,\n userno: _vm.userno,\n \"im-account\": _vm.imAccount,\n \"user-name\": _vm.userName,\n \"is-mobile\": _vm.isMobile\n }\n })\n ],\n 1\n ),\n _c(\"NewFeatureGuide\", {\n attrs: {\n visible: _vm.showGuide,\n steps: _vm.guideSteps,\n \"is-mobile\": _vm.isMobile,\n isFirstStep: _vm.isGuideFirstStep,\n isLastStep: _vm.isGuideLastStep\n },\n on: {\n \"update:isFirstStep\": function($event) {\n _vm.isGuideFirstStep = $event\n },\n \"update:is-first-step\": function($event) {\n _vm.isGuideFirstStep = $event\n },\n \"update:isLastStep\": function($event) {\n _vm.isGuideLastStep = $event\n },\n \"update:is-last-step\": function($event) {\n _vm.isGuideLastStep = $event\n },\n skip: _vm.onGuideSkip,\n complete: _vm.onGuideComplete,\n closed: _vm.handleGuideClosed\n }\n })\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KnowledgeBaseMain.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2268609640-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1207
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"kb-root\" },\n [\n _c(\n \"div\",\n { staticClass: \"kb-tabs\", class: { \"kb-tabs--mobile\": _vm.isMobile } },\n [\n !_vm.isMobile\n ? _c(\n \"button\",\n {\n staticClass: \"kb-native-back\",\n attrs: { title: \"返回\" },\n on: { click: _vm.handleNativeBack }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"20\",\n height: \"20\",\n fill: \"none\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15 18l-6-6 6-6\",\n stroke: \"currentColor\",\n \"stroke-width\": \"2\",\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\"\n }\n })\n ]\n )\n ]\n )\n : _vm._e(),\n _c(\n \"button\",\n {\n staticClass: \"kb-tab\",\n class: { active: _vm.viewMode === \"manage\" },\n on: {\n click: function($event) {\n _vm.viewMode = \"manage\"\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13zM6 20V4h5v7h7v9H6z\"\n }\n })\n ]\n ),\n _vm._v(\" 知识库管理 \")\n ]\n ),\n _c(\n \"button\",\n {\n staticClass: \"kb-tab\",\n class: { active: _vm.viewMode === \"chat\" },\n on: {\n click: function($event) {\n _vm.viewMode = \"chat\"\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"16\",\n height: \"16\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d:\n \"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z\"\n }\n })\n ]\n ),\n _vm._v(\" 知识库 AI \")\n ]\n )\n ]\n ),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.viewMode === \"manage\" && !_vm.isMobile,\n expression: \"viewMode === 'manage' && !isMobile\"\n }\n ],\n staticClass: \"kb-layout\"\n },\n [\n _c(\n \"div\",\n { staticClass: \"kb-sidebar\" },\n [\n _c(\"CategoryManager\", {\n ref: \"categoryRef\",\n attrs: {\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": false,\n \"is-admin\": _vm.effectiveIsAdmin\n },\n on: {\n select: _vm.handleCategorySelect,\n list: _vm.setCategoryList\n }\n })\n ],\n 1\n ),\n _c(\n \"div\",\n { staticClass: \"kb-main\" },\n [\n _c(\"DocumentList\", {\n ref: \"docListRef\",\n attrs: {\n \"selected-category\": _vm.currentCategory,\n \"category-list\": _vm.categoryList,\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": false,\n \"is-admin\": _vm.effectiveIsAdmin\n },\n on: { \"move-target\": _vm.handleMoveTarget }\n })\n ],\n 1\n )\n ]\n ),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.viewMode === \"manage\" && _vm.isMobile,\n expression: \"viewMode === 'manage' && isMobile\"\n }\n ],\n staticClass: \"kb-m-layout\"\n },\n [\n _c(\"div\", { staticClass: \"kb-m-navbar\" }, [\n _c(\n \"button\",\n {\n staticClass: \"kb-m-back\",\n attrs: {\n title: _vm.manageView === \"docs\" ? \"返回\" : \"返回上一页\"\n },\n on: {\n click: function($event) {\n _vm.manageView === \"docs\"\n ? _vm.mobileManageBack()\n : _vm.handleNativeBack()\n }\n }\n },\n [\n _c(\n \"svg\",\n {\n attrs: {\n viewBox: \"0 0 24 24\",\n width: \"20\",\n height: \"20\",\n fill: \"currentColor\"\n }\n },\n [\n _c(\"path\", {\n attrs: {\n d: \"M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z\"\n }\n })\n ]\n )\n ]\n ),\n _c(\"span\", { staticClass: \"kb-m-nav-title\" }, [\n _vm._v(\n _vm._s(\n _vm.manageView === \"docs\" && _vm.currentCategory\n ? _vm.currentCategory.name || \"全部文件\"\n : _vm.manageView === \"docs\"\n ? \"全部文件\"\n : \"知识库管理\"\n )\n )\n ])\n ]),\n _c(\n \"div\",\n { staticClass: \"kb-m-page\" },\n [\n _vm.manageView === \"categories\"\n ? _c(\"CategoryManager\", {\n ref: \"categoryRef\",\n staticClass: \"kb-m-full\",\n attrs: {\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": true,\n \"is-admin\": _vm.effectiveIsAdmin\n },\n on: {\n select: _vm.handleCategorySelect,\n list: _vm.setCategoryList,\n \"docs-entry\": _vm.handleMobileGoDocs\n }\n })\n : _c(\"DocumentList\", {\n ref: \"docListRef\",\n staticClass: \"kb-m-full\",\n attrs: {\n \"selected-category\": _vm.currentCategory,\n \"category-list\": _vm.categoryList,\n \"company-id\": _vm.currentCompanyId,\n \"is-mobile\": true,\n \"is-admin\": _vm.effectiveIsAdmin\n },\n on: { \"move-target\": _vm.handleMoveTarget }\n })\n ],\n 1\n )\n ]\n ),\n _c(\n \"div\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.viewMode === \"chat\",\n expression: \"viewMode === 'chat'\"\n }\n ],\n staticClass: \"kb-chat-view\",\n class: { \"kb-chat-view--mobile\": _vm.isMobile }\n },\n [\n _c(\"KbChatMain\", {\n key: \"kb-chat-\" + _vm.currentCompanyId + \"-\" + _vm.userno,\n attrs: {\n \"company-id\": _vm.currentCompanyId,\n userno: _vm.userno,\n \"im-account\": _vm.imAccount,\n \"user-name\": _vm.userName,\n \"is-mobile\": _vm.isMobile\n }\n })\n ],\n 1\n ),\n _c(\"NewFeatureGuide\", {\n attrs: {\n visible: _vm.showGuide,\n steps: _vm.guideSteps,\n \"is-mobile\": _vm.isMobile,\n isFirstStep: _vm.isGuideFirstStep,\n isLastStep: _vm.isGuideLastStep\n },\n on: {\n \"update:isFirstStep\": function($event) {\n _vm.isGuideFirstStep = $event\n },\n \"update:is-first-step\": function($event) {\n _vm.isGuideFirstStep = $event\n },\n \"update:isLastStep\": function($event) {\n _vm.isGuideLastStep = $event\n },\n \"update:is-last-step\": function($event) {\n _vm.isGuideLastStep = $event\n },\n skip: _vm.onGuideSkip,\n complete: _vm.onGuideComplete,\n closed: _vm.handleGuideClosed\n }\n })\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KnowledgeBaseMain.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2268609640-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1208
1208
|
|
|
1209
1209
|
/***/ }),
|
|
1210
1210
|
|
|
@@ -1800,7 +1800,7 @@ eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../.
|
|
|
1800
1800
|
/*! no static exports found */
|
|
1801
1801
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1802
1802
|
|
|
1803
|
-
eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"\\n.kb-root[data-v-84b8c396] {\\r\\n display: flex;\\r\\n flex-direction: column;\\r\\n width: 100%;\\r\\n max-width: 100%;\\r\\n height: 100%;\\r\\n min-height: 400px;\\r\\n overflow-x: hidden;\\r\\n box-sizing: border-box;\\r\\n background: #fafbfc;\\r\\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\\r\\n font-size: 16px !important;\\r\\n color: #303133;\\n}\\r\\n\\r\\n/* 完全隔离主应用 lib-flexible 的影响 */\\n.kb-root[data-v-84b8c396],\\r\\n.kb-root *[data-v-84b8c396],\\r\\n.kb-root *[data-v-84b8c396]::before,\\r\\n.kb-root *[data-v-84b8c396]::after {\\r\\n font-size: 16px !important;\\n}\\r\\n\\r\\n/* ===== 顶部切换栏 ===== */\\n.kb-tabs[data-v-84b8c396] {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 0;\\r\\n height: 48px;\\r\\n padding: 0 20px;\\r\\n background: #fff;\\r\\n border-bottom: 1px solid #ebeef5;\\r\\n flex-shrink: 0;\\r\\n min-width: 0;\\r\\n overflow: hidden;\\n}\\n.kb-tab[data-v-84b8c396] {\\r\\n height: 100%;\\r\\n padding: 0 20px;\\r\\n border: none;\\r\\n background: transparent;\\r\\n font-size: 16px !important;\\r\\n color: #909399;\\r\\n cursor: pointer;\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\r\\n position: relative;\\r\\n transition: all 0.2s;\\r\\n white-space: nowrap;\\r\\n font-weight: 500;\\n}\\n.kb-tab[data-v-84b8c396]:hover {\\r\\n color: #303133;\\n}\\n.kb-tab.active[data-v-84b8c396] {\\r\\n color: #409eff;\\r\\n font-weight: 600;\\n}\\n.kb-tab.active[data-v-84b8c396]::after {\\r\\n content: '';\\r\\n position: absolute;\\r\\n bottom: 0;\\r\\n left: 50%;\\r\\n transform: translateX(-50%);\\r\\n width: 60%;\\r\\n height: 2px;\\r\\n background: #409eff;\\n}\\n.kb-tab svg[data-v-84b8c396],\\r\\n.kb-tab i[data-v-84b8c396] {\\r\\n flex-shrink: 0;\\r\\n transition: transform 0.2s;\\n}\\n.kb-tab:hover svg[data-v-84b8c396],\\r\\n.kb-tab:hover i[data-v-84b8c396] {\\r\\n transform: scale(1.1);\\n}\\r\\n\\r\\n/* ===== 管理布局 ===== */\\n.kb-layout[data-v-84b8c396] {\\r\\n display: flex;\\r\\n align-items: flex-start;\\r\\n flex: 1;\\r\\n min-height: 0;\\r\\n overflow: hidden;\\n}\\n.kb-sidebar[data-v-84b8c396] {\\r\\n width: clamp(240px, 20vw, 280px);\\r\\n border-right: 1px solid #ebeef5;\\r\\n background: #fafbfc;\\r\\n flex-shrink: 0;\\r\\n height: 100%;\\r\\n overflow: hidden;\\n}\\n.kb-main[data-v-84b8c396] {\\r\\n flex: 1;\\r\\n min-width: 0;\\r\\n height: 100%;\\r\\n overflow: hidden;\\r\\n background: #fff;\\n}\\n.kb-chat-view[data-v-84b8c396] {\\r\\n display: flex;\\r\\n flex: 1;\\r\\n min-height: 0;\\r\\n overflow: hidden;\\n}\\n.kb-chat-view[data-v-84b8c396] .kb-chat-root {\\r\\n width: 100%;\\n}\\r\\n\\r\\n/* ===== 引导动画:抵消 .kb-root 全局字号强制规则,还原引导组件原始排版 ===== */\\n.kb-root[data-v-84b8c396] .guide-caption-desc {\\r\\n font-size: 13px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-nav-btn {\\r\\n font-size: 12px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-skip {\\r\\n font-size: 14px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-splash-title {\\r\\n font-size: 20px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-splash-desc {\\r\\n font-size: 14px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-splash-action-btn {\\r\\n font-size: 15px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-page-tip {\\r\\n font-size: 13px !important;\\n}\\r\\n\\r\\n/* ===== 移动端:管理页导航(目录 ⇄ 文档列表) ===== */\\n.kb-chat-view--mobile[data-v-84b8c396] {\\r\\n width: 100%;\\r\\n max-width: 100%;\\r\\n min-width: 0;\\r\\n overflow: hidden;\\n}\\n.kb-chat-view--mobile[data-v-84b8c396] .kb-chat-root {\\r\\n width: 100%;\\n}\\n.kb-m-layout[data-v-84b8c396] {\\r\\n display: flex;\\r\\n flex-direction: column;\\r\\n flex: 1;\\r\\n min-width: 0;\\r\\n max-width: 100%;\\r\\n min-height: 0;\\r\\n overflow: hidden;\\r\\n background: #fafbfc;\\n}\\n.kb-m-navbar[data-v-84b8c396] {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\r\\n height: 48px;\\r\\n padding: 0 8px 0 4px;\\r\\n background: #fff;\\r\\n border-bottom: 1px solid #ebeef5;\\r\\n flex-shrink: 0;\\n}\\n.kb-m-back[data-v-84b8c396] {\\r\\n width: 36px;\\r\\n height: 36px;\\r\\n display: flex;\\r\\n align-items: center;\\r\\n justify-content: center;\\r\\n border: none;\\r\\n background: transparent;\\r\\n color: #303133;\\r\\n cursor: pointer;\\r\\n border-radius: 8px;\\r\\n transition: background 0.2s;\\n}\\n.kb-m-back
|
|
1803
|
+
eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"\\n.kb-root[data-v-84b8c396] {\\r\\n display: flex;\\r\\n flex-direction: column;\\r\\n width: 100%;\\r\\n max-width: 100%;\\r\\n height: 100%;\\r\\n min-height: 400px;\\r\\n overflow-x: hidden;\\r\\n box-sizing: border-box;\\r\\n background: #fafbfc;\\r\\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\\r\\n font-size: 16px !important;\\r\\n color: #303133;\\n}\\r\\n\\r\\n/* 完全隔离主应用 lib-flexible 的影响 */\\n.kb-root[data-v-84b8c396],\\r\\n.kb-root *[data-v-84b8c396],\\r\\n.kb-root *[data-v-84b8c396]::before,\\r\\n.kb-root *[data-v-84b8c396]::after {\\r\\n font-size: 16px !important;\\n}\\r\\n\\r\\n/* ===== 顶部切换栏 ===== */\\n.kb-tabs[data-v-84b8c396] {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 0;\\r\\n height: 48px;\\r\\n padding: 0 20px;\\r\\n background: #fff;\\r\\n border-bottom: 1px solid #ebeef5;\\r\\n flex-shrink: 0;\\r\\n min-width: 0;\\r\\n overflow: hidden;\\n}\\n.kb-tab[data-v-84b8c396] {\\r\\n height: 100%;\\r\\n padding: 0 20px;\\r\\n border: none;\\r\\n background: transparent;\\r\\n font-size: 16px !important;\\r\\n color: #909399;\\r\\n cursor: pointer;\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\r\\n position: relative;\\r\\n transition: all 0.2s;\\r\\n white-space: nowrap;\\r\\n font-weight: 500;\\n}\\n.kb-tab[data-v-84b8c396]:hover {\\r\\n color: #303133;\\n}\\n.kb-tab.active[data-v-84b8c396] {\\r\\n color: #409eff;\\r\\n font-weight: 600;\\n}\\n.kb-tab.active[data-v-84b8c396]::after {\\r\\n content: '';\\r\\n position: absolute;\\r\\n bottom: 0;\\r\\n left: 50%;\\r\\n transform: translateX(-50%);\\r\\n width: 60%;\\r\\n height: 2px;\\r\\n background: #409eff;\\n}\\n.kb-tab svg[data-v-84b8c396],\\r\\n.kb-tab i[data-v-84b8c396] {\\r\\n flex-shrink: 0;\\r\\n transition: transform 0.2s;\\n}\\r\\n\\r\\n/* H5:切换栏等宽平分,避免窄屏溢出;PC 样式不变 */\\n.kb-tabs--mobile[data-v-84b8c396] {\\r\\n padding: 0 8px;\\n}\\n.kb-tabs--mobile .kb-tab[data-v-84b8c396] {\\r\\n flex: 1;\\r\\n min-width: 0;\\r\\n justify-content: center;\\r\\n padding: 0 8px;\\r\\n font-size: 15px !important;\\n}\\r\\n\\r\\n/* PC 顶部栏返回按钮:返回宿主 App 原生页面 */\\n.kb-native-back[data-v-84b8c396] {\\r\\n display: inline-flex;\\r\\n align-items: center;\\r\\n justify-content: center;\\r\\n width: 36px;\\r\\n height: 36px;\\r\\n padding: 0;\\r\\n margin-right: 4px;\\r\\n border: none;\\r\\n border-radius: 10px;\\r\\n background: transparent;\\r\\n color: #333;\\r\\n cursor: pointer;\\r\\n flex-shrink: 0;\\r\\n transition: background 0.2s;\\n}\\n.kb-native-back svg[data-v-84b8c396] {\\r\\n display: block;\\n}\\n.kb-native-back[data-v-84b8c396]:hover {\\r\\n background: #f0f2f5;\\n}\\n.kb-tab:hover svg[data-v-84b8c396],\\r\\n.kb-tab:hover i[data-v-84b8c396] {\\r\\n transform: scale(1.1);\\n}\\r\\n\\r\\n/* ===== 管理布局 ===== */\\n.kb-layout[data-v-84b8c396] {\\r\\n display: flex;\\r\\n align-items: flex-start;\\r\\n flex: 1;\\r\\n min-height: 0;\\r\\n overflow: hidden;\\n}\\n.kb-sidebar[data-v-84b8c396] {\\r\\n width: clamp(240px, 20vw, 280px);\\r\\n border-right: 1px solid #ebeef5;\\r\\n background: #fafbfc;\\r\\n flex-shrink: 0;\\r\\n height: 100%;\\r\\n overflow: hidden;\\n}\\n.kb-main[data-v-84b8c396] {\\r\\n flex: 1;\\r\\n min-width: 0;\\r\\n height: 100%;\\r\\n overflow: hidden;\\r\\n background: #fff;\\n}\\n.kb-chat-view[data-v-84b8c396] {\\r\\n display: flex;\\r\\n flex: 1;\\r\\n min-height: 0;\\r\\n overflow: hidden;\\n}\\n.kb-chat-view[data-v-84b8c396] .kb-chat-root {\\r\\n width: 100%;\\n}\\r\\n\\r\\n/* ===== 引导动画:抵消 .kb-root 全局字号强制规则,还原引导组件原始排版 ===== */\\n.kb-root[data-v-84b8c396] .guide-caption-desc {\\r\\n font-size: 13px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-nav-btn {\\r\\n font-size: 12px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-skip {\\r\\n font-size: 14px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-splash-title {\\r\\n font-size: 20px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-splash-desc {\\r\\n font-size: 14px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-splash-action-btn {\\r\\n font-size: 15px !important;\\n}\\n.kb-root[data-v-84b8c396] .guide-page-tip {\\r\\n font-size: 13px !important;\\n}\\r\\n\\r\\n/* ===== 移动端:管理页导航(目录 ⇄ 文档列表) ===== */\\n.kb-chat-view--mobile[data-v-84b8c396] {\\r\\n width: 100%;\\r\\n max-width: 100%;\\r\\n min-width: 0;\\r\\n overflow: hidden;\\n}\\n.kb-chat-view--mobile[data-v-84b8c396] .kb-chat-root {\\r\\n width: 100%;\\n}\\n.kb-m-layout[data-v-84b8c396] {\\r\\n display: flex;\\r\\n flex-direction: column;\\r\\n flex: 1;\\r\\n min-width: 0;\\r\\n max-width: 100%;\\r\\n min-height: 0;\\r\\n overflow: hidden;\\r\\n background: #fafbfc;\\n}\\n.kb-m-navbar[data-v-84b8c396] {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\r\\n height: 48px;\\r\\n padding: 0 8px 0 4px;\\r\\n background: #fff;\\r\\n border-bottom: 1px solid #ebeef5;\\r\\n flex-shrink: 0;\\n}\\n.kb-m-back[data-v-84b8c396] {\\r\\n width: 36px;\\r\\n height: 36px;\\r\\n display: flex;\\r\\n align-items: center;\\r\\n justify-content: center;\\r\\n border: none;\\r\\n background: transparent;\\r\\n color: #303133;\\r\\n cursor: pointer;\\r\\n border-radius: 8px;\\r\\n transition: background 0.2s;\\n}\\n.kb-m-back[data-v-84b8c396]:active {\\r\\n background: #f0f2f5;\\n}\\n.kb-m-nav-title[data-v-84b8c396] {\\r\\n flex: 1;\\r\\n min-width: 0;\\r\\n overflow: hidden;\\r\\n text-overflow: ellipsis;\\r\\n white-space: nowrap;\\r\\n font-size: 16px;\\r\\n font-weight: 600;\\r\\n color: #303133;\\n}\\n.kb-m-page[data-v-84b8c396] {\\r\\n flex: 1;\\r\\n min-height: 0;\\r\\n overflow: hidden;\\n}\\n.kb-m-full[data-v-84b8c396] {\\r\\n width: 100%;\\r\\n height: 100%;\\n}\\r\\n\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KnowledgeBaseMain.vue?./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1804
1804
|
|
|
1805
1805
|
/***/ }),
|
|
1806
1806
|
|
|
@@ -1811,7 +1811,7 @@ eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../.
|
|
|
1811
1811
|
/*! no static exports found */
|
|
1812
1812
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1813
1813
|
|
|
1814
|
-
eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\r\\n/* 全局(非 scoped):移动端挂载到 body 的 Element 弹层适配($confirm/$alert 消息框等,scoped 样式无法覆盖)。\\r\\n 仅 768px 以下生效,PC 端不受影响 */\\n@media (max-width: 768px) {\\n.el-message-box {\\r\\n width: 86% !important;\\r\\n max-width: 86vw;\\n}\\n.el-message-box .el-message-box__message {\\r\\n word-break: break-all;\\n}\\n}\\r\\n\\r\\n/* 全局:H5 自绘弹框共享控件样式(MobileDialog 内使用,供各组件复用;不依赖 Element UI) */\\n.kbm-field {\\r\\n margin-bottom: 14px;\\n}\\n.kbm-field-label {\\r\\n display: block;\\r\\n margin-bottom: 6px;\\r\\n font-size: 14px;\\r\\n color: #303133;\\n}\\n.kbm-field-row {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\n}\\n.kbm-field-row .kbm-input {\\r\\n flex: 1;\\n}\\n.kbm-field-remove {\\r\\n flex-shrink: 0;\\r\\n width: 30px;\\r\\n height: 30px;\\r\\n display: flex;\\r\\n align-items: center;\\r\\n justify-content: center;\\r\\n border: none;\\r\\n background: transparent;\\r\\n color: #909399;\\r\\n border-radius: 50%;\\n}\\n.kbm-input {\\r\\n width: 100%;\\r\\n height: 38px;\\r\\n padding: 0 10px;\\r\\n border: 1px solid #dcdfe6;\\r\\n border-radius: 6px;\\r\\n background: #fff;\\r\\n font-size: 14px;\\r\\n color: #303133;\\r\\n outline: none;\\r\\n box-sizing: border-box;\\r\\n -webkit-appearance: none;\\r\\n -moz-appearance: none;\\r\\n appearance: none;\\n}\\n.kbm-input:focus {\\r\\n border-color: #1677ff;\\n}\\n.kbm-btn {\\r\\n flex: 1;\\r\\n height: 40px;\\r\\n border: 1px solid #dcdfe6;\\r\\n border-radius: 6px;\\r\\n background: #fff;\\r\\n font-size: 15px;\\r\\n color: #606266;\\n}\\n.kbm-btn.primary {\\r\\n border-color: #1677ff;\\r\\n background: #1677ff;\\r\\n color: #fff;\\n}\\n.kbm-btn:disabled {\\r\\n opacity: 0.6;\\n}\\n.kbm-loading {\\r\\n padding: 20px 0;\\r\\n text-align: center;\\r\\n font-size: 13px;\\r\\n color: #909399;\\n}\\n.kbm-empty {\\r\\n padding: 30px 0;\\r\\n text-align: center;\\r\\n font-size: 13px;\\r\\n color: #909399;\\n}\\r\\n/* 自绘下拉选择(替代原生 select:原生弹层宽度不可控,会超出弹框) */\\n.kbm-select {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n justify-content: space-between;\\r\\n gap: 8px;\\r\\n text-align: left;\\n}\\n.kbm-select-placeholder {\\r\\n color: #a8abb2;\\n}\\n.kbm-select-arrow {\\r\\n flex-shrink: 0;\\r\\n color: #c0c4cc;\\n}\\n.kbm-picker-mask {\\r\\n position: fixed;\\r\\n top: 0;\\r\\n right: 0;\\r\\n bottom: 0;\\r\\n left: 0;\\r\\n z-index: 3100;\\r\\n background: rgba(0, 0, 0, 0.45);\\r\\n display: flex;\\r\\n align-items: flex-end;\\r\\n -webkit-animation: kbmPickerFade 0.18s ease;\\r\\n animation: kbmPickerFade 0.18s ease;\\n}\\n.kbm-picker {\\r\\n width: 100%;\\r\\n max-height: 60vh;\\r\\n display: flex;\\r\\n flex-direction: column;\\r\\n background: #fff;\\r\\n border-radius: 16px 16px 0 0;\\r\\n padding-bottom: env(safe-area-inset-bottom);\\r\\n -webkit-animation: kbmPickerUp 0.22s ease;\\r\\n animation: kbmPickerUp 0.22s ease;\\n}\\n.kbm-picker-title {\\r\\n flex-shrink: 0;\\r\\n padding: 14px 16px 10px;\\r\\n font-size: 13px;\\r\\n color: #909399;\\r\\n text-align: center;\\n}\\n.kbm-picker-list {\\r\\n flex: 1;\\r\\n min-height: 0;\\r\\n overflow-y: auto;\\r\\n -webkit-overflow-scrolling: touch;\\r\\n padding: 0 8px 8px;\\n}\\n.kbm-picker-row {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\r\\n padding: 11px 10px;\\r\\n border-radius: 8px;\\r\\n font-size: 15px;\\r\\n color: #303133;\\n}\\n.kbm-picker-row:active {\\r\\n background: #f5f7fa;\\n}\\n.kbm-picker-row.active {\\r\\n background: #e8f3ff;\\r\\n color: #1677ff;\\n}\\n.kbm-picker-row svg {\\r\\n flex-shrink: 0;\\n}\\n.kbm-picker-name {\\r\\n flex: 1;\\r\\n overflow: hidden;\\r\\n text-overflow: ellipsis;\\r\\n white-space: nowrap;\\n}\\n.kbm-picker-cancel {\\r\\n flex-shrink: 0;\\r\\n border-top: 1px solid #f0f2f5;\\r\\n padding: 13px 0;\\r\\n text-align: center;\\r\\n font-size: 15px;\\r\\n color: #606266;\\n}\\n.kbm-picker-cancel:active {\\r\\n background: #f5f7fa;\\n}\\n@-webkit-keyframes kbmPickerFade {\\nfrom { opacity: 0;\\n}\\nto { opacity: 1;\\n}\\n}\\n@keyframes kbmPickerFade {\\nfrom { opacity: 0;\\n}\\nto { opacity: 1;\\n}\\n}\\n@-webkit-keyframes kbmPickerUp {\\nfrom { transform: translateY(40%);\\n}\\nto { transform: translateY(0);\\n}\\n}\\n@keyframes kbmPickerUp {\\nfrom { transform: translateY(40%);\\n}\\nto { transform: translateY(0);\\n}\\n}\\r\\n\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KnowledgeBaseMain.vue?./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1814
|
+
eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\r\\n/* 全局(非 scoped):移动端挂载到 body 的 Element 弹层适配($confirm/$alert 消息框等,scoped 样式无法覆盖)。\\r\\n 仅 768px 以下生效,PC 端不受影响 */\\n@media (max-width: 768px) {\\n.el-message-box {\\r\\n width: 86% !important;\\r\\n max-width: 86vw;\\n}\\n.el-message-box .el-message-box__message {\\r\\n word-break: break-all;\\n}\\n}\\r\\n\\r\\n/* 全局:H5 自绘弹框共享控件样式(MobileDialog 内使用,供各组件复用;不依赖 Element UI) */\\n.kbm-field {\\r\\n margin-bottom: 14px;\\n}\\n.kbm-field-label {\\r\\n display: block;\\r\\n margin-bottom: 6px;\\r\\n font-size: 14px;\\r\\n color: #303133;\\n}\\n.kbm-field-row {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\n}\\n.kbm-field-row .kbm-input {\\r\\n flex: 1;\\n}\\n.kbm-field-remove {\\r\\n flex-shrink: 0;\\r\\n width: 30px;\\r\\n height: 30px;\\r\\n display: flex;\\r\\n align-items: center;\\r\\n justify-content: center;\\r\\n border: none;\\r\\n background: transparent;\\r\\n color: #909399;\\r\\n border-radius: 50%;\\n}\\n.kbm-input {\\r\\n width: 100%;\\r\\n height: 38px;\\r\\n padding: 0 10px;\\r\\n border: 1px solid #dcdfe6;\\r\\n border-radius: 6px;\\r\\n background: #fff;\\r\\n font-size: 14px;\\r\\n color: #303133;\\r\\n outline: none;\\r\\n box-sizing: border-box;\\r\\n -webkit-appearance: none;\\r\\n -moz-appearance: none;\\r\\n appearance: none;\\n}\\n.kbm-input:focus {\\r\\n border-color: #1677ff;\\n}\\n.kbm-btn {\\r\\n flex: 1;\\r\\n height: 40px;\\r\\n border: 1px solid #dcdfe6;\\r\\n border-radius: 6px;\\r\\n background: #fff;\\r\\n font-size: 15px;\\r\\n color: #606266;\\n}\\n.kbm-btn.primary {\\r\\n border-color: #1677ff;\\r\\n background: #1677ff;\\r\\n color: #fff;\\n}\\n.kbm-btn:disabled {\\r\\n opacity: 0.6;\\n}\\n.kbm-loading {\\r\\n padding: 20px 0;\\r\\n text-align: center;\\r\\n font-size: 13px;\\r\\n color: #909399;\\n}\\n.kbm-empty {\\r\\n padding: 30px 0;\\r\\n text-align: center;\\r\\n font-size: 13px;\\r\\n color: #909399;\\n}\\r\\n/* 自绘下拉选择(替代原生 select:原生弹层宽度不可控,会超出弹框) */\\n.kbm-select {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n justify-content: space-between;\\r\\n gap: 8px;\\r\\n text-align: left;\\n}\\n.kbm-select-placeholder {\\r\\n color: #a8abb2;\\n}\\n.kbm-select-arrow {\\r\\n flex-shrink: 0;\\r\\n color: #c0c4cc;\\n}\\n.kbm-picker-mask {\\r\\n position: fixed;\\r\\n top: 0;\\r\\n right: 0;\\r\\n bottom: 0;\\r\\n left: 0;\\r\\n z-index: 3100;\\r\\n background: rgba(0, 0, 0, 0.45);\\r\\n display: flex;\\r\\n align-items: flex-end;\\r\\n -webkit-animation: kbmPickerFade 0.18s ease;\\r\\n animation: kbmPickerFade 0.18s ease;\\n}\\n.kbm-picker {\\r\\n width: 100%;\\r\\n max-height: 60vh;\\r\\n display: flex;\\r\\n flex-direction: column;\\r\\n background: #fff;\\r\\n border-radius: 16px 16px 0 0;\\r\\n padding-bottom: env(safe-area-inset-bottom);\\r\\n -webkit-animation: kbmPickerUp 0.22s ease;\\r\\n animation: kbmPickerUp 0.22s ease;\\n}\\n.kbm-picker-title {\\r\\n flex-shrink: 0;\\r\\n padding: 14px 16px 10px;\\r\\n font-size: 13px;\\r\\n color: #909399;\\r\\n text-align: center;\\n}\\n.kbm-picker-list {\\r\\n flex: 1;\\r\\n min-height: 0;\\r\\n overflow-y: auto;\\r\\n -webkit-overflow-scrolling: touch;\\r\\n padding: 0 8px 8px;\\n}\\n.kbm-picker-row {\\r\\n display: flex;\\r\\n align-items: center;\\r\\n gap: 8px;\\r\\n padding: 11px 10px;\\r\\n border-radius: 8px;\\r\\n font-size: 15px;\\r\\n color: #303133;\\n}\\n.kbm-picker-row:active {\\r\\n background: #f5f7fa;\\n}\\n.kbm-picker-row.active {\\r\\n background: #e8f3ff;\\r\\n color: #1677ff;\\n}\\n.kbm-picker-row svg {\\r\\n flex-shrink: 0;\\n}\\n.kbm-picker-name {\\r\\n flex: 1;\\r\\n overflow: hidden;\\r\\n text-overflow: ellipsis;\\r\\n white-space: nowrap;\\n}\\n.kbm-picker-cancel {\\r\\n flex-shrink: 0;\\r\\n border-top: 1px solid #f0f2f5;\\r\\n padding: 13px 0;\\r\\n text-align: center;\\r\\n font-size: 15px;\\r\\n color: #606266;\\n}\\n.kbm-picker-cancel:active {\\r\\n background: #f5f7fa;\\n}\\n@-webkit-keyframes kbmPickerFade {\\nfrom { opacity: 0;\\n}\\nto { opacity: 1;\\n}\\n}\\n@keyframes kbmPickerFade {\\nfrom { opacity: 0;\\n}\\nto { opacity: 1;\\n}\\n}\\n@-webkit-keyframes kbmPickerUp {\\nfrom { transform: translateY(40%);\\n}\\nto { transform: translateY(0);\\n}\\n}\\n@keyframes kbmPickerUp {\\nfrom { transform: translateY(40%);\\n}\\nto { transform: translateY(0);\\n}\\n}\\r\\n\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/KnowledgeBaseMain.vue?./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
1815
1815
|
|
|
1816
1816
|
/***/ }),
|
|
1817
1817
|
|
|
@@ -14747,7 +14747,7 @@ eval("// Generated by CoffeeScript 1.12.7\n(function() {\n var XMLDocument, XML
|
|
|
14747
14747
|
/*! exports provided: name, version, main, private, scripts, dependencies, devDependencies, browserslist, directories, keywords, license, files, default */
|
|
14748
14748
|
/***/ (function(module) {
|
|
14749
14749
|
|
|
14750
|
-
eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.26-beta.
|
|
14750
|
+
eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.26-beta.4\\\",\\\"main\\\":\\\"lib/ZjPluginIntelligent.umd.min.js\\\",\\\"private\\\":false,\\\"scripts\\\":{\\\"serve\\\":\\\"set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve\\\",\\\"build\\\":\\\"set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build\\\",\\\"lib\\\":\\\"node scripts/build-lib.js\\\",\\\"lib:test\\\":\\\"node scripts/build-lib.js test\\\"},\\\"dependencies\\\":{\\\"@jboltai/tokui\\\":\\\"^0.1.6\\\",\\\"crypto-js\\\":\\\"^4.2.0\\\",\\\"docx\\\":\\\"^8.5.0\\\",\\\"html2canvas\\\":\\\"^1.4.1\\\",\\\"mammoth\\\":\\\"^1.6.0\\\",\\\"markdown-it\\\":\\\"^13.0.2\\\",\\\"marked\\\":\\\"^4.3.0\\\",\\\"xlsx\\\":\\\"^0.18.5\\\"},\\\"devDependencies\\\":{\\\"@babel/preset-env\\\":\\\"^7.14.9\\\",\\\"@vue/cli-plugin-babel\\\":\\\"~4.5.0\\\",\\\"@vue/cli-plugin-eslint\\\":\\\"~4.5.0\\\",\\\"@vue/cli-plugin-vuex\\\":\\\"~4.5.0\\\",\\\"@vue/cli-service\\\":\\\"~4.5.0\\\",\\\"axios\\\":\\\"^0.27.2\\\",\\\"babel-eslint\\\":\\\"^10.1.0\\\",\\\"babel-plugin-component\\\":\\\"^1.1.1\\\",\\\"compression-webpack-plugin\\\":\\\"^6.0.3\\\",\\\"core-js\\\":\\\"^3.6.5\\\",\\\"element-ui\\\":\\\"^2.15.4\\\",\\\"eslint\\\":\\\"^6.8.0\\\",\\\"eslint-plugin-vue\\\":\\\"^7.13.0\\\",\\\"js-cookie\\\":\\\"^2.2.1\\\",\\\"obs-upload\\\":\\\"^1.0.4\\\",\\\"pako\\\":\\\"^2.1.0\\\",\\\"reconnecting-websocket\\\":\\\"^4.4.0\\\",\\\"sass\\\":\\\"^1.71.0\\\",\\\"sass-loader\\\":\\\"^8.0.2\\\",\\\"snowflake-id\\\":\\\"^1.1.0\\\",\\\"vue\\\":\\\"^2.6.11\\\",\\\"vue-template-compiler\\\":\\\"^2.6.11\\\",\\\"vuex\\\":\\\"^3.4.0\\\",\\\"webpack-bundle-analyzer\\\":\\\"^4.4.2\\\"},\\\"browserslist\\\":[\\\"> 1%\\\",\\\"last 2 versions\\\",\\\"not dead\\\"],\\\"directories\\\":{\\\"lib\\\":\\\"lib\\\"},\\\"keywords\\\":[],\\\"license\\\":\\\"ISC\\\",\\\"files\\\":[\\\"lib\\\"]}\");\n\n//# sourceURL=webpack://ZjPluginIntelligent/./package.json?");
|
|
14751
14751
|
|
|
14752
14752
|
/***/ }),
|
|
14753
14753
|
|