zj-plugin-intelligent 1.2.18-beta.6 → 1.2.18-beta.8

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.
@@ -1000,7 +1000,7 @@ eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@
1000
1000
  /***/ (function(module, exports, __webpack_require__) {
1001
1001
 
1002
1002
  "use strict";
1003
- 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//\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 // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: 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 },\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.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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;'\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 = 7;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 7:\n _context6.next = 9;\n return _this8.selectSession(item.sessionId);\n\n case 9:\n // 移动端:选中后跳到聊天详情页\n if (_this8.isMobileActive) {\n _this8.showH5List = false; // 推入历史状态,使 Android 系统返回手势先触发 popstate 被拦截\n\n history.pushState({\n page: 'kbChatDetail'\n }, '');\n }\n\n case 10:\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 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 _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 4;\n _context8.next = 7;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 7:\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 {\n role: m.role,\n content: m.content || '',\n thinking: m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '',\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 16;\n break;\n\n case 13:\n _context8.prev = 13;\n _context8.t0 = _context8[\"catch\"](4);\n console.error('加载消息失败', _context8.t0);\n\n case 16:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[4, 13]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (!_this12.activeSessionId) {\n _context9.next = 8;\n break;\n }\n\n _context9.prev = 1;\n _context9.next = 4;\n return (0, _kbChat.closeKbSession)(_this12.activeSessionId);\n\n case 4:\n _context9.next = 8;\n break;\n\n case 6:\n _context9.prev = 6;\n _context9.t0 = _context9[\"catch\"](1);\n\n case 8:\n _context9.next = 10;\n return _this12.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this13.inputText.trim();\n\n if (!(!text || !_this13.activeSessionId || _this13.streaming)) {\n _context10.next = 3;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 3:\n _this13.inputText = '';\n requestSessionId = _this13.activeSessionId;\n requestId = ++_this13.streamRequestId;\n\n _this13.clearTypeTimer();\n\n _this13.messages.push({\n role: 'user',\n content: text\n });\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this13.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this13.messages[msgIdx];\n _this13.streamMsg = streamMsg;\n _this13.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this13.streamRequestId && requestSessionId === _this13.activeSessionId && _this13.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 _this13._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this13.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 _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this13.finishStream();\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this13.activeSessionId, text);\n _this13.eventSource = new EventSource(url);\n\n _this13.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n if (!_this13.messages[msgIdx].toolCalls) _this13.messages[msgIdx].toolCalls = [];\n\n _this13.messages[msgIdx].toolCalls.push(e.data);\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n }\n });\n\n _this13.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this13.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 _this13.closeEventSource();\n });\n\n _this13.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n _this13.messages[msgIdx].msgKey = e.data;\n }\n });\n\n _this13.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this13.closeEventSource();\n });\n\n case 28:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\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 _this14 = 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 _this14.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this15 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var list;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (_this15.companyId) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n _context11.prev = 2;\n _context11.next = 5;\n return (0, _kbChat.getKbSessionList)(_this15.companyId);\n\n case 5:\n _context11.t0 = _context11.sent;\n\n if (_context11.t0) {\n _context11.next = 8;\n break;\n }\n\n _context11.t0 = [];\n\n case 8:\n list = _context11.t0;\n _this15.sessionList = list;\n _context11.next = 15;\n break;\n\n case 12:\n _context11.prev = 12;\n _context11.t1 = _context11[\"catch\"](2);\n console.error('刷新会话列表失败', _context11.t1);\n\n case 15:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, 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");
1003
+ 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//\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 // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: 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 },\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.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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;'\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 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 _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 4;\n _context8.next = 7;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 7:\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 {\n role: m.role,\n content: m.content || '',\n thinking: m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '',\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 16;\n break;\n\n case 13:\n _context8.prev = 13;\n _context8.t0 = _context8[\"catch\"](4);\n console.error('加载消息失败', _context8.t0);\n\n case 16:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[4, 13]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (!_this12.activeSessionId) {\n _context9.next = 8;\n break;\n }\n\n _context9.prev = 1;\n _context9.next = 4;\n return (0, _kbChat.closeKbSession)(_this12.activeSessionId);\n\n case 4:\n _context9.next = 8;\n break;\n\n case 6:\n _context9.prev = 6;\n _context9.t0 = _context9[\"catch\"](1);\n\n case 8:\n _context9.next = 10;\n return _this12.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this13.inputText.trim();\n\n if (!(!text || !_this13.activeSessionId || _this13.streaming)) {\n _context10.next = 3;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 3:\n _this13.inputText = '';\n requestSessionId = _this13.activeSessionId;\n requestId = ++_this13.streamRequestId;\n\n _this13.clearTypeTimer();\n\n _this13.messages.push({\n role: 'user',\n content: text\n });\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this13.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this13.messages[msgIdx];\n _this13.streamMsg = streamMsg;\n _this13.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this13.streamRequestId && requestSessionId === _this13.activeSessionId && _this13.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 _this13._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this13.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 _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this13.finishStream();\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this13.activeSessionId, text);\n _this13.eventSource = new EventSource(url);\n\n _this13.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n if (!_this13.messages[msgIdx].toolCalls) _this13.messages[msgIdx].toolCalls = [];\n\n _this13.messages[msgIdx].toolCalls.push(e.data);\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n }\n });\n\n _this13.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this13.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 _this13.closeEventSource();\n });\n\n _this13.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n _this13.messages[msgIdx].msgKey = e.data;\n }\n });\n\n _this13.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this13.closeEventSource();\n });\n\n case 28:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\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 _this14 = 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 _this14.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this15 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var list;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (_this15.companyId) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n _context11.prev = 2;\n _context11.next = 5;\n return (0, _kbChat.getKbSessionList)(_this15.companyId);\n\n case 5:\n _context11.t0 = _context11.sent;\n\n if (_context11.t0) {\n _context11.next = 8;\n break;\n }\n\n _context11.t0 = [];\n\n case 8:\n list = _context11.t0;\n _this15.sessionList = list;\n _context11.next = 15;\n break;\n\n case 12:\n _context11.prev = 12;\n _context11.t1 = _context11[\"catch\"](2);\n console.error('刷新会话列表失败', _context11.t1);\n\n case 15:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, 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");
1004
1004
 
1005
1005
  /***/ }),
1006
1006
 
@@ -14701,7 +14701,7 @@ eval("// Generated by CoffeeScript 1.12.7\n(function() {\n var XMLDocument, XML
14701
14701
  /*! exports provided: name, version, main, private, scripts, dependencies, devDependencies, browserslist, directories, keywords, license, files, default */
14702
14702
  /***/ (function(module) {
14703
14703
 
14704
- eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.18-beta.6\\\",\\\"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?");
14704
+ eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.18-beta.8\\\",\\\"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?");
14705
14705
 
14706
14706
  /***/ }),
14707
14707
 
@@ -15653,7 +15653,7 @@ eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@b
15653
15653
  /***/ (function(module, exports, __webpack_require__) {
15654
15654
 
15655
15655
  "use strict";
15656
- 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.installNativeFallbacks = installNativeFallbacks;\nexports.hasElementUI = hasElementUI;\nexports.flattenFolderTree = flattenFolderTree;\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\n/**\n * 知识库 H5 原生兜底(无 Element UI 依赖)\n *\n * 背景:知识库模块在 PC 宿主(已全局注册 Element UI)中依赖 $message/$confirm/v-loading。\n * H5 宿主没有 Element UI,这些能力缺失会导致点击无响应。\n * 本文件仅在宿主未提供这些能力时,注入极简原生实现(DOM toast + 自绘确认框 + 轻量 loading 遮罩),\n * 不引入 element-ui 库;PC 宿主已有真实现,自动跳过。\n */\nvar FALLBACK_STYLE_ID = 'kb-native-fallback-style';\nvar FALLBACK_CSS = \"\\n.kb-native-toast{position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:99999;\\ndisplay:flex;align-items:center;gap:6px;max-width:76vw;padding:9px 16px;border-radius:6px;\\nbackground:#fff;box-shadow:0 4px 16px rgba(0,0,0,.14);font-size:14px;color:#303133;\\nword-break:break-all;animation:kbToastIn .2s ease;}\\n.kb-native-toast.is-success{color:#52c41a}.kb-native-toast.is-warning{color:#e6a23c}\\n.kb-native-toast.is-error{color:#f56c6c}.kb-native-toast.is-info{color:#303133}\\n@keyframes kbToastIn{from{opacity:0;transform:translate(-50%,-8px)}to{opacity:1;transform:translate(-50%,0)}}\\n.kb-native-loading-mask{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;align-items:center;justify-content:center;\\nbackground:rgba(255,255,255,.75);border-radius:inherit;}\\n.kb-native-loading-spinner{width:26px;height:26px;border:3px solid #e5e7eb;border-top-color:#1677ff;\\nborder-radius:50%;animation:kbSpin .8s linear infinite;}\\n@keyframes kbSpin{to{transform:rotate(360deg)}}\\n/* \\u81EA\\u7ED8\\u786E\\u8BA4\\u6846\\uFF08\\u66FF\\u4EE3 window.confirm\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99998;background:rgba(0,0,0,.45);\\ndisplay:flex;align-items:center;justify-content:center;animation:kbFadeIn .18s ease;}\\n.kb-confirm-box{width:82vw;max-width:320px;background:#fff;border-radius:12px;overflow:hidden;\\nbox-shadow:0 12px 32px rgba(0,0,0,.18);animation:kbPopIn .18s ease;}\\n.kb-confirm-title{padding:18px 20px 0;text-align:center;font-size:16px;font-weight:600;color:#303133;}\\n.kb-confirm-msg{padding:10px 20px 20px;text-align:center;font-size:14px;line-height:1.5;color:#606266;\\nword-break:break-all;max-height:50vh;overflow-y:auto;white-space:pre-wrap;}\\n.kb-confirm-btns{display:flex;border-top:1px solid #f0f2f5;}\\n.kb-confirm-btn{flex:1;height:44px;border:none;background:#fff;font-size:15px;color:#606266;cursor:pointer;}\\n.kb-confirm-btn:active{background:#f5f7fa;}\\n.kb-confirm-btn + .kb-confirm-btn{border-left:1px solid #f0f2f5;}\\n.kb-confirm-btn.primary{color:#1677ff;font-weight:600;}\\n.kb-confirm-btn.primary.is-danger{color:#f56c6c;}\\n@keyframes kbFadeIn{from{opacity:0}to{opacity:1}}\\n@keyframes kbPopIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}\\n\";\n\nfunction injectStyle() {\n if (typeof document === 'undefined' || document.getElementById(FALLBACK_STYLE_ID)) return;\n var tag = document.createElement('style');\n tag.id = FALLBACK_STYLE_ID;\n tag.textContent = FALLBACK_CSS;\n document.head.appendChild(tag);\n}\n\nfunction showToast(opts) {\n if (typeof document === 'undefined') return;\n injectStyle();\n var o = typeof opts === 'string' ? {\n message: opts\n } : opts || {};\n var el = document.createElement('div');\n el.className = \"kb-native-toast is-\".concat(o.type || 'info');\n el.textContent = o.message || '';\n document.body.appendChild(el);\n var duration = o.duration || 2200;\n setTimeout(function () {\n el.parentNode && el.parentNode.removeChild(el);\n }, duration);\n}\n\nfunction escapeHtml(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;'\n }[c];\n });\n}\n/**\n * 自绘确认框(不调起系统 window.confirm)\n * 返回 Promise:确定 resolve,取消 reject('cancel'),与 Element UI $confirm 用法一致\n */\n\n\nfunction showConfirm(opts) {\n if (typeof document === 'undefined') {\n return window.confirm(\"\".concat(opts.title || '提示', \"\\n\").concat(opts.message || '')) ? Promise.resolve() : Promise.reject(new Error('cancel'));\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n var isDanger = opts.type === 'error';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(opts.title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(opts.message || ''), \"</div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\").concat(isDanger ? ' is-danger' : '', \"\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n\n var done = function done(fn, arg) {\n return function () {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n };\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', done(reject, new Error('cancel')));\n btns[1].addEventListener('click', done(resolve));\n });\n}\n/**\n * 给 Vue 原型/指令补齐知识库依赖的最小能力(已存在则不覆盖)\n */\n\n\nfunction installNativeFallbacks(Vue) {\n injectStyle();\n\n if (!Vue.prototype.$confirm) {\n Vue.prototype.$confirm = function kbNativeConfirm(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showConfirm((0, _objectSpread2.default)({\n title: title,\n message: message\n }, options || {}));\n };\n }\n\n if (!Vue.prototype.$message) {\n var message = function message(opts) {\n return showToast(opts);\n };\n\n ['success', 'warning', 'error', 'info'].forEach(function (type) {\n message[type] = function (opts) {\n return showToast(typeof opts === 'string' ? {\n message: opts,\n type: type\n } : (0, _objectSpread2.default)((0, _objectSpread2.default)({}, opts), {}, {\n type: type\n }));\n };\n });\n Vue.prototype.$message = message;\n }\n\n if (!Vue.directive('loading')) {\n var toggle = function toggle(el, on) {\n if (on && !el.__kbLoadingMask) {\n var mask = document.createElement('div');\n mask.className = 'kb-native-loading-mask';\n mask.innerHTML = '<div class=\"kb-native-loading-spinner\"></div>';\n var pos = (el.currentStyle || window.getComputedStyle && getComputedStyle(el) || {}).position;\n if (!pos || pos === 'static') el.style.position = 'relative';\n el.appendChild(mask);\n el.__kbLoadingMask = mask;\n } else if (!on && el.__kbLoadingMask) {\n el.__kbLoadingMask.parentNode && el.__kbLoadingMask.parentNode.removeChild(el.__kbLoadingMask);\n el.__kbLoadingMask = null;\n }\n };\n\n Vue.directive('loading', {\n bind: function bind(el, binding) {\n toggle(el, binding.value);\n },\n update: function update(el, binding) {\n toggle(el, binding.value);\n },\n unbind: function unbind(el) {\n toggle(el, false);\n }\n });\n }\n}\n/**\n * 判断宿主是否注册了 Element UI(检查全局/原型链上的 el-button 组件)\n *\n * 背景:平板横屏等宽屏场景若只按 innerWidth 判定移动端,会把无 Element UI 的 H5 宿主\n * 误判为 PC 模式,渲染出大量 el-* 未知元素导致页面不可用。\n * 因此无 Element UI 的宿主一律走自绘 UI 分支(isMobile=true),与宽度无关。\n *\n * @param {Object} vm 组件实例(this)或 Vue 构造器\n */\n\n\nfunction hasElementUI(vm) {\n try {\n var comps = vm && (vm.$options && vm.$options.components || vm.options && vm.options.components) || {};\n return !!(comps.elButton || comps.ElButton || comps['el-button']);\n } catch (e) {\n return false;\n }\n}\n/**\n * 展平文件夹树为 {node, depth} 数组(前序遍历,全部展开),供移动端下拉/列表选择\n */\n\n\nfunction flattenFolderTree(nodes) {\n var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n var out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];\n ;\n (nodes || []).forEach(function (n) {\n out.push({\n node: n,\n depth: depth\n });\n if (n.children && n.children.length) flattenFolderTree(n.children, depth + 1, out);\n });\n return out;\n}\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/native.js?");
15656
+ 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.installNativeFallbacks = installNativeFallbacks;\nexports.hasElementUI = hasElementUI;\nexports.flattenFolderTree = flattenFolderTree;\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\n/**\n * 知识库 H5 原生兜底(无 Element UI 依赖)\n *\n * 背景:知识库模块在 PC 宿主(已全局注册 Element UI)中依赖 $message/$confirm/v-loading。\n * H5 宿主没有 Element UI,这些能力缺失会导致点击无响应。\n * 本文件仅在宿主未提供这些能力时,注入极简原生实现(DOM toast + 自绘确认框 + 轻量 loading 遮罩),\n * 不引入 element-ui 库;PC 宿主已有真实现,自动跳过。\n */\nvar FALLBACK_STYLE_ID = 'kb-native-fallback-style';\nvar FALLBACK_CSS = \"\\n.kb-native-toast{position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:99999;\\ndisplay:flex;align-items:center;gap:6px;max-width:76vw;padding:9px 16px;border-radius:6px;\\nbackground:#fff;box-shadow:0 4px 16px rgba(0,0,0,.14);font-size:14px;color:#303133;\\nword-break:break-all;animation:kbToastIn .2s ease;}\\n.kb-native-toast.is-success{color:#52c41a}.kb-native-toast.is-warning{color:#e6a23c}\\n.kb-native-toast.is-error{color:#f56c6c}.kb-native-toast.is-info{color:#303133}\\n@keyframes kbToastIn{from{opacity:0;transform:translate(-50%,-8px)}to{opacity:1;transform:translate(-50%,0)}}\\n.kb-native-loading-mask{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;align-items:center;justify-content:center;\\nbackground:rgba(255,255,255,.75);border-radius:inherit;}\\n.kb-native-loading-spinner{width:26px;height:26px;border:3px solid #e5e7eb;border-top-color:#1677ff;\\nborder-radius:50%;animation:kbSpin .8s linear infinite;}\\n@keyframes kbSpin{to{transform:rotate(360deg)}}\\n/* \\u81EA\\u7ED8\\u786E\\u8BA4\\u6846\\uFF08\\u66FF\\u4EE3 window.confirm\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99998;background:rgba(0,0,0,.45);\\ndisplay:flex;align-items:center;justify-content:center;animation:kbFadeIn .18s ease;}\\n.kb-confirm-box{width:82vw;max-width:320px;background:#fff;border-radius:12px;overflow:hidden;\\nbox-shadow:0 12px 32px rgba(0,0,0,.18);animation:kbPopIn .18s ease;}\\n.kb-confirm-title{padding:18px 20px 0;text-align:center;font-size:16px;font-weight:600;color:#303133;}\\n.kb-confirm-msg{padding:10px 20px 20px;text-align:center;font-size:14px;line-height:1.5;color:#606266;\\nword-break:break-all;max-height:50vh;overflow-y:auto;white-space:pre-wrap;}\\n/* \\u81EA\\u7ED8\\u8F93\\u5165\\u6846\\u5185\\u7684 input\\uFF08\\u66FF\\u4EE3 Element UI $prompt\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-input{margin:14px 20px 18px;padding:0 12px;height:40px;border:1px solid #dcdfe6;border-radius:8px;\\nfont-size:15px;color:#303133;outline:none;width:calc(100% - 40px);box-sizing:border-box;}\\n.kb-confirm-input:focus{border-color:#1677ff;}\\n.kb-confirm-error{padding:0 20px 12px;margin-top:-8px;text-align:left;font-size:12px;color:#f56c6c;}\\n.kb-confirm-btns{display:flex;border-top:1px solid #f0f2f5;}\\n.kb-confirm-btn{flex:1;height:44px;border:none;background:#fff;font-size:15px;color:#606266;cursor:pointer;}\\n.kb-confirm-btn:active{background:#f5f7fa;}\\n.kb-confirm-btn + .kb-confirm-btn{border-left:1px solid #f0f2f5;}\\n.kb-confirm-btn.primary{color:#1677ff;font-weight:600;}\\n.kb-confirm-btn.primary.is-danger{color:#f56c6c;}\\n@keyframes kbFadeIn{from{opacity:0}to{opacity:1}}\\n@keyframes kbPopIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}\\n\";\n\nfunction injectStyle() {\n if (typeof document === 'undefined' || document.getElementById(FALLBACK_STYLE_ID)) return;\n var tag = document.createElement('style');\n tag.id = FALLBACK_STYLE_ID;\n tag.textContent = FALLBACK_CSS;\n document.head.appendChild(tag);\n}\n\nfunction showToast(opts) {\n if (typeof document === 'undefined') return;\n injectStyle();\n var o = typeof opts === 'string' ? {\n message: opts\n } : opts || {};\n var el = document.createElement('div');\n el.className = \"kb-native-toast is-\".concat(o.type || 'info');\n el.textContent = o.message || '';\n document.body.appendChild(el);\n var duration = o.duration || 2200;\n setTimeout(function () {\n el.parentNode && el.parentNode.removeChild(el);\n }, duration);\n}\n\nfunction escapeHtml(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;'\n }[c];\n });\n}\n/**\n * 自绘确认框(不调起系统 window.confirm)\n * 返回 Promise:确定 resolve,取消 reject('cancel'),与 Element UI $confirm 用法一致\n */\n\n\nfunction showConfirm(opts) {\n if (typeof document === 'undefined') {\n return window.confirm(\"\".concat(opts.title || '提示', \"\\n\").concat(opts.message || '')) ? Promise.resolve() : Promise.reject(new Error('cancel'));\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n var isDanger = opts.type === 'error';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(opts.title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(opts.message || ''), \"</div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\").concat(isDanger ? ' is-danger' : '', \"\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n\n var done = function done(fn, arg) {\n return function () {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n };\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', done(reject, new Error('cancel')));\n btns[1].addEventListener('click', done(resolve));\n });\n}\n/**\n * 自绘输入框(不调起系统 window.prompt)\n * 返回 Promise:确定 resolve({ value }),取消 reject('cancel'),与 Element UI $prompt 用法一致\n * 支持 inputValue / inputPattern / inputErrorMessage\n */\n\n\nfunction showPrompt(message, title, options) {\n var opts = options || {};\n\n if (typeof document === 'undefined') {\n var val = window.prompt(\"\".concat(title || '提示', \"\\n\").concat(message || ''), opts.inputValue || '');\n return val === null ? Promise.reject(new Error('cancel')) : Promise.resolve({\n value: val\n });\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(message || ''), \"</div>\\n <input type=\\\"text\\\" class=\\\"kb-confirm-input\\\" />\\n <div class=\\\"kb-confirm-error\\\" style=\\\"display:none\\\"></div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n var input = mask.querySelector('.kb-confirm-input');\n var errEl = mask.querySelector('.kb-confirm-error');\n input.value = opts.inputValue || '';\n setTimeout(function () {\n return input.focus();\n }, 30);\n\n var close = function close(fn, arg) {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n }; // 校验失败时不关闭弹窗,就地提示(与 Element UI $prompt 行为一致)\n\n\n var submit = function submit() {\n var value = input.value;\n\n if (opts.inputPattern && !opts.inputPattern.test(value)) {\n errEl.textContent = opts.inputErrorMessage || '输入不合法';\n errEl.style.display = 'block';\n return;\n }\n\n close(resolve, {\n value: value\n });\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', function () {\n return close(reject, new Error('cancel'));\n });\n btns[1].addEventListener('click', submit);\n input.addEventListener('keydown', function (e) {\n if (e.key === 'Enter') submit();else if (e.key === 'Escape') close(reject, new Error('cancel'));\n });\n });\n}\n/**\n * 给 Vue 原型/指令补齐知识库依赖的最小能力(已存在则不覆盖)\n */\n\n\nfunction installNativeFallbacks(Vue) {\n injectStyle();\n\n if (!Vue.prototype.$confirm) {\n Vue.prototype.$confirm = function kbNativeConfirm(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showConfirm((0, _objectSpread2.default)({\n title: title,\n message: message\n }, options || {}));\n };\n }\n\n if (!Vue.prototype.$prompt) {\n Vue.prototype.$prompt = function kbNativePrompt(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showPrompt(message, title, options);\n };\n }\n\n if (!Vue.prototype.$message) {\n var message = function message(opts) {\n return showToast(opts);\n };\n\n ['success', 'warning', 'error', 'info'].forEach(function (type) {\n message[type] = function (opts) {\n return showToast(typeof opts === 'string' ? {\n message: opts,\n type: type\n } : (0, _objectSpread2.default)((0, _objectSpread2.default)({}, opts), {}, {\n type: type\n }));\n };\n });\n Vue.prototype.$message = message;\n }\n\n if (!Vue.directive('loading')) {\n var toggle = function toggle(el, on) {\n if (on && !el.__kbLoadingMask) {\n var mask = document.createElement('div');\n mask.className = 'kb-native-loading-mask';\n mask.innerHTML = '<div class=\"kb-native-loading-spinner\"></div>';\n var pos = (el.currentStyle || window.getComputedStyle && getComputedStyle(el) || {}).position;\n if (!pos || pos === 'static') el.style.position = 'relative';\n el.appendChild(mask);\n el.__kbLoadingMask = mask;\n } else if (!on && el.__kbLoadingMask) {\n el.__kbLoadingMask.parentNode && el.__kbLoadingMask.parentNode.removeChild(el.__kbLoadingMask);\n el.__kbLoadingMask = null;\n }\n };\n\n Vue.directive('loading', {\n bind: function bind(el, binding) {\n toggle(el, binding.value);\n },\n update: function update(el, binding) {\n toggle(el, binding.value);\n },\n unbind: function unbind(el) {\n toggle(el, false);\n }\n });\n }\n}\n/**\n * 判断宿主是否注册了 Element UI(检查全局/原型链上的 el-button 组件)\n *\n * 背景:平板横屏等宽屏场景若只按 innerWidth 判定移动端,会把无 Element UI 的 H5 宿主\n * 误判为 PC 模式,渲染出大量 el-* 未知元素导致页面不可用。\n * 因此无 Element UI 的宿主一律走自绘 UI 分支(isMobile=true),与宽度无关。\n *\n * @param {Object} vm 组件实例(this)或 Vue 构造器\n */\n\n\nfunction hasElementUI(vm) {\n try {\n var comps = vm && (vm.$options && vm.$options.components || vm.options && vm.options.components) || {};\n return !!(comps.elButton || comps.ElButton || comps['el-button']);\n } catch (e) {\n return false;\n }\n}\n/**\n * 展平文件夹树为 {node, depth} 数组(前序遍历,全部展开),供移动端下拉/列表选择\n */\n\n\nfunction flattenFolderTree(nodes) {\n var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n var out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];\n ;\n (nodes || []).forEach(function (n) {\n out.push({\n node: n,\n depth: depth\n });\n if (n.children && n.children.length) flattenFolderTree(n.children, depth + 1, out);\n });\n return out;\n}\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/native.js?");
15657
15657
 
15658
15658
  /***/ }),
15659
15659
 
@@ -1009,7 +1009,7 @@ eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@
1009
1009
  /***/ (function(module, exports, __webpack_require__) {
1010
1010
 
1011
1011
  "use strict";
1012
- 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//\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 // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: 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 },\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.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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;'\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 = 7;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 7:\n _context6.next = 9;\n return _this8.selectSession(item.sessionId);\n\n case 9:\n // 移动端:选中后跳到聊天详情页\n if (_this8.isMobileActive) {\n _this8.showH5List = false; // 推入历史状态,使 Android 系统返回手势先触发 popstate 被拦截\n\n history.pushState({\n page: 'kbChatDetail'\n }, '');\n }\n\n case 10:\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 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 _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 4;\n _context8.next = 7;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 7:\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 {\n role: m.role,\n content: m.content || '',\n thinking: m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '',\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 16;\n break;\n\n case 13:\n _context8.prev = 13;\n _context8.t0 = _context8[\"catch\"](4);\n console.error('加载消息失败', _context8.t0);\n\n case 16:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[4, 13]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (!_this12.activeSessionId) {\n _context9.next = 8;\n break;\n }\n\n _context9.prev = 1;\n _context9.next = 4;\n return (0, _kbChat.closeKbSession)(_this12.activeSessionId);\n\n case 4:\n _context9.next = 8;\n break;\n\n case 6:\n _context9.prev = 6;\n _context9.t0 = _context9[\"catch\"](1);\n\n case 8:\n _context9.next = 10;\n return _this12.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this13.inputText.trim();\n\n if (!(!text || !_this13.activeSessionId || _this13.streaming)) {\n _context10.next = 3;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 3:\n _this13.inputText = '';\n requestSessionId = _this13.activeSessionId;\n requestId = ++_this13.streamRequestId;\n\n _this13.clearTypeTimer();\n\n _this13.messages.push({\n role: 'user',\n content: text\n });\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this13.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this13.messages[msgIdx];\n _this13.streamMsg = streamMsg;\n _this13.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this13.streamRequestId && requestSessionId === _this13.activeSessionId && _this13.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 _this13._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this13.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 _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this13.finishStream();\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this13.activeSessionId, text);\n _this13.eventSource = new EventSource(url);\n\n _this13.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n if (!_this13.messages[msgIdx].toolCalls) _this13.messages[msgIdx].toolCalls = [];\n\n _this13.messages[msgIdx].toolCalls.push(e.data);\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n }\n });\n\n _this13.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this13.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 _this13.closeEventSource();\n });\n\n _this13.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n _this13.messages[msgIdx].msgKey = e.data;\n }\n });\n\n _this13.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this13.closeEventSource();\n });\n\n case 28:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\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 _this14 = 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 _this14.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this15 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var list;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (_this15.companyId) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n _context11.prev = 2;\n _context11.next = 5;\n return (0, _kbChat.getKbSessionList)(_this15.companyId);\n\n case 5:\n _context11.t0 = _context11.sent;\n\n if (_context11.t0) {\n _context11.next = 8;\n break;\n }\n\n _context11.t0 = [];\n\n case 8:\n list = _context11.t0;\n _this15.sessionList = list;\n _context11.next = 15;\n break;\n\n case 12:\n _context11.prev = 12;\n _context11.t1 = _context11[\"catch\"](2);\n console.error('刷新会话列表失败', _context11.t1);\n\n case 15:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, 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");
1012
+ 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//\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 // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: 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 },\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.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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;'\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 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 _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 4;\n _context8.next = 7;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 7:\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 {\n role: m.role,\n content: m.content || '',\n thinking: m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '',\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 16;\n break;\n\n case 13:\n _context8.prev = 13;\n _context8.t0 = _context8[\"catch\"](4);\n console.error('加载消息失败', _context8.t0);\n\n case 16:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[4, 13]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (!_this12.activeSessionId) {\n _context9.next = 8;\n break;\n }\n\n _context9.prev = 1;\n _context9.next = 4;\n return (0, _kbChat.closeKbSession)(_this12.activeSessionId);\n\n case 4:\n _context9.next = 8;\n break;\n\n case 6:\n _context9.prev = 6;\n _context9.t0 = _context9[\"catch\"](1);\n\n case 8:\n _context9.next = 10;\n return _this12.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this13.inputText.trim();\n\n if (!(!text || !_this13.activeSessionId || _this13.streaming)) {\n _context10.next = 3;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 3:\n _this13.inputText = '';\n requestSessionId = _this13.activeSessionId;\n requestId = ++_this13.streamRequestId;\n\n _this13.clearTypeTimer();\n\n _this13.messages.push({\n role: 'user',\n content: text\n });\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this13.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this13.messages[msgIdx];\n _this13.streamMsg = streamMsg;\n _this13.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this13.streamRequestId && requestSessionId === _this13.activeSessionId && _this13.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 _this13._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this13.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 _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this13.finishStream();\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this13.activeSessionId, text);\n _this13.eventSource = new EventSource(url);\n\n _this13.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n if (!_this13.messages[msgIdx].toolCalls) _this13.messages[msgIdx].toolCalls = [];\n\n _this13.messages[msgIdx].toolCalls.push(e.data);\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n }\n });\n\n _this13.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this13.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 _this13.closeEventSource();\n });\n\n _this13.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n _this13.messages[msgIdx].msgKey = e.data;\n }\n });\n\n _this13.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this13.closeEventSource();\n });\n\n case 28:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\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 _this14 = 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 _this14.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this15 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var list;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (_this15.companyId) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n _context11.prev = 2;\n _context11.next = 5;\n return (0, _kbChat.getKbSessionList)(_this15.companyId);\n\n case 5:\n _context11.t0 = _context11.sent;\n\n if (_context11.t0) {\n _context11.next = 8;\n break;\n }\n\n _context11.t0 = [];\n\n case 8:\n list = _context11.t0;\n _this15.sessionList = list;\n _context11.next = 15;\n break;\n\n case 12:\n _context11.prev = 12;\n _context11.t1 = _context11[\"catch\"](2);\n console.error('刷新会话列表失败', _context11.t1);\n\n case 15:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, 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");
1013
1013
 
1014
1014
  /***/ }),
1015
1015
 
@@ -14710,7 +14710,7 @@ eval("// Generated by CoffeeScript 1.12.7\n(function() {\n var XMLDocument, XML
14710
14710
  /*! exports provided: name, version, main, private, scripts, dependencies, devDependencies, browserslist, directories, keywords, license, files, default */
14711
14711
  /***/ (function(module) {
14712
14712
 
14713
- eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.18-beta.6\\\",\\\"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?");
14713
+ eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.18-beta.8\\\",\\\"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?");
14714
14714
 
14715
14715
  /***/ }),
14716
14716
 
@@ -15662,7 +15662,7 @@ eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@b
15662
15662
  /***/ (function(module, exports, __webpack_require__) {
15663
15663
 
15664
15664
  "use strict";
15665
- 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.installNativeFallbacks = installNativeFallbacks;\nexports.hasElementUI = hasElementUI;\nexports.flattenFolderTree = flattenFolderTree;\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\n/**\n * 知识库 H5 原生兜底(无 Element UI 依赖)\n *\n * 背景:知识库模块在 PC 宿主(已全局注册 Element UI)中依赖 $message/$confirm/v-loading。\n * H5 宿主没有 Element UI,这些能力缺失会导致点击无响应。\n * 本文件仅在宿主未提供这些能力时,注入极简原生实现(DOM toast + 自绘确认框 + 轻量 loading 遮罩),\n * 不引入 element-ui 库;PC 宿主已有真实现,自动跳过。\n */\nvar FALLBACK_STYLE_ID = 'kb-native-fallback-style';\nvar FALLBACK_CSS = \"\\n.kb-native-toast{position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:99999;\\ndisplay:flex;align-items:center;gap:6px;max-width:76vw;padding:9px 16px;border-radius:6px;\\nbackground:#fff;box-shadow:0 4px 16px rgba(0,0,0,.14);font-size:14px;color:#303133;\\nword-break:break-all;animation:kbToastIn .2s ease;}\\n.kb-native-toast.is-success{color:#52c41a}.kb-native-toast.is-warning{color:#e6a23c}\\n.kb-native-toast.is-error{color:#f56c6c}.kb-native-toast.is-info{color:#303133}\\n@keyframes kbToastIn{from{opacity:0;transform:translate(-50%,-8px)}to{opacity:1;transform:translate(-50%,0)}}\\n.kb-native-loading-mask{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;align-items:center;justify-content:center;\\nbackground:rgba(255,255,255,.75);border-radius:inherit;}\\n.kb-native-loading-spinner{width:26px;height:26px;border:3px solid #e5e7eb;border-top-color:#1677ff;\\nborder-radius:50%;animation:kbSpin .8s linear infinite;}\\n@keyframes kbSpin{to{transform:rotate(360deg)}}\\n/* \\u81EA\\u7ED8\\u786E\\u8BA4\\u6846\\uFF08\\u66FF\\u4EE3 window.confirm\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99998;background:rgba(0,0,0,.45);\\ndisplay:flex;align-items:center;justify-content:center;animation:kbFadeIn .18s ease;}\\n.kb-confirm-box{width:82vw;max-width:320px;background:#fff;border-radius:12px;overflow:hidden;\\nbox-shadow:0 12px 32px rgba(0,0,0,.18);animation:kbPopIn .18s ease;}\\n.kb-confirm-title{padding:18px 20px 0;text-align:center;font-size:16px;font-weight:600;color:#303133;}\\n.kb-confirm-msg{padding:10px 20px 20px;text-align:center;font-size:14px;line-height:1.5;color:#606266;\\nword-break:break-all;max-height:50vh;overflow-y:auto;white-space:pre-wrap;}\\n.kb-confirm-btns{display:flex;border-top:1px solid #f0f2f5;}\\n.kb-confirm-btn{flex:1;height:44px;border:none;background:#fff;font-size:15px;color:#606266;cursor:pointer;}\\n.kb-confirm-btn:active{background:#f5f7fa;}\\n.kb-confirm-btn + .kb-confirm-btn{border-left:1px solid #f0f2f5;}\\n.kb-confirm-btn.primary{color:#1677ff;font-weight:600;}\\n.kb-confirm-btn.primary.is-danger{color:#f56c6c;}\\n@keyframes kbFadeIn{from{opacity:0}to{opacity:1}}\\n@keyframes kbPopIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}\\n\";\n\nfunction injectStyle() {\n if (typeof document === 'undefined' || document.getElementById(FALLBACK_STYLE_ID)) return;\n var tag = document.createElement('style');\n tag.id = FALLBACK_STYLE_ID;\n tag.textContent = FALLBACK_CSS;\n document.head.appendChild(tag);\n}\n\nfunction showToast(opts) {\n if (typeof document === 'undefined') return;\n injectStyle();\n var o = typeof opts === 'string' ? {\n message: opts\n } : opts || {};\n var el = document.createElement('div');\n el.className = \"kb-native-toast is-\".concat(o.type || 'info');\n el.textContent = o.message || '';\n document.body.appendChild(el);\n var duration = o.duration || 2200;\n setTimeout(function () {\n el.parentNode && el.parentNode.removeChild(el);\n }, duration);\n}\n\nfunction escapeHtml(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;'\n }[c];\n });\n}\n/**\n * 自绘确认框(不调起系统 window.confirm)\n * 返回 Promise:确定 resolve,取消 reject('cancel'),与 Element UI $confirm 用法一致\n */\n\n\nfunction showConfirm(opts) {\n if (typeof document === 'undefined') {\n return window.confirm(\"\".concat(opts.title || '提示', \"\\n\").concat(opts.message || '')) ? Promise.resolve() : Promise.reject(new Error('cancel'));\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n var isDanger = opts.type === 'error';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(opts.title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(opts.message || ''), \"</div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\").concat(isDanger ? ' is-danger' : '', \"\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n\n var done = function done(fn, arg) {\n return function () {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n };\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', done(reject, new Error('cancel')));\n btns[1].addEventListener('click', done(resolve));\n });\n}\n/**\n * 给 Vue 原型/指令补齐知识库依赖的最小能力(已存在则不覆盖)\n */\n\n\nfunction installNativeFallbacks(Vue) {\n injectStyle();\n\n if (!Vue.prototype.$confirm) {\n Vue.prototype.$confirm = function kbNativeConfirm(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showConfirm((0, _objectSpread2.default)({\n title: title,\n message: message\n }, options || {}));\n };\n }\n\n if (!Vue.prototype.$message) {\n var message = function message(opts) {\n return showToast(opts);\n };\n\n ['success', 'warning', 'error', 'info'].forEach(function (type) {\n message[type] = function (opts) {\n return showToast(typeof opts === 'string' ? {\n message: opts,\n type: type\n } : (0, _objectSpread2.default)((0, _objectSpread2.default)({}, opts), {}, {\n type: type\n }));\n };\n });\n Vue.prototype.$message = message;\n }\n\n if (!Vue.directive('loading')) {\n var toggle = function toggle(el, on) {\n if (on && !el.__kbLoadingMask) {\n var mask = document.createElement('div');\n mask.className = 'kb-native-loading-mask';\n mask.innerHTML = '<div class=\"kb-native-loading-spinner\"></div>';\n var pos = (el.currentStyle || window.getComputedStyle && getComputedStyle(el) || {}).position;\n if (!pos || pos === 'static') el.style.position = 'relative';\n el.appendChild(mask);\n el.__kbLoadingMask = mask;\n } else if (!on && el.__kbLoadingMask) {\n el.__kbLoadingMask.parentNode && el.__kbLoadingMask.parentNode.removeChild(el.__kbLoadingMask);\n el.__kbLoadingMask = null;\n }\n };\n\n Vue.directive('loading', {\n bind: function bind(el, binding) {\n toggle(el, binding.value);\n },\n update: function update(el, binding) {\n toggle(el, binding.value);\n },\n unbind: function unbind(el) {\n toggle(el, false);\n }\n });\n }\n}\n/**\n * 判断宿主是否注册了 Element UI(检查全局/原型链上的 el-button 组件)\n *\n * 背景:平板横屏等宽屏场景若只按 innerWidth 判定移动端,会把无 Element UI 的 H5 宿主\n * 误判为 PC 模式,渲染出大量 el-* 未知元素导致页面不可用。\n * 因此无 Element UI 的宿主一律走自绘 UI 分支(isMobile=true),与宽度无关。\n *\n * @param {Object} vm 组件实例(this)或 Vue 构造器\n */\n\n\nfunction hasElementUI(vm) {\n try {\n var comps = vm && (vm.$options && vm.$options.components || vm.options && vm.options.components) || {};\n return !!(comps.elButton || comps.ElButton || comps['el-button']);\n } catch (e) {\n return false;\n }\n}\n/**\n * 展平文件夹树为 {node, depth} 数组(前序遍历,全部展开),供移动端下拉/列表选择\n */\n\n\nfunction flattenFolderTree(nodes) {\n var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n var out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];\n ;\n (nodes || []).forEach(function (n) {\n out.push({\n node: n,\n depth: depth\n });\n if (n.children && n.children.length) flattenFolderTree(n.children, depth + 1, out);\n });\n return out;\n}\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/native.js?");
15665
+ 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.installNativeFallbacks = installNativeFallbacks;\nexports.hasElementUI = hasElementUI;\nexports.flattenFolderTree = flattenFolderTree;\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\n/**\n * 知识库 H5 原生兜底(无 Element UI 依赖)\n *\n * 背景:知识库模块在 PC 宿主(已全局注册 Element UI)中依赖 $message/$confirm/v-loading。\n * H5 宿主没有 Element UI,这些能力缺失会导致点击无响应。\n * 本文件仅在宿主未提供这些能力时,注入极简原生实现(DOM toast + 自绘确认框 + 轻量 loading 遮罩),\n * 不引入 element-ui 库;PC 宿主已有真实现,自动跳过。\n */\nvar FALLBACK_STYLE_ID = 'kb-native-fallback-style';\nvar FALLBACK_CSS = \"\\n.kb-native-toast{position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:99999;\\ndisplay:flex;align-items:center;gap:6px;max-width:76vw;padding:9px 16px;border-radius:6px;\\nbackground:#fff;box-shadow:0 4px 16px rgba(0,0,0,.14);font-size:14px;color:#303133;\\nword-break:break-all;animation:kbToastIn .2s ease;}\\n.kb-native-toast.is-success{color:#52c41a}.kb-native-toast.is-warning{color:#e6a23c}\\n.kb-native-toast.is-error{color:#f56c6c}.kb-native-toast.is-info{color:#303133}\\n@keyframes kbToastIn{from{opacity:0;transform:translate(-50%,-8px)}to{opacity:1;transform:translate(-50%,0)}}\\n.kb-native-loading-mask{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;align-items:center;justify-content:center;\\nbackground:rgba(255,255,255,.75);border-radius:inherit;}\\n.kb-native-loading-spinner{width:26px;height:26px;border:3px solid #e5e7eb;border-top-color:#1677ff;\\nborder-radius:50%;animation:kbSpin .8s linear infinite;}\\n@keyframes kbSpin{to{transform:rotate(360deg)}}\\n/* \\u81EA\\u7ED8\\u786E\\u8BA4\\u6846\\uFF08\\u66FF\\u4EE3 window.confirm\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99998;background:rgba(0,0,0,.45);\\ndisplay:flex;align-items:center;justify-content:center;animation:kbFadeIn .18s ease;}\\n.kb-confirm-box{width:82vw;max-width:320px;background:#fff;border-radius:12px;overflow:hidden;\\nbox-shadow:0 12px 32px rgba(0,0,0,.18);animation:kbPopIn .18s ease;}\\n.kb-confirm-title{padding:18px 20px 0;text-align:center;font-size:16px;font-weight:600;color:#303133;}\\n.kb-confirm-msg{padding:10px 20px 20px;text-align:center;font-size:14px;line-height:1.5;color:#606266;\\nword-break:break-all;max-height:50vh;overflow-y:auto;white-space:pre-wrap;}\\n/* \\u81EA\\u7ED8\\u8F93\\u5165\\u6846\\u5185\\u7684 input\\uFF08\\u66FF\\u4EE3 Element UI $prompt\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-input{margin:14px 20px 18px;padding:0 12px;height:40px;border:1px solid #dcdfe6;border-radius:8px;\\nfont-size:15px;color:#303133;outline:none;width:calc(100% - 40px);box-sizing:border-box;}\\n.kb-confirm-input:focus{border-color:#1677ff;}\\n.kb-confirm-error{padding:0 20px 12px;margin-top:-8px;text-align:left;font-size:12px;color:#f56c6c;}\\n.kb-confirm-btns{display:flex;border-top:1px solid #f0f2f5;}\\n.kb-confirm-btn{flex:1;height:44px;border:none;background:#fff;font-size:15px;color:#606266;cursor:pointer;}\\n.kb-confirm-btn:active{background:#f5f7fa;}\\n.kb-confirm-btn + .kb-confirm-btn{border-left:1px solid #f0f2f5;}\\n.kb-confirm-btn.primary{color:#1677ff;font-weight:600;}\\n.kb-confirm-btn.primary.is-danger{color:#f56c6c;}\\n@keyframes kbFadeIn{from{opacity:0}to{opacity:1}}\\n@keyframes kbPopIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}\\n\";\n\nfunction injectStyle() {\n if (typeof document === 'undefined' || document.getElementById(FALLBACK_STYLE_ID)) return;\n var tag = document.createElement('style');\n tag.id = FALLBACK_STYLE_ID;\n tag.textContent = FALLBACK_CSS;\n document.head.appendChild(tag);\n}\n\nfunction showToast(opts) {\n if (typeof document === 'undefined') return;\n injectStyle();\n var o = typeof opts === 'string' ? {\n message: opts\n } : opts || {};\n var el = document.createElement('div');\n el.className = \"kb-native-toast is-\".concat(o.type || 'info');\n el.textContent = o.message || '';\n document.body.appendChild(el);\n var duration = o.duration || 2200;\n setTimeout(function () {\n el.parentNode && el.parentNode.removeChild(el);\n }, duration);\n}\n\nfunction escapeHtml(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;'\n }[c];\n });\n}\n/**\n * 自绘确认框(不调起系统 window.confirm)\n * 返回 Promise:确定 resolve,取消 reject('cancel'),与 Element UI $confirm 用法一致\n */\n\n\nfunction showConfirm(opts) {\n if (typeof document === 'undefined') {\n return window.confirm(\"\".concat(opts.title || '提示', \"\\n\").concat(opts.message || '')) ? Promise.resolve() : Promise.reject(new Error('cancel'));\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n var isDanger = opts.type === 'error';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(opts.title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(opts.message || ''), \"</div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\").concat(isDanger ? ' is-danger' : '', \"\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n\n var done = function done(fn, arg) {\n return function () {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n };\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', done(reject, new Error('cancel')));\n btns[1].addEventListener('click', done(resolve));\n });\n}\n/**\n * 自绘输入框(不调起系统 window.prompt)\n * 返回 Promise:确定 resolve({ value }),取消 reject('cancel'),与 Element UI $prompt 用法一致\n * 支持 inputValue / inputPattern / inputErrorMessage\n */\n\n\nfunction showPrompt(message, title, options) {\n var opts = options || {};\n\n if (typeof document === 'undefined') {\n var val = window.prompt(\"\".concat(title || '提示', \"\\n\").concat(message || ''), opts.inputValue || '');\n return val === null ? Promise.reject(new Error('cancel')) : Promise.resolve({\n value: val\n });\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(message || ''), \"</div>\\n <input type=\\\"text\\\" class=\\\"kb-confirm-input\\\" />\\n <div class=\\\"kb-confirm-error\\\" style=\\\"display:none\\\"></div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n var input = mask.querySelector('.kb-confirm-input');\n var errEl = mask.querySelector('.kb-confirm-error');\n input.value = opts.inputValue || '';\n setTimeout(function () {\n return input.focus();\n }, 30);\n\n var close = function close(fn, arg) {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n }; // 校验失败时不关闭弹窗,就地提示(与 Element UI $prompt 行为一致)\n\n\n var submit = function submit() {\n var value = input.value;\n\n if (opts.inputPattern && !opts.inputPattern.test(value)) {\n errEl.textContent = opts.inputErrorMessage || '输入不合法';\n errEl.style.display = 'block';\n return;\n }\n\n close(resolve, {\n value: value\n });\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', function () {\n return close(reject, new Error('cancel'));\n });\n btns[1].addEventListener('click', submit);\n input.addEventListener('keydown', function (e) {\n if (e.key === 'Enter') submit();else if (e.key === 'Escape') close(reject, new Error('cancel'));\n });\n });\n}\n/**\n * 给 Vue 原型/指令补齐知识库依赖的最小能力(已存在则不覆盖)\n */\n\n\nfunction installNativeFallbacks(Vue) {\n injectStyle();\n\n if (!Vue.prototype.$confirm) {\n Vue.prototype.$confirm = function kbNativeConfirm(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showConfirm((0, _objectSpread2.default)({\n title: title,\n message: message\n }, options || {}));\n };\n }\n\n if (!Vue.prototype.$prompt) {\n Vue.prototype.$prompt = function kbNativePrompt(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showPrompt(message, title, options);\n };\n }\n\n if (!Vue.prototype.$message) {\n var message = function message(opts) {\n return showToast(opts);\n };\n\n ['success', 'warning', 'error', 'info'].forEach(function (type) {\n message[type] = function (opts) {\n return showToast(typeof opts === 'string' ? {\n message: opts,\n type: type\n } : (0, _objectSpread2.default)((0, _objectSpread2.default)({}, opts), {}, {\n type: type\n }));\n };\n });\n Vue.prototype.$message = message;\n }\n\n if (!Vue.directive('loading')) {\n var toggle = function toggle(el, on) {\n if (on && !el.__kbLoadingMask) {\n var mask = document.createElement('div');\n mask.className = 'kb-native-loading-mask';\n mask.innerHTML = '<div class=\"kb-native-loading-spinner\"></div>';\n var pos = (el.currentStyle || window.getComputedStyle && getComputedStyle(el) || {}).position;\n if (!pos || pos === 'static') el.style.position = 'relative';\n el.appendChild(mask);\n el.__kbLoadingMask = mask;\n } else if (!on && el.__kbLoadingMask) {\n el.__kbLoadingMask.parentNode && el.__kbLoadingMask.parentNode.removeChild(el.__kbLoadingMask);\n el.__kbLoadingMask = null;\n }\n };\n\n Vue.directive('loading', {\n bind: function bind(el, binding) {\n toggle(el, binding.value);\n },\n update: function update(el, binding) {\n toggle(el, binding.value);\n },\n unbind: function unbind(el) {\n toggle(el, false);\n }\n });\n }\n}\n/**\n * 判断宿主是否注册了 Element UI(检查全局/原型链上的 el-button 组件)\n *\n * 背景:平板横屏等宽屏场景若只按 innerWidth 判定移动端,会把无 Element UI 的 H5 宿主\n * 误判为 PC 模式,渲染出大量 el-* 未知元素导致页面不可用。\n * 因此无 Element UI 的宿主一律走自绘 UI 分支(isMobile=true),与宽度无关。\n *\n * @param {Object} vm 组件实例(this)或 Vue 构造器\n */\n\n\nfunction hasElementUI(vm) {\n try {\n var comps = vm && (vm.$options && vm.$options.components || vm.options && vm.options.components) || {};\n return !!(comps.elButton || comps.ElButton || comps['el-button']);\n } catch (e) {\n return false;\n }\n}\n/**\n * 展平文件夹树为 {node, depth} 数组(前序遍历,全部展开),供移动端下拉/列表选择\n */\n\n\nfunction flattenFolderTree(nodes) {\n var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n var out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];\n ;\n (nodes || []).forEach(function (n) {\n out.push({\n node: n,\n depth: depth\n });\n if (n.children && n.children.length) flattenFolderTree(n.children, depth + 1, out);\n });\n return out;\n}\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/native.js?");
15666
15666
 
15667
15667
  /***/ }),
15668
15668
 
@@ -1009,7 +1009,7 @@ eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@
1009
1009
  /***/ (function(module, exports, __webpack_require__) {
1010
1010
 
1011
1011
  "use strict";
1012
- 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//\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 // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: 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 },\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.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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;'\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 = 7;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 7:\n _context6.next = 9;\n return _this8.selectSession(item.sessionId);\n\n case 9:\n // 移动端:选中后跳到聊天详情页\n if (_this8.isMobileActive) {\n _this8.showH5List = false; // 推入历史状态,使 Android 系统返回手势先触发 popstate 被拦截\n\n history.pushState({\n page: 'kbChatDetail'\n }, '');\n }\n\n case 10:\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 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 _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 4;\n _context8.next = 7;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 7:\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 {\n role: m.role,\n content: m.content || '',\n thinking: m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '',\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 16;\n break;\n\n case 13:\n _context8.prev = 13;\n _context8.t0 = _context8[\"catch\"](4);\n console.error('加载消息失败', _context8.t0);\n\n case 16:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[4, 13]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (!_this12.activeSessionId) {\n _context9.next = 8;\n break;\n }\n\n _context9.prev = 1;\n _context9.next = 4;\n return (0, _kbChat.closeKbSession)(_this12.activeSessionId);\n\n case 4:\n _context9.next = 8;\n break;\n\n case 6:\n _context9.prev = 6;\n _context9.t0 = _context9[\"catch\"](1);\n\n case 8:\n _context9.next = 10;\n return _this12.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this13.inputText.trim();\n\n if (!(!text || !_this13.activeSessionId || _this13.streaming)) {\n _context10.next = 3;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 3:\n _this13.inputText = '';\n requestSessionId = _this13.activeSessionId;\n requestId = ++_this13.streamRequestId;\n\n _this13.clearTypeTimer();\n\n _this13.messages.push({\n role: 'user',\n content: text\n });\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this13.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this13.messages[msgIdx];\n _this13.streamMsg = streamMsg;\n _this13.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this13.streamRequestId && requestSessionId === _this13.activeSessionId && _this13.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 _this13._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this13.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 _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this13.finishStream();\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this13.activeSessionId, text);\n _this13.eventSource = new EventSource(url);\n\n _this13.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n if (!_this13.messages[msgIdx].toolCalls) _this13.messages[msgIdx].toolCalls = [];\n\n _this13.messages[msgIdx].toolCalls.push(e.data);\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n }\n });\n\n _this13.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this13.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 _this13.closeEventSource();\n });\n\n _this13.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n _this13.messages[msgIdx].msgKey = e.data;\n }\n });\n\n _this13.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this13.closeEventSource();\n });\n\n case 28:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\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 _this14 = 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 _this14.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this15 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var list;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (_this15.companyId) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n _context11.prev = 2;\n _context11.next = 5;\n return (0, _kbChat.getKbSessionList)(_this15.companyId);\n\n case 5:\n _context11.t0 = _context11.sent;\n\n if (_context11.t0) {\n _context11.next = 8;\n break;\n }\n\n _context11.t0 = [];\n\n case 8:\n list = _context11.t0;\n _this15.sessionList = list;\n _context11.next = 15;\n break;\n\n case 12:\n _context11.prev = 12;\n _context11.t1 = _context11[\"catch\"](2);\n console.error('刷新会话列表失败', _context11.t1);\n\n case 15:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, 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");
1012
+ 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//\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 // 当前公司是否没有知识库文档(无资料时展示遮罩)\n noDocument: false,\n // 移动端状态(仿 AI 助理):showH5List=true 显示会话列表页,false 显示聊天详情页\n localIsMobile: _isMobile,\n showH5List: _isMobile,\n // 移动端会话「…」弹出的操作单\n mobileAction: 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 },\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.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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;'\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 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 _this11.activeSessionId = sessionId;\n _this11.messages = [];\n _this11.streamMsg = null;\n _context8.prev = 4;\n _context8.next = 7;\n return (0, _kbChat.getKbSessionMessages)(sessionId);\n\n case 7:\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 {\n role: m.role,\n content: m.content || '',\n thinking: m.thinking || m.reasoning || m.reasoning_result || m.reasoningContent || m.reasoning_content || m.thinkingContent || m.thinking_content || '',\n _key: m.createTime ? 'msg_' + m.createTime + '_' + i : 'msg_' + Date.now() + '_' + Math.random()\n };\n });\n\n _this11.$nextTick(function () {\n return _this11.scrollToBottom();\n });\n\n _context8.next = 16;\n break;\n\n case 13:\n _context8.prev = 13;\n _context8.t0 = _context8[\"catch\"](4);\n console.error('加载消息失败', _context8.t0);\n\n case 16:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, null, [[4, 13]]);\n }))();\n },\n handleResetSession: function handleResetSession() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (!_this12.activeSessionId) {\n _context9.next = 8;\n break;\n }\n\n _context9.prev = 1;\n _context9.next = 4;\n return (0, _kbChat.closeKbSession)(_this12.activeSessionId);\n\n case 4:\n _context9.next = 8;\n break;\n\n case 6:\n _context9.prev = 6;\n _context9.t0 = _context9[\"catch\"](1);\n\n case 8:\n _context9.next = 10;\n return _this12.autoCreateSession();\n\n case 10:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[1, 6]]);\n }))();\n },\n handleSend: function handleSend(quickText) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee10() {\n var text, requestSessionId, requestId, msgKey, msgIdx, streamMsg, isCurrentStream, answerBuffer, finalContent, streamDone, url;\n return regeneratorRuntime.wrap(function _callee10$(_context10) {\n while (1) {\n switch (_context10.prev = _context10.next) {\n case 0:\n text = typeof quickText === 'string' ? quickText.trim() : _this13.inputText.trim();\n\n if (!(!text || !_this13.activeSessionId || _this13.streaming)) {\n _context10.next = 3;\n break;\n }\n\n return _context10.abrupt(\"return\");\n\n case 3:\n _this13.inputText = '';\n requestSessionId = _this13.activeSessionId;\n requestId = ++_this13.streamRequestId;\n\n _this13.clearTypeTimer();\n\n _this13.messages.push({\n role: 'user',\n content: text\n });\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n }); // 流式消息:作为 messages 中的对象(带 _key/loading),与 ChatDetail 一致\n\n\n msgKey = 'stream_' + requestId;\n msgIdx = _this13.messages.push({\n role: 'assistant',\n content: '',\n thinking: '',\n toolCalls: [],\n loading: true,\n _key: msgKey\n }) - 1;\n streamMsg = _this13.messages[msgIdx];\n _this13.streamMsg = streamMsg;\n _this13.streaming = true;\n\n isCurrentStream = function isCurrentStream() {\n return requestId === _this13.streamRequestId && requestSessionId === _this13.activeSessionId && _this13.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 _this13._typeTimer = setInterval(function () {\n if (!isCurrentStream()) return;\n var msg = _this13.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 _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n } // 流结束且所有内容都已逐行露出 → 收尾\n\n\n if (streamDone && nextLen >= source.length) {\n _this13.finishStream();\n }\n }, 30);\n url = (0, _kbChat.createKbStreamUrl)(_this13.activeSessionId, text);\n _this13.eventSource = new EventSource(url);\n\n _this13.eventSource.addEventListener('tool_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n if (!_this13.messages[msgIdx].toolCalls) _this13.messages[msgIdx].toolCalls = [];\n\n _this13.messages[msgIdx].toolCalls.push(e.data);\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n }\n });\n\n _this13.eventSource.addEventListener('answer', function (e) {\n if (!isCurrentStream() || streamDone) return;\n answerBuffer += e.data;\n });\n\n _this13.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 _this13.closeEventSource();\n });\n\n _this13.eventSource.addEventListener('msgKey', function (e) {\n if (!isCurrentStream()) return;\n\n if (e.data && _this13.messages[msgIdx]) {\n _this13.messages[msgIdx].msgKey = e.data;\n }\n });\n\n _this13.eventSource.addEventListener('reasoning', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking += e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('reasoning_result', function (e) {\n if (!isCurrentStream() || streamDone) return;\n streamMsg.thinking = e.data;\n\n _this13.$nextTick(function () {\n return _this13.scrollToBottom();\n });\n });\n\n _this13.eventSource.addEventListener('error', function () {\n if (!isCurrentStream() || streamDone) return;\n if (!answerBuffer) answerBuffer = '❌ 连接中断';\n streamDone = true;\n\n _this13.closeEventSource();\n });\n\n case 28:\n case \"end\":\n return _context10.stop();\n }\n }\n }, _callee10);\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 _this14 = 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 _this14.scrollToBottom();\n });\n },\n\n /** 流式结束后刷新会话列表(更新标题/最后消息) */\n refreshSessionList: function refreshSessionList() {\n var _this15 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee11() {\n var list;\n return regeneratorRuntime.wrap(function _callee11$(_context11) {\n while (1) {\n switch (_context11.prev = _context11.next) {\n case 0:\n if (_this15.companyId) {\n _context11.next = 2;\n break;\n }\n\n return _context11.abrupt(\"return\");\n\n case 2:\n _context11.prev = 2;\n _context11.next = 5;\n return (0, _kbChat.getKbSessionList)(_this15.companyId);\n\n case 5:\n _context11.t0 = _context11.sent;\n\n if (_context11.t0) {\n _context11.next = 8;\n break;\n }\n\n _context11.t0 = [];\n\n case 8:\n list = _context11.t0;\n _this15.sessionList = list;\n _context11.next = 15;\n break;\n\n case 12:\n _context11.prev = 12;\n _context11.t1 = _context11[\"catch\"](2);\n console.error('刷新会话列表失败', _context11.t1);\n\n case 15:\n case \"end\":\n return _context11.stop();\n }\n }\n }, _callee11, 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");
1013
1013
 
1014
1014
  /***/ }),
1015
1015
 
@@ -14710,7 +14710,7 @@ eval("// Generated by CoffeeScript 1.12.7\n(function() {\n var XMLDocument, XML
14710
14710
  /*! exports provided: name, version, main, private, scripts, dependencies, devDependencies, browserslist, directories, keywords, license, files, default */
14711
14711
  /***/ (function(module) {
14712
14712
 
14713
- eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.18-beta.6\\\",\\\"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?");
14713
+ eval("module.exports = JSON.parse(\"{\\\"name\\\":\\\"zj-plugin-intelligent\\\",\\\"version\\\":\\\"1.2.18-beta.8\\\",\\\"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?");
14714
14714
 
14715
14715
  /***/ }),
14716
14716
 
@@ -15662,7 +15662,7 @@ eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@b
15662
15662
  /***/ (function(module, exports, __webpack_require__) {
15663
15663
 
15664
15664
  "use strict";
15665
- 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.installNativeFallbacks = installNativeFallbacks;\nexports.hasElementUI = hasElementUI;\nexports.flattenFolderTree = flattenFolderTree;\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\n/**\n * 知识库 H5 原生兜底(无 Element UI 依赖)\n *\n * 背景:知识库模块在 PC 宿主(已全局注册 Element UI)中依赖 $message/$confirm/v-loading。\n * H5 宿主没有 Element UI,这些能力缺失会导致点击无响应。\n * 本文件仅在宿主未提供这些能力时,注入极简原生实现(DOM toast + 自绘确认框 + 轻量 loading 遮罩),\n * 不引入 element-ui 库;PC 宿主已有真实现,自动跳过。\n */\nvar FALLBACK_STYLE_ID = 'kb-native-fallback-style';\nvar FALLBACK_CSS = \"\\n.kb-native-toast{position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:99999;\\ndisplay:flex;align-items:center;gap:6px;max-width:76vw;padding:9px 16px;border-radius:6px;\\nbackground:#fff;box-shadow:0 4px 16px rgba(0,0,0,.14);font-size:14px;color:#303133;\\nword-break:break-all;animation:kbToastIn .2s ease;}\\n.kb-native-toast.is-success{color:#52c41a}.kb-native-toast.is-warning{color:#e6a23c}\\n.kb-native-toast.is-error{color:#f56c6c}.kb-native-toast.is-info{color:#303133}\\n@keyframes kbToastIn{from{opacity:0;transform:translate(-50%,-8px)}to{opacity:1;transform:translate(-50%,0)}}\\n.kb-native-loading-mask{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;align-items:center;justify-content:center;\\nbackground:rgba(255,255,255,.75);border-radius:inherit;}\\n.kb-native-loading-spinner{width:26px;height:26px;border:3px solid #e5e7eb;border-top-color:#1677ff;\\nborder-radius:50%;animation:kbSpin .8s linear infinite;}\\n@keyframes kbSpin{to{transform:rotate(360deg)}}\\n/* \\u81EA\\u7ED8\\u786E\\u8BA4\\u6846\\uFF08\\u66FF\\u4EE3 window.confirm\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99998;background:rgba(0,0,0,.45);\\ndisplay:flex;align-items:center;justify-content:center;animation:kbFadeIn .18s ease;}\\n.kb-confirm-box{width:82vw;max-width:320px;background:#fff;border-radius:12px;overflow:hidden;\\nbox-shadow:0 12px 32px rgba(0,0,0,.18);animation:kbPopIn .18s ease;}\\n.kb-confirm-title{padding:18px 20px 0;text-align:center;font-size:16px;font-weight:600;color:#303133;}\\n.kb-confirm-msg{padding:10px 20px 20px;text-align:center;font-size:14px;line-height:1.5;color:#606266;\\nword-break:break-all;max-height:50vh;overflow-y:auto;white-space:pre-wrap;}\\n.kb-confirm-btns{display:flex;border-top:1px solid #f0f2f5;}\\n.kb-confirm-btn{flex:1;height:44px;border:none;background:#fff;font-size:15px;color:#606266;cursor:pointer;}\\n.kb-confirm-btn:active{background:#f5f7fa;}\\n.kb-confirm-btn + .kb-confirm-btn{border-left:1px solid #f0f2f5;}\\n.kb-confirm-btn.primary{color:#1677ff;font-weight:600;}\\n.kb-confirm-btn.primary.is-danger{color:#f56c6c;}\\n@keyframes kbFadeIn{from{opacity:0}to{opacity:1}}\\n@keyframes kbPopIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}\\n\";\n\nfunction injectStyle() {\n if (typeof document === 'undefined' || document.getElementById(FALLBACK_STYLE_ID)) return;\n var tag = document.createElement('style');\n tag.id = FALLBACK_STYLE_ID;\n tag.textContent = FALLBACK_CSS;\n document.head.appendChild(tag);\n}\n\nfunction showToast(opts) {\n if (typeof document === 'undefined') return;\n injectStyle();\n var o = typeof opts === 'string' ? {\n message: opts\n } : opts || {};\n var el = document.createElement('div');\n el.className = \"kb-native-toast is-\".concat(o.type || 'info');\n el.textContent = o.message || '';\n document.body.appendChild(el);\n var duration = o.duration || 2200;\n setTimeout(function () {\n el.parentNode && el.parentNode.removeChild(el);\n }, duration);\n}\n\nfunction escapeHtml(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;'\n }[c];\n });\n}\n/**\n * 自绘确认框(不调起系统 window.confirm)\n * 返回 Promise:确定 resolve,取消 reject('cancel'),与 Element UI $confirm 用法一致\n */\n\n\nfunction showConfirm(opts) {\n if (typeof document === 'undefined') {\n return window.confirm(\"\".concat(opts.title || '提示', \"\\n\").concat(opts.message || '')) ? Promise.resolve() : Promise.reject(new Error('cancel'));\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n var isDanger = opts.type === 'error';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(opts.title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(opts.message || ''), \"</div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\").concat(isDanger ? ' is-danger' : '', \"\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n\n var done = function done(fn, arg) {\n return function () {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n };\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', done(reject, new Error('cancel')));\n btns[1].addEventListener('click', done(resolve));\n });\n}\n/**\n * 给 Vue 原型/指令补齐知识库依赖的最小能力(已存在则不覆盖)\n */\n\n\nfunction installNativeFallbacks(Vue) {\n injectStyle();\n\n if (!Vue.prototype.$confirm) {\n Vue.prototype.$confirm = function kbNativeConfirm(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showConfirm((0, _objectSpread2.default)({\n title: title,\n message: message\n }, options || {}));\n };\n }\n\n if (!Vue.prototype.$message) {\n var message = function message(opts) {\n return showToast(opts);\n };\n\n ['success', 'warning', 'error', 'info'].forEach(function (type) {\n message[type] = function (opts) {\n return showToast(typeof opts === 'string' ? {\n message: opts,\n type: type\n } : (0, _objectSpread2.default)((0, _objectSpread2.default)({}, opts), {}, {\n type: type\n }));\n };\n });\n Vue.prototype.$message = message;\n }\n\n if (!Vue.directive('loading')) {\n var toggle = function toggle(el, on) {\n if (on && !el.__kbLoadingMask) {\n var mask = document.createElement('div');\n mask.className = 'kb-native-loading-mask';\n mask.innerHTML = '<div class=\"kb-native-loading-spinner\"></div>';\n var pos = (el.currentStyle || window.getComputedStyle && getComputedStyle(el) || {}).position;\n if (!pos || pos === 'static') el.style.position = 'relative';\n el.appendChild(mask);\n el.__kbLoadingMask = mask;\n } else if (!on && el.__kbLoadingMask) {\n el.__kbLoadingMask.parentNode && el.__kbLoadingMask.parentNode.removeChild(el.__kbLoadingMask);\n el.__kbLoadingMask = null;\n }\n };\n\n Vue.directive('loading', {\n bind: function bind(el, binding) {\n toggle(el, binding.value);\n },\n update: function update(el, binding) {\n toggle(el, binding.value);\n },\n unbind: function unbind(el) {\n toggle(el, false);\n }\n });\n }\n}\n/**\n * 判断宿主是否注册了 Element UI(检查全局/原型链上的 el-button 组件)\n *\n * 背景:平板横屏等宽屏场景若只按 innerWidth 判定移动端,会把无 Element UI 的 H5 宿主\n * 误判为 PC 模式,渲染出大量 el-* 未知元素导致页面不可用。\n * 因此无 Element UI 的宿主一律走自绘 UI 分支(isMobile=true),与宽度无关。\n *\n * @param {Object} vm 组件实例(this)或 Vue 构造器\n */\n\n\nfunction hasElementUI(vm) {\n try {\n var comps = vm && (vm.$options && vm.$options.components || vm.options && vm.options.components) || {};\n return !!(comps.elButton || comps.ElButton || comps['el-button']);\n } catch (e) {\n return false;\n }\n}\n/**\n * 展平文件夹树为 {node, depth} 数组(前序遍历,全部展开),供移动端下拉/列表选择\n */\n\n\nfunction flattenFolderTree(nodes) {\n var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n var out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];\n ;\n (nodes || []).forEach(function (n) {\n out.push({\n node: n,\n depth: depth\n });\n if (n.children && n.children.length) flattenFolderTree(n.children, depth + 1, out);\n });\n return out;\n}\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/native.js?");
15665
+ 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.installNativeFallbacks = installNativeFallbacks;\nexports.hasElementUI = hasElementUI;\nexports.flattenFolderTree = flattenFolderTree;\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\n/**\n * 知识库 H5 原生兜底(无 Element UI 依赖)\n *\n * 背景:知识库模块在 PC 宿主(已全局注册 Element UI)中依赖 $message/$confirm/v-loading。\n * H5 宿主没有 Element UI,这些能力缺失会导致点击无响应。\n * 本文件仅在宿主未提供这些能力时,注入极简原生实现(DOM toast + 自绘确认框 + 轻量 loading 遮罩),\n * 不引入 element-ui 库;PC 宿主已有真实现,自动跳过。\n */\nvar FALLBACK_STYLE_ID = 'kb-native-fallback-style';\nvar FALLBACK_CSS = \"\\n.kb-native-toast{position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:99999;\\ndisplay:flex;align-items:center;gap:6px;max-width:76vw;padding:9px 16px;border-radius:6px;\\nbackground:#fff;box-shadow:0 4px 16px rgba(0,0,0,.14);font-size:14px;color:#303133;\\nword-break:break-all;animation:kbToastIn .2s ease;}\\n.kb-native-toast.is-success{color:#52c41a}.kb-native-toast.is-warning{color:#e6a23c}\\n.kb-native-toast.is-error{color:#f56c6c}.kb-native-toast.is-info{color:#303133}\\n@keyframes kbToastIn{from{opacity:0;transform:translate(-50%,-8px)}to{opacity:1;transform:translate(-50%,0)}}\\n.kb-native-loading-mask{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;align-items:center;justify-content:center;\\nbackground:rgba(255,255,255,.75);border-radius:inherit;}\\n.kb-native-loading-spinner{width:26px;height:26px;border:3px solid #e5e7eb;border-top-color:#1677ff;\\nborder-radius:50%;animation:kbSpin .8s linear infinite;}\\n@keyframes kbSpin{to{transform:rotate(360deg)}}\\n/* \\u81EA\\u7ED8\\u786E\\u8BA4\\u6846\\uFF08\\u66FF\\u4EE3 window.confirm\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99998;background:rgba(0,0,0,.45);\\ndisplay:flex;align-items:center;justify-content:center;animation:kbFadeIn .18s ease;}\\n.kb-confirm-box{width:82vw;max-width:320px;background:#fff;border-radius:12px;overflow:hidden;\\nbox-shadow:0 12px 32px rgba(0,0,0,.18);animation:kbPopIn .18s ease;}\\n.kb-confirm-title{padding:18px 20px 0;text-align:center;font-size:16px;font-weight:600;color:#303133;}\\n.kb-confirm-msg{padding:10px 20px 20px;text-align:center;font-size:14px;line-height:1.5;color:#606266;\\nword-break:break-all;max-height:50vh;overflow-y:auto;white-space:pre-wrap;}\\n/* \\u81EA\\u7ED8\\u8F93\\u5165\\u6846\\u5185\\u7684 input\\uFF08\\u66FF\\u4EE3 Element UI $prompt\\uFF0CH5 \\u65E0 Element UI \\u65F6\\u4F7F\\u7528\\uFF09 */\\n.kb-confirm-input{margin:14px 20px 18px;padding:0 12px;height:40px;border:1px solid #dcdfe6;border-radius:8px;\\nfont-size:15px;color:#303133;outline:none;width:calc(100% - 40px);box-sizing:border-box;}\\n.kb-confirm-input:focus{border-color:#1677ff;}\\n.kb-confirm-error{padding:0 20px 12px;margin-top:-8px;text-align:left;font-size:12px;color:#f56c6c;}\\n.kb-confirm-btns{display:flex;border-top:1px solid #f0f2f5;}\\n.kb-confirm-btn{flex:1;height:44px;border:none;background:#fff;font-size:15px;color:#606266;cursor:pointer;}\\n.kb-confirm-btn:active{background:#f5f7fa;}\\n.kb-confirm-btn + .kb-confirm-btn{border-left:1px solid #f0f2f5;}\\n.kb-confirm-btn.primary{color:#1677ff;font-weight:600;}\\n.kb-confirm-btn.primary.is-danger{color:#f56c6c;}\\n@keyframes kbFadeIn{from{opacity:0}to{opacity:1}}\\n@keyframes kbPopIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}\\n\";\n\nfunction injectStyle() {\n if (typeof document === 'undefined' || document.getElementById(FALLBACK_STYLE_ID)) return;\n var tag = document.createElement('style');\n tag.id = FALLBACK_STYLE_ID;\n tag.textContent = FALLBACK_CSS;\n document.head.appendChild(tag);\n}\n\nfunction showToast(opts) {\n if (typeof document === 'undefined') return;\n injectStyle();\n var o = typeof opts === 'string' ? {\n message: opts\n } : opts || {};\n var el = document.createElement('div');\n el.className = \"kb-native-toast is-\".concat(o.type || 'info');\n el.textContent = o.message || '';\n document.body.appendChild(el);\n var duration = o.duration || 2200;\n setTimeout(function () {\n el.parentNode && el.parentNode.removeChild(el);\n }, duration);\n}\n\nfunction escapeHtml(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;'\n }[c];\n });\n}\n/**\n * 自绘确认框(不调起系统 window.confirm)\n * 返回 Promise:确定 resolve,取消 reject('cancel'),与 Element UI $confirm 用法一致\n */\n\n\nfunction showConfirm(opts) {\n if (typeof document === 'undefined') {\n return window.confirm(\"\".concat(opts.title || '提示', \"\\n\").concat(opts.message || '')) ? Promise.resolve() : Promise.reject(new Error('cancel'));\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n var isDanger = opts.type === 'error';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(opts.title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(opts.message || ''), \"</div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\").concat(isDanger ? ' is-danger' : '', \"\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n\n var done = function done(fn, arg) {\n return function () {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n };\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', done(reject, new Error('cancel')));\n btns[1].addEventListener('click', done(resolve));\n });\n}\n/**\n * 自绘输入框(不调起系统 window.prompt)\n * 返回 Promise:确定 resolve({ value }),取消 reject('cancel'),与 Element UI $prompt 用法一致\n * 支持 inputValue / inputPattern / inputErrorMessage\n */\n\n\nfunction showPrompt(message, title, options) {\n var opts = options || {};\n\n if (typeof document === 'undefined') {\n var val = window.prompt(\"\".concat(title || '提示', \"\\n\").concat(message || ''), opts.inputValue || '');\n return val === null ? Promise.reject(new Error('cancel')) : Promise.resolve({\n value: val\n });\n }\n\n injectStyle();\n return new Promise(function (resolve, reject) {\n var mask = document.createElement('div');\n mask.className = 'kb-confirm-mask';\n mask.innerHTML = \"\\n <div class=\\\"kb-confirm-box\\\">\\n <div class=\\\"kb-confirm-title\\\">\".concat(escapeHtml(title || '提示'), \"</div>\\n <div class=\\\"kb-confirm-msg\\\">\").concat(escapeHtml(message || ''), \"</div>\\n <input type=\\\"text\\\" class=\\\"kb-confirm-input\\\" />\\n <div class=\\\"kb-confirm-error\\\" style=\\\"display:none\\\"></div>\\n <div class=\\\"kb-confirm-btns\\\">\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn\\\">\").concat(escapeHtml(opts.cancelButtonText || '取消'), \"</button>\\n <button type=\\\"button\\\" class=\\\"kb-confirm-btn primary\\\">\").concat(escapeHtml(opts.confirmButtonText || '确定'), \"</button>\\n </div>\\n </div>\");\n document.body.appendChild(mask);\n var input = mask.querySelector('.kb-confirm-input');\n var errEl = mask.querySelector('.kb-confirm-error');\n input.value = opts.inputValue || '';\n setTimeout(function () {\n return input.focus();\n }, 30);\n\n var close = function close(fn, arg) {\n mask.parentNode && mask.parentNode.removeChild(mask);\n fn(arg);\n }; // 校验失败时不关闭弹窗,就地提示(与 Element UI $prompt 行为一致)\n\n\n var submit = function submit() {\n var value = input.value;\n\n if (opts.inputPattern && !opts.inputPattern.test(value)) {\n errEl.textContent = opts.inputErrorMessage || '输入不合法';\n errEl.style.display = 'block';\n return;\n }\n\n close(resolve, {\n value: value\n });\n };\n\n var btns = mask.querySelectorAll('.kb-confirm-btn');\n btns[0].addEventListener('click', function () {\n return close(reject, new Error('cancel'));\n });\n btns[1].addEventListener('click', submit);\n input.addEventListener('keydown', function (e) {\n if (e.key === 'Enter') submit();else if (e.key === 'Escape') close(reject, new Error('cancel'));\n });\n });\n}\n/**\n * 给 Vue 原型/指令补齐知识库依赖的最小能力(已存在则不覆盖)\n */\n\n\nfunction installNativeFallbacks(Vue) {\n injectStyle();\n\n if (!Vue.prototype.$confirm) {\n Vue.prototype.$confirm = function kbNativeConfirm(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showConfirm((0, _objectSpread2.default)({\n title: title,\n message: message\n }, options || {}));\n };\n }\n\n if (!Vue.prototype.$prompt) {\n Vue.prototype.$prompt = function kbNativePrompt(message) {\n var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '提示';\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return showPrompt(message, title, options);\n };\n }\n\n if (!Vue.prototype.$message) {\n var message = function message(opts) {\n return showToast(opts);\n };\n\n ['success', 'warning', 'error', 'info'].forEach(function (type) {\n message[type] = function (opts) {\n return showToast(typeof opts === 'string' ? {\n message: opts,\n type: type\n } : (0, _objectSpread2.default)((0, _objectSpread2.default)({}, opts), {}, {\n type: type\n }));\n };\n });\n Vue.prototype.$message = message;\n }\n\n if (!Vue.directive('loading')) {\n var toggle = function toggle(el, on) {\n if (on && !el.__kbLoadingMask) {\n var mask = document.createElement('div');\n mask.className = 'kb-native-loading-mask';\n mask.innerHTML = '<div class=\"kb-native-loading-spinner\"></div>';\n var pos = (el.currentStyle || window.getComputedStyle && getComputedStyle(el) || {}).position;\n if (!pos || pos === 'static') el.style.position = 'relative';\n el.appendChild(mask);\n el.__kbLoadingMask = mask;\n } else if (!on && el.__kbLoadingMask) {\n el.__kbLoadingMask.parentNode && el.__kbLoadingMask.parentNode.removeChild(el.__kbLoadingMask);\n el.__kbLoadingMask = null;\n }\n };\n\n Vue.directive('loading', {\n bind: function bind(el, binding) {\n toggle(el, binding.value);\n },\n update: function update(el, binding) {\n toggle(el, binding.value);\n },\n unbind: function unbind(el) {\n toggle(el, false);\n }\n });\n }\n}\n/**\n * 判断宿主是否注册了 Element UI(检查全局/原型链上的 el-button 组件)\n *\n * 背景:平板横屏等宽屏场景若只按 innerWidth 判定移动端,会把无 Element UI 的 H5 宿主\n * 误判为 PC 模式,渲染出大量 el-* 未知元素导致页面不可用。\n * 因此无 Element UI 的宿主一律走自绘 UI 分支(isMobile=true),与宽度无关。\n *\n * @param {Object} vm 组件实例(this)或 Vue 构造器\n */\n\n\nfunction hasElementUI(vm) {\n try {\n var comps = vm && (vm.$options && vm.$options.components || vm.options && vm.options.components) || {};\n return !!(comps.elButton || comps.ElButton || comps['el-button']);\n } catch (e) {\n return false;\n }\n}\n/**\n * 展平文件夹树为 {node, depth} 数组(前序遍历,全部展开),供移动端下拉/列表选择\n */\n\n\nfunction flattenFolderTree(nodes) {\n var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n var out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];\n ;\n (nodes || []).forEach(function (n) {\n out.push({\n node: n,\n depth: depth\n });\n if (n.children && n.children.length) flattenFolderTree(n.children, depth + 1, out);\n });\n return out;\n}\n\n//# sourceURL=webpack://ZjPluginIntelligent/./src/lib/knowledgeBase/native.js?");
15666
15666
 
15667
15667
  /***/ }),
15668
15668
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zj-plugin-intelligent",
3
- "version": "1.2.18-beta.6",
3
+ "version": "1.2.18-beta.8",
4
4
  "main": "lib/ZjPluginIntelligent.umd.min.js",
5
5
  "private": false,
6
6
  "scripts": {