zj-plugin-ai-assistant 0.2.0-beta.1 → 0.2.0-beta.2

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.
package/lib/.gz ADDED
Binary file
@@ -229,19 +229,19 @@ eval("__webpack_require__.r(__webpack_exports__);\n// This file is imported into
229
229
  /***/ (function(module, exports, __webpack_require__) {
230
230
 
231
231
  "use strict";
232
- eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireWildcard */ \"./node_modules/@babel/runtime/helpers/interopRequireWildcard.js\").default;\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 _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _createForOfIteratorHelper2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/createForOfIteratorHelper */ \"./node_modules/@babel/runtime/helpers/createForOfIteratorHelper.js\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _aiAssistant = _interopRequireDefault(__webpack_require__(/*! ./ai-assistant.png */ \"./src/lib/components/approvalAi/ai-assistant.png\"));\n\nvar aiApi = _interopRequireWildcard(__webpack_require__(/*! ./apiApprovalAi */ \"./src/lib/components/approvalAi/apiApprovalAi.js\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.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//\nvar md = new _markdownIt.default({\n html: false,\n linkify: true\n}); // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\nvar VALUE_NAME = {\n comFeePlan: \"公司费用计划\",\n comFeeApplication: \"公司费用\",\n comFeeAccept: \"公司费用验收\",\n comFeeCredit: \"公司费用挂账\",\n comFeePay: \"公司费用支付\",\n employeeHonor: \"员工荣誉\",\n employeeRegularization: \"员工转正\",\n employeeTurnover: \"离职\",\n employeeturnoverhandover: \"离职交接\",\n employeeChangestation: \"调岗\",\n employeeChangesalary: \"调薪\",\n overTimeApply: \"加班\",\n carApproval: \"车辆\",\n reimburse: \"出差报销\",\n leave: \"请假\",\n patch: \"补卡\",\n trip: \"出差\",\n out: \"外出\",\n lan: \"借款\",\n officialDocument: \"公文\",\n notice: \"公告\",\n qgbCompanyPlanSummery: \"公司费用预算汇总\",\n sealApproval: \"印章\"\n}; // 审批状态码 -> 中文(内置映射,替代宿主项目全局 $approvalStatus)\n\nvar STATUS_MAP = {\n \"1\": \"审批中\",\n \"-1\": \"审批中\",\n \"0\": \"已拒绝\",\n \"2\": \"已通过\",\n \"-3\": \"已通过\",\n \"4\": \"草稿\",\n \"3\": \"已通过\",\n \"5\": \"已完成\",\n \"6\": \"已撤销\"\n}; // 概览文案启发式字段匹配\n\nvar FIELD_HINTS = [{\n key: \"applicant\",\n labels: [\"申请人\", \"提交人\", \"经办人\"]\n}, {\n key: \"unit\",\n labels: [\"申请单位\", \"公司\", \"单位\"]\n}, {\n key: \"dept\",\n labels: [\"申请部门\", \"部门\"]\n}, {\n key: \"reason\",\n labels: [\"原因\", \"事由\", \"用途\", \"摘要\", \"标题\", \"说明\", \"内容\", \"目的\"]\n}, {\n key: \"amount\",\n labels: [\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]\n}];\n\nfunction normalizeLabel(label) {\n return String(label).trim().replace(/[::]\\s*$/g, \"\");\n}\n\nfunction resolveStatusText(status, override) {\n if (override) return override;\n if (!status && status !== 0) return \"待处理\";\n var mapped = STATUS_MAP[String(status)];\n return mapped || \"未知状态\";\n}\n\nvar msgSeq = 0;\nvar _default = {\n name: \"ApprovalAiAssistant\",\n props: {\n id: {\n type: String,\n default: \"\"\n },\n type: {\n type: String,\n default: \"\"\n },\n status: {\n type: String,\n default: \"0\"\n },\n index: {\n type: Number,\n default: 1\n },\n commentId: {\n type: String,\n default: \"\"\n },\n // ---- 以下为进包后新增的配置项,用于解耦宿主角色的上下文依赖 ----\n // AI 服务基础地址(示例 https://api.xxx.cn),未配置则走本地兜底\n baseUrl: {\n type: String,\n default: \"\"\n },\n // 当前用户信息(替代宿主 Vuex $store.state.user)\n userId: {\n type: String,\n default: \"\"\n },\n userName: {\n type: String,\n default: \"\"\n },\n // 审批状态文案覆盖(默认内置状态码映射;传入则优先使用)\n statusTextOverride: {\n type: String,\n default: \"\"\n },\n // 布局边界选择器(替代硬编码 .approval-content)\n containerSelector: {\n type: String,\n default: \".approval-content\"\n },\n // 详情采集根选择器(替代硬编码 .detail-root)\n detailRootSelector: {\n type: String,\n default: \".detail-root\"\n },\n // 机器人图标,不传时使用包内内置图标\n robotIconUrl: {\n type: String,\n default: \"\"\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n panelVisible: false,\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 24,\n tucked: false,\n panelBottom: 88,\n dragging: false,\n moved: false,\n startX: 0,\n startY: 0,\n startLeft: 0,\n startBottom: 0\n };\n },\n computed: {\n iconUrl: function iconUrl() {\n return this.robotIconUrl || this.defaultIcon;\n },\n typeName: function typeName() {\n if (!this.type) return \"\";\n return VALUE_NAME[this.type] || this.type;\n },\n statusText: function statusText() {\n return resolveStatusText(this.status, this.statusTextOverride);\n },\n statusTone: function statusTone() {\n var t = this.statusText;\n if (t === \"已通过\" || t === \"已完成\") return \"ok\";\n if (t === \"已拒绝\" || t === \"已驳回\" || t === \"已退回\" || t === \"已终止\") return \"no\";\n return \"go\";\n }\n },\n created: function created() {// 注入 AI 服务地址\n },\n mounted: function mounted() {\n var _this = this;\n\n window.addEventListener(\"mousemove\", this.onDrag);\n window.addEventListener(\"mouseup\", this.endDrag);\n this.$nextTick(function () {\n return _this.snapBtn();\n });\n },\n beforeDestroy: function beforeDestroy() {\n window.removeEventListener(\"mousemove\", this.onDrag);\n window.removeEventListener(\"mouseup\", this.endDrag);\n },\n watch: {\n // 切换审批单时重置对话上下文\n id: function id() {\n this.sessionId = \"\";\n this.messages = [];\n this.fields = [];\n this.rawDetail = null;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val\n });\n }\n },\n methods: {\n getContainer: function getContainer() {\n // 普通模式:以配置的审批容器为边界;找不到时回退到父级包裹容器\n var selector = this.containerSelector;\n var el = selector ? this.$el.closest(selector) : null;\n return el || this.$el.parentElement;\n },\n getDetailRoot: function getDetailRoot() {\n var box = this.getContainer();\n if (!box) return null;\n var explicit = this.detailRootSelector ? box.querySelector(this.detailRootSelector) : null;\n if (explicit) return explicit; // 子应用/iframe 模式:容器内可能没有 detail-root 标记,\n // 回退到首个详情子节点(排除 AI 面板自身)\n\n var kids = Array.from(box.children).filter(function (c) {\n return !c.classList.contains(\"ai-assistant\");\n });\n return kids.length ? kids[0] : null;\n },\n togglePanel: function togglePanel() {\n var _this2 = this;\n\n // 拖拽后松手会触发一次 click,这里拦截,避免误展开/收起面板\n if (this.moved) {\n this.moved = false;\n return;\n }\n\n if (this.panelVisible) {\n this.closePanel();\n return;\n }\n\n this.panelVisible = true;\n this.$nextTick(function () {\n _this2.snapPanel();\n\n if (!_this2.messages.length) {\n _this2.greet();\n }\n\n _this2.scrollBottom();\n\n if (_this2.$refs.input) _this2.$refs.input.focus();\n });\n },\n closePanel: function closePanel() {\n this.panelVisible = false;\n },\n snapBtn: function snapBtn() {\n var box = this.getContainer();\n if (!box) return; // 默认贴右下角完整展开:右侧留 20px\n\n if (!this.tucked && !this.btnLeft) {\n this.btnLeft = Math.max(0, box.clientWidth - 20 - 46);\n } // 若按钮在可视范围外则修正\n\n\n this.btnLeft = Math.max(0, Math.min(this.btnLeft, box.clientWidth - 46));\n this.btnBottom = Math.max(8, Math.min(this.btnBottom, box.clientHeight - 44 - 8));\n },\n snapPanel: function snapPanel() {\n var box = this.getContainer();\n var el = this.$el.querySelector(\".ai-panel\");\n if (!box || !el) return;\n var margin = 24; // 上下安全距离,避免遮挡\n\n var ph = el.clientHeight || 560;\n var desired = this.btnBottom + 62;\n this.panelBottom = Math.max(margin, Math.min(desired, box.clientHeight - ph - margin));\n },\n // 打开时 AI 先总结当前审批\n greet: function greet() {\n var _this3 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var summary, amount, res, base;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n _this3.collectDetail();\n\n summary = _this3.buildSummary();\n amount = _this3.findField([\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]); // 已接入后端时,优先用 AI 生成的总结\n\n console.log(_this3.id, _this3.aiEnabled());\n\n if (!(_this3.aiEnabled() && _this3.id)) {\n _context.next = 9;\n break;\n }\n\n _context.next = 7;\n return aiApi.getSummary(_this3.id);\n\n case 7:\n res = _context.sent;\n\n if (res && res.data && res.data.summary) {\n summary = res.data.summary;\n }\n\n case 9:\n base = \"\\u4F60\\u597D\\uFF0C\\u6211\\u662F AI \\u5BA1\\u6279\\u52A9\\u624B\\uD83D\\uDC4B\\n\\n\\u5DF2\\u7ECF\\u4E3A\\u4F60\\u68B3\\u7406\\u4E86\\u5F53\\u524D\\u5BA1\\u6279\\u7684\\u6982\\u89C8\\uFF1A\\n\\n\".concat(summary); // const tail = this.fields.length\n // ? `\\n\\n这份单据的关键字段共 ${this.fields.length} 项${amount ? `,其中金额相关「${amount}」` : \"\"}。你可以继续问我,比如「这笔审批是做什么的」「申请人是谁」。`\n // : \"\\n\\n暂时未采集到该单据的明细字段。\"\n // this.push(\"assistant\", base + tail)\n\n _this3.push(\"assistant\", base);\n\n case 11:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n sendMessage: function sendMessage() {\n var _this4 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return;\n this.push(\"user\", text);\n this.inputText = \"\";\n this.sending = true;\n this.collectDetail(); // 直接走后端流式问答(已接入时);未接入则返回本地兜底\n\n var msg = null;\n\n if (this.aiEnabled() && this.id) {\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: \"\"\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk) {\n if (msg) {\n msg.content += chunk;\n\n _this4.followBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this4.fallbackReply(text);\n } else {\n _this4.push(\"assistant\", _this4.fallbackReply(text));\n }\n\n _this4.streaming = false;\n _this4.sending = false;\n\n _this4.followBottom();\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this5 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n payload = {\n context: {\n type: _this5.type,\n status: _this5.status,\n typeName: _this5.typeName,\n id: _this5.id,\n summary: _this5.buildSummary(),\n fields: _this5.fields,\n detailData: _this5.rawDetail\n },\n question: question\n };\n _context2.next = 3;\n return _this5.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context2.sent;\n return _context2.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this5.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 当前用户信息(来自 props,替代宿主 Vuex)\n userInfo: function userInfo() {\n return {\n no: this.userId || \"\",\n name: this.userName || \"\"\n };\n },\n // 创建/复用 AI 对话会话\n ensureSession: function ensureSession() {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (!_this6.sessionId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\", _this6.sessionId);\n\n case 2:\n u = _this6.userInfo();\n _context3.next = 5;\n return aiApi.createChatSession({\n approvalNo: _this6.id,\n userId: u.no,\n userName: u.name\n });\n\n case 5:\n res = _context3.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context3.next = 9;\n break;\n }\n\n _this6.sessionId = res.data.sessionId;\n return _context3.abrupt(\"return\", _this6.sessionId);\n\n case 9:\n return _context3.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this7.aiEnabled()) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 2:\n _context4.prev = 2;\n _context4.next = 5;\n return _this7.ensureSession();\n\n case 5:\n sessionId = _context4.sent;\n\n if (sessionId) {\n _context4.next = 8;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 8:\n text = \"\";\n failed = false;\n _context4.next = 12;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n userId: _this7.userInfo().no,\n userName: _this7.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\" || event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data);\n } else if (event === \"agent_result\") {\n text = data;\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 12:\n if (!failed) {\n _context4.next = 14;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 14:\n return _context4.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 17:\n _context4.prev = 17;\n _context4.t0 = _context4[\"catch\"](2);\n return _context4.abrupt(\"return\", null);\n\n case 20:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[2, 17]]);\n }))();\n },\n // 无法从数据中检索到时的兜底:诚实告知查询不到,不编造\n fallbackReply: function fallbackReply(question) {\n var q = String(question).trim();\n var list = '';\n return \"\\u6682\\u65F6\\u65E0\\u6CD5\\u67E5\\u8BE2\\u5230\\u4E0E\\u300C\".concat(q, \"\\u300D\\u5BF9\\u5E94\\u7684\\u4FE1\\u606F\\u3002\").concat(list);\n },\n findField: function findField(labels) {\n var _iterator = (0, _createForOfIteratorHelper2.default)(this.rawEntries()),\n _step;\n\n try {\n var _loop = function _loop() {\n var e = _step.value;\n if (labels.some(function (l) {\n return e.label.indexOf(l) > -1;\n })) return {\n v: e.value\n };\n };\n\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var _ret = _loop();\n\n if ((0, _typeof2.default)(_ret) === \"object\") return _ret.v;\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return \"\";\n },\n // 合并 DOM 采集字段 + 详情页完整原始业务对象,得到可检索的条目\n rawEntries: function rawEntries() {\n var _this8 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this8.stringify(value);\n\n if (!v) return;\n var k = \"\".concat(label, \"__\").concat(v);\n if (seen.has(k)) return;\n seen.add(k);\n list.push({\n label: label,\n value: v\n });\n };\n\n var _iterator2 = (0, _createForOfIteratorHelper2.default)(this.fields),\n _step2;\n\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var f = _step2.value;\n pushEntry(f.label, f.value);\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n\n var raw = this.rawDetail && this.rawDetail.data;\n\n if (raw && (0, _typeof2.default)(raw) === \"object\") {\n for (var _i = 0, _Object$keys = Object.keys(raw); _i < _Object$keys.length; _i++) {\n var key = _Object$keys[_i];\n pushEntry(key, raw[key]);\n }\n }\n\n return list;\n },\n stringify: function stringify(v) {\n if (v === null || v === undefined) return \"\";\n\n if ((0, _typeof2.default)(v) === \"object\") {\n try {\n return JSON.stringify(v);\n } catch (e) {\n return \"\";\n }\n }\n\n return String(v);\n },\n // 通用采集详情页数据:兼容 el-form-item / el-descriptions / 自定义 span标签:值 / 表格,\n // 并尽量读取详情页组件的原始业务对象(info/form) 作为完整数据\n collectDetail: function collectDetail() {\n var root = this.getDetailRoot();\n var seen = {};\n\n var push = function push(label, value) {\n label = normalizeLabel(label);\n var v = String(value === null || value === undefined ? \"\" : value).replace(/\\s+/g, \" \").trim();\n if (!label || !v || v === \"暂无\" || label === \"图片\" || label === \"操作\") return;\n if (seen[label] !== undefined) return;\n seen[label] = v;\n };\n\n if (root) {\n // 1) el-form-item\n root.querySelectorAll(\".el-form-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-form-item__label\");\n var contentEl = item.querySelector(\".el-form-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 2) el-descriptions 描述项\n\n root.querySelectorAll(\".el-descriptions-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-descriptions-item__label\");\n var contentEl = item.querySelector(\".el-descriptions-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 3) 自定义 label:value(span 以「:」/「:」结尾,值在大括号内剩余文本)\n\n root.querySelectorAll(\"span\").forEach(function (span) {\n var txt = (span.textContent || \"\").trim();\n var m = txt.match(/^(.+?)([::])$/);\n if (!m) return;\n var parent = span.parentElement;\n if (!parent) return;\n var value = (parent.textContent || \"\").replace(txt, \"\").replace(/\\s+/g, \" \").trim();\n push(m[1], value);\n }); // 4) 表格:表头 -> 逐行数据\n\n root.querySelectorAll(\"table\").forEach(function (table) {\n var rows = table.querySelectorAll(\"tr\");\n var headers = [];\n\n if (rows.length) {\n headers = Array.from(rows[0].querySelectorAll(\"th,td\")).map(function (c) {\n return normalizeLabel(c.textContent);\n });\n }\n\n rows.forEach(function (tr, idx) {\n if (idx === 0) return;\n var cells = Array.from(tr.querySelectorAll(\"th,td\")).map(function (c) {\n return (c.textContent || \"\").replace(/\\s+/g, \" \").trim();\n });\n if (!cells.length) return;\n\n if (headers.length) {\n headers.forEach(function (h, j) {\n return h && cells[j] !== undefined && push(h, cells[j]);\n });\n } else {\n cells.forEach(function (c, j) {\n return c && push(\"\\u7B2C\".concat(idx, \"\\u884C\\u5B57\\u6BB5\").concat(j + 1), c);\n });\n }\n });\n });\n } // 5) 读取详情组件的原始业务对象,作为完整结构化数据补全\n\n\n var raw = this.readRawDetail();\n this.rawDetail = raw;\n if (raw) this.flattenTo(seen, raw);\n this.fields = Object.keys(seen).map(function (label) {\n return {\n label: label,\n value: seen[label]\n };\n }).slice(0, 40);\n },\n // 寻找详情页组件的业务数据对象(info / form 等)\n readRawDetail: function readRawDetail() {\n var _this9 = this;\n\n var root = this.getDetailRoot();\n if (!root) return null;\n\n var _iterator3 = (0, _createForOfIteratorHelper2.default)(root.querySelectorAll(\"*\")),\n _step3;\n\n try {\n for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {\n var el = _step3.value;\n var vm = el.__vue__;\n if (!vm || !vm.$data || (0, _typeof2.default)(vm.$data) !== \"object\") continue;\n var d = vm.$data;\n\n var _loop2 = function _loop2() {\n var key = _arr[_i2];\n var obj = d[key];\n\n if (obj && (0, _typeof2.default)(obj) === \"object\" && !Array.isArray(obj)) {\n var keys = Object.keys(obj).filter(function (k) {\n return obj[k] !== \"\" && obj[k] !== null && obj[k] !== undefined;\n });\n\n if (keys.length) {\n return {\n v: {\n key: key,\n data: _this9.jsonSafe(obj)\n }\n };\n }\n }\n };\n\n for (var _i2 = 0, _arr = [\"info\", \"form\"]; _i2 < _arr.length; _i2++) {\n var _ret2 = _loop2();\n\n if ((0, _typeof2.default)(_ret2) === \"object\") return _ret2.v;\n }\n }\n } catch (err) {\n _iterator3.e(err);\n } finally {\n _iterator3.f();\n }\n\n return null;\n },\n jsonSafe: function jsonSafe(obj) {\n try {\n return JSON.parse(JSON.stringify(obj, function (k, v) {\n return typeof v === \"function\" ? undefined : v;\n }));\n } catch (e) {\n return obj;\n }\n },\n // 把原始业务对象摊平成 label:value,补进已采集集合\n flattenTo: function flattenTo(seen, raw) {\n var walk = function walk(obj, path) {\n if (obj === null || obj === undefined) return;\n\n if (Array.isArray(obj)) {\n obj.forEach(function (item, i) {\n return walk(item, path ? \"\".concat(path, \"[\").concat(i, \"]\") : \"[\".concat(i, \"]\"));\n });\n return;\n }\n\n if ((0, _typeof2.default)(obj) === \"object\") {\n Object.keys(obj).forEach(function (k) {\n return walk(obj[k], path ? \"\".concat(path, \".\").concat(k) : k);\n });\n return;\n }\n\n var label = path || \"\";\n var v = String(obj).replace(/\\s+/g, \" \").trim();\n if (label && v && seen[label] === undefined) seen[label] = v;\n };\n\n walk(raw.data, raw.key);\n },\n // 依据采集字段生成“当前审批是做什么的”概览(启发式)\n buildSummary: function buildSummary() {\n var hits = {};\n FIELD_HINTS.forEach(function (hint) {\n hits[hint.key] = \"\";\n });\n this.fields.forEach(function (f) {\n var _iterator4 = (0, _createForOfIteratorHelper2.default)(FIELD_HINTS),\n _step4;\n\n try {\n for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {\n var hint = _step4.value;\n if (hits[hint.key]) continue;\n\n if (hint.labels.some(function (l) {\n return f.label.indexOf(l) > -1;\n })) {\n hits[hint.key] = f.value;\n break;\n }\n }\n } catch (err) {\n _iterator4.e(err);\n } finally {\n _iterator4.f();\n }\n });\n var name = this.typeName || \"该单据\";\n var parts = [];\n if (hits.applicant) parts.push(\"\\u7533\\u8BF7\\u4EBA\\u300C\".concat(hits.applicant, \"\\u300D\"));\n if (hits.unit) parts.push(\"\\u6240\\u5C5E\\u5355\\u4F4D\\u300C\".concat(hits.unit, \"\\u300D\"));\n var verb = \"\".concat(name === \"该单据\" ? \"该审批\" : \"\\u8BE5\\u300C\".concat(name, \"\\u300D\\u5BA1\\u6279\"));\n var summary = \"\".concat(verb, \"\\u662F\\u4E00\\u7B14\\u4E1A\\u52A1\\u5BA1\\u6279\");\n if (parts.length) summary += \"\\uFF0C\\u7531\".concat(parts.join(\",\"), \"\\u53D1\\u8D77\");\n if (hits.reason) summary += \"\\uFF0C\\u4E3B\\u8981\\u4E8B\\u7531\\uFF1A\".concat(hits.reason);\n if (hits.amount) summary += \"\\uFF0C\\u6D89\\u53CA\\u91D1\\u989D\\u300C\".concat(hits.amount, \"\\u300D\");\n summary += \"\\u3002\\u5F53\\u524D\\u5BA1\\u6279\\u72B6\\u6001\\u4E3A\\u300C\".concat(this.statusText, \"\\u300D\\u3002\");\n return summary;\n },\n push: function push(role, content) {\n this.messages.push({\n id: ++msgSeq,\n role: role,\n content: content\n });\n },\n // 气泡渲染:助手消息转 Markdown,用户消息按纯文本处理(转义防 XSS)\n renderBubble: function renderBubble(m) {\n var text = m && m.content != null ? String(m.content) : \"\";\n if (m && m.role === \"user\") return this.escapeHtml(text);\n return md.render(text || \"\");\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this10 = this;\n\n this.$nextTick(function () {\n var el = _this10.$refs.chatBody;\n if (el) el.scrollTop = el.scrollHeight;\n });\n },\n // 用户手动滚动时记录是否仍停留底部;上滚离开底部即暂停自动跟随\n onChatScroll: function onChatScroll() {\n var el = this.$refs.chatBody;\n if (!el) return;\n var nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;\n this.autoFollow = nearBottom;\n },\n // 仅当用户停留在底部时才自动滚到底,避免回答过程中打断用户向上查看\n followBottom: function followBottom() {\n if (!this.autoFollow) return;\n this.scrollBottom();\n },\n startDrag: function startDrag(e) {\n if (e.button !== 0) return;\n this.dragging = true;\n this.moved = false;\n this.tucked = false;\n this.startX = e.clientX;\n this.startY = e.clientY;\n this.startLeft = this.btnLeft;\n this.startBottom = this.btnBottom; // 拖动时防止文本/图片被选中导致跟手中断\n\n e.preventDefault();\n },\n onDrag: function onDrag(e) {\n if (!this.dragging) return;\n var dx = e.clientX - this.startX;\n var dy = e.clientY - this.startY;\n\n if (!this.moved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this.moved = true;\n }\n\n var box = this.getContainer();\n var maxW = box ? box.clientWidth - 46 : 9999;\n var maxH = box ? box.clientHeight - 44 : 9999;\n var left = Math.max(0, Math.min(this.startLeft + dx, Math.max(0, maxW)));\n var bottom = Math.max(8, Math.min(this.startBottom - dy, Math.max(8, maxH - 8))); // 直接写 DOM,图标即时跟手,不经过 Vue 重渲染\n\n var el = this.$refs.floatEl;\n\n if (el) {\n el.style.left = left + \"px\";\n el.style.bottom = bottom + \"px\";\n }\n\n if (this.panelVisible) {\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n if (!this.dragging) return;\n this.dragging = false;\n if (!this.moved) return; // 读取拖拽后的实际位置\n\n var el = this.$refs.floatEl;\n var left = el ? parseInt(el.style.left || \"0\", 10) : this.btnLeft;\n var bottom = el ? parseInt(el.style.bottom || \"0\", 10) : this.btnBottom;\n this.btnLeft = left;\n this.btnBottom = bottom; // 拖动过才吸附贴边;靠近右边时收成半角标\n\n var box = this.getContainer();\n if (!box) return;\n\n if (box.clientWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = box.clientWidth - 46;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.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");
232
+ eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireWildcard */ \"./node_modules/@babel/runtime/helpers/interopRequireWildcard.js\").default;\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 _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _createForOfIteratorHelper2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/createForOfIteratorHelper */ \"./node_modules/@babel/runtime/helpers/createForOfIteratorHelper.js\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _aiAssistant = _interopRequireDefault(__webpack_require__(/*! ./ai-assistant.png */ \"./src/lib/components/approvalAi/ai-assistant.png\"));\n\nvar aiApi = _interopRequireWildcard(__webpack_require__(/*! ./apiApprovalAi */ \"./src/lib/components/approvalAi/apiApprovalAi.js\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.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//\nvar md = new _markdownIt.default({\n html: false,\n linkify: true\n}); // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\nvar VALUE_NAME = {\n comFeePlan: \"公司费用计划\",\n comFeeApplication: \"公司费用\",\n comFeeAccept: \"公司费用验收\",\n comFeeCredit: \"公司费用挂账\",\n comFeePay: \"公司费用支付\",\n employeeHonor: \"员工荣誉\",\n employeeRegularization: \"员工转正\",\n employeeTurnover: \"离职\",\n employeeturnoverhandover: \"离职交接\",\n employeeChangestation: \"调岗\",\n employeeChangesalary: \"调薪\",\n overTimeApply: \"加班\",\n carApproval: \"车辆\",\n reimburse: \"出差报销\",\n leave: \"请假\",\n patch: \"补卡\",\n trip: \"出差\",\n out: \"外出\",\n lan: \"借款\",\n officialDocument: \"公文\",\n notice: \"公告\",\n qgbCompanyPlanSummery: \"公司费用预算汇总\",\n sealApproval: \"印章\"\n}; // 审批状态码 -> 中文(内置映射,替代宿主项目全局 $approvalStatus)\n\nvar STATUS_MAP = {\n \"1\": \"审批中\",\n \"-1\": \"审批中\",\n \"0\": \"已拒绝\",\n \"2\": \"已通过\",\n \"-3\": \"已通过\",\n \"4\": \"草稿\",\n \"3\": \"已通过\",\n \"5\": \"已完成\",\n \"6\": \"已撤销\"\n}; // 概览文案启发式字段匹配\n\nvar FIELD_HINTS = [{\n key: \"applicant\",\n labels: [\"申请人\", \"提交人\", \"经办人\"]\n}, {\n key: \"unit\",\n labels: [\"申请单位\", \"公司\", \"单位\"]\n}, {\n key: \"dept\",\n labels: [\"申请部门\", \"部门\"]\n}, {\n key: \"reason\",\n labels: [\"原因\", \"事由\", \"用途\", \"摘要\", \"标题\", \"说明\", \"内容\", \"目的\"]\n}, {\n key: \"amount\",\n labels: [\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]\n}];\n\nfunction normalizeLabel(label) {\n return String(label).trim().replace(/[::]\\s*$/g, \"\");\n}\n\nfunction resolveStatusText(status, override) {\n if (override) return override;\n if (!status && status !== 0) return \"待处理\";\n var mapped = STATUS_MAP[String(status)];\n return mapped || \"未知状态\";\n}\n\nvar msgSeq = 0;\nvar _default = {\n name: \"ApprovalAiAssistant\",\n props: {\n id: {\n type: String,\n default: \"\"\n },\n type: {\n type: String,\n default: \"\"\n },\n status: {\n type: String,\n default: \"0\"\n },\n index: {\n type: Number,\n default: 1\n },\n commentId: {\n type: String,\n default: \"\"\n },\n // ---- 以下为进包后新增的配置项,用于解耦宿主角色的上下文依赖 ----\n // AI 服务基础地址(示例 https://api.xxx.cn),未配置则走本地兜底\n baseUrl: {\n type: String,\n default: \"\"\n },\n // 当前用户信息(替代宿主 Vuex $store.state.user)\n userId: {\n type: String,\n default: \"\"\n },\n userName: {\n type: String,\n default: \"\"\n },\n // 审批状态文案覆盖(默认内置状态码映射;传入则优先使用)\n statusTextOverride: {\n type: String,\n default: \"\"\n },\n // 布局边界选择器(替代硬编码 .approval-content)\n containerSelector: {\n type: String,\n default: \".approval-content\"\n },\n // 详情采集根选择器(替代硬编码 .detail-root)\n detailRootSelector: {\n type: String,\n default: \".detail-root\"\n },\n // 机器人图标,不传时使用包内内置图标\n robotIconUrl: {\n type: String,\n default: \"\"\n },\n // H5 底部操作栏高度(px):作为兜底基准,用于把浮动按钮放在操作栏上方;PC 传 0 不受影响\n bottomBarHeight: {\n type: Number,\n default: 0\n },\n // H5 底部操作栏元素选择器:运行时测量该元素,把悬浮窗放在其正上方,避免遮挡(默认 .btn-wrapper)\n bottomBarSelector: {\n type: String,\n default: \".btn-wrapper\"\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n panelVisible: false,\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 24,\n tucked: false,\n panelBottom: 88,\n dragging: false,\n moved: false,\n isMobile: false,\n startX: 0,\n startY: 0,\n startLeft: 0,\n startBottom: 0\n };\n },\n computed: {\n iconUrl: function iconUrl() {\n return this.robotIconUrl || this.defaultIcon;\n },\n typeName: function typeName() {\n if (!this.type) return \"\";\n return VALUE_NAME[this.type] || this.type;\n },\n statusText: function statusText() {\n return resolveStatusText(this.status, this.statusTextOverride);\n },\n statusTone: function statusTone() {\n var t = this.statusText;\n if (t === \"已通过\" || t === \"已完成\") return \"ok\";\n if (t === \"已拒绝\" || t === \"已驳回\" || t === \"已退回\" || t === \"已终止\") return \"no\";\n return \"go\";\n }\n },\n created: function created() {// 注入 AI 服务地址\n },\n mounted: function mounted() {\n var _this = this;\n\n window.addEventListener(\"mousemove\", this.onDrag);\n window.addEventListener(\"mouseup\", this.endDrag);\n this.initMobile();\n this.$nextTick(function () {\n return _this.snapBtn();\n });\n },\n beforeDestroy: function beforeDestroy() {\n window.removeEventListener(\"mousemove\", this.onDrag);\n window.removeEventListener(\"mouseup\", this.endDrag);\n if (this.__mqUnlisten) this.__mqUnlisten();\n },\n watch: {\n // 切换审批单时重置对话上下文\n id: function id() {\n this.sessionId = \"\";\n this.messages = [];\n this.fields = [];\n this.rawDetail = null;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val\n });\n }\n },\n methods: {\n // 统一取鼠标/触摸的坐标点\n point: function point(e) {\n var t = e && e.touches && e.touches[0] || e && e.changedTouches && e.changedTouches[0];\n return t ? {\n clientX: t.clientX,\n clientY: t.clientY\n } : {\n clientX: e.clientX,\n clientY: e.clientY\n };\n },\n // 按钮距容器底部的最小基准:8px + H5 底部操作栏高度,保证按钮在操作栏上方\n baseBottom: function baseBottom() {\n return 8 + (Number(this.bottomBarHeight) || 0);\n },\n // H5 下测量底部操作栏元素,返回“视口底部到该元素顶部”的距离 + 安全边距,\n // 使悬浮窗固定在该元素正上方、不遮挡其展示;PC/找不到时回退到 baseBottom()\n barBottomOffset: function barBottomOffset() {\n if (!this.isMobile) return this.baseBottom();\n var sel = this.bottomBarSelector;\n\n if (sel) {\n var el = null;\n\n try {\n el = document.querySelector(sel);\n } catch (e) {\n el = null;\n }\n\n var rect = el && el.getBoundingClientRect();\n\n if (rect && rect.top >= 0 && rect.top <= window.innerHeight) {\n return window.innerHeight - rect.top + 12;\n }\n }\n\n return this.baseBottom();\n },\n // 是否 H5 窄屏(与 CSS media query 保持一致的 480px 边界)\n initMobile: function initMobile() {\n var _this2 = this;\n\n var mq = window.matchMedia && window.matchMedia(\"(max-width: 480px)\");\n\n var apply = function apply() {\n _this2.isMobile = !mq || mq.matches;\n };\n\n apply();\n if (mq && mq.addEventListener) mq.addEventListener(\"change\", apply);else if (mq && mq.addListener) mq.addListener(apply);\n\n this.__mqUnlisten = function () {\n if (mq && mq.removeEventListener) mq.removeEventListener(\"change\", apply);else if (mq && mq.removeListener) mq.removeListener(apply);\n };\n },\n getContainer: function getContainer() {\n // 普通模式:以配置的审批容器为边界;找不到时回退到父级包裹容器\n var selector = this.containerSelector;\n var el = selector ? this.$el.closest(selector) : null;\n return el || this.$el.parentElement;\n },\n getDetailRoot: function getDetailRoot() {\n var box = this.getContainer();\n if (!box) return null;\n var explicit = this.detailRootSelector ? box.querySelector(this.detailRootSelector) : null;\n if (explicit) return explicit; // 子应用/iframe 模式:容器内可能没有 detail-root 标记,\n // 回退到首个详情子节点(排除 AI 面板自身)\n\n var kids = Array.from(box.children).filter(function (c) {\n return !c.classList.contains(\"ai-assistant\");\n });\n return kids.length ? kids[0] : null;\n },\n togglePanel: function togglePanel() {\n var _this3 = this;\n\n // 拖拽后松手会触发一次 click,这里拦截,避免误展开/收起面板\n if (this.moved) {\n this.moved = false;\n return;\n }\n\n if (this.panelVisible) {\n this.closePanel();\n return;\n }\n\n this.panelVisible = true;\n this.$nextTick(function () {\n _this3.snapPanel();\n\n if (!_this3.messages.length) {\n _this3.greet();\n }\n\n _this3.scrollBottom();\n\n if (_this3.$refs.input) _this3.$refs.input.focus();\n });\n },\n closePanel: function closePanel() {\n this.panelVisible = false;\n },\n snapBtn: function snapBtn() {\n var box = this.getContainer();\n if (!box) return; // 默认贴右下角完整展开:右侧留 20px\n\n if (!this.tucked && !this.btnLeft) {\n this.btnLeft = Math.max(0, box.clientWidth - 20 - 46);\n } // 若按钮在可视范围外则修正\n\n\n this.btnLeft = Math.max(0, Math.min(this.btnLeft, box.clientWidth - 46));\n this.btnBottom = Math.max(this.barBottomOffset(), Math.min(this.btnBottom, box.clientHeight - 44 - 8));\n },\n snapPanel: function snapPanel() {\n var box = this.getContainer();\n var el = this.$el.querySelector(\".ai-panel\");\n if (!box || !el) return;\n var margin = 24; // 上下安全距离,避免遮挡\n\n var ph = el.clientHeight || 560;\n var desired = this.btnBottom + 62;\n this.panelBottom = Math.max(margin, Math.min(desired, box.clientHeight - ph - margin));\n },\n // 打开时 AI 先总结当前审批\n greet: function greet() {\n var _this4 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var summary, amount, res, base;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n _this4.collectDetail();\n\n summary = _this4.buildSummary();\n amount = _this4.findField([\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]); // 已接入后端时,优先用 AI 生成的总结\n\n console.log(_this4.id, _this4.aiEnabled());\n\n if (!(_this4.aiEnabled() && _this4.id)) {\n _context.next = 9;\n break;\n }\n\n _context.next = 7;\n return aiApi.getSummary(_this4.id);\n\n case 7:\n res = _context.sent;\n\n if (res && res.data && res.data.summary) {\n summary = res.data.summary;\n }\n\n case 9:\n base = \"\\u4F60\\u597D\\uFF0C\\u6211\\u662F AI \\u5BA1\\u6279\\u52A9\\u624B\\uD83D\\uDC4B\\n\\n\\u5DF2\\u7ECF\\u4E3A\\u4F60\\u68B3\\u7406\\u4E86\\u5F53\\u524D\\u5BA1\\u6279\\u7684\\u6982\\u89C8\\uFF1A\\n\\n\".concat(summary); // const tail = this.fields.length\n // ? `\\n\\n这份单据的关键字段共 ${this.fields.length} 项${amount ? `,其中金额相关「${amount}」` : \"\"}。你可以继续问我,比如「这笔审批是做什么的」「申请人是谁」。`\n // : \"\\n\\n暂时未采集到该单据的明细字段。\"\n // this.push(\"assistant\", base + tail)\n\n _this4.push(\"assistant\", base);\n\n case 11:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n sendMessage: function sendMessage() {\n var _this5 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return;\n this.push(\"user\", text);\n this.inputText = \"\";\n this.sending = true;\n this.collectDetail(); // 直接走后端流式问答(已接入时);未接入则返回本地兜底\n\n var msg = null;\n\n if (this.aiEnabled() && this.id) {\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: \"\"\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk) {\n if (msg) {\n msg.content += chunk;\n\n _this5.followBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this5.fallbackReply(text);\n } else {\n _this5.push(\"assistant\", _this5.fallbackReply(text));\n }\n\n _this5.streaming = false;\n _this5.sending = false;\n\n _this5.followBottom();\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n payload = {\n context: {\n type: _this6.type,\n status: _this6.status,\n typeName: _this6.typeName,\n id: _this6.id,\n summary: _this6.buildSummary(),\n fields: _this6.fields,\n detailData: _this6.rawDetail\n },\n question: question\n };\n _context2.next = 3;\n return _this6.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context2.sent;\n return _context2.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this6.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 当前用户信息(来自 props,替代宿主 Vuex)\n userInfo: function userInfo() {\n return {\n no: this.userId || \"\",\n name: this.userName || \"\"\n };\n },\n // 创建/复用 AI 对话会话\n ensureSession: function ensureSession() {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (!_this7.sessionId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\", _this7.sessionId);\n\n case 2:\n u = _this7.userInfo();\n _context3.next = 5;\n return aiApi.createChatSession({\n approvalNo: _this7.id,\n userId: u.no,\n userName: u.name\n });\n\n case 5:\n res = _context3.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context3.next = 9;\n break;\n }\n\n _this7.sessionId = res.data.sessionId;\n return _context3.abrupt(\"return\", _this7.sessionId);\n\n case 9:\n return _context3.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this8 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this8.aiEnabled()) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 2:\n _context4.prev = 2;\n _context4.next = 5;\n return _this8.ensureSession();\n\n case 5:\n sessionId = _context4.sent;\n\n if (sessionId) {\n _context4.next = 8;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 8:\n text = \"\";\n failed = false;\n _context4.next = 12;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n userId: _this8.userInfo().no,\n userName: _this8.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\" || event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data);\n } else if (event === \"agent_result\") {\n text = data;\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 12:\n if (!failed) {\n _context4.next = 14;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 14:\n return _context4.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 17:\n _context4.prev = 17;\n _context4.t0 = _context4[\"catch\"](2);\n return _context4.abrupt(\"return\", null);\n\n case 20:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[2, 17]]);\n }))();\n },\n // 无法从数据中检索到时的兜底:诚实告知查询不到,不编造\n fallbackReply: function fallbackReply(question) {\n var q = String(question).trim();\n var list = '';\n return \"\\u6682\\u65F6\\u65E0\\u6CD5\\u67E5\\u8BE2\\u5230\\u4E0E\\u300C\".concat(q, \"\\u300D\\u5BF9\\u5E94\\u7684\\u4FE1\\u606F\\u3002\").concat(list);\n },\n findField: function findField(labels) {\n var _iterator = (0, _createForOfIteratorHelper2.default)(this.rawEntries()),\n _step;\n\n try {\n var _loop = function _loop() {\n var e = _step.value;\n if (labels.some(function (l) {\n return e.label.indexOf(l) > -1;\n })) return {\n v: e.value\n };\n };\n\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var _ret = _loop();\n\n if ((0, _typeof2.default)(_ret) === \"object\") return _ret.v;\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return \"\";\n },\n // 合并 DOM 采集字段 + 详情页完整原始业务对象,得到可检索的条目\n rawEntries: function rawEntries() {\n var _this9 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this9.stringify(value);\n\n if (!v) return;\n var k = \"\".concat(label, \"__\").concat(v);\n if (seen.has(k)) return;\n seen.add(k);\n list.push({\n label: label,\n value: v\n });\n };\n\n var _iterator2 = (0, _createForOfIteratorHelper2.default)(this.fields),\n _step2;\n\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var f = _step2.value;\n pushEntry(f.label, f.value);\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n\n var raw = this.rawDetail && this.rawDetail.data;\n\n if (raw && (0, _typeof2.default)(raw) === \"object\") {\n for (var _i = 0, _Object$keys = Object.keys(raw); _i < _Object$keys.length; _i++) {\n var key = _Object$keys[_i];\n pushEntry(key, raw[key]);\n }\n }\n\n return list;\n },\n stringify: function stringify(v) {\n if (v === null || v === undefined) return \"\";\n\n if ((0, _typeof2.default)(v) === \"object\") {\n try {\n return JSON.stringify(v);\n } catch (e) {\n return \"\";\n }\n }\n\n return String(v);\n },\n // 通用采集详情页数据:兼容 el-form-item / el-descriptions / 自定义 span标签:值 / 表格,\n // 并尽量读取详情页组件的原始业务对象(info/form) 作为完整数据\n collectDetail: function collectDetail() {\n var root = this.getDetailRoot();\n var seen = {};\n\n var push = function push(label, value) {\n label = normalizeLabel(label);\n var v = String(value === null || value === undefined ? \"\" : value).replace(/\\s+/g, \" \").trim();\n if (!label || !v || v === \"暂无\" || label === \"图片\" || label === \"操作\") return;\n if (seen[label] !== undefined) return;\n seen[label] = v;\n };\n\n if (root) {\n // 1) el-form-item\n root.querySelectorAll(\".el-form-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-form-item__label\");\n var contentEl = item.querySelector(\".el-form-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 2) el-descriptions 描述项\n\n root.querySelectorAll(\".el-descriptions-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-descriptions-item__label\");\n var contentEl = item.querySelector(\".el-descriptions-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 3) 自定义 label:value(span 以「:」/「:」结尾,值在大括号内剩余文本)\n\n root.querySelectorAll(\"span\").forEach(function (span) {\n var txt = (span.textContent || \"\").trim();\n var m = txt.match(/^(.+?)([::])$/);\n if (!m) return;\n var parent = span.parentElement;\n if (!parent) return;\n var value = (parent.textContent || \"\").replace(txt, \"\").replace(/\\s+/g, \" \").trim();\n push(m[1], value);\n }); // 4) 表格:表头 -> 逐行数据\n\n root.querySelectorAll(\"table\").forEach(function (table) {\n var rows = table.querySelectorAll(\"tr\");\n var headers = [];\n\n if (rows.length) {\n headers = Array.from(rows[0].querySelectorAll(\"th,td\")).map(function (c) {\n return normalizeLabel(c.textContent);\n });\n }\n\n rows.forEach(function (tr, idx) {\n if (idx === 0) return;\n var cells = Array.from(tr.querySelectorAll(\"th,td\")).map(function (c) {\n return (c.textContent || \"\").replace(/\\s+/g, \" \").trim();\n });\n if (!cells.length) return;\n\n if (headers.length) {\n headers.forEach(function (h, j) {\n return h && cells[j] !== undefined && push(h, cells[j]);\n });\n } else {\n cells.forEach(function (c, j) {\n return c && push(\"\\u7B2C\".concat(idx, \"\\u884C\\u5B57\\u6BB5\").concat(j + 1), c);\n });\n }\n });\n });\n } // 5) 读取详情组件的原始业务对象,作为完整结构化数据补全\n\n\n var raw = this.readRawDetail();\n this.rawDetail = raw;\n if (raw) this.flattenTo(seen, raw);\n this.fields = Object.keys(seen).map(function (label) {\n return {\n label: label,\n value: seen[label]\n };\n }).slice(0, 40);\n },\n // 寻找详情页组件的业务数据对象(info / form 等)\n readRawDetail: function readRawDetail() {\n var _this10 = this;\n\n var root = this.getDetailRoot();\n if (!root) return null;\n\n var _iterator3 = (0, _createForOfIteratorHelper2.default)(root.querySelectorAll(\"*\")),\n _step3;\n\n try {\n for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {\n var el = _step3.value;\n var vm = el.__vue__;\n if (!vm || !vm.$data || (0, _typeof2.default)(vm.$data) !== \"object\") continue;\n var d = vm.$data;\n\n var _loop2 = function _loop2() {\n var key = _arr[_i2];\n var obj = d[key];\n\n if (obj && (0, _typeof2.default)(obj) === \"object\" && !Array.isArray(obj)) {\n var keys = Object.keys(obj).filter(function (k) {\n return obj[k] !== \"\" && obj[k] !== null && obj[k] !== undefined;\n });\n\n if (keys.length) {\n return {\n v: {\n key: key,\n data: _this10.jsonSafe(obj)\n }\n };\n }\n }\n };\n\n for (var _i2 = 0, _arr = [\"info\", \"form\"]; _i2 < _arr.length; _i2++) {\n var _ret2 = _loop2();\n\n if ((0, _typeof2.default)(_ret2) === \"object\") return _ret2.v;\n }\n }\n } catch (err) {\n _iterator3.e(err);\n } finally {\n _iterator3.f();\n }\n\n return null;\n },\n jsonSafe: function jsonSafe(obj) {\n try {\n return JSON.parse(JSON.stringify(obj, function (k, v) {\n return typeof v === \"function\" ? undefined : v;\n }));\n } catch (e) {\n return obj;\n }\n },\n // 把原始业务对象摊平成 label:value,补进已采集集合\n flattenTo: function flattenTo(seen, raw) {\n var walk = function walk(obj, path) {\n if (obj === null || obj === undefined) return;\n\n if (Array.isArray(obj)) {\n obj.forEach(function (item, i) {\n return walk(item, path ? \"\".concat(path, \"[\").concat(i, \"]\") : \"[\".concat(i, \"]\"));\n });\n return;\n }\n\n if ((0, _typeof2.default)(obj) === \"object\") {\n Object.keys(obj).forEach(function (k) {\n return walk(obj[k], path ? \"\".concat(path, \".\").concat(k) : k);\n });\n return;\n }\n\n var label = path || \"\";\n var v = String(obj).replace(/\\s+/g, \" \").trim();\n if (label && v && seen[label] === undefined) seen[label] = v;\n };\n\n walk(raw.data, raw.key);\n },\n // 依据采集字段生成“当前审批是做什么的”概览(启发式)\n buildSummary: function buildSummary() {\n var hits = {};\n FIELD_HINTS.forEach(function (hint) {\n hits[hint.key] = \"\";\n });\n this.fields.forEach(function (f) {\n var _iterator4 = (0, _createForOfIteratorHelper2.default)(FIELD_HINTS),\n _step4;\n\n try {\n for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {\n var hint = _step4.value;\n if (hits[hint.key]) continue;\n\n if (hint.labels.some(function (l) {\n return f.label.indexOf(l) > -1;\n })) {\n hits[hint.key] = f.value;\n break;\n }\n }\n } catch (err) {\n _iterator4.e(err);\n } finally {\n _iterator4.f();\n }\n });\n var name = this.typeName || \"该单据\";\n var parts = [];\n if (hits.applicant) parts.push(\"\\u7533\\u8BF7\\u4EBA\\u300C\".concat(hits.applicant, \"\\u300D\"));\n if (hits.unit) parts.push(\"\\u6240\\u5C5E\\u5355\\u4F4D\\u300C\".concat(hits.unit, \"\\u300D\"));\n var verb = \"\".concat(name === \"该单据\" ? \"该审批\" : \"\\u8BE5\\u300C\".concat(name, \"\\u300D\\u5BA1\\u6279\"));\n var summary = \"\".concat(verb, \"\\u662F\\u4E00\\u7B14\\u4E1A\\u52A1\\u5BA1\\u6279\");\n if (parts.length) summary += \"\\uFF0C\\u7531\".concat(parts.join(\",\"), \"\\u53D1\\u8D77\");\n if (hits.reason) summary += \"\\uFF0C\\u4E3B\\u8981\\u4E8B\\u7531\\uFF1A\".concat(hits.reason);\n if (hits.amount) summary += \"\\uFF0C\\u6D89\\u53CA\\u91D1\\u989D\\u300C\".concat(hits.amount, \"\\u300D\");\n summary += \"\\u3002\\u5F53\\u524D\\u5BA1\\u6279\\u72B6\\u6001\\u4E3A\\u300C\".concat(this.statusText, \"\\u300D\\u3002\");\n return summary;\n },\n push: function push(role, content) {\n this.messages.push({\n id: ++msgSeq,\n role: role,\n content: content\n });\n },\n // 气泡渲染:助手消息转 Markdown,用户消息按纯文本处理(转义防 XSS)\n renderBubble: function renderBubble(m) {\n var text = m && m.content != null ? String(m.content) : \"\";\n if (m && m.role === \"user\") return this.escapeHtml(text);\n return md.render(text || \"\");\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this11 = this;\n\n this.$nextTick(function () {\n var el = _this11.$refs.chatBody;\n if (el) el.scrollTop = el.scrollHeight;\n });\n },\n // 用户手动滚动时记录是否仍停留底部;上滚离开底部即暂停自动跟随\n onChatScroll: function onChatScroll() {\n var el = this.$refs.chatBody;\n if (!el) return;\n var nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;\n this.autoFollow = nearBottom;\n },\n // 仅当用户停留在底部时才自动滚到底,避免回答过程中打断用户向上查看\n followBottom: function followBottom() {\n if (!this.autoFollow) return;\n this.scrollBottom();\n },\n startDrag: function startDrag(e) {\n if (e.button !== undefined && e.button !== 0) return;\n var point = this.point(e);\n if (!point) return;\n this.dragging = true;\n this.moved = false;\n this.tucked = false;\n this.startX = point.clientX;\n this.startY = point.clientY;\n this.startLeft = this.btnLeft;\n this.startBottom = this.btnBottom; // 拖动时防止文本/图片被选中导致跟手中断;\n // 仅鼠标 preventDefault,触摸靠 touch-action:none,避免吞掉点击(打开/收起面板)\n\n if (e.type === \"mousedown\") e.preventDefault();\n },\n onDrag: function onDrag(e) {\n if (!this.dragging) return;\n var point = this.point(e);\n if (!point) return;\n var dx = point.clientX - this.startX;\n var dy = point.clientY - this.startY;\n\n if (!this.moved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this.moved = true;\n }\n\n var box = this.getContainer();\n var maxW = box ? box.clientWidth - 46 : 9999;\n var maxH = box ? box.clientHeight - 44 : 9999;\n var minBottom = this.barBottomOffset();\n var left = Math.max(0, Math.min(this.startLeft + dx, Math.max(0, maxW)));\n var bottom = Math.max(minBottom, Math.min(this.startBottom - dy, Math.max(minBottom, maxH - 8))); // 直接写 DOM,图标即时跟手,不经过 Vue 重渲染\n\n var el = this.$refs.floatEl;\n\n if (el) {\n el.style.left = left + \"px\";\n el.style.bottom = bottom + \"px\";\n }\n\n if (this.panelVisible) {\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n if (!this.dragging) return;\n this.dragging = false;\n if (!this.moved) return; // 读取拖拽后的实际位置\n\n var el = this.$refs.floatEl;\n var left = el ? parseInt(el.style.left || \"0\", 10) : this.btnLeft;\n var bottom = el ? parseInt(el.style.bottom || \"0\", 10) : this.btnBottom;\n this.btnLeft = left;\n this.btnBottom = bottom; // 拖动过才吸附贴边;靠近右边时收成半角标\n\n var box = this.getContainer();\n if (!box) return;\n\n if (box.clientWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = box.clientWidth - 46;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.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");
233
233
 
234
234
  /***/ }),
235
235
 
236
- /***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"09ea9ef8-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&":
236
+ /***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"02165064-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&":
237
237
  /*!*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
238
- !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"09ea9ef8-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true& ***!
238
+ !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"02165064-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true& ***!
239
239
  \*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
240
240
  /*! exports provided: render, staticRenderFns */
241
241
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
242
242
 
243
243
  "use strict";
244
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"ai-assistant\" },\n [\n _c(\"transition\", { attrs: { name: \"ai-fade\" } }, [\n _vm.panelVisible\n ? _c(\n \"div\",\n {\n staticClass: \"ai-panel\",\n style: { bottom: _vm.panelBottom + \"px\" },\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"ai-panel__header\" }, [\n _c(\"span\", { staticClass: \"ai-panel__icon\" }, [\n _c(\"img\", { attrs: { src: _vm.iconUrl, alt: \"AI\" } })\n ]),\n _c(\"span\", { staticClass: \"ai-panel__title\" }, [\n _vm._v(\"AI 审批助手\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"ai-panel__close\",\n on: { click: _vm.closePanel }\n },\n [_vm._v(\"✕\")]\n )\n ]),\n _c(\n \"div\",\n {\n ref: \"chatBody\",\n staticClass: \"ai-chat\",\n on: {\n \"&scroll\": function($event) {\n return _vm.onChatScroll.apply(null, arguments)\n }\n }\n },\n [\n _vm._l(_vm.messages, function(m) {\n return [\n _c(\n \"div\",\n {\n key: m.id,\n staticClass: \"msg\",\n class: \"msg--\" + m.role\n },\n [\n m.role === \"assistant\"\n ? _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ])\n : _vm._e(),\n _c(\"div\", {\n staticClass: \"msg__bubble\",\n domProps: {\n innerHTML: _vm._s(_vm.renderBubble(m))\n }\n })\n ]\n )\n ]\n }),\n _vm.sending && !_vm.streaming\n ? _c(\"div\", { staticClass: \"msg msg--assistant\" }, [\n _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]),\n _c(\n \"div\",\n { staticClass: \"msg__bubble msg__bubble--typing\" },\n [_c(\"span\"), _c(\"span\"), _c(\"span\")]\n )\n ])\n : _vm._e()\n ],\n 2\n ),\n _c(\"div\", { staticClass: \"ai-input\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"input\",\n staticClass: \"ai-input__box\",\n attrs: { rows: \"1\", placeholder: \"输入你的问题\" },\n domProps: { value: _vm.inputText },\n on: {\n keydown: function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.sendMessage.apply(null, arguments)\n },\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n }\n }\n }),\n _c(\n \"button\",\n {\n staticClass: \"ai-input__send\",\n attrs: { disabled: _vm.sending || !_vm.inputText.trim() },\n on: { click: _vm.sendMessage }\n },\n [_vm._v(\"发送\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]),\n _c(\n \"div\",\n {\n ref: \"floatEl\",\n staticClass: \"ai-float\",\n class: {\n tucked: _vm.tucked,\n \"is-open\": _vm.panelVisible,\n dragging: _vm.dragging\n },\n style: { left: _vm.btnLeft + \"px\", bottom: _vm.btnBottom + \"px\" },\n on: { mousedown: _vm.startDrag }\n },\n [\n _c(\n \"button\",\n {\n staticClass: \"ai-float__btn\",\n class: { \"is-active\": _vm.panelVisible },\n attrs: {\n title: _vm.panelVisible ? \"收起 AI 助手\" : \"打开 AI 助手\"\n },\n on: { click: _vm.togglePanel, mouseup: _vm.stopDrag }\n },\n [\n _c(\"img\", {\n staticClass: \"ai-float__img\",\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]\n )\n ]\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2209ea9ef8-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
244
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"ai-assistant\" },\n [\n _vm.panelVisible && _vm.isMobile\n ? _c(\"div\", { staticClass: \"ai-mask\", on: { click: _vm.closePanel } })\n : _vm._e(),\n _c(\"transition\", { attrs: { name: \"ai-fade\" } }, [\n _vm.panelVisible\n ? _c(\n \"div\",\n {\n staticClass: \"ai-panel\",\n style: { bottom: _vm.panelBottom + \"px\" },\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"ai-panel__header\" }, [\n _c(\"span\", { staticClass: \"ai-panel__icon\" }, [\n _c(\"img\", { attrs: { src: _vm.iconUrl, alt: \"AI\" } })\n ]),\n _c(\"span\", { staticClass: \"ai-panel__title\" }, [\n _vm._v(\"AI 审批助手\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"ai-panel__close\",\n on: { click: _vm.closePanel }\n },\n [_vm._v(\"✕\")]\n )\n ]),\n _c(\n \"div\",\n {\n ref: \"chatBody\",\n staticClass: \"ai-chat\",\n on: {\n \"&scroll\": function($event) {\n return _vm.onChatScroll.apply(null, arguments)\n }\n }\n },\n [\n _vm._l(_vm.messages, function(m) {\n return [\n _c(\n \"div\",\n {\n key: m.id,\n staticClass: \"msg\",\n class: \"msg--\" + m.role\n },\n [\n m.role === \"assistant\"\n ? _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ])\n : _vm._e(),\n _c(\"div\", {\n staticClass: \"msg__bubble\",\n domProps: {\n innerHTML: _vm._s(_vm.renderBubble(m))\n }\n })\n ]\n )\n ]\n }),\n _vm.sending && !_vm.streaming\n ? _c(\"div\", { staticClass: \"msg msg--assistant\" }, [\n _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]),\n _c(\n \"div\",\n { staticClass: \"msg__bubble msg__bubble--typing\" },\n [_c(\"span\"), _c(\"span\"), _c(\"span\")]\n )\n ])\n : _vm._e()\n ],\n 2\n ),\n _c(\"div\", { staticClass: \"ai-input\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"input\",\n staticClass: \"ai-input__box\",\n attrs: { rows: \"1\", placeholder: \"输入你的问题\" },\n domProps: { value: _vm.inputText },\n on: {\n keydown: function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.sendMessage.apply(null, arguments)\n },\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n }\n }\n }),\n _c(\n \"button\",\n {\n staticClass: \"ai-input__send\",\n attrs: { disabled: _vm.sending || !_vm.inputText.trim() },\n on: { click: _vm.sendMessage }\n },\n [_vm._v(\"发送\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]),\n _c(\n \"div\",\n {\n ref: \"floatEl\",\n staticClass: \"ai-float\",\n class: {\n tucked: _vm.tucked,\n \"is-open\": _vm.panelVisible,\n dragging: _vm.dragging\n },\n style: { left: _vm.btnLeft + \"px\", bottom: _vm.btnBottom + \"px\" },\n on: {\n mousedown: _vm.startDrag,\n touchstart: _vm.startDrag,\n touchmove: _vm.onDrag,\n touchend: _vm.endDrag,\n touchcancel: _vm.endDrag\n }\n },\n [\n _c(\n \"button\",\n {\n staticClass: \"ai-float__btn\",\n class: { \"is-active\": _vm.panelVisible },\n attrs: {\n title: _vm.panelVisible ? \"收起 AI 助手\" : \"打开 AI 助手\"\n },\n on: { click: _vm.togglePanel, mouseup: _vm.stopDrag }\n },\n [\n _c(\"img\", {\n staticClass: \"ai-float__img\",\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]\n )\n ]\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2202165064-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
245
245
 
246
246
  /***/ }),
247
247
 
@@ -252,7 +252,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
252
252
  /*! no static exports found */
253
253
  /***/ (function(module, exports, __webpack_require__) {
254
254
 
255
- eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"@charset \\\"UTF-8\\\";\\n.ai-assistant[data-v-5d775cd8] {\\n position: absolute;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n pointer-events: none;\\n z-index: 20;\\n}\\n.ai-float[data-v-5d775cd8] {\\n position: absolute;\\n pointer-events: auto;\\n z-index: 21;\\n transition: transform 0.25s ease;\\n}\\n.ai-float.dragging[data-v-5d775cd8] {\\n transition: none;\\n}\\n.ai-float.tucked[data-v-5d775cd8] {\\n transform: translateX(50%);\\n}\\n.ai-float.tucked[data-v-5d775cd8]:hover, .ai-float.tucked.is-open[data-v-5d775cd8] {\\n transform: translateX(0);\\n}\\n.ai-float__btn[data-v-5d775cd8] {\\n width: 46px;\\n height: 46px;\\n border-radius: 50%;\\n border: 1px solid #e4e7ed;\\n background: #fff;\\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n cursor: pointer;\\n color: #3067d9;\\n transition: box-shadow 0.2s, transform 0.2s;\\n}\\n.ai-float__btn[data-v-5d775cd8]:hover {\\n box-shadow: 0 4px 16px rgba(48, 103, 217, 0.3);\\n}\\n.ai-float__btn.is-active[data-v-5d775cd8] {\\n background: #fff;\\n color: #3067d9;\\n transform: none;\\n}\\n.ai-float__btn .ai-float__img[data-v-5d775cd8] {\\n width: 28px;\\n height: 28px;\\n border-radius: 50%;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n position: absolute;\\n right: 74px;\\n width: 384px;\\n height: 560px; /* 默认高度 */\\n max-height: calc(100% - 48px); /* 上下各留 24px 安全距离,容器不够高时自动压缩 */\\n max-width: calc(100% - 98px); /* 右侧留按钮位(74px)+左侧 24px 安全,窄容器不溢出遮挡 */\\n background: #fff;\\n border: 1px solid #e4e7ed;\\n border-radius: 10px;\\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);\\n pointer-events: auto;\\n z-index: 22;\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n}\\n.ai-panel__header[data-v-5d775cd8] {\\n height: 46px;\\n padding: 0 12px;\\n display: flex;\\n align-items: center;\\n background: #fafbfc;\\n border-bottom: 1px solid #f0f2f5;\\n gap: 8px;\\n}\\n.ai-panel__icon[data-v-5d775cd8] {\\n color: #3067d9;\\n display: flex;\\n}\\n.ai-panel__icon img[data-v-5d775cd8] {\\n width: 22px;\\n height: 22px;\\n border-radius: 50%;\\n}\\n.ai-panel__title[data-v-5d775cd8] {\\n font-size: 15px;\\n font-weight: 600;\\n color: #1f2329;\\n}\\n.ai-panel__status[data-v-5d775cd8] {\\n font-size: 12px;\\n padding: 1px 8px;\\n border-radius: 10px;\\n}\\n.ai-panel__status.status-ok[data-v-5d775cd8] {\\n color: #1a7f37;\\n background: #e6f4ea;\\n}\\n.ai-panel__status.status-no[data-v-5d775cd8] {\\n color: #d83931;\\n background: #fdeeee;\\n}\\n.ai-panel__status.status-go[data-v-5d775cd8] {\\n color: #3067d9;\\n background: #e8efff;\\n}\\n.ai-panel__close[data-v-5d775cd8] {\\n margin-left: auto;\\n cursor: pointer;\\n color: #86909c;\\n font-size: 16px;\\n line-height: 1;\\n padding: 0 2px;\\n}\\n.ai-panel__close[data-v-5d775cd8]:hover {\\n color: #1f2329;\\n}\\n.ai-chat[data-v-5d775cd8] {\\n flex: 1;\\n min-height: 120px;\\n padding: 14px;\\n overflow-y: auto;\\n background: #fff;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar {\\n width: 6px;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar-thumb {\\n background: #e4e7ed;\\n border-radius: 3px;\\n}\\n.msg[data-v-5d775cd8] {\\n display: flex;\\n margin-bottom: 14px;\\n}\\n.msg--assistant[data-v-5d775cd8] {\\n align-items: flex-start;\\n}\\n.msg--user[data-v-5d775cd8] {\\n justify-content: flex-end;\\n}\\n.msg__avatar[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n width: 30px;\\n height: 30px;\\n margin-right: 8px;\\n border-radius: 50%;\\n background: #e8efff;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\\n.msg__avatar img[data-v-5d775cd8] {\\n width: 30px;\\n height: 30px;\\n border-radius: 50%;\\n}\\n.msg__bubble[data-v-5d775cd8] {\\n max-width: 76%;\\n padding: 9px 12px;\\n border-radius: 10px;\\n font-size: 13px;\\n line-height: 1.7;\\n word-break: break-word;\\n color: #4e5969;\\n background: #f5f6f7;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n.msg__bubble h2[data-v-5d775cd8],\\n.msg__bubble h3[data-v-5d775cd8],\\n.msg__bubble h4[data-v-5d775cd8],\\n.msg__bubble h5[data-v-5d775cd8],\\n.msg__bubble h6[data-v-5d775cd8] {\\n font-size: 14px;\\n font-weight: 600;\\n color: #1f2329;\\n margin: 8px 0 4px;\\n}\\n.msg__bubble h1[data-v-5d775cd8]:first-child,\\n.msg__bubble h2[data-v-5d775cd8]:first-child,\\n.msg__bubble h3[data-v-5d775cd8]:first-child,\\n.msg__bubble h4[data-v-5d775cd8]:first-child,\\n.msg__bubble h5[data-v-5d775cd8]:first-child,\\n.msg__bubble h6[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble p[data-v-5d775cd8] {\\n margin: 4px 0;\\n}\\n.msg__bubble p[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble ul[data-v-5d775cd8],\\n.msg__bubble ol[data-v-5d775cd8] {\\n margin: 4px 0;\\n padding-left: 0;\\n list-style: none;\\n}\\n.msg__bubble li[data-v-5d775cd8] {\\n margin: 2px 0;\\n}\\n.msg__bubble strong[data-v-5d775cd8] {\\n color: #1f2329;\\n font-weight: 600;\\n}\\n.msg__bubble a[data-v-5d775cd8] {\\n color: #3067d9;\\n text-decoration: none;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n background: #f0f2f5;\\n color: #d83931;\\n padding: 1px 4px;\\n border-radius: 3px;\\n font-size: 12px;\\n}\\n.msg__bubble pre[data-v-5d775cd8] {\\n background: #f6f7f8;\\n padding: 8px;\\n border-radius: 6px;\\n overflow-x: auto;\\n margin: 6px 0;\\n}\\n.msg__bubble pre code[data-v-5d775cd8] {\\n background: none;\\n color: #1f2329;\\n padding: 0;\\n}\\n.msg--user .msg__bubble[data-v-5d775cd8] {\\n color: #fff;\\n background: #3067d9;\\n}\\n.msg__bubble--typing[data-v-5d775cd8] {\\n display: flex;\\n gap: 4px;\\n padding: 12px;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8] {\\n width: 6px;\\n height: 6px;\\n border-radius: 50%;\\n background: #86909c;\\n -webkit-animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(2) {\\n -webkit-animation-delay: 0.2s;\\n animation-delay: 0.2s;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(3) {\\n -webkit-animation-delay: 0.4s;\\n animation-delay: 0.4s;\\n}\\n@-webkit-keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n@keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n.ai-quick[data-v-5d775cd8] {\\n display: flex;\\n flex-wrap: wrap;\\n gap: 6px;\\n padding: 0 14px 10px;\\n background: #fff;\\n}\\n.ai-quick__item[data-v-5d775cd8] {\\n border: 1px solid #dce3f0;\\n background: #f7f9ff;\\n color: #3067d9;\\n font-size: 12px;\\n padding: 4px 10px;\\n border-radius: 14px;\\n cursor: pointer;\\n}\\n.ai-quick__item[data-v-5d775cd8]:hover {\\n border-color: #3067d9;\\n background: #eef3ff;\\n}\\n.ai-input[data-v-5d775cd8] {\\n display: flex;\\n align-items: flex-end;\\n gap: 8px;\\n padding: 10px 12px;\\n border-top: 1px solid #f0f2f5;\\n background: #fafbfc;\\n}\\n.ai-input__box[data-v-5d775cd8] {\\n flex: 1;\\n resize: none;\\n border: 1px solid #e4e7ed;\\n border-radius: 6px;\\n padding: 7px 10px;\\n font-size: 13px;\\n line-height: 1.5;\\n font-family: inherit;\\n color: #1f2329;\\n background: #fff;\\n outline: none;\\n max-height: 96px;\\n}\\n.ai-input__box[data-v-5d775cd8]:focus {\\n border-color: #3067d9;\\n}\\n.ai-input__send[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n border: none;\\n background: #3067d9;\\n color: #fff;\\n font-size: 13px;\\n padding: 7px 16px;\\n border-radius: 6px;\\n cursor: pointer;\\n}\\n.ai-input__send[data-v-5d775cd8]:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n.ai-fade-enter-active[data-v-5d775cd8],\\n.ai-fade-leave-active[data-v-5d775cd8] {\\n transition: opacity 0.2s, transform 0.2s;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n.ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 0;\\n transform: translateY(10px);\\n}\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
255
+ eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"@charset \\\"UTF-8\\\";\\n.ai-assistant[data-v-5d775cd8] {\\n position: absolute;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n pointer-events: none;\\n z-index: 20;\\n}\\n.ai-float[data-v-5d775cd8] {\\n position: absolute;\\n pointer-events: auto;\\n z-index: 21;\\n transition: transform 0.25s ease;\\n touch-action: none; /* 触摸拖拽时不触发页面滚动/手势,保证跟手 */\\n}\\n.ai-float.dragging[data-v-5d775cd8] {\\n transition: none;\\n}\\n.ai-float.tucked[data-v-5d775cd8] {\\n transform: translateX(50%);\\n}\\n.ai-float.tucked[data-v-5d775cd8]:hover, .ai-float.tucked.is-open[data-v-5d775cd8] {\\n transform: translateX(0);\\n}\\n.ai-float__btn[data-v-5d775cd8] {\\n width: 46px;\\n height: 46px;\\n border-radius: 50%;\\n border: 1px solid #e4e7ed;\\n background: #fff;\\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n cursor: pointer;\\n color: #3067d9;\\n transition: box-shadow 0.2s, transform 0.2s;\\n}\\n.ai-float__btn[data-v-5d775cd8]:hover {\\n box-shadow: 0 4px 16px rgba(48, 103, 217, 0.3);\\n}\\n.ai-float__btn.is-active[data-v-5d775cd8] {\\n background: #fff;\\n color: #3067d9;\\n transform: none;\\n}\\n.ai-float__btn .ai-float__img[data-v-5d775cd8] {\\n width: 28px;\\n height: 28px;\\n border-radius: 50%;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n position: absolute;\\n right: 74px;\\n width: 384px;\\n height: 560px; /* 默认高度 */\\n max-height: calc(100% - 48px); /* 上下各留 24px 安全距离,容器不够高时自动压缩 */\\n max-width: calc(100% - 98px); /* 右侧留按钮位(74px)+左侧 24px 安全,窄容器不溢出遮挡 */\\n background: #fff;\\n border: 1px solid #e4e7ed;\\n border-radius: 10px;\\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);\\n pointer-events: auto;\\n z-index: 22;\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n}\\n.ai-panel__header[data-v-5d775cd8] {\\n height: 46px;\\n padding: 0 12px;\\n display: flex;\\n align-items: center;\\n background: #fafbfc;\\n border-bottom: 1px solid #f0f2f5;\\n gap: 8px;\\n}\\n.ai-panel__icon[data-v-5d775cd8] {\\n color: #3067d9;\\n display: flex;\\n}\\n.ai-panel__icon img[data-v-5d775cd8] {\\n width: 22px;\\n height: 22px;\\n border-radius: 50%;\\n}\\n.ai-panel__title[data-v-5d775cd8] {\\n font-size: 15px;\\n font-weight: 600;\\n color: #1f2329;\\n}\\n.ai-panel__status[data-v-5d775cd8] {\\n font-size: 12px;\\n padding: 1px 8px;\\n border-radius: 10px;\\n}\\n.ai-panel__status.status-ok[data-v-5d775cd8] {\\n color: #1a7f37;\\n background: #e6f4ea;\\n}\\n.ai-panel__status.status-no[data-v-5d775cd8] {\\n color: #d83931;\\n background: #fdeeee;\\n}\\n.ai-panel__status.status-go[data-v-5d775cd8] {\\n color: #3067d9;\\n background: #e8efff;\\n}\\n.ai-panel__close[data-v-5d775cd8] {\\n margin-left: auto;\\n cursor: pointer;\\n color: #86909c;\\n font-size: 16px;\\n line-height: 1;\\n padding: 0 2px;\\n}\\n.ai-panel__close[data-v-5d775cd8]:hover {\\n color: #1f2329;\\n}\\n.ai-chat[data-v-5d775cd8] {\\n flex: 1;\\n min-height: 120px;\\n padding: 14px;\\n overflow-y: auto;\\n background: #fff;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar {\\n width: 6px;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar-thumb {\\n background: #e4e7ed;\\n border-radius: 3px;\\n}\\n.msg[data-v-5d775cd8] {\\n display: flex;\\n margin-bottom: 14px;\\n}\\n.msg--assistant[data-v-5d775cd8] {\\n align-items: flex-start;\\n}\\n.msg--user[data-v-5d775cd8] {\\n justify-content: flex-end;\\n}\\n.msg__avatar[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n width: 30px;\\n height: 30px;\\n margin-right: 8px;\\n border-radius: 50%;\\n background: #e8efff;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\\n.msg__avatar img[data-v-5d775cd8] {\\n width: 30px;\\n height: 30px;\\n border-radius: 50%;\\n}\\n.msg__bubble[data-v-5d775cd8] {\\n max-width: 76%;\\n padding: 9px 12px;\\n border-radius: 10px;\\n font-size: 13px;\\n line-height: 1.7;\\n word-break: break-word;\\n color: #4e5969;\\n background: #f5f6f7;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n.msg__bubble h2[data-v-5d775cd8],\\n.msg__bubble h3[data-v-5d775cd8],\\n.msg__bubble h4[data-v-5d775cd8],\\n.msg__bubble h5[data-v-5d775cd8],\\n.msg__bubble h6[data-v-5d775cd8] {\\n font-size: 14px;\\n font-weight: 600;\\n color: #1f2329;\\n margin: 8px 0 4px;\\n}\\n.msg__bubble h1[data-v-5d775cd8]:first-child,\\n.msg__bubble h2[data-v-5d775cd8]:first-child,\\n.msg__bubble h3[data-v-5d775cd8]:first-child,\\n.msg__bubble h4[data-v-5d775cd8]:first-child,\\n.msg__bubble h5[data-v-5d775cd8]:first-child,\\n.msg__bubble h6[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble p[data-v-5d775cd8] {\\n margin: 4px 0;\\n}\\n.msg__bubble p[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble ul[data-v-5d775cd8],\\n.msg__bubble ol[data-v-5d775cd8] {\\n margin: 4px 0;\\n padding-left: 0;\\n list-style: none;\\n}\\n.msg__bubble li[data-v-5d775cd8] {\\n margin: 2px 0;\\n}\\n.msg__bubble strong[data-v-5d775cd8] {\\n color: #1f2329;\\n font-weight: 600;\\n}\\n.msg__bubble a[data-v-5d775cd8] {\\n color: #3067d9;\\n text-decoration: none;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n background: #f0f2f5;\\n color: #d83931;\\n padding: 1px 4px;\\n border-radius: 3px;\\n font-size: 12px;\\n}\\n.msg__bubble pre[data-v-5d775cd8] {\\n background: #f6f7f8;\\n padding: 8px;\\n border-radius: 6px;\\n overflow-x: auto;\\n margin: 6px 0;\\n}\\n.msg__bubble pre code[data-v-5d775cd8] {\\n background: none;\\n color: #1f2329;\\n padding: 0;\\n}\\n.msg--user .msg__bubble[data-v-5d775cd8] {\\n color: #fff;\\n background: #3067d9;\\n}\\n.msg__bubble--typing[data-v-5d775cd8] {\\n display: flex;\\n gap: 4px;\\n padding: 12px;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8] {\\n width: 6px;\\n height: 6px;\\n border-radius: 50%;\\n background: #86909c;\\n -webkit-animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(2) {\\n -webkit-animation-delay: 0.2s;\\n animation-delay: 0.2s;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(3) {\\n -webkit-animation-delay: 0.4s;\\n animation-delay: 0.4s;\\n}\\n@-webkit-keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n@keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n.ai-quick[data-v-5d775cd8] {\\n display: flex;\\n flex-wrap: wrap;\\n gap: 6px;\\n padding: 0 14px 10px;\\n background: #fff;\\n}\\n.ai-quick__item[data-v-5d775cd8] {\\n border: 1px solid #dce3f0;\\n background: #f7f9ff;\\n color: #3067d9;\\n font-size: 12px;\\n padding: 4px 10px;\\n border-radius: 14px;\\n cursor: pointer;\\n}\\n.ai-quick__item[data-v-5d775cd8]:hover {\\n border-color: #3067d9;\\n background: #eef3ff;\\n}\\n.ai-input[data-v-5d775cd8] {\\n display: flex;\\n align-items: flex-end;\\n gap: 8px;\\n padding: 10px 12px;\\n border-top: 1px solid #f0f2f5;\\n background: #fafbfc;\\n}\\n.ai-input__box[data-v-5d775cd8] {\\n flex: 1;\\n resize: none;\\n border: 1px solid #e4e7ed;\\n border-radius: 6px;\\n padding: 7px 10px;\\n font-size: 13px;\\n line-height: 1.5;\\n font-family: inherit;\\n color: #1f2329;\\n background: #fff;\\n outline: none;\\n max-height: 96px;\\n}\\n.ai-input__box[data-v-5d775cd8]:focus {\\n border-color: #3067d9;\\n}\\n.ai-input__send[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n border: none;\\n background: #3067d9;\\n color: #fff;\\n font-size: 13px;\\n padding: 7px 16px;\\n border-radius: 6px;\\n cursor: pointer;\\n}\\n.ai-input__send[data-v-5d775cd8]:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n.ai-fade-enter-active[data-v-5d775cd8],\\n.ai-fade-leave-active[data-v-5d775cd8] {\\n transition: opacity 0.2s, transform 0.2s;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n.ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 0;\\n transform: translateY(10px);\\n}\\n\\n/* ============ H5 兼容:点击 AI 后从底部弹出,占 80% 高度、宽度占满 ============ */\\n/* 仅影响窄屏移动端,不影响 PC 端逻辑(PC 宽度 > 480px 仍走上方右侧悬浮面板) */\\n@media (max-width: 480px) {\\n /* 整层提到宿主底部操作栏(通常 z-index 高)之上,保证浮球/面板/遮罩不被遮挡 */\\n.ai-assistant[data-v-5d775cd8] {\\n z-index: 3000;\\n}\\n /* 审批面板打开时的阴影遮罩层 */\\n.ai-mask[data-v-5d775cd8] {\\n position: fixed;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n background: rgba(0, 0, 0, 0.45);\\n pointer-events: auto;\\n z-index: 999;\\n}\\n /* 浮球改为视口固定:在可滚动的 H5 长详情页上始终贴在当前视口,并保持置顶 */\\n.ai-float[data-v-5d775cd8] {\\n position: fixed;\\n z-index: 1000;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n /* 脱离容器,固定到视口底部,宽度占满、高度占视口 80% */\\n position: fixed;\\n top: auto;\\n left: 0;\\n right: 0;\\n bottom: 0 !important; /* 覆盖内联面板 bottom 绑定,始终贴底 */\\n width: 100%;\\n max-width: 100%;\\n height: 85%;\\n max-height: 85%; /* 兼容个别不允许 vh 的旧内核 */\\n border-radius: 16px 16px 0 0;\\n z-index: 1001; /* 位于遮罩层之上 */\\n}\\n /* 底部弹出/收起动画:从视口底部滑入 */\\n.ai-fade-enter-active[data-v-5d775cd8],\\n .ai-fade-leave-active[data-v-5d775cd8] {\\n transition: transform 0.28s ease;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n .ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 1;\\n transform: translateY(100%);\\n}\\n /* H5 放大审批详情/聊天文字,提升可读性 */\\n.msg__bubble[data-v-5d775cd8] {\\n font-size: 15px;\\n line-height: 1.8;\\n padding: 11px 14px;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n .msg__bubble h2[data-v-5d775cd8],\\n .msg__bubble h3[data-v-5d775cd8],\\n .msg__bubble h4[data-v-5d775cd8],\\n .msg__bubble h5[data-v-5d775cd8],\\n .msg__bubble h6[data-v-5d775cd8] {\\n font-size: 17px;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n font-size: 14px;\\n}\\n /* 输入区针对全面屏 Home 指示条预留安全距离 */\\n.ai-input[data-v-5d775cd8] {\\n padding-bottom: calc(10px + env(safe-area-inset-bottom));\\n}\\n}\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
256
256
 
257
257
  /***/ }),
258
258
 
@@ -1225,7 +1225,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nod
1225
1225
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1226
1226
 
1227
1227
  "use strict";
1228
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"09ea9ef8-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=5d775cd8&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"09ea9ef8-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?");
1228
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"02165064-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=5d775cd8&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"02165064-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?");
1229
1229
 
1230
1230
  /***/ }),
1231
1231
 
@@ -238,19 +238,19 @@ eval("__webpack_require__.r(__webpack_exports__);\n// This file is imported into
238
238
  /***/ (function(module, exports, __webpack_require__) {
239
239
 
240
240
  "use strict";
241
- eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireWildcard */ \"./node_modules/@babel/runtime/helpers/interopRequireWildcard.js\").default;\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 _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _createForOfIteratorHelper2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/createForOfIteratorHelper */ \"./node_modules/@babel/runtime/helpers/createForOfIteratorHelper.js\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _aiAssistant = _interopRequireDefault(__webpack_require__(/*! ./ai-assistant.png */ \"./src/lib/components/approvalAi/ai-assistant.png\"));\n\nvar aiApi = _interopRequireWildcard(__webpack_require__(/*! ./apiApprovalAi */ \"./src/lib/components/approvalAi/apiApprovalAi.js\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.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//\nvar md = new _markdownIt.default({\n html: false,\n linkify: true\n}); // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\nvar VALUE_NAME = {\n comFeePlan: \"公司费用计划\",\n comFeeApplication: \"公司费用\",\n comFeeAccept: \"公司费用验收\",\n comFeeCredit: \"公司费用挂账\",\n comFeePay: \"公司费用支付\",\n employeeHonor: \"员工荣誉\",\n employeeRegularization: \"员工转正\",\n employeeTurnover: \"离职\",\n employeeturnoverhandover: \"离职交接\",\n employeeChangestation: \"调岗\",\n employeeChangesalary: \"调薪\",\n overTimeApply: \"加班\",\n carApproval: \"车辆\",\n reimburse: \"出差报销\",\n leave: \"请假\",\n patch: \"补卡\",\n trip: \"出差\",\n out: \"外出\",\n lan: \"借款\",\n officialDocument: \"公文\",\n notice: \"公告\",\n qgbCompanyPlanSummery: \"公司费用预算汇总\",\n sealApproval: \"印章\"\n}; // 审批状态码 -> 中文(内置映射,替代宿主项目全局 $approvalStatus)\n\nvar STATUS_MAP = {\n \"1\": \"审批中\",\n \"-1\": \"审批中\",\n \"0\": \"已拒绝\",\n \"2\": \"已通过\",\n \"-3\": \"已通过\",\n \"4\": \"草稿\",\n \"3\": \"已通过\",\n \"5\": \"已完成\",\n \"6\": \"已撤销\"\n}; // 概览文案启发式字段匹配\n\nvar FIELD_HINTS = [{\n key: \"applicant\",\n labels: [\"申请人\", \"提交人\", \"经办人\"]\n}, {\n key: \"unit\",\n labels: [\"申请单位\", \"公司\", \"单位\"]\n}, {\n key: \"dept\",\n labels: [\"申请部门\", \"部门\"]\n}, {\n key: \"reason\",\n labels: [\"原因\", \"事由\", \"用途\", \"摘要\", \"标题\", \"说明\", \"内容\", \"目的\"]\n}, {\n key: \"amount\",\n labels: [\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]\n}];\n\nfunction normalizeLabel(label) {\n return String(label).trim().replace(/[::]\\s*$/g, \"\");\n}\n\nfunction resolveStatusText(status, override) {\n if (override) return override;\n if (!status && status !== 0) return \"待处理\";\n var mapped = STATUS_MAP[String(status)];\n return mapped || \"未知状态\";\n}\n\nvar msgSeq = 0;\nvar _default = {\n name: \"ApprovalAiAssistant\",\n props: {\n id: {\n type: String,\n default: \"\"\n },\n type: {\n type: String,\n default: \"\"\n },\n status: {\n type: String,\n default: \"0\"\n },\n index: {\n type: Number,\n default: 1\n },\n commentId: {\n type: String,\n default: \"\"\n },\n // ---- 以下为进包后新增的配置项,用于解耦宿主角色的上下文依赖 ----\n // AI 服务基础地址(示例 https://api.xxx.cn),未配置则走本地兜底\n baseUrl: {\n type: String,\n default: \"\"\n },\n // 当前用户信息(替代宿主 Vuex $store.state.user)\n userId: {\n type: String,\n default: \"\"\n },\n userName: {\n type: String,\n default: \"\"\n },\n // 审批状态文案覆盖(默认内置状态码映射;传入则优先使用)\n statusTextOverride: {\n type: String,\n default: \"\"\n },\n // 布局边界选择器(替代硬编码 .approval-content)\n containerSelector: {\n type: String,\n default: \".approval-content\"\n },\n // 详情采集根选择器(替代硬编码 .detail-root)\n detailRootSelector: {\n type: String,\n default: \".detail-root\"\n },\n // 机器人图标,不传时使用包内内置图标\n robotIconUrl: {\n type: String,\n default: \"\"\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n panelVisible: false,\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 24,\n tucked: false,\n panelBottom: 88,\n dragging: false,\n moved: false,\n startX: 0,\n startY: 0,\n startLeft: 0,\n startBottom: 0\n };\n },\n computed: {\n iconUrl: function iconUrl() {\n return this.robotIconUrl || this.defaultIcon;\n },\n typeName: function typeName() {\n if (!this.type) return \"\";\n return VALUE_NAME[this.type] || this.type;\n },\n statusText: function statusText() {\n return resolveStatusText(this.status, this.statusTextOverride);\n },\n statusTone: function statusTone() {\n var t = this.statusText;\n if (t === \"已通过\" || t === \"已完成\") return \"ok\";\n if (t === \"已拒绝\" || t === \"已驳回\" || t === \"已退回\" || t === \"已终止\") return \"no\";\n return \"go\";\n }\n },\n created: function created() {// 注入 AI 服务地址\n },\n mounted: function mounted() {\n var _this = this;\n\n window.addEventListener(\"mousemove\", this.onDrag);\n window.addEventListener(\"mouseup\", this.endDrag);\n this.$nextTick(function () {\n return _this.snapBtn();\n });\n },\n beforeDestroy: function beforeDestroy() {\n window.removeEventListener(\"mousemove\", this.onDrag);\n window.removeEventListener(\"mouseup\", this.endDrag);\n },\n watch: {\n // 切换审批单时重置对话上下文\n id: function id() {\n this.sessionId = \"\";\n this.messages = [];\n this.fields = [];\n this.rawDetail = null;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val\n });\n }\n },\n methods: {\n getContainer: function getContainer() {\n // 普通模式:以配置的审批容器为边界;找不到时回退到父级包裹容器\n var selector = this.containerSelector;\n var el = selector ? this.$el.closest(selector) : null;\n return el || this.$el.parentElement;\n },\n getDetailRoot: function getDetailRoot() {\n var box = this.getContainer();\n if (!box) return null;\n var explicit = this.detailRootSelector ? box.querySelector(this.detailRootSelector) : null;\n if (explicit) return explicit; // 子应用/iframe 模式:容器内可能没有 detail-root 标记,\n // 回退到首个详情子节点(排除 AI 面板自身)\n\n var kids = Array.from(box.children).filter(function (c) {\n return !c.classList.contains(\"ai-assistant\");\n });\n return kids.length ? kids[0] : null;\n },\n togglePanel: function togglePanel() {\n var _this2 = this;\n\n // 拖拽后松手会触发一次 click,这里拦截,避免误展开/收起面板\n if (this.moved) {\n this.moved = false;\n return;\n }\n\n if (this.panelVisible) {\n this.closePanel();\n return;\n }\n\n this.panelVisible = true;\n this.$nextTick(function () {\n _this2.snapPanel();\n\n if (!_this2.messages.length) {\n _this2.greet();\n }\n\n _this2.scrollBottom();\n\n if (_this2.$refs.input) _this2.$refs.input.focus();\n });\n },\n closePanel: function closePanel() {\n this.panelVisible = false;\n },\n snapBtn: function snapBtn() {\n var box = this.getContainer();\n if (!box) return; // 默认贴右下角完整展开:右侧留 20px\n\n if (!this.tucked && !this.btnLeft) {\n this.btnLeft = Math.max(0, box.clientWidth - 20 - 46);\n } // 若按钮在可视范围外则修正\n\n\n this.btnLeft = Math.max(0, Math.min(this.btnLeft, box.clientWidth - 46));\n this.btnBottom = Math.max(8, Math.min(this.btnBottom, box.clientHeight - 44 - 8));\n },\n snapPanel: function snapPanel() {\n var box = this.getContainer();\n var el = this.$el.querySelector(\".ai-panel\");\n if (!box || !el) return;\n var margin = 24; // 上下安全距离,避免遮挡\n\n var ph = el.clientHeight || 560;\n var desired = this.btnBottom + 62;\n this.panelBottom = Math.max(margin, Math.min(desired, box.clientHeight - ph - margin));\n },\n // 打开时 AI 先总结当前审批\n greet: function greet() {\n var _this3 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var summary, amount, res, base;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n _this3.collectDetail();\n\n summary = _this3.buildSummary();\n amount = _this3.findField([\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]); // 已接入后端时,优先用 AI 生成的总结\n\n console.log(_this3.id, _this3.aiEnabled());\n\n if (!(_this3.aiEnabled() && _this3.id)) {\n _context.next = 9;\n break;\n }\n\n _context.next = 7;\n return aiApi.getSummary(_this3.id);\n\n case 7:\n res = _context.sent;\n\n if (res && res.data && res.data.summary) {\n summary = res.data.summary;\n }\n\n case 9:\n base = \"\\u4F60\\u597D\\uFF0C\\u6211\\u662F AI \\u5BA1\\u6279\\u52A9\\u624B\\uD83D\\uDC4B\\n\\n\\u5DF2\\u7ECF\\u4E3A\\u4F60\\u68B3\\u7406\\u4E86\\u5F53\\u524D\\u5BA1\\u6279\\u7684\\u6982\\u89C8\\uFF1A\\n\\n\".concat(summary); // const tail = this.fields.length\n // ? `\\n\\n这份单据的关键字段共 ${this.fields.length} 项${amount ? `,其中金额相关「${amount}」` : \"\"}。你可以继续问我,比如「这笔审批是做什么的」「申请人是谁」。`\n // : \"\\n\\n暂时未采集到该单据的明细字段。\"\n // this.push(\"assistant\", base + tail)\n\n _this3.push(\"assistant\", base);\n\n case 11:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n sendMessage: function sendMessage() {\n var _this4 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return;\n this.push(\"user\", text);\n this.inputText = \"\";\n this.sending = true;\n this.collectDetail(); // 直接走后端流式问答(已接入时);未接入则返回本地兜底\n\n var msg = null;\n\n if (this.aiEnabled() && this.id) {\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: \"\"\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk) {\n if (msg) {\n msg.content += chunk;\n\n _this4.followBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this4.fallbackReply(text);\n } else {\n _this4.push(\"assistant\", _this4.fallbackReply(text));\n }\n\n _this4.streaming = false;\n _this4.sending = false;\n\n _this4.followBottom();\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this5 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n payload = {\n context: {\n type: _this5.type,\n status: _this5.status,\n typeName: _this5.typeName,\n id: _this5.id,\n summary: _this5.buildSummary(),\n fields: _this5.fields,\n detailData: _this5.rawDetail\n },\n question: question\n };\n _context2.next = 3;\n return _this5.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context2.sent;\n return _context2.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this5.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 当前用户信息(来自 props,替代宿主 Vuex)\n userInfo: function userInfo() {\n return {\n no: this.userId || \"\",\n name: this.userName || \"\"\n };\n },\n // 创建/复用 AI 对话会话\n ensureSession: function ensureSession() {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (!_this6.sessionId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\", _this6.sessionId);\n\n case 2:\n u = _this6.userInfo();\n _context3.next = 5;\n return aiApi.createChatSession({\n approvalNo: _this6.id,\n userId: u.no,\n userName: u.name\n });\n\n case 5:\n res = _context3.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context3.next = 9;\n break;\n }\n\n _this6.sessionId = res.data.sessionId;\n return _context3.abrupt(\"return\", _this6.sessionId);\n\n case 9:\n return _context3.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this7.aiEnabled()) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 2:\n _context4.prev = 2;\n _context4.next = 5;\n return _this7.ensureSession();\n\n case 5:\n sessionId = _context4.sent;\n\n if (sessionId) {\n _context4.next = 8;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 8:\n text = \"\";\n failed = false;\n _context4.next = 12;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n userId: _this7.userInfo().no,\n userName: _this7.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\" || event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data);\n } else if (event === \"agent_result\") {\n text = data;\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 12:\n if (!failed) {\n _context4.next = 14;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 14:\n return _context4.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 17:\n _context4.prev = 17;\n _context4.t0 = _context4[\"catch\"](2);\n return _context4.abrupt(\"return\", null);\n\n case 20:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[2, 17]]);\n }))();\n },\n // 无法从数据中检索到时的兜底:诚实告知查询不到,不编造\n fallbackReply: function fallbackReply(question) {\n var q = String(question).trim();\n var list = '';\n return \"\\u6682\\u65F6\\u65E0\\u6CD5\\u67E5\\u8BE2\\u5230\\u4E0E\\u300C\".concat(q, \"\\u300D\\u5BF9\\u5E94\\u7684\\u4FE1\\u606F\\u3002\").concat(list);\n },\n findField: function findField(labels) {\n var _iterator = (0, _createForOfIteratorHelper2.default)(this.rawEntries()),\n _step;\n\n try {\n var _loop = function _loop() {\n var e = _step.value;\n if (labels.some(function (l) {\n return e.label.indexOf(l) > -1;\n })) return {\n v: e.value\n };\n };\n\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var _ret = _loop();\n\n if ((0, _typeof2.default)(_ret) === \"object\") return _ret.v;\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return \"\";\n },\n // 合并 DOM 采集字段 + 详情页完整原始业务对象,得到可检索的条目\n rawEntries: function rawEntries() {\n var _this8 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this8.stringify(value);\n\n if (!v) return;\n var k = \"\".concat(label, \"__\").concat(v);\n if (seen.has(k)) return;\n seen.add(k);\n list.push({\n label: label,\n value: v\n });\n };\n\n var _iterator2 = (0, _createForOfIteratorHelper2.default)(this.fields),\n _step2;\n\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var f = _step2.value;\n pushEntry(f.label, f.value);\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n\n var raw = this.rawDetail && this.rawDetail.data;\n\n if (raw && (0, _typeof2.default)(raw) === \"object\") {\n for (var _i = 0, _Object$keys = Object.keys(raw); _i < _Object$keys.length; _i++) {\n var key = _Object$keys[_i];\n pushEntry(key, raw[key]);\n }\n }\n\n return list;\n },\n stringify: function stringify(v) {\n if (v === null || v === undefined) return \"\";\n\n if ((0, _typeof2.default)(v) === \"object\") {\n try {\n return JSON.stringify(v);\n } catch (e) {\n return \"\";\n }\n }\n\n return String(v);\n },\n // 通用采集详情页数据:兼容 el-form-item / el-descriptions / 自定义 span标签:值 / 表格,\n // 并尽量读取详情页组件的原始业务对象(info/form) 作为完整数据\n collectDetail: function collectDetail() {\n var root = this.getDetailRoot();\n var seen = {};\n\n var push = function push(label, value) {\n label = normalizeLabel(label);\n var v = String(value === null || value === undefined ? \"\" : value).replace(/\\s+/g, \" \").trim();\n if (!label || !v || v === \"暂无\" || label === \"图片\" || label === \"操作\") return;\n if (seen[label] !== undefined) return;\n seen[label] = v;\n };\n\n if (root) {\n // 1) el-form-item\n root.querySelectorAll(\".el-form-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-form-item__label\");\n var contentEl = item.querySelector(\".el-form-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 2) el-descriptions 描述项\n\n root.querySelectorAll(\".el-descriptions-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-descriptions-item__label\");\n var contentEl = item.querySelector(\".el-descriptions-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 3) 自定义 label:value(span 以「:」/「:」结尾,值在大括号内剩余文本)\n\n root.querySelectorAll(\"span\").forEach(function (span) {\n var txt = (span.textContent || \"\").trim();\n var m = txt.match(/^(.+?)([::])$/);\n if (!m) return;\n var parent = span.parentElement;\n if (!parent) return;\n var value = (parent.textContent || \"\").replace(txt, \"\").replace(/\\s+/g, \" \").trim();\n push(m[1], value);\n }); // 4) 表格:表头 -> 逐行数据\n\n root.querySelectorAll(\"table\").forEach(function (table) {\n var rows = table.querySelectorAll(\"tr\");\n var headers = [];\n\n if (rows.length) {\n headers = Array.from(rows[0].querySelectorAll(\"th,td\")).map(function (c) {\n return normalizeLabel(c.textContent);\n });\n }\n\n rows.forEach(function (tr, idx) {\n if (idx === 0) return;\n var cells = Array.from(tr.querySelectorAll(\"th,td\")).map(function (c) {\n return (c.textContent || \"\").replace(/\\s+/g, \" \").trim();\n });\n if (!cells.length) return;\n\n if (headers.length) {\n headers.forEach(function (h, j) {\n return h && cells[j] !== undefined && push(h, cells[j]);\n });\n } else {\n cells.forEach(function (c, j) {\n return c && push(\"\\u7B2C\".concat(idx, \"\\u884C\\u5B57\\u6BB5\").concat(j + 1), c);\n });\n }\n });\n });\n } // 5) 读取详情组件的原始业务对象,作为完整结构化数据补全\n\n\n var raw = this.readRawDetail();\n this.rawDetail = raw;\n if (raw) this.flattenTo(seen, raw);\n this.fields = Object.keys(seen).map(function (label) {\n return {\n label: label,\n value: seen[label]\n };\n }).slice(0, 40);\n },\n // 寻找详情页组件的业务数据对象(info / form 等)\n readRawDetail: function readRawDetail() {\n var _this9 = this;\n\n var root = this.getDetailRoot();\n if (!root) return null;\n\n var _iterator3 = (0, _createForOfIteratorHelper2.default)(root.querySelectorAll(\"*\")),\n _step3;\n\n try {\n for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {\n var el = _step3.value;\n var vm = el.__vue__;\n if (!vm || !vm.$data || (0, _typeof2.default)(vm.$data) !== \"object\") continue;\n var d = vm.$data;\n\n var _loop2 = function _loop2() {\n var key = _arr[_i2];\n var obj = d[key];\n\n if (obj && (0, _typeof2.default)(obj) === \"object\" && !Array.isArray(obj)) {\n var keys = Object.keys(obj).filter(function (k) {\n return obj[k] !== \"\" && obj[k] !== null && obj[k] !== undefined;\n });\n\n if (keys.length) {\n return {\n v: {\n key: key,\n data: _this9.jsonSafe(obj)\n }\n };\n }\n }\n };\n\n for (var _i2 = 0, _arr = [\"info\", \"form\"]; _i2 < _arr.length; _i2++) {\n var _ret2 = _loop2();\n\n if ((0, _typeof2.default)(_ret2) === \"object\") return _ret2.v;\n }\n }\n } catch (err) {\n _iterator3.e(err);\n } finally {\n _iterator3.f();\n }\n\n return null;\n },\n jsonSafe: function jsonSafe(obj) {\n try {\n return JSON.parse(JSON.stringify(obj, function (k, v) {\n return typeof v === \"function\" ? undefined : v;\n }));\n } catch (e) {\n return obj;\n }\n },\n // 把原始业务对象摊平成 label:value,补进已采集集合\n flattenTo: function flattenTo(seen, raw) {\n var walk = function walk(obj, path) {\n if (obj === null || obj === undefined) return;\n\n if (Array.isArray(obj)) {\n obj.forEach(function (item, i) {\n return walk(item, path ? \"\".concat(path, \"[\").concat(i, \"]\") : \"[\".concat(i, \"]\"));\n });\n return;\n }\n\n if ((0, _typeof2.default)(obj) === \"object\") {\n Object.keys(obj).forEach(function (k) {\n return walk(obj[k], path ? \"\".concat(path, \".\").concat(k) : k);\n });\n return;\n }\n\n var label = path || \"\";\n var v = String(obj).replace(/\\s+/g, \" \").trim();\n if (label && v && seen[label] === undefined) seen[label] = v;\n };\n\n walk(raw.data, raw.key);\n },\n // 依据采集字段生成“当前审批是做什么的”概览(启发式)\n buildSummary: function buildSummary() {\n var hits = {};\n FIELD_HINTS.forEach(function (hint) {\n hits[hint.key] = \"\";\n });\n this.fields.forEach(function (f) {\n var _iterator4 = (0, _createForOfIteratorHelper2.default)(FIELD_HINTS),\n _step4;\n\n try {\n for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {\n var hint = _step4.value;\n if (hits[hint.key]) continue;\n\n if (hint.labels.some(function (l) {\n return f.label.indexOf(l) > -1;\n })) {\n hits[hint.key] = f.value;\n break;\n }\n }\n } catch (err) {\n _iterator4.e(err);\n } finally {\n _iterator4.f();\n }\n });\n var name = this.typeName || \"该单据\";\n var parts = [];\n if (hits.applicant) parts.push(\"\\u7533\\u8BF7\\u4EBA\\u300C\".concat(hits.applicant, \"\\u300D\"));\n if (hits.unit) parts.push(\"\\u6240\\u5C5E\\u5355\\u4F4D\\u300C\".concat(hits.unit, \"\\u300D\"));\n var verb = \"\".concat(name === \"该单据\" ? \"该审批\" : \"\\u8BE5\\u300C\".concat(name, \"\\u300D\\u5BA1\\u6279\"));\n var summary = \"\".concat(verb, \"\\u662F\\u4E00\\u7B14\\u4E1A\\u52A1\\u5BA1\\u6279\");\n if (parts.length) summary += \"\\uFF0C\\u7531\".concat(parts.join(\",\"), \"\\u53D1\\u8D77\");\n if (hits.reason) summary += \"\\uFF0C\\u4E3B\\u8981\\u4E8B\\u7531\\uFF1A\".concat(hits.reason);\n if (hits.amount) summary += \"\\uFF0C\\u6D89\\u53CA\\u91D1\\u989D\\u300C\".concat(hits.amount, \"\\u300D\");\n summary += \"\\u3002\\u5F53\\u524D\\u5BA1\\u6279\\u72B6\\u6001\\u4E3A\\u300C\".concat(this.statusText, \"\\u300D\\u3002\");\n return summary;\n },\n push: function push(role, content) {\n this.messages.push({\n id: ++msgSeq,\n role: role,\n content: content\n });\n },\n // 气泡渲染:助手消息转 Markdown,用户消息按纯文本处理(转义防 XSS)\n renderBubble: function renderBubble(m) {\n var text = m && m.content != null ? String(m.content) : \"\";\n if (m && m.role === \"user\") return this.escapeHtml(text);\n return md.render(text || \"\");\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this10 = this;\n\n this.$nextTick(function () {\n var el = _this10.$refs.chatBody;\n if (el) el.scrollTop = el.scrollHeight;\n });\n },\n // 用户手动滚动时记录是否仍停留底部;上滚离开底部即暂停自动跟随\n onChatScroll: function onChatScroll() {\n var el = this.$refs.chatBody;\n if (!el) return;\n var nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;\n this.autoFollow = nearBottom;\n },\n // 仅当用户停留在底部时才自动滚到底,避免回答过程中打断用户向上查看\n followBottom: function followBottom() {\n if (!this.autoFollow) return;\n this.scrollBottom();\n },\n startDrag: function startDrag(e) {\n if (e.button !== 0) return;\n this.dragging = true;\n this.moved = false;\n this.tucked = false;\n this.startX = e.clientX;\n this.startY = e.clientY;\n this.startLeft = this.btnLeft;\n this.startBottom = this.btnBottom; // 拖动时防止文本/图片被选中导致跟手中断\n\n e.preventDefault();\n },\n onDrag: function onDrag(e) {\n if (!this.dragging) return;\n var dx = e.clientX - this.startX;\n var dy = e.clientY - this.startY;\n\n if (!this.moved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this.moved = true;\n }\n\n var box = this.getContainer();\n var maxW = box ? box.clientWidth - 46 : 9999;\n var maxH = box ? box.clientHeight - 44 : 9999;\n var left = Math.max(0, Math.min(this.startLeft + dx, Math.max(0, maxW)));\n var bottom = Math.max(8, Math.min(this.startBottom - dy, Math.max(8, maxH - 8))); // 直接写 DOM,图标即时跟手,不经过 Vue 重渲染\n\n var el = this.$refs.floatEl;\n\n if (el) {\n el.style.left = left + \"px\";\n el.style.bottom = bottom + \"px\";\n }\n\n if (this.panelVisible) {\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n if (!this.dragging) return;\n this.dragging = false;\n if (!this.moved) return; // 读取拖拽后的实际位置\n\n var el = this.$refs.floatEl;\n var left = el ? parseInt(el.style.left || \"0\", 10) : this.btnLeft;\n var bottom = el ? parseInt(el.style.bottom || \"0\", 10) : this.btnBottom;\n this.btnLeft = left;\n this.btnBottom = bottom; // 拖动过才吸附贴边;靠近右边时收成半角标\n\n var box = this.getContainer();\n if (!box) return;\n\n if (box.clientWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = box.clientWidth - 46;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.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");
241
+ eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireWildcard */ \"./node_modules/@babel/runtime/helpers/interopRequireWildcard.js\").default;\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 _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _createForOfIteratorHelper2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/createForOfIteratorHelper */ \"./node_modules/@babel/runtime/helpers/createForOfIteratorHelper.js\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _aiAssistant = _interopRequireDefault(__webpack_require__(/*! ./ai-assistant.png */ \"./src/lib/components/approvalAi/ai-assistant.png\"));\n\nvar aiApi = _interopRequireWildcard(__webpack_require__(/*! ./apiApprovalAi */ \"./src/lib/components/approvalAi/apiApprovalAi.js\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.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//\nvar md = new _markdownIt.default({\n html: false,\n linkify: true\n}); // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\nvar VALUE_NAME = {\n comFeePlan: \"公司费用计划\",\n comFeeApplication: \"公司费用\",\n comFeeAccept: \"公司费用验收\",\n comFeeCredit: \"公司费用挂账\",\n comFeePay: \"公司费用支付\",\n employeeHonor: \"员工荣誉\",\n employeeRegularization: \"员工转正\",\n employeeTurnover: \"离职\",\n employeeturnoverhandover: \"离职交接\",\n employeeChangestation: \"调岗\",\n employeeChangesalary: \"调薪\",\n overTimeApply: \"加班\",\n carApproval: \"车辆\",\n reimburse: \"出差报销\",\n leave: \"请假\",\n patch: \"补卡\",\n trip: \"出差\",\n out: \"外出\",\n lan: \"借款\",\n officialDocument: \"公文\",\n notice: \"公告\",\n qgbCompanyPlanSummery: \"公司费用预算汇总\",\n sealApproval: \"印章\"\n}; // 审批状态码 -> 中文(内置映射,替代宿主项目全局 $approvalStatus)\n\nvar STATUS_MAP = {\n \"1\": \"审批中\",\n \"-1\": \"审批中\",\n \"0\": \"已拒绝\",\n \"2\": \"已通过\",\n \"-3\": \"已通过\",\n \"4\": \"草稿\",\n \"3\": \"已通过\",\n \"5\": \"已完成\",\n \"6\": \"已撤销\"\n}; // 概览文案启发式字段匹配\n\nvar FIELD_HINTS = [{\n key: \"applicant\",\n labels: [\"申请人\", \"提交人\", \"经办人\"]\n}, {\n key: \"unit\",\n labels: [\"申请单位\", \"公司\", \"单位\"]\n}, {\n key: \"dept\",\n labels: [\"申请部门\", \"部门\"]\n}, {\n key: \"reason\",\n labels: [\"原因\", \"事由\", \"用途\", \"摘要\", \"标题\", \"说明\", \"内容\", \"目的\"]\n}, {\n key: \"amount\",\n labels: [\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]\n}];\n\nfunction normalizeLabel(label) {\n return String(label).trim().replace(/[::]\\s*$/g, \"\");\n}\n\nfunction resolveStatusText(status, override) {\n if (override) return override;\n if (!status && status !== 0) return \"待处理\";\n var mapped = STATUS_MAP[String(status)];\n return mapped || \"未知状态\";\n}\n\nvar msgSeq = 0;\nvar _default = {\n name: \"ApprovalAiAssistant\",\n props: {\n id: {\n type: String,\n default: \"\"\n },\n type: {\n type: String,\n default: \"\"\n },\n status: {\n type: String,\n default: \"0\"\n },\n index: {\n type: Number,\n default: 1\n },\n commentId: {\n type: String,\n default: \"\"\n },\n // ---- 以下为进包后新增的配置项,用于解耦宿主角色的上下文依赖 ----\n // AI 服务基础地址(示例 https://api.xxx.cn),未配置则走本地兜底\n baseUrl: {\n type: String,\n default: \"\"\n },\n // 当前用户信息(替代宿主 Vuex $store.state.user)\n userId: {\n type: String,\n default: \"\"\n },\n userName: {\n type: String,\n default: \"\"\n },\n // 审批状态文案覆盖(默认内置状态码映射;传入则优先使用)\n statusTextOverride: {\n type: String,\n default: \"\"\n },\n // 布局边界选择器(替代硬编码 .approval-content)\n containerSelector: {\n type: String,\n default: \".approval-content\"\n },\n // 详情采集根选择器(替代硬编码 .detail-root)\n detailRootSelector: {\n type: String,\n default: \".detail-root\"\n },\n // 机器人图标,不传时使用包内内置图标\n robotIconUrl: {\n type: String,\n default: \"\"\n },\n // H5 底部操作栏高度(px):作为兜底基准,用于把浮动按钮放在操作栏上方;PC 传 0 不受影响\n bottomBarHeight: {\n type: Number,\n default: 0\n },\n // H5 底部操作栏元素选择器:运行时测量该元素,把悬浮窗放在其正上方,避免遮挡(默认 .btn-wrapper)\n bottomBarSelector: {\n type: String,\n default: \".btn-wrapper\"\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n panelVisible: false,\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 24,\n tucked: false,\n panelBottom: 88,\n dragging: false,\n moved: false,\n isMobile: false,\n startX: 0,\n startY: 0,\n startLeft: 0,\n startBottom: 0\n };\n },\n computed: {\n iconUrl: function iconUrl() {\n return this.robotIconUrl || this.defaultIcon;\n },\n typeName: function typeName() {\n if (!this.type) return \"\";\n return VALUE_NAME[this.type] || this.type;\n },\n statusText: function statusText() {\n return resolveStatusText(this.status, this.statusTextOverride);\n },\n statusTone: function statusTone() {\n var t = this.statusText;\n if (t === \"已通过\" || t === \"已完成\") return \"ok\";\n if (t === \"已拒绝\" || t === \"已驳回\" || t === \"已退回\" || t === \"已终止\") return \"no\";\n return \"go\";\n }\n },\n created: function created() {// 注入 AI 服务地址\n },\n mounted: function mounted() {\n var _this = this;\n\n window.addEventListener(\"mousemove\", this.onDrag);\n window.addEventListener(\"mouseup\", this.endDrag);\n this.initMobile();\n this.$nextTick(function () {\n return _this.snapBtn();\n });\n },\n beforeDestroy: function beforeDestroy() {\n window.removeEventListener(\"mousemove\", this.onDrag);\n window.removeEventListener(\"mouseup\", this.endDrag);\n if (this.__mqUnlisten) this.__mqUnlisten();\n },\n watch: {\n // 切换审批单时重置对话上下文\n id: function id() {\n this.sessionId = \"\";\n this.messages = [];\n this.fields = [];\n this.rawDetail = null;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val\n });\n }\n },\n methods: {\n // 统一取鼠标/触摸的坐标点\n point: function point(e) {\n var t = e && e.touches && e.touches[0] || e && e.changedTouches && e.changedTouches[0];\n return t ? {\n clientX: t.clientX,\n clientY: t.clientY\n } : {\n clientX: e.clientX,\n clientY: e.clientY\n };\n },\n // 按钮距容器底部的最小基准:8px + H5 底部操作栏高度,保证按钮在操作栏上方\n baseBottom: function baseBottom() {\n return 8 + (Number(this.bottomBarHeight) || 0);\n },\n // H5 下测量底部操作栏元素,返回“视口底部到该元素顶部”的距离 + 安全边距,\n // 使悬浮窗固定在该元素正上方、不遮挡其展示;PC/找不到时回退到 baseBottom()\n barBottomOffset: function barBottomOffset() {\n if (!this.isMobile) return this.baseBottom();\n var sel = this.bottomBarSelector;\n\n if (sel) {\n var el = null;\n\n try {\n el = document.querySelector(sel);\n } catch (e) {\n el = null;\n }\n\n var rect = el && el.getBoundingClientRect();\n\n if (rect && rect.top >= 0 && rect.top <= window.innerHeight) {\n return window.innerHeight - rect.top + 12;\n }\n }\n\n return this.baseBottom();\n },\n // 是否 H5 窄屏(与 CSS media query 保持一致的 480px 边界)\n initMobile: function initMobile() {\n var _this2 = this;\n\n var mq = window.matchMedia && window.matchMedia(\"(max-width: 480px)\");\n\n var apply = function apply() {\n _this2.isMobile = !mq || mq.matches;\n };\n\n apply();\n if (mq && mq.addEventListener) mq.addEventListener(\"change\", apply);else if (mq && mq.addListener) mq.addListener(apply);\n\n this.__mqUnlisten = function () {\n if (mq && mq.removeEventListener) mq.removeEventListener(\"change\", apply);else if (mq && mq.removeListener) mq.removeListener(apply);\n };\n },\n getContainer: function getContainer() {\n // 普通模式:以配置的审批容器为边界;找不到时回退到父级包裹容器\n var selector = this.containerSelector;\n var el = selector ? this.$el.closest(selector) : null;\n return el || this.$el.parentElement;\n },\n getDetailRoot: function getDetailRoot() {\n var box = this.getContainer();\n if (!box) return null;\n var explicit = this.detailRootSelector ? box.querySelector(this.detailRootSelector) : null;\n if (explicit) return explicit; // 子应用/iframe 模式:容器内可能没有 detail-root 标记,\n // 回退到首个详情子节点(排除 AI 面板自身)\n\n var kids = Array.from(box.children).filter(function (c) {\n return !c.classList.contains(\"ai-assistant\");\n });\n return kids.length ? kids[0] : null;\n },\n togglePanel: function togglePanel() {\n var _this3 = this;\n\n // 拖拽后松手会触发一次 click,这里拦截,避免误展开/收起面板\n if (this.moved) {\n this.moved = false;\n return;\n }\n\n if (this.panelVisible) {\n this.closePanel();\n return;\n }\n\n this.panelVisible = true;\n this.$nextTick(function () {\n _this3.snapPanel();\n\n if (!_this3.messages.length) {\n _this3.greet();\n }\n\n _this3.scrollBottom();\n\n if (_this3.$refs.input) _this3.$refs.input.focus();\n });\n },\n closePanel: function closePanel() {\n this.panelVisible = false;\n },\n snapBtn: function snapBtn() {\n var box = this.getContainer();\n if (!box) return; // 默认贴右下角完整展开:右侧留 20px\n\n if (!this.tucked && !this.btnLeft) {\n this.btnLeft = Math.max(0, box.clientWidth - 20 - 46);\n } // 若按钮在可视范围外则修正\n\n\n this.btnLeft = Math.max(0, Math.min(this.btnLeft, box.clientWidth - 46));\n this.btnBottom = Math.max(this.barBottomOffset(), Math.min(this.btnBottom, box.clientHeight - 44 - 8));\n },\n snapPanel: function snapPanel() {\n var box = this.getContainer();\n var el = this.$el.querySelector(\".ai-panel\");\n if (!box || !el) return;\n var margin = 24; // 上下安全距离,避免遮挡\n\n var ph = el.clientHeight || 560;\n var desired = this.btnBottom + 62;\n this.panelBottom = Math.max(margin, Math.min(desired, box.clientHeight - ph - margin));\n },\n // 打开时 AI 先总结当前审批\n greet: function greet() {\n var _this4 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var summary, amount, res, base;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n _this4.collectDetail();\n\n summary = _this4.buildSummary();\n amount = _this4.findField([\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]); // 已接入后端时,优先用 AI 生成的总结\n\n console.log(_this4.id, _this4.aiEnabled());\n\n if (!(_this4.aiEnabled() && _this4.id)) {\n _context.next = 9;\n break;\n }\n\n _context.next = 7;\n return aiApi.getSummary(_this4.id);\n\n case 7:\n res = _context.sent;\n\n if (res && res.data && res.data.summary) {\n summary = res.data.summary;\n }\n\n case 9:\n base = \"\\u4F60\\u597D\\uFF0C\\u6211\\u662F AI \\u5BA1\\u6279\\u52A9\\u624B\\uD83D\\uDC4B\\n\\n\\u5DF2\\u7ECF\\u4E3A\\u4F60\\u68B3\\u7406\\u4E86\\u5F53\\u524D\\u5BA1\\u6279\\u7684\\u6982\\u89C8\\uFF1A\\n\\n\".concat(summary); // const tail = this.fields.length\n // ? `\\n\\n这份单据的关键字段共 ${this.fields.length} 项${amount ? `,其中金额相关「${amount}」` : \"\"}。你可以继续问我,比如「这笔审批是做什么的」「申请人是谁」。`\n // : \"\\n\\n暂时未采集到该单据的明细字段。\"\n // this.push(\"assistant\", base + tail)\n\n _this4.push(\"assistant\", base);\n\n case 11:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n sendMessage: function sendMessage() {\n var _this5 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return;\n this.push(\"user\", text);\n this.inputText = \"\";\n this.sending = true;\n this.collectDetail(); // 直接走后端流式问答(已接入时);未接入则返回本地兜底\n\n var msg = null;\n\n if (this.aiEnabled() && this.id) {\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: \"\"\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk) {\n if (msg) {\n msg.content += chunk;\n\n _this5.followBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this5.fallbackReply(text);\n } else {\n _this5.push(\"assistant\", _this5.fallbackReply(text));\n }\n\n _this5.streaming = false;\n _this5.sending = false;\n\n _this5.followBottom();\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n payload = {\n context: {\n type: _this6.type,\n status: _this6.status,\n typeName: _this6.typeName,\n id: _this6.id,\n summary: _this6.buildSummary(),\n fields: _this6.fields,\n detailData: _this6.rawDetail\n },\n question: question\n };\n _context2.next = 3;\n return _this6.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context2.sent;\n return _context2.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this6.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 当前用户信息(来自 props,替代宿主 Vuex)\n userInfo: function userInfo() {\n return {\n no: this.userId || \"\",\n name: this.userName || \"\"\n };\n },\n // 创建/复用 AI 对话会话\n ensureSession: function ensureSession() {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (!_this7.sessionId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\", _this7.sessionId);\n\n case 2:\n u = _this7.userInfo();\n _context3.next = 5;\n return aiApi.createChatSession({\n approvalNo: _this7.id,\n userId: u.no,\n userName: u.name\n });\n\n case 5:\n res = _context3.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context3.next = 9;\n break;\n }\n\n _this7.sessionId = res.data.sessionId;\n return _context3.abrupt(\"return\", _this7.sessionId);\n\n case 9:\n return _context3.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this8 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this8.aiEnabled()) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 2:\n _context4.prev = 2;\n _context4.next = 5;\n return _this8.ensureSession();\n\n case 5:\n sessionId = _context4.sent;\n\n if (sessionId) {\n _context4.next = 8;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 8:\n text = \"\";\n failed = false;\n _context4.next = 12;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n userId: _this8.userInfo().no,\n userName: _this8.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\" || event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data);\n } else if (event === \"agent_result\") {\n text = data;\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 12:\n if (!failed) {\n _context4.next = 14;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 14:\n return _context4.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 17:\n _context4.prev = 17;\n _context4.t0 = _context4[\"catch\"](2);\n return _context4.abrupt(\"return\", null);\n\n case 20:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[2, 17]]);\n }))();\n },\n // 无法从数据中检索到时的兜底:诚实告知查询不到,不编造\n fallbackReply: function fallbackReply(question) {\n var q = String(question).trim();\n var list = '';\n return \"\\u6682\\u65F6\\u65E0\\u6CD5\\u67E5\\u8BE2\\u5230\\u4E0E\\u300C\".concat(q, \"\\u300D\\u5BF9\\u5E94\\u7684\\u4FE1\\u606F\\u3002\").concat(list);\n },\n findField: function findField(labels) {\n var _iterator = (0, _createForOfIteratorHelper2.default)(this.rawEntries()),\n _step;\n\n try {\n var _loop = function _loop() {\n var e = _step.value;\n if (labels.some(function (l) {\n return e.label.indexOf(l) > -1;\n })) return {\n v: e.value\n };\n };\n\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var _ret = _loop();\n\n if ((0, _typeof2.default)(_ret) === \"object\") return _ret.v;\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return \"\";\n },\n // 合并 DOM 采集字段 + 详情页完整原始业务对象,得到可检索的条目\n rawEntries: function rawEntries() {\n var _this9 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this9.stringify(value);\n\n if (!v) return;\n var k = \"\".concat(label, \"__\").concat(v);\n if (seen.has(k)) return;\n seen.add(k);\n list.push({\n label: label,\n value: v\n });\n };\n\n var _iterator2 = (0, _createForOfIteratorHelper2.default)(this.fields),\n _step2;\n\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var f = _step2.value;\n pushEntry(f.label, f.value);\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n\n var raw = this.rawDetail && this.rawDetail.data;\n\n if (raw && (0, _typeof2.default)(raw) === \"object\") {\n for (var _i = 0, _Object$keys = Object.keys(raw); _i < _Object$keys.length; _i++) {\n var key = _Object$keys[_i];\n pushEntry(key, raw[key]);\n }\n }\n\n return list;\n },\n stringify: function stringify(v) {\n if (v === null || v === undefined) return \"\";\n\n if ((0, _typeof2.default)(v) === \"object\") {\n try {\n return JSON.stringify(v);\n } catch (e) {\n return \"\";\n }\n }\n\n return String(v);\n },\n // 通用采集详情页数据:兼容 el-form-item / el-descriptions / 自定义 span标签:值 / 表格,\n // 并尽量读取详情页组件的原始业务对象(info/form) 作为完整数据\n collectDetail: function collectDetail() {\n var root = this.getDetailRoot();\n var seen = {};\n\n var push = function push(label, value) {\n label = normalizeLabel(label);\n var v = String(value === null || value === undefined ? \"\" : value).replace(/\\s+/g, \" \").trim();\n if (!label || !v || v === \"暂无\" || label === \"图片\" || label === \"操作\") return;\n if (seen[label] !== undefined) return;\n seen[label] = v;\n };\n\n if (root) {\n // 1) el-form-item\n root.querySelectorAll(\".el-form-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-form-item__label\");\n var contentEl = item.querySelector(\".el-form-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 2) el-descriptions 描述项\n\n root.querySelectorAll(\".el-descriptions-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-descriptions-item__label\");\n var contentEl = item.querySelector(\".el-descriptions-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 3) 自定义 label:value(span 以「:」/「:」结尾,值在大括号内剩余文本)\n\n root.querySelectorAll(\"span\").forEach(function (span) {\n var txt = (span.textContent || \"\").trim();\n var m = txt.match(/^(.+?)([::])$/);\n if (!m) return;\n var parent = span.parentElement;\n if (!parent) return;\n var value = (parent.textContent || \"\").replace(txt, \"\").replace(/\\s+/g, \" \").trim();\n push(m[1], value);\n }); // 4) 表格:表头 -> 逐行数据\n\n root.querySelectorAll(\"table\").forEach(function (table) {\n var rows = table.querySelectorAll(\"tr\");\n var headers = [];\n\n if (rows.length) {\n headers = Array.from(rows[0].querySelectorAll(\"th,td\")).map(function (c) {\n return normalizeLabel(c.textContent);\n });\n }\n\n rows.forEach(function (tr, idx) {\n if (idx === 0) return;\n var cells = Array.from(tr.querySelectorAll(\"th,td\")).map(function (c) {\n return (c.textContent || \"\").replace(/\\s+/g, \" \").trim();\n });\n if (!cells.length) return;\n\n if (headers.length) {\n headers.forEach(function (h, j) {\n return h && cells[j] !== undefined && push(h, cells[j]);\n });\n } else {\n cells.forEach(function (c, j) {\n return c && push(\"\\u7B2C\".concat(idx, \"\\u884C\\u5B57\\u6BB5\").concat(j + 1), c);\n });\n }\n });\n });\n } // 5) 读取详情组件的原始业务对象,作为完整结构化数据补全\n\n\n var raw = this.readRawDetail();\n this.rawDetail = raw;\n if (raw) this.flattenTo(seen, raw);\n this.fields = Object.keys(seen).map(function (label) {\n return {\n label: label,\n value: seen[label]\n };\n }).slice(0, 40);\n },\n // 寻找详情页组件的业务数据对象(info / form 等)\n readRawDetail: function readRawDetail() {\n var _this10 = this;\n\n var root = this.getDetailRoot();\n if (!root) return null;\n\n var _iterator3 = (0, _createForOfIteratorHelper2.default)(root.querySelectorAll(\"*\")),\n _step3;\n\n try {\n for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {\n var el = _step3.value;\n var vm = el.__vue__;\n if (!vm || !vm.$data || (0, _typeof2.default)(vm.$data) !== \"object\") continue;\n var d = vm.$data;\n\n var _loop2 = function _loop2() {\n var key = _arr[_i2];\n var obj = d[key];\n\n if (obj && (0, _typeof2.default)(obj) === \"object\" && !Array.isArray(obj)) {\n var keys = Object.keys(obj).filter(function (k) {\n return obj[k] !== \"\" && obj[k] !== null && obj[k] !== undefined;\n });\n\n if (keys.length) {\n return {\n v: {\n key: key,\n data: _this10.jsonSafe(obj)\n }\n };\n }\n }\n };\n\n for (var _i2 = 0, _arr = [\"info\", \"form\"]; _i2 < _arr.length; _i2++) {\n var _ret2 = _loop2();\n\n if ((0, _typeof2.default)(_ret2) === \"object\") return _ret2.v;\n }\n }\n } catch (err) {\n _iterator3.e(err);\n } finally {\n _iterator3.f();\n }\n\n return null;\n },\n jsonSafe: function jsonSafe(obj) {\n try {\n return JSON.parse(JSON.stringify(obj, function (k, v) {\n return typeof v === \"function\" ? undefined : v;\n }));\n } catch (e) {\n return obj;\n }\n },\n // 把原始业务对象摊平成 label:value,补进已采集集合\n flattenTo: function flattenTo(seen, raw) {\n var walk = function walk(obj, path) {\n if (obj === null || obj === undefined) return;\n\n if (Array.isArray(obj)) {\n obj.forEach(function (item, i) {\n return walk(item, path ? \"\".concat(path, \"[\").concat(i, \"]\") : \"[\".concat(i, \"]\"));\n });\n return;\n }\n\n if ((0, _typeof2.default)(obj) === \"object\") {\n Object.keys(obj).forEach(function (k) {\n return walk(obj[k], path ? \"\".concat(path, \".\").concat(k) : k);\n });\n return;\n }\n\n var label = path || \"\";\n var v = String(obj).replace(/\\s+/g, \" \").trim();\n if (label && v && seen[label] === undefined) seen[label] = v;\n };\n\n walk(raw.data, raw.key);\n },\n // 依据采集字段生成“当前审批是做什么的”概览(启发式)\n buildSummary: function buildSummary() {\n var hits = {};\n FIELD_HINTS.forEach(function (hint) {\n hits[hint.key] = \"\";\n });\n this.fields.forEach(function (f) {\n var _iterator4 = (0, _createForOfIteratorHelper2.default)(FIELD_HINTS),\n _step4;\n\n try {\n for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {\n var hint = _step4.value;\n if (hits[hint.key]) continue;\n\n if (hint.labels.some(function (l) {\n return f.label.indexOf(l) > -1;\n })) {\n hits[hint.key] = f.value;\n break;\n }\n }\n } catch (err) {\n _iterator4.e(err);\n } finally {\n _iterator4.f();\n }\n });\n var name = this.typeName || \"该单据\";\n var parts = [];\n if (hits.applicant) parts.push(\"\\u7533\\u8BF7\\u4EBA\\u300C\".concat(hits.applicant, \"\\u300D\"));\n if (hits.unit) parts.push(\"\\u6240\\u5C5E\\u5355\\u4F4D\\u300C\".concat(hits.unit, \"\\u300D\"));\n var verb = \"\".concat(name === \"该单据\" ? \"该审批\" : \"\\u8BE5\\u300C\".concat(name, \"\\u300D\\u5BA1\\u6279\"));\n var summary = \"\".concat(verb, \"\\u662F\\u4E00\\u7B14\\u4E1A\\u52A1\\u5BA1\\u6279\");\n if (parts.length) summary += \"\\uFF0C\\u7531\".concat(parts.join(\",\"), \"\\u53D1\\u8D77\");\n if (hits.reason) summary += \"\\uFF0C\\u4E3B\\u8981\\u4E8B\\u7531\\uFF1A\".concat(hits.reason);\n if (hits.amount) summary += \"\\uFF0C\\u6D89\\u53CA\\u91D1\\u989D\\u300C\".concat(hits.amount, \"\\u300D\");\n summary += \"\\u3002\\u5F53\\u524D\\u5BA1\\u6279\\u72B6\\u6001\\u4E3A\\u300C\".concat(this.statusText, \"\\u300D\\u3002\");\n return summary;\n },\n push: function push(role, content) {\n this.messages.push({\n id: ++msgSeq,\n role: role,\n content: content\n });\n },\n // 气泡渲染:助手消息转 Markdown,用户消息按纯文本处理(转义防 XSS)\n renderBubble: function renderBubble(m) {\n var text = m && m.content != null ? String(m.content) : \"\";\n if (m && m.role === \"user\") return this.escapeHtml(text);\n return md.render(text || \"\");\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this11 = this;\n\n this.$nextTick(function () {\n var el = _this11.$refs.chatBody;\n if (el) el.scrollTop = el.scrollHeight;\n });\n },\n // 用户手动滚动时记录是否仍停留底部;上滚离开底部即暂停自动跟随\n onChatScroll: function onChatScroll() {\n var el = this.$refs.chatBody;\n if (!el) return;\n var nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;\n this.autoFollow = nearBottom;\n },\n // 仅当用户停留在底部时才自动滚到底,避免回答过程中打断用户向上查看\n followBottom: function followBottom() {\n if (!this.autoFollow) return;\n this.scrollBottom();\n },\n startDrag: function startDrag(e) {\n if (e.button !== undefined && e.button !== 0) return;\n var point = this.point(e);\n if (!point) return;\n this.dragging = true;\n this.moved = false;\n this.tucked = false;\n this.startX = point.clientX;\n this.startY = point.clientY;\n this.startLeft = this.btnLeft;\n this.startBottom = this.btnBottom; // 拖动时防止文本/图片被选中导致跟手中断;\n // 仅鼠标 preventDefault,触摸靠 touch-action:none,避免吞掉点击(打开/收起面板)\n\n if (e.type === \"mousedown\") e.preventDefault();\n },\n onDrag: function onDrag(e) {\n if (!this.dragging) return;\n var point = this.point(e);\n if (!point) return;\n var dx = point.clientX - this.startX;\n var dy = point.clientY - this.startY;\n\n if (!this.moved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this.moved = true;\n }\n\n var box = this.getContainer();\n var maxW = box ? box.clientWidth - 46 : 9999;\n var maxH = box ? box.clientHeight - 44 : 9999;\n var minBottom = this.barBottomOffset();\n var left = Math.max(0, Math.min(this.startLeft + dx, Math.max(0, maxW)));\n var bottom = Math.max(minBottom, Math.min(this.startBottom - dy, Math.max(minBottom, maxH - 8))); // 直接写 DOM,图标即时跟手,不经过 Vue 重渲染\n\n var el = this.$refs.floatEl;\n\n if (el) {\n el.style.left = left + \"px\";\n el.style.bottom = bottom + \"px\";\n }\n\n if (this.panelVisible) {\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n if (!this.dragging) return;\n this.dragging = false;\n if (!this.moved) return; // 读取拖拽后的实际位置\n\n var el = this.$refs.floatEl;\n var left = el ? parseInt(el.style.left || \"0\", 10) : this.btnLeft;\n var bottom = el ? parseInt(el.style.bottom || \"0\", 10) : this.btnBottom;\n this.btnLeft = left;\n this.btnBottom = bottom; // 拖动过才吸附贴边;靠近右边时收成半角标\n\n var box = this.getContainer();\n if (!box) return;\n\n if (box.clientWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = box.clientWidth - 46;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.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");
242
242
 
243
243
  /***/ }),
244
244
 
245
- /***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"09ea9ef8-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&":
245
+ /***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"02165064-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&":
246
246
  /*!*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
247
- !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"09ea9ef8-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true& ***!
247
+ !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"02165064-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true& ***!
248
248
  \*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
249
249
  /*! exports provided: render, staticRenderFns */
250
250
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
251
251
 
252
252
  "use strict";
253
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"ai-assistant\" },\n [\n _c(\"transition\", { attrs: { name: \"ai-fade\" } }, [\n _vm.panelVisible\n ? _c(\n \"div\",\n {\n staticClass: \"ai-panel\",\n style: { bottom: _vm.panelBottom + \"px\" },\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"ai-panel__header\" }, [\n _c(\"span\", { staticClass: \"ai-panel__icon\" }, [\n _c(\"img\", { attrs: { src: _vm.iconUrl, alt: \"AI\" } })\n ]),\n _c(\"span\", { staticClass: \"ai-panel__title\" }, [\n _vm._v(\"AI 审批助手\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"ai-panel__close\",\n on: { click: _vm.closePanel }\n },\n [_vm._v(\"✕\")]\n )\n ]),\n _c(\n \"div\",\n {\n ref: \"chatBody\",\n staticClass: \"ai-chat\",\n on: {\n \"&scroll\": function($event) {\n return _vm.onChatScroll.apply(null, arguments)\n }\n }\n },\n [\n _vm._l(_vm.messages, function(m) {\n return [\n _c(\n \"div\",\n {\n key: m.id,\n staticClass: \"msg\",\n class: \"msg--\" + m.role\n },\n [\n m.role === \"assistant\"\n ? _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ])\n : _vm._e(),\n _c(\"div\", {\n staticClass: \"msg__bubble\",\n domProps: {\n innerHTML: _vm._s(_vm.renderBubble(m))\n }\n })\n ]\n )\n ]\n }),\n _vm.sending && !_vm.streaming\n ? _c(\"div\", { staticClass: \"msg msg--assistant\" }, [\n _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]),\n _c(\n \"div\",\n { staticClass: \"msg__bubble msg__bubble--typing\" },\n [_c(\"span\"), _c(\"span\"), _c(\"span\")]\n )\n ])\n : _vm._e()\n ],\n 2\n ),\n _c(\"div\", { staticClass: \"ai-input\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"input\",\n staticClass: \"ai-input__box\",\n attrs: { rows: \"1\", placeholder: \"输入你的问题\" },\n domProps: { value: _vm.inputText },\n on: {\n keydown: function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.sendMessage.apply(null, arguments)\n },\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n }\n }\n }),\n _c(\n \"button\",\n {\n staticClass: \"ai-input__send\",\n attrs: { disabled: _vm.sending || !_vm.inputText.trim() },\n on: { click: _vm.sendMessage }\n },\n [_vm._v(\"发送\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]),\n _c(\n \"div\",\n {\n ref: \"floatEl\",\n staticClass: \"ai-float\",\n class: {\n tucked: _vm.tucked,\n \"is-open\": _vm.panelVisible,\n dragging: _vm.dragging\n },\n style: { left: _vm.btnLeft + \"px\", bottom: _vm.btnBottom + \"px\" },\n on: { mousedown: _vm.startDrag }\n },\n [\n _c(\n \"button\",\n {\n staticClass: \"ai-float__btn\",\n class: { \"is-active\": _vm.panelVisible },\n attrs: {\n title: _vm.panelVisible ? \"收起 AI 助手\" : \"打开 AI 助手\"\n },\n on: { click: _vm.togglePanel, mouseup: _vm.stopDrag }\n },\n [\n _c(\"img\", {\n staticClass: \"ai-float__img\",\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]\n )\n ]\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2209ea9ef8-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
253
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"ai-assistant\" },\n [\n _vm.panelVisible && _vm.isMobile\n ? _c(\"div\", { staticClass: \"ai-mask\", on: { click: _vm.closePanel } })\n : _vm._e(),\n _c(\"transition\", { attrs: { name: \"ai-fade\" } }, [\n _vm.panelVisible\n ? _c(\n \"div\",\n {\n staticClass: \"ai-panel\",\n style: { bottom: _vm.panelBottom + \"px\" },\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"ai-panel__header\" }, [\n _c(\"span\", { staticClass: \"ai-panel__icon\" }, [\n _c(\"img\", { attrs: { src: _vm.iconUrl, alt: \"AI\" } })\n ]),\n _c(\"span\", { staticClass: \"ai-panel__title\" }, [\n _vm._v(\"AI 审批助手\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"ai-panel__close\",\n on: { click: _vm.closePanel }\n },\n [_vm._v(\"✕\")]\n )\n ]),\n _c(\n \"div\",\n {\n ref: \"chatBody\",\n staticClass: \"ai-chat\",\n on: {\n \"&scroll\": function($event) {\n return _vm.onChatScroll.apply(null, arguments)\n }\n }\n },\n [\n _vm._l(_vm.messages, function(m) {\n return [\n _c(\n \"div\",\n {\n key: m.id,\n staticClass: \"msg\",\n class: \"msg--\" + m.role\n },\n [\n m.role === \"assistant\"\n ? _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ])\n : _vm._e(),\n _c(\"div\", {\n staticClass: \"msg__bubble\",\n domProps: {\n innerHTML: _vm._s(_vm.renderBubble(m))\n }\n })\n ]\n )\n ]\n }),\n _vm.sending && !_vm.streaming\n ? _c(\"div\", { staticClass: \"msg msg--assistant\" }, [\n _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]),\n _c(\n \"div\",\n { staticClass: \"msg__bubble msg__bubble--typing\" },\n [_c(\"span\"), _c(\"span\"), _c(\"span\")]\n )\n ])\n : _vm._e()\n ],\n 2\n ),\n _c(\"div\", { staticClass: \"ai-input\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"input\",\n staticClass: \"ai-input__box\",\n attrs: { rows: \"1\", placeholder: \"输入你的问题\" },\n domProps: { value: _vm.inputText },\n on: {\n keydown: function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.sendMessage.apply(null, arguments)\n },\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n }\n }\n }),\n _c(\n \"button\",\n {\n staticClass: \"ai-input__send\",\n attrs: { disabled: _vm.sending || !_vm.inputText.trim() },\n on: { click: _vm.sendMessage }\n },\n [_vm._v(\"发送\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]),\n _c(\n \"div\",\n {\n ref: \"floatEl\",\n staticClass: \"ai-float\",\n class: {\n tucked: _vm.tucked,\n \"is-open\": _vm.panelVisible,\n dragging: _vm.dragging\n },\n style: { left: _vm.btnLeft + \"px\", bottom: _vm.btnBottom + \"px\" },\n on: {\n mousedown: _vm.startDrag,\n touchstart: _vm.startDrag,\n touchmove: _vm.onDrag,\n touchend: _vm.endDrag,\n touchcancel: _vm.endDrag\n }\n },\n [\n _c(\n \"button\",\n {\n staticClass: \"ai-float__btn\",\n class: { \"is-active\": _vm.panelVisible },\n attrs: {\n title: _vm.panelVisible ? \"收起 AI 助手\" : \"打开 AI 助手\"\n },\n on: { click: _vm.togglePanel, mouseup: _vm.stopDrag }\n },\n [\n _c(\"img\", {\n staticClass: \"ai-float__img\",\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]\n )\n ]\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2202165064-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
254
254
 
255
255
  /***/ }),
256
256
 
@@ -261,7 +261,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
261
261
  /*! no static exports found */
262
262
  /***/ (function(module, exports, __webpack_require__) {
263
263
 
264
- eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"@charset \\\"UTF-8\\\";\\n.ai-assistant[data-v-5d775cd8] {\\n position: absolute;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n pointer-events: none;\\n z-index: 20;\\n}\\n.ai-float[data-v-5d775cd8] {\\n position: absolute;\\n pointer-events: auto;\\n z-index: 21;\\n transition: transform 0.25s ease;\\n}\\n.ai-float.dragging[data-v-5d775cd8] {\\n transition: none;\\n}\\n.ai-float.tucked[data-v-5d775cd8] {\\n transform: translateX(50%);\\n}\\n.ai-float.tucked[data-v-5d775cd8]:hover, .ai-float.tucked.is-open[data-v-5d775cd8] {\\n transform: translateX(0);\\n}\\n.ai-float__btn[data-v-5d775cd8] {\\n width: 46px;\\n height: 46px;\\n border-radius: 50%;\\n border: 1px solid #e4e7ed;\\n background: #fff;\\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n cursor: pointer;\\n color: #3067d9;\\n transition: box-shadow 0.2s, transform 0.2s;\\n}\\n.ai-float__btn[data-v-5d775cd8]:hover {\\n box-shadow: 0 4px 16px rgba(48, 103, 217, 0.3);\\n}\\n.ai-float__btn.is-active[data-v-5d775cd8] {\\n background: #fff;\\n color: #3067d9;\\n transform: none;\\n}\\n.ai-float__btn .ai-float__img[data-v-5d775cd8] {\\n width: 28px;\\n height: 28px;\\n border-radius: 50%;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n position: absolute;\\n right: 74px;\\n width: 384px;\\n height: 560px; /* 默认高度 */\\n max-height: calc(100% - 48px); /* 上下各留 24px 安全距离,容器不够高时自动压缩 */\\n max-width: calc(100% - 98px); /* 右侧留按钮位(74px)+左侧 24px 安全,窄容器不溢出遮挡 */\\n background: #fff;\\n border: 1px solid #e4e7ed;\\n border-radius: 10px;\\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);\\n pointer-events: auto;\\n z-index: 22;\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n}\\n.ai-panel__header[data-v-5d775cd8] {\\n height: 46px;\\n padding: 0 12px;\\n display: flex;\\n align-items: center;\\n background: #fafbfc;\\n border-bottom: 1px solid #f0f2f5;\\n gap: 8px;\\n}\\n.ai-panel__icon[data-v-5d775cd8] {\\n color: #3067d9;\\n display: flex;\\n}\\n.ai-panel__icon img[data-v-5d775cd8] {\\n width: 22px;\\n height: 22px;\\n border-radius: 50%;\\n}\\n.ai-panel__title[data-v-5d775cd8] {\\n font-size: 15px;\\n font-weight: 600;\\n color: #1f2329;\\n}\\n.ai-panel__status[data-v-5d775cd8] {\\n font-size: 12px;\\n padding: 1px 8px;\\n border-radius: 10px;\\n}\\n.ai-panel__status.status-ok[data-v-5d775cd8] {\\n color: #1a7f37;\\n background: #e6f4ea;\\n}\\n.ai-panel__status.status-no[data-v-5d775cd8] {\\n color: #d83931;\\n background: #fdeeee;\\n}\\n.ai-panel__status.status-go[data-v-5d775cd8] {\\n color: #3067d9;\\n background: #e8efff;\\n}\\n.ai-panel__close[data-v-5d775cd8] {\\n margin-left: auto;\\n cursor: pointer;\\n color: #86909c;\\n font-size: 16px;\\n line-height: 1;\\n padding: 0 2px;\\n}\\n.ai-panel__close[data-v-5d775cd8]:hover {\\n color: #1f2329;\\n}\\n.ai-chat[data-v-5d775cd8] {\\n flex: 1;\\n min-height: 120px;\\n padding: 14px;\\n overflow-y: auto;\\n background: #fff;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar {\\n width: 6px;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar-thumb {\\n background: #e4e7ed;\\n border-radius: 3px;\\n}\\n.msg[data-v-5d775cd8] {\\n display: flex;\\n margin-bottom: 14px;\\n}\\n.msg--assistant[data-v-5d775cd8] {\\n align-items: flex-start;\\n}\\n.msg--user[data-v-5d775cd8] {\\n justify-content: flex-end;\\n}\\n.msg__avatar[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n width: 30px;\\n height: 30px;\\n margin-right: 8px;\\n border-radius: 50%;\\n background: #e8efff;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\\n.msg__avatar img[data-v-5d775cd8] {\\n width: 30px;\\n height: 30px;\\n border-radius: 50%;\\n}\\n.msg__bubble[data-v-5d775cd8] {\\n max-width: 76%;\\n padding: 9px 12px;\\n border-radius: 10px;\\n font-size: 13px;\\n line-height: 1.7;\\n word-break: break-word;\\n color: #4e5969;\\n background: #f5f6f7;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n.msg__bubble h2[data-v-5d775cd8],\\n.msg__bubble h3[data-v-5d775cd8],\\n.msg__bubble h4[data-v-5d775cd8],\\n.msg__bubble h5[data-v-5d775cd8],\\n.msg__bubble h6[data-v-5d775cd8] {\\n font-size: 14px;\\n font-weight: 600;\\n color: #1f2329;\\n margin: 8px 0 4px;\\n}\\n.msg__bubble h1[data-v-5d775cd8]:first-child,\\n.msg__bubble h2[data-v-5d775cd8]:first-child,\\n.msg__bubble h3[data-v-5d775cd8]:first-child,\\n.msg__bubble h4[data-v-5d775cd8]:first-child,\\n.msg__bubble h5[data-v-5d775cd8]:first-child,\\n.msg__bubble h6[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble p[data-v-5d775cd8] {\\n margin: 4px 0;\\n}\\n.msg__bubble p[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble ul[data-v-5d775cd8],\\n.msg__bubble ol[data-v-5d775cd8] {\\n margin: 4px 0;\\n padding-left: 0;\\n list-style: none;\\n}\\n.msg__bubble li[data-v-5d775cd8] {\\n margin: 2px 0;\\n}\\n.msg__bubble strong[data-v-5d775cd8] {\\n color: #1f2329;\\n font-weight: 600;\\n}\\n.msg__bubble a[data-v-5d775cd8] {\\n color: #3067d9;\\n text-decoration: none;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n background: #f0f2f5;\\n color: #d83931;\\n padding: 1px 4px;\\n border-radius: 3px;\\n font-size: 12px;\\n}\\n.msg__bubble pre[data-v-5d775cd8] {\\n background: #f6f7f8;\\n padding: 8px;\\n border-radius: 6px;\\n overflow-x: auto;\\n margin: 6px 0;\\n}\\n.msg__bubble pre code[data-v-5d775cd8] {\\n background: none;\\n color: #1f2329;\\n padding: 0;\\n}\\n.msg--user .msg__bubble[data-v-5d775cd8] {\\n color: #fff;\\n background: #3067d9;\\n}\\n.msg__bubble--typing[data-v-5d775cd8] {\\n display: flex;\\n gap: 4px;\\n padding: 12px;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8] {\\n width: 6px;\\n height: 6px;\\n border-radius: 50%;\\n background: #86909c;\\n -webkit-animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(2) {\\n -webkit-animation-delay: 0.2s;\\n animation-delay: 0.2s;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(3) {\\n -webkit-animation-delay: 0.4s;\\n animation-delay: 0.4s;\\n}\\n@-webkit-keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n@keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n.ai-quick[data-v-5d775cd8] {\\n display: flex;\\n flex-wrap: wrap;\\n gap: 6px;\\n padding: 0 14px 10px;\\n background: #fff;\\n}\\n.ai-quick__item[data-v-5d775cd8] {\\n border: 1px solid #dce3f0;\\n background: #f7f9ff;\\n color: #3067d9;\\n font-size: 12px;\\n padding: 4px 10px;\\n border-radius: 14px;\\n cursor: pointer;\\n}\\n.ai-quick__item[data-v-5d775cd8]:hover {\\n border-color: #3067d9;\\n background: #eef3ff;\\n}\\n.ai-input[data-v-5d775cd8] {\\n display: flex;\\n align-items: flex-end;\\n gap: 8px;\\n padding: 10px 12px;\\n border-top: 1px solid #f0f2f5;\\n background: #fafbfc;\\n}\\n.ai-input__box[data-v-5d775cd8] {\\n flex: 1;\\n resize: none;\\n border: 1px solid #e4e7ed;\\n border-radius: 6px;\\n padding: 7px 10px;\\n font-size: 13px;\\n line-height: 1.5;\\n font-family: inherit;\\n color: #1f2329;\\n background: #fff;\\n outline: none;\\n max-height: 96px;\\n}\\n.ai-input__box[data-v-5d775cd8]:focus {\\n border-color: #3067d9;\\n}\\n.ai-input__send[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n border: none;\\n background: #3067d9;\\n color: #fff;\\n font-size: 13px;\\n padding: 7px 16px;\\n border-radius: 6px;\\n cursor: pointer;\\n}\\n.ai-input__send[data-v-5d775cd8]:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n.ai-fade-enter-active[data-v-5d775cd8],\\n.ai-fade-leave-active[data-v-5d775cd8] {\\n transition: opacity 0.2s, transform 0.2s;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n.ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 0;\\n transform: translateY(10px);\\n}\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
264
+ eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"@charset \\\"UTF-8\\\";\\n.ai-assistant[data-v-5d775cd8] {\\n position: absolute;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n pointer-events: none;\\n z-index: 20;\\n}\\n.ai-float[data-v-5d775cd8] {\\n position: absolute;\\n pointer-events: auto;\\n z-index: 21;\\n transition: transform 0.25s ease;\\n touch-action: none; /* 触摸拖拽时不触发页面滚动/手势,保证跟手 */\\n}\\n.ai-float.dragging[data-v-5d775cd8] {\\n transition: none;\\n}\\n.ai-float.tucked[data-v-5d775cd8] {\\n transform: translateX(50%);\\n}\\n.ai-float.tucked[data-v-5d775cd8]:hover, .ai-float.tucked.is-open[data-v-5d775cd8] {\\n transform: translateX(0);\\n}\\n.ai-float__btn[data-v-5d775cd8] {\\n width: 46px;\\n height: 46px;\\n border-radius: 50%;\\n border: 1px solid #e4e7ed;\\n background: #fff;\\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n cursor: pointer;\\n color: #3067d9;\\n transition: box-shadow 0.2s, transform 0.2s;\\n}\\n.ai-float__btn[data-v-5d775cd8]:hover {\\n box-shadow: 0 4px 16px rgba(48, 103, 217, 0.3);\\n}\\n.ai-float__btn.is-active[data-v-5d775cd8] {\\n background: #fff;\\n color: #3067d9;\\n transform: none;\\n}\\n.ai-float__btn .ai-float__img[data-v-5d775cd8] {\\n width: 28px;\\n height: 28px;\\n border-radius: 50%;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n position: absolute;\\n right: 74px;\\n width: 384px;\\n height: 560px; /* 默认高度 */\\n max-height: calc(100% - 48px); /* 上下各留 24px 安全距离,容器不够高时自动压缩 */\\n max-width: calc(100% - 98px); /* 右侧留按钮位(74px)+左侧 24px 安全,窄容器不溢出遮挡 */\\n background: #fff;\\n border: 1px solid #e4e7ed;\\n border-radius: 10px;\\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);\\n pointer-events: auto;\\n z-index: 22;\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n}\\n.ai-panel__header[data-v-5d775cd8] {\\n height: 46px;\\n padding: 0 12px;\\n display: flex;\\n align-items: center;\\n background: #fafbfc;\\n border-bottom: 1px solid #f0f2f5;\\n gap: 8px;\\n}\\n.ai-panel__icon[data-v-5d775cd8] {\\n color: #3067d9;\\n display: flex;\\n}\\n.ai-panel__icon img[data-v-5d775cd8] {\\n width: 22px;\\n height: 22px;\\n border-radius: 50%;\\n}\\n.ai-panel__title[data-v-5d775cd8] {\\n font-size: 15px;\\n font-weight: 600;\\n color: #1f2329;\\n}\\n.ai-panel__status[data-v-5d775cd8] {\\n font-size: 12px;\\n padding: 1px 8px;\\n border-radius: 10px;\\n}\\n.ai-panel__status.status-ok[data-v-5d775cd8] {\\n color: #1a7f37;\\n background: #e6f4ea;\\n}\\n.ai-panel__status.status-no[data-v-5d775cd8] {\\n color: #d83931;\\n background: #fdeeee;\\n}\\n.ai-panel__status.status-go[data-v-5d775cd8] {\\n color: #3067d9;\\n background: #e8efff;\\n}\\n.ai-panel__close[data-v-5d775cd8] {\\n margin-left: auto;\\n cursor: pointer;\\n color: #86909c;\\n font-size: 16px;\\n line-height: 1;\\n padding: 0 2px;\\n}\\n.ai-panel__close[data-v-5d775cd8]:hover {\\n color: #1f2329;\\n}\\n.ai-chat[data-v-5d775cd8] {\\n flex: 1;\\n min-height: 120px;\\n padding: 14px;\\n overflow-y: auto;\\n background: #fff;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar {\\n width: 6px;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar-thumb {\\n background: #e4e7ed;\\n border-radius: 3px;\\n}\\n.msg[data-v-5d775cd8] {\\n display: flex;\\n margin-bottom: 14px;\\n}\\n.msg--assistant[data-v-5d775cd8] {\\n align-items: flex-start;\\n}\\n.msg--user[data-v-5d775cd8] {\\n justify-content: flex-end;\\n}\\n.msg__avatar[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n width: 30px;\\n height: 30px;\\n margin-right: 8px;\\n border-radius: 50%;\\n background: #e8efff;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\\n.msg__avatar img[data-v-5d775cd8] {\\n width: 30px;\\n height: 30px;\\n border-radius: 50%;\\n}\\n.msg__bubble[data-v-5d775cd8] {\\n max-width: 76%;\\n padding: 9px 12px;\\n border-radius: 10px;\\n font-size: 13px;\\n line-height: 1.7;\\n word-break: break-word;\\n color: #4e5969;\\n background: #f5f6f7;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n.msg__bubble h2[data-v-5d775cd8],\\n.msg__bubble h3[data-v-5d775cd8],\\n.msg__bubble h4[data-v-5d775cd8],\\n.msg__bubble h5[data-v-5d775cd8],\\n.msg__bubble h6[data-v-5d775cd8] {\\n font-size: 14px;\\n font-weight: 600;\\n color: #1f2329;\\n margin: 8px 0 4px;\\n}\\n.msg__bubble h1[data-v-5d775cd8]:first-child,\\n.msg__bubble h2[data-v-5d775cd8]:first-child,\\n.msg__bubble h3[data-v-5d775cd8]:first-child,\\n.msg__bubble h4[data-v-5d775cd8]:first-child,\\n.msg__bubble h5[data-v-5d775cd8]:first-child,\\n.msg__bubble h6[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble p[data-v-5d775cd8] {\\n margin: 4px 0;\\n}\\n.msg__bubble p[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble ul[data-v-5d775cd8],\\n.msg__bubble ol[data-v-5d775cd8] {\\n margin: 4px 0;\\n padding-left: 0;\\n list-style: none;\\n}\\n.msg__bubble li[data-v-5d775cd8] {\\n margin: 2px 0;\\n}\\n.msg__bubble strong[data-v-5d775cd8] {\\n color: #1f2329;\\n font-weight: 600;\\n}\\n.msg__bubble a[data-v-5d775cd8] {\\n color: #3067d9;\\n text-decoration: none;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n background: #f0f2f5;\\n color: #d83931;\\n padding: 1px 4px;\\n border-radius: 3px;\\n font-size: 12px;\\n}\\n.msg__bubble pre[data-v-5d775cd8] {\\n background: #f6f7f8;\\n padding: 8px;\\n border-radius: 6px;\\n overflow-x: auto;\\n margin: 6px 0;\\n}\\n.msg__bubble pre code[data-v-5d775cd8] {\\n background: none;\\n color: #1f2329;\\n padding: 0;\\n}\\n.msg--user .msg__bubble[data-v-5d775cd8] {\\n color: #fff;\\n background: #3067d9;\\n}\\n.msg__bubble--typing[data-v-5d775cd8] {\\n display: flex;\\n gap: 4px;\\n padding: 12px;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8] {\\n width: 6px;\\n height: 6px;\\n border-radius: 50%;\\n background: #86909c;\\n -webkit-animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(2) {\\n -webkit-animation-delay: 0.2s;\\n animation-delay: 0.2s;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(3) {\\n -webkit-animation-delay: 0.4s;\\n animation-delay: 0.4s;\\n}\\n@-webkit-keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n@keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n.ai-quick[data-v-5d775cd8] {\\n display: flex;\\n flex-wrap: wrap;\\n gap: 6px;\\n padding: 0 14px 10px;\\n background: #fff;\\n}\\n.ai-quick__item[data-v-5d775cd8] {\\n border: 1px solid #dce3f0;\\n background: #f7f9ff;\\n color: #3067d9;\\n font-size: 12px;\\n padding: 4px 10px;\\n border-radius: 14px;\\n cursor: pointer;\\n}\\n.ai-quick__item[data-v-5d775cd8]:hover {\\n border-color: #3067d9;\\n background: #eef3ff;\\n}\\n.ai-input[data-v-5d775cd8] {\\n display: flex;\\n align-items: flex-end;\\n gap: 8px;\\n padding: 10px 12px;\\n border-top: 1px solid #f0f2f5;\\n background: #fafbfc;\\n}\\n.ai-input__box[data-v-5d775cd8] {\\n flex: 1;\\n resize: none;\\n border: 1px solid #e4e7ed;\\n border-radius: 6px;\\n padding: 7px 10px;\\n font-size: 13px;\\n line-height: 1.5;\\n font-family: inherit;\\n color: #1f2329;\\n background: #fff;\\n outline: none;\\n max-height: 96px;\\n}\\n.ai-input__box[data-v-5d775cd8]:focus {\\n border-color: #3067d9;\\n}\\n.ai-input__send[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n border: none;\\n background: #3067d9;\\n color: #fff;\\n font-size: 13px;\\n padding: 7px 16px;\\n border-radius: 6px;\\n cursor: pointer;\\n}\\n.ai-input__send[data-v-5d775cd8]:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n.ai-fade-enter-active[data-v-5d775cd8],\\n.ai-fade-leave-active[data-v-5d775cd8] {\\n transition: opacity 0.2s, transform 0.2s;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n.ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 0;\\n transform: translateY(10px);\\n}\\n\\n/* ============ H5 兼容:点击 AI 后从底部弹出,占 80% 高度、宽度占满 ============ */\\n/* 仅影响窄屏移动端,不影响 PC 端逻辑(PC 宽度 > 480px 仍走上方右侧悬浮面板) */\\n@media (max-width: 480px) {\\n /* 整层提到宿主底部操作栏(通常 z-index 高)之上,保证浮球/面板/遮罩不被遮挡 */\\n.ai-assistant[data-v-5d775cd8] {\\n z-index: 3000;\\n}\\n /* 审批面板打开时的阴影遮罩层 */\\n.ai-mask[data-v-5d775cd8] {\\n position: fixed;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n background: rgba(0, 0, 0, 0.45);\\n pointer-events: auto;\\n z-index: 999;\\n}\\n /* 浮球改为视口固定:在可滚动的 H5 长详情页上始终贴在当前视口,并保持置顶 */\\n.ai-float[data-v-5d775cd8] {\\n position: fixed;\\n z-index: 1000;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n /* 脱离容器,固定到视口底部,宽度占满、高度占视口 80% */\\n position: fixed;\\n top: auto;\\n left: 0;\\n right: 0;\\n bottom: 0 !important; /* 覆盖内联面板 bottom 绑定,始终贴底 */\\n width: 100%;\\n max-width: 100%;\\n height: 85%;\\n max-height: 85%; /* 兼容个别不允许 vh 的旧内核 */\\n border-radius: 16px 16px 0 0;\\n z-index: 1001; /* 位于遮罩层之上 */\\n}\\n /* 底部弹出/收起动画:从视口底部滑入 */\\n.ai-fade-enter-active[data-v-5d775cd8],\\n .ai-fade-leave-active[data-v-5d775cd8] {\\n transition: transform 0.28s ease;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n .ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 1;\\n transform: translateY(100%);\\n}\\n /* H5 放大审批详情/聊天文字,提升可读性 */\\n.msg__bubble[data-v-5d775cd8] {\\n font-size: 15px;\\n line-height: 1.8;\\n padding: 11px 14px;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n .msg__bubble h2[data-v-5d775cd8],\\n .msg__bubble h3[data-v-5d775cd8],\\n .msg__bubble h4[data-v-5d775cd8],\\n .msg__bubble h5[data-v-5d775cd8],\\n .msg__bubble h6[data-v-5d775cd8] {\\n font-size: 17px;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n font-size: 14px;\\n}\\n /* 输入区针对全面屏 Home 指示条预留安全距离 */\\n.ai-input[data-v-5d775cd8] {\\n padding-bottom: calc(10px + env(safe-area-inset-bottom));\\n}\\n}\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
265
265
 
266
266
  /***/ }),
267
267
 
@@ -1234,7 +1234,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nod
1234
1234
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1235
1235
 
1236
1236
  "use strict";
1237
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"09ea9ef8-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=5d775cd8&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"09ea9ef8-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?");
1237
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"02165064-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=5d775cd8&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"02165064-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?");
1238
1238
 
1239
1239
  /***/ }),
1240
1240
 
@@ -238,19 +238,19 @@ eval("__webpack_require__.r(__webpack_exports__);\n// This file is imported into
238
238
  /***/ (function(module, exports, __webpack_require__) {
239
239
 
240
240
  "use strict";
241
- eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireWildcard */ \"./node_modules/@babel/runtime/helpers/interopRequireWildcard.js\").default;\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 _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _createForOfIteratorHelper2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/createForOfIteratorHelper */ \"./node_modules/@babel/runtime/helpers/createForOfIteratorHelper.js\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _aiAssistant = _interopRequireDefault(__webpack_require__(/*! ./ai-assistant.png */ \"./src/lib/components/approvalAi/ai-assistant.png\"));\n\nvar aiApi = _interopRequireWildcard(__webpack_require__(/*! ./apiApprovalAi */ \"./src/lib/components/approvalAi/apiApprovalAi.js\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.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//\nvar md = new _markdownIt.default({\n html: false,\n linkify: true\n}); // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\nvar VALUE_NAME = {\n comFeePlan: \"公司费用计划\",\n comFeeApplication: \"公司费用\",\n comFeeAccept: \"公司费用验收\",\n comFeeCredit: \"公司费用挂账\",\n comFeePay: \"公司费用支付\",\n employeeHonor: \"员工荣誉\",\n employeeRegularization: \"员工转正\",\n employeeTurnover: \"离职\",\n employeeturnoverhandover: \"离职交接\",\n employeeChangestation: \"调岗\",\n employeeChangesalary: \"调薪\",\n overTimeApply: \"加班\",\n carApproval: \"车辆\",\n reimburse: \"出差报销\",\n leave: \"请假\",\n patch: \"补卡\",\n trip: \"出差\",\n out: \"外出\",\n lan: \"借款\",\n officialDocument: \"公文\",\n notice: \"公告\",\n qgbCompanyPlanSummery: \"公司费用预算汇总\",\n sealApproval: \"印章\"\n}; // 审批状态码 -> 中文(内置映射,替代宿主项目全局 $approvalStatus)\n\nvar STATUS_MAP = {\n \"1\": \"审批中\",\n \"-1\": \"审批中\",\n \"0\": \"已拒绝\",\n \"2\": \"已通过\",\n \"-3\": \"已通过\",\n \"4\": \"草稿\",\n \"3\": \"已通过\",\n \"5\": \"已完成\",\n \"6\": \"已撤销\"\n}; // 概览文案启发式字段匹配\n\nvar FIELD_HINTS = [{\n key: \"applicant\",\n labels: [\"申请人\", \"提交人\", \"经办人\"]\n}, {\n key: \"unit\",\n labels: [\"申请单位\", \"公司\", \"单位\"]\n}, {\n key: \"dept\",\n labels: [\"申请部门\", \"部门\"]\n}, {\n key: \"reason\",\n labels: [\"原因\", \"事由\", \"用途\", \"摘要\", \"标题\", \"说明\", \"内容\", \"目的\"]\n}, {\n key: \"amount\",\n labels: [\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]\n}];\n\nfunction normalizeLabel(label) {\n return String(label).trim().replace(/[::]\\s*$/g, \"\");\n}\n\nfunction resolveStatusText(status, override) {\n if (override) return override;\n if (!status && status !== 0) return \"待处理\";\n var mapped = STATUS_MAP[String(status)];\n return mapped || \"未知状态\";\n}\n\nvar msgSeq = 0;\nvar _default = {\n name: \"ApprovalAiAssistant\",\n props: {\n id: {\n type: String,\n default: \"\"\n },\n type: {\n type: String,\n default: \"\"\n },\n status: {\n type: String,\n default: \"0\"\n },\n index: {\n type: Number,\n default: 1\n },\n commentId: {\n type: String,\n default: \"\"\n },\n // ---- 以下为进包后新增的配置项,用于解耦宿主角色的上下文依赖 ----\n // AI 服务基础地址(示例 https://api.xxx.cn),未配置则走本地兜底\n baseUrl: {\n type: String,\n default: \"\"\n },\n // 当前用户信息(替代宿主 Vuex $store.state.user)\n userId: {\n type: String,\n default: \"\"\n },\n userName: {\n type: String,\n default: \"\"\n },\n // 审批状态文案覆盖(默认内置状态码映射;传入则优先使用)\n statusTextOverride: {\n type: String,\n default: \"\"\n },\n // 布局边界选择器(替代硬编码 .approval-content)\n containerSelector: {\n type: String,\n default: \".approval-content\"\n },\n // 详情采集根选择器(替代硬编码 .detail-root)\n detailRootSelector: {\n type: String,\n default: \".detail-root\"\n },\n // 机器人图标,不传时使用包内内置图标\n robotIconUrl: {\n type: String,\n default: \"\"\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n panelVisible: false,\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 24,\n tucked: false,\n panelBottom: 88,\n dragging: false,\n moved: false,\n startX: 0,\n startY: 0,\n startLeft: 0,\n startBottom: 0\n };\n },\n computed: {\n iconUrl: function iconUrl() {\n return this.robotIconUrl || this.defaultIcon;\n },\n typeName: function typeName() {\n if (!this.type) return \"\";\n return VALUE_NAME[this.type] || this.type;\n },\n statusText: function statusText() {\n return resolveStatusText(this.status, this.statusTextOverride);\n },\n statusTone: function statusTone() {\n var t = this.statusText;\n if (t === \"已通过\" || t === \"已完成\") return \"ok\";\n if (t === \"已拒绝\" || t === \"已驳回\" || t === \"已退回\" || t === \"已终止\") return \"no\";\n return \"go\";\n }\n },\n created: function created() {// 注入 AI 服务地址\n },\n mounted: function mounted() {\n var _this = this;\n\n window.addEventListener(\"mousemove\", this.onDrag);\n window.addEventListener(\"mouseup\", this.endDrag);\n this.$nextTick(function () {\n return _this.snapBtn();\n });\n },\n beforeDestroy: function beforeDestroy() {\n window.removeEventListener(\"mousemove\", this.onDrag);\n window.removeEventListener(\"mouseup\", this.endDrag);\n },\n watch: {\n // 切换审批单时重置对话上下文\n id: function id() {\n this.sessionId = \"\";\n this.messages = [];\n this.fields = [];\n this.rawDetail = null;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val\n });\n }\n },\n methods: {\n getContainer: function getContainer() {\n // 普通模式:以配置的审批容器为边界;找不到时回退到父级包裹容器\n var selector = this.containerSelector;\n var el = selector ? this.$el.closest(selector) : null;\n return el || this.$el.parentElement;\n },\n getDetailRoot: function getDetailRoot() {\n var box = this.getContainer();\n if (!box) return null;\n var explicit = this.detailRootSelector ? box.querySelector(this.detailRootSelector) : null;\n if (explicit) return explicit; // 子应用/iframe 模式:容器内可能没有 detail-root 标记,\n // 回退到首个详情子节点(排除 AI 面板自身)\n\n var kids = Array.from(box.children).filter(function (c) {\n return !c.classList.contains(\"ai-assistant\");\n });\n return kids.length ? kids[0] : null;\n },\n togglePanel: function togglePanel() {\n var _this2 = this;\n\n // 拖拽后松手会触发一次 click,这里拦截,避免误展开/收起面板\n if (this.moved) {\n this.moved = false;\n return;\n }\n\n if (this.panelVisible) {\n this.closePanel();\n return;\n }\n\n this.panelVisible = true;\n this.$nextTick(function () {\n _this2.snapPanel();\n\n if (!_this2.messages.length) {\n _this2.greet();\n }\n\n _this2.scrollBottom();\n\n if (_this2.$refs.input) _this2.$refs.input.focus();\n });\n },\n closePanel: function closePanel() {\n this.panelVisible = false;\n },\n snapBtn: function snapBtn() {\n var box = this.getContainer();\n if (!box) return; // 默认贴右下角完整展开:右侧留 20px\n\n if (!this.tucked && !this.btnLeft) {\n this.btnLeft = Math.max(0, box.clientWidth - 20 - 46);\n } // 若按钮在可视范围外则修正\n\n\n this.btnLeft = Math.max(0, Math.min(this.btnLeft, box.clientWidth - 46));\n this.btnBottom = Math.max(8, Math.min(this.btnBottom, box.clientHeight - 44 - 8));\n },\n snapPanel: function snapPanel() {\n var box = this.getContainer();\n var el = this.$el.querySelector(\".ai-panel\");\n if (!box || !el) return;\n var margin = 24; // 上下安全距离,避免遮挡\n\n var ph = el.clientHeight || 560;\n var desired = this.btnBottom + 62;\n this.panelBottom = Math.max(margin, Math.min(desired, box.clientHeight - ph - margin));\n },\n // 打开时 AI 先总结当前审批\n greet: function greet() {\n var _this3 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var summary, amount, res, base;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n _this3.collectDetail();\n\n summary = _this3.buildSummary();\n amount = _this3.findField([\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]); // 已接入后端时,优先用 AI 生成的总结\n\n console.log(_this3.id, _this3.aiEnabled());\n\n if (!(_this3.aiEnabled() && _this3.id)) {\n _context.next = 9;\n break;\n }\n\n _context.next = 7;\n return aiApi.getSummary(_this3.id);\n\n case 7:\n res = _context.sent;\n\n if (res && res.data && res.data.summary) {\n summary = res.data.summary;\n }\n\n case 9:\n base = \"\\u4F60\\u597D\\uFF0C\\u6211\\u662F AI \\u5BA1\\u6279\\u52A9\\u624B\\uD83D\\uDC4B\\n\\n\\u5DF2\\u7ECF\\u4E3A\\u4F60\\u68B3\\u7406\\u4E86\\u5F53\\u524D\\u5BA1\\u6279\\u7684\\u6982\\u89C8\\uFF1A\\n\\n\".concat(summary); // const tail = this.fields.length\n // ? `\\n\\n这份单据的关键字段共 ${this.fields.length} 项${amount ? `,其中金额相关「${amount}」` : \"\"}。你可以继续问我,比如「这笔审批是做什么的」「申请人是谁」。`\n // : \"\\n\\n暂时未采集到该单据的明细字段。\"\n // this.push(\"assistant\", base + tail)\n\n _this3.push(\"assistant\", base);\n\n case 11:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n sendMessage: function sendMessage() {\n var _this4 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return;\n this.push(\"user\", text);\n this.inputText = \"\";\n this.sending = true;\n this.collectDetail(); // 直接走后端流式问答(已接入时);未接入则返回本地兜底\n\n var msg = null;\n\n if (this.aiEnabled() && this.id) {\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: \"\"\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk) {\n if (msg) {\n msg.content += chunk;\n\n _this4.followBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this4.fallbackReply(text);\n } else {\n _this4.push(\"assistant\", _this4.fallbackReply(text));\n }\n\n _this4.streaming = false;\n _this4.sending = false;\n\n _this4.followBottom();\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this5 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n payload = {\n context: {\n type: _this5.type,\n status: _this5.status,\n typeName: _this5.typeName,\n id: _this5.id,\n summary: _this5.buildSummary(),\n fields: _this5.fields,\n detailData: _this5.rawDetail\n },\n question: question\n };\n _context2.next = 3;\n return _this5.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context2.sent;\n return _context2.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this5.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 当前用户信息(来自 props,替代宿主 Vuex)\n userInfo: function userInfo() {\n return {\n no: this.userId || \"\",\n name: this.userName || \"\"\n };\n },\n // 创建/复用 AI 对话会话\n ensureSession: function ensureSession() {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (!_this6.sessionId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\", _this6.sessionId);\n\n case 2:\n u = _this6.userInfo();\n _context3.next = 5;\n return aiApi.createChatSession({\n approvalNo: _this6.id,\n userId: u.no,\n userName: u.name\n });\n\n case 5:\n res = _context3.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context3.next = 9;\n break;\n }\n\n _this6.sessionId = res.data.sessionId;\n return _context3.abrupt(\"return\", _this6.sessionId);\n\n case 9:\n return _context3.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this7.aiEnabled()) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 2:\n _context4.prev = 2;\n _context4.next = 5;\n return _this7.ensureSession();\n\n case 5:\n sessionId = _context4.sent;\n\n if (sessionId) {\n _context4.next = 8;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 8:\n text = \"\";\n failed = false;\n _context4.next = 12;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n userId: _this7.userInfo().no,\n userName: _this7.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\" || event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data);\n } else if (event === \"agent_result\") {\n text = data;\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 12:\n if (!failed) {\n _context4.next = 14;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 14:\n return _context4.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 17:\n _context4.prev = 17;\n _context4.t0 = _context4[\"catch\"](2);\n return _context4.abrupt(\"return\", null);\n\n case 20:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[2, 17]]);\n }))();\n },\n // 无法从数据中检索到时的兜底:诚实告知查询不到,不编造\n fallbackReply: function fallbackReply(question) {\n var q = String(question).trim();\n var list = '';\n return \"\\u6682\\u65F6\\u65E0\\u6CD5\\u67E5\\u8BE2\\u5230\\u4E0E\\u300C\".concat(q, \"\\u300D\\u5BF9\\u5E94\\u7684\\u4FE1\\u606F\\u3002\").concat(list);\n },\n findField: function findField(labels) {\n var _iterator = (0, _createForOfIteratorHelper2.default)(this.rawEntries()),\n _step;\n\n try {\n var _loop = function _loop() {\n var e = _step.value;\n if (labels.some(function (l) {\n return e.label.indexOf(l) > -1;\n })) return {\n v: e.value\n };\n };\n\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var _ret = _loop();\n\n if ((0, _typeof2.default)(_ret) === \"object\") return _ret.v;\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return \"\";\n },\n // 合并 DOM 采集字段 + 详情页完整原始业务对象,得到可检索的条目\n rawEntries: function rawEntries() {\n var _this8 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this8.stringify(value);\n\n if (!v) return;\n var k = \"\".concat(label, \"__\").concat(v);\n if (seen.has(k)) return;\n seen.add(k);\n list.push({\n label: label,\n value: v\n });\n };\n\n var _iterator2 = (0, _createForOfIteratorHelper2.default)(this.fields),\n _step2;\n\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var f = _step2.value;\n pushEntry(f.label, f.value);\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n\n var raw = this.rawDetail && this.rawDetail.data;\n\n if (raw && (0, _typeof2.default)(raw) === \"object\") {\n for (var _i = 0, _Object$keys = Object.keys(raw); _i < _Object$keys.length; _i++) {\n var key = _Object$keys[_i];\n pushEntry(key, raw[key]);\n }\n }\n\n return list;\n },\n stringify: function stringify(v) {\n if (v === null || v === undefined) return \"\";\n\n if ((0, _typeof2.default)(v) === \"object\") {\n try {\n return JSON.stringify(v);\n } catch (e) {\n return \"\";\n }\n }\n\n return String(v);\n },\n // 通用采集详情页数据:兼容 el-form-item / el-descriptions / 自定义 span标签:值 / 表格,\n // 并尽量读取详情页组件的原始业务对象(info/form) 作为完整数据\n collectDetail: function collectDetail() {\n var root = this.getDetailRoot();\n var seen = {};\n\n var push = function push(label, value) {\n label = normalizeLabel(label);\n var v = String(value === null || value === undefined ? \"\" : value).replace(/\\s+/g, \" \").trim();\n if (!label || !v || v === \"暂无\" || label === \"图片\" || label === \"操作\") return;\n if (seen[label] !== undefined) return;\n seen[label] = v;\n };\n\n if (root) {\n // 1) el-form-item\n root.querySelectorAll(\".el-form-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-form-item__label\");\n var contentEl = item.querySelector(\".el-form-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 2) el-descriptions 描述项\n\n root.querySelectorAll(\".el-descriptions-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-descriptions-item__label\");\n var contentEl = item.querySelector(\".el-descriptions-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 3) 自定义 label:value(span 以「:」/「:」结尾,值在大括号内剩余文本)\n\n root.querySelectorAll(\"span\").forEach(function (span) {\n var txt = (span.textContent || \"\").trim();\n var m = txt.match(/^(.+?)([::])$/);\n if (!m) return;\n var parent = span.parentElement;\n if (!parent) return;\n var value = (parent.textContent || \"\").replace(txt, \"\").replace(/\\s+/g, \" \").trim();\n push(m[1], value);\n }); // 4) 表格:表头 -> 逐行数据\n\n root.querySelectorAll(\"table\").forEach(function (table) {\n var rows = table.querySelectorAll(\"tr\");\n var headers = [];\n\n if (rows.length) {\n headers = Array.from(rows[0].querySelectorAll(\"th,td\")).map(function (c) {\n return normalizeLabel(c.textContent);\n });\n }\n\n rows.forEach(function (tr, idx) {\n if (idx === 0) return;\n var cells = Array.from(tr.querySelectorAll(\"th,td\")).map(function (c) {\n return (c.textContent || \"\").replace(/\\s+/g, \" \").trim();\n });\n if (!cells.length) return;\n\n if (headers.length) {\n headers.forEach(function (h, j) {\n return h && cells[j] !== undefined && push(h, cells[j]);\n });\n } else {\n cells.forEach(function (c, j) {\n return c && push(\"\\u7B2C\".concat(idx, \"\\u884C\\u5B57\\u6BB5\").concat(j + 1), c);\n });\n }\n });\n });\n } // 5) 读取详情组件的原始业务对象,作为完整结构化数据补全\n\n\n var raw = this.readRawDetail();\n this.rawDetail = raw;\n if (raw) this.flattenTo(seen, raw);\n this.fields = Object.keys(seen).map(function (label) {\n return {\n label: label,\n value: seen[label]\n };\n }).slice(0, 40);\n },\n // 寻找详情页组件的业务数据对象(info / form 等)\n readRawDetail: function readRawDetail() {\n var _this9 = this;\n\n var root = this.getDetailRoot();\n if (!root) return null;\n\n var _iterator3 = (0, _createForOfIteratorHelper2.default)(root.querySelectorAll(\"*\")),\n _step3;\n\n try {\n for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {\n var el = _step3.value;\n var vm = el.__vue__;\n if (!vm || !vm.$data || (0, _typeof2.default)(vm.$data) !== \"object\") continue;\n var d = vm.$data;\n\n var _loop2 = function _loop2() {\n var key = _arr[_i2];\n var obj = d[key];\n\n if (obj && (0, _typeof2.default)(obj) === \"object\" && !Array.isArray(obj)) {\n var keys = Object.keys(obj).filter(function (k) {\n return obj[k] !== \"\" && obj[k] !== null && obj[k] !== undefined;\n });\n\n if (keys.length) {\n return {\n v: {\n key: key,\n data: _this9.jsonSafe(obj)\n }\n };\n }\n }\n };\n\n for (var _i2 = 0, _arr = [\"info\", \"form\"]; _i2 < _arr.length; _i2++) {\n var _ret2 = _loop2();\n\n if ((0, _typeof2.default)(_ret2) === \"object\") return _ret2.v;\n }\n }\n } catch (err) {\n _iterator3.e(err);\n } finally {\n _iterator3.f();\n }\n\n return null;\n },\n jsonSafe: function jsonSafe(obj) {\n try {\n return JSON.parse(JSON.stringify(obj, function (k, v) {\n return typeof v === \"function\" ? undefined : v;\n }));\n } catch (e) {\n return obj;\n }\n },\n // 把原始业务对象摊平成 label:value,补进已采集集合\n flattenTo: function flattenTo(seen, raw) {\n var walk = function walk(obj, path) {\n if (obj === null || obj === undefined) return;\n\n if (Array.isArray(obj)) {\n obj.forEach(function (item, i) {\n return walk(item, path ? \"\".concat(path, \"[\").concat(i, \"]\") : \"[\".concat(i, \"]\"));\n });\n return;\n }\n\n if ((0, _typeof2.default)(obj) === \"object\") {\n Object.keys(obj).forEach(function (k) {\n return walk(obj[k], path ? \"\".concat(path, \".\").concat(k) : k);\n });\n return;\n }\n\n var label = path || \"\";\n var v = String(obj).replace(/\\s+/g, \" \").trim();\n if (label && v && seen[label] === undefined) seen[label] = v;\n };\n\n walk(raw.data, raw.key);\n },\n // 依据采集字段生成“当前审批是做什么的”概览(启发式)\n buildSummary: function buildSummary() {\n var hits = {};\n FIELD_HINTS.forEach(function (hint) {\n hits[hint.key] = \"\";\n });\n this.fields.forEach(function (f) {\n var _iterator4 = (0, _createForOfIteratorHelper2.default)(FIELD_HINTS),\n _step4;\n\n try {\n for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {\n var hint = _step4.value;\n if (hits[hint.key]) continue;\n\n if (hint.labels.some(function (l) {\n return f.label.indexOf(l) > -1;\n })) {\n hits[hint.key] = f.value;\n break;\n }\n }\n } catch (err) {\n _iterator4.e(err);\n } finally {\n _iterator4.f();\n }\n });\n var name = this.typeName || \"该单据\";\n var parts = [];\n if (hits.applicant) parts.push(\"\\u7533\\u8BF7\\u4EBA\\u300C\".concat(hits.applicant, \"\\u300D\"));\n if (hits.unit) parts.push(\"\\u6240\\u5C5E\\u5355\\u4F4D\\u300C\".concat(hits.unit, \"\\u300D\"));\n var verb = \"\".concat(name === \"该单据\" ? \"该审批\" : \"\\u8BE5\\u300C\".concat(name, \"\\u300D\\u5BA1\\u6279\"));\n var summary = \"\".concat(verb, \"\\u662F\\u4E00\\u7B14\\u4E1A\\u52A1\\u5BA1\\u6279\");\n if (parts.length) summary += \"\\uFF0C\\u7531\".concat(parts.join(\",\"), \"\\u53D1\\u8D77\");\n if (hits.reason) summary += \"\\uFF0C\\u4E3B\\u8981\\u4E8B\\u7531\\uFF1A\".concat(hits.reason);\n if (hits.amount) summary += \"\\uFF0C\\u6D89\\u53CA\\u91D1\\u989D\\u300C\".concat(hits.amount, \"\\u300D\");\n summary += \"\\u3002\\u5F53\\u524D\\u5BA1\\u6279\\u72B6\\u6001\\u4E3A\\u300C\".concat(this.statusText, \"\\u300D\\u3002\");\n return summary;\n },\n push: function push(role, content) {\n this.messages.push({\n id: ++msgSeq,\n role: role,\n content: content\n });\n },\n // 气泡渲染:助手消息转 Markdown,用户消息按纯文本处理(转义防 XSS)\n renderBubble: function renderBubble(m) {\n var text = m && m.content != null ? String(m.content) : \"\";\n if (m && m.role === \"user\") return this.escapeHtml(text);\n return md.render(text || \"\");\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this10 = this;\n\n this.$nextTick(function () {\n var el = _this10.$refs.chatBody;\n if (el) el.scrollTop = el.scrollHeight;\n });\n },\n // 用户手动滚动时记录是否仍停留底部;上滚离开底部即暂停自动跟随\n onChatScroll: function onChatScroll() {\n var el = this.$refs.chatBody;\n if (!el) return;\n var nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;\n this.autoFollow = nearBottom;\n },\n // 仅当用户停留在底部时才自动滚到底,避免回答过程中打断用户向上查看\n followBottom: function followBottom() {\n if (!this.autoFollow) return;\n this.scrollBottom();\n },\n startDrag: function startDrag(e) {\n if (e.button !== 0) return;\n this.dragging = true;\n this.moved = false;\n this.tucked = false;\n this.startX = e.clientX;\n this.startY = e.clientY;\n this.startLeft = this.btnLeft;\n this.startBottom = this.btnBottom; // 拖动时防止文本/图片被选中导致跟手中断\n\n e.preventDefault();\n },\n onDrag: function onDrag(e) {\n if (!this.dragging) return;\n var dx = e.clientX - this.startX;\n var dy = e.clientY - this.startY;\n\n if (!this.moved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this.moved = true;\n }\n\n var box = this.getContainer();\n var maxW = box ? box.clientWidth - 46 : 9999;\n var maxH = box ? box.clientHeight - 44 : 9999;\n var left = Math.max(0, Math.min(this.startLeft + dx, Math.max(0, maxW)));\n var bottom = Math.max(8, Math.min(this.startBottom - dy, Math.max(8, maxH - 8))); // 直接写 DOM,图标即时跟手,不经过 Vue 重渲染\n\n var el = this.$refs.floatEl;\n\n if (el) {\n el.style.left = left + \"px\";\n el.style.bottom = bottom + \"px\";\n }\n\n if (this.panelVisible) {\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n if (!this.dragging) return;\n this.dragging = false;\n if (!this.moved) return; // 读取拖拽后的实际位置\n\n var el = this.$refs.floatEl;\n var left = el ? parseInt(el.style.left || \"0\", 10) : this.btnLeft;\n var bottom = el ? parseInt(el.style.bottom || \"0\", 10) : this.btnBottom;\n this.btnLeft = left;\n this.btnBottom = bottom; // 拖动过才吸附贴边;靠近右边时收成半角标\n\n var box = this.getContainer();\n if (!box) return;\n\n if (box.clientWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = box.clientWidth - 46;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.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");
241
+ eval("\n\nvar _interopRequireWildcard = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireWildcard */ \"./node_modules/@babel/runtime/helpers/interopRequireWildcard.js\").default;\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 _typeof2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _createForOfIteratorHelper2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/createForOfIteratorHelper */ \"./node_modules/@babel/runtime/helpers/createForOfIteratorHelper.js\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _aiAssistant = _interopRequireDefault(__webpack_require__(/*! ./ai-assistant.png */ \"./src/lib/components/approvalAi/ai-assistant.png\"));\n\nvar aiApi = _interopRequireWildcard(__webpack_require__(/*! ./apiApprovalAi */ \"./src/lib/components/approvalAi/apiApprovalAi.js\"));\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.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//\nvar md = new _markdownIt.default({\n html: false,\n linkify: true\n}); // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\nvar VALUE_NAME = {\n comFeePlan: \"公司费用计划\",\n comFeeApplication: \"公司费用\",\n comFeeAccept: \"公司费用验收\",\n comFeeCredit: \"公司费用挂账\",\n comFeePay: \"公司费用支付\",\n employeeHonor: \"员工荣誉\",\n employeeRegularization: \"员工转正\",\n employeeTurnover: \"离职\",\n employeeturnoverhandover: \"离职交接\",\n employeeChangestation: \"调岗\",\n employeeChangesalary: \"调薪\",\n overTimeApply: \"加班\",\n carApproval: \"车辆\",\n reimburse: \"出差报销\",\n leave: \"请假\",\n patch: \"补卡\",\n trip: \"出差\",\n out: \"外出\",\n lan: \"借款\",\n officialDocument: \"公文\",\n notice: \"公告\",\n qgbCompanyPlanSummery: \"公司费用预算汇总\",\n sealApproval: \"印章\"\n}; // 审批状态码 -> 中文(内置映射,替代宿主项目全局 $approvalStatus)\n\nvar STATUS_MAP = {\n \"1\": \"审批中\",\n \"-1\": \"审批中\",\n \"0\": \"已拒绝\",\n \"2\": \"已通过\",\n \"-3\": \"已通过\",\n \"4\": \"草稿\",\n \"3\": \"已通过\",\n \"5\": \"已完成\",\n \"6\": \"已撤销\"\n}; // 概览文案启发式字段匹配\n\nvar FIELD_HINTS = [{\n key: \"applicant\",\n labels: [\"申请人\", \"提交人\", \"经办人\"]\n}, {\n key: \"unit\",\n labels: [\"申请单位\", \"公司\", \"单位\"]\n}, {\n key: \"dept\",\n labels: [\"申请部门\", \"部门\"]\n}, {\n key: \"reason\",\n labels: [\"原因\", \"事由\", \"用途\", \"摘要\", \"标题\", \"说明\", \"内容\", \"目的\"]\n}, {\n key: \"amount\",\n labels: [\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]\n}];\n\nfunction normalizeLabel(label) {\n return String(label).trim().replace(/[::]\\s*$/g, \"\");\n}\n\nfunction resolveStatusText(status, override) {\n if (override) return override;\n if (!status && status !== 0) return \"待处理\";\n var mapped = STATUS_MAP[String(status)];\n return mapped || \"未知状态\";\n}\n\nvar msgSeq = 0;\nvar _default = {\n name: \"ApprovalAiAssistant\",\n props: {\n id: {\n type: String,\n default: \"\"\n },\n type: {\n type: String,\n default: \"\"\n },\n status: {\n type: String,\n default: \"0\"\n },\n index: {\n type: Number,\n default: 1\n },\n commentId: {\n type: String,\n default: \"\"\n },\n // ---- 以下为进包后新增的配置项,用于解耦宿主角色的上下文依赖 ----\n // AI 服务基础地址(示例 https://api.xxx.cn),未配置则走本地兜底\n baseUrl: {\n type: String,\n default: \"\"\n },\n // 当前用户信息(替代宿主 Vuex $store.state.user)\n userId: {\n type: String,\n default: \"\"\n },\n userName: {\n type: String,\n default: \"\"\n },\n // 审批状态文案覆盖(默认内置状态码映射;传入则优先使用)\n statusTextOverride: {\n type: String,\n default: \"\"\n },\n // 布局边界选择器(替代硬编码 .approval-content)\n containerSelector: {\n type: String,\n default: \".approval-content\"\n },\n // 详情采集根选择器(替代硬编码 .detail-root)\n detailRootSelector: {\n type: String,\n default: \".detail-root\"\n },\n // 机器人图标,不传时使用包内内置图标\n robotIconUrl: {\n type: String,\n default: \"\"\n },\n // H5 底部操作栏高度(px):作为兜底基准,用于把浮动按钮放在操作栏上方;PC 传 0 不受影响\n bottomBarHeight: {\n type: Number,\n default: 0\n },\n // H5 底部操作栏元素选择器:运行时测量该元素,把悬浮窗放在其正上方,避免遮挡(默认 .btn-wrapper)\n bottomBarSelector: {\n type: String,\n default: \".btn-wrapper\"\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n panelVisible: false,\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 24,\n tucked: false,\n panelBottom: 88,\n dragging: false,\n moved: false,\n isMobile: false,\n startX: 0,\n startY: 0,\n startLeft: 0,\n startBottom: 0\n };\n },\n computed: {\n iconUrl: function iconUrl() {\n return this.robotIconUrl || this.defaultIcon;\n },\n typeName: function typeName() {\n if (!this.type) return \"\";\n return VALUE_NAME[this.type] || this.type;\n },\n statusText: function statusText() {\n return resolveStatusText(this.status, this.statusTextOverride);\n },\n statusTone: function statusTone() {\n var t = this.statusText;\n if (t === \"已通过\" || t === \"已完成\") return \"ok\";\n if (t === \"已拒绝\" || t === \"已驳回\" || t === \"已退回\" || t === \"已终止\") return \"no\";\n return \"go\";\n }\n },\n created: function created() {// 注入 AI 服务地址\n },\n mounted: function mounted() {\n var _this = this;\n\n window.addEventListener(\"mousemove\", this.onDrag);\n window.addEventListener(\"mouseup\", this.endDrag);\n this.initMobile();\n this.$nextTick(function () {\n return _this.snapBtn();\n });\n },\n beforeDestroy: function beforeDestroy() {\n window.removeEventListener(\"mousemove\", this.onDrag);\n window.removeEventListener(\"mouseup\", this.endDrag);\n if (this.__mqUnlisten) this.__mqUnlisten();\n },\n watch: {\n // 切换审批单时重置对话上下文\n id: function id() {\n this.sessionId = \"\";\n this.messages = [];\n this.fields = [];\n this.rawDetail = null;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val\n });\n }\n },\n methods: {\n // 统一取鼠标/触摸的坐标点\n point: function point(e) {\n var t = e && e.touches && e.touches[0] || e && e.changedTouches && e.changedTouches[0];\n return t ? {\n clientX: t.clientX,\n clientY: t.clientY\n } : {\n clientX: e.clientX,\n clientY: e.clientY\n };\n },\n // 按钮距容器底部的最小基准:8px + H5 底部操作栏高度,保证按钮在操作栏上方\n baseBottom: function baseBottom() {\n return 8 + (Number(this.bottomBarHeight) || 0);\n },\n // H5 下测量底部操作栏元素,返回“视口底部到该元素顶部”的距离 + 安全边距,\n // 使悬浮窗固定在该元素正上方、不遮挡其展示;PC/找不到时回退到 baseBottom()\n barBottomOffset: function barBottomOffset() {\n if (!this.isMobile) return this.baseBottom();\n var sel = this.bottomBarSelector;\n\n if (sel) {\n var el = null;\n\n try {\n el = document.querySelector(sel);\n } catch (e) {\n el = null;\n }\n\n var rect = el && el.getBoundingClientRect();\n\n if (rect && rect.top >= 0 && rect.top <= window.innerHeight) {\n return window.innerHeight - rect.top + 12;\n }\n }\n\n return this.baseBottom();\n },\n // 是否 H5 窄屏(与 CSS media query 保持一致的 480px 边界)\n initMobile: function initMobile() {\n var _this2 = this;\n\n var mq = window.matchMedia && window.matchMedia(\"(max-width: 480px)\");\n\n var apply = function apply() {\n _this2.isMobile = !mq || mq.matches;\n };\n\n apply();\n if (mq && mq.addEventListener) mq.addEventListener(\"change\", apply);else if (mq && mq.addListener) mq.addListener(apply);\n\n this.__mqUnlisten = function () {\n if (mq && mq.removeEventListener) mq.removeEventListener(\"change\", apply);else if (mq && mq.removeListener) mq.removeListener(apply);\n };\n },\n getContainer: function getContainer() {\n // 普通模式:以配置的审批容器为边界;找不到时回退到父级包裹容器\n var selector = this.containerSelector;\n var el = selector ? this.$el.closest(selector) : null;\n return el || this.$el.parentElement;\n },\n getDetailRoot: function getDetailRoot() {\n var box = this.getContainer();\n if (!box) return null;\n var explicit = this.detailRootSelector ? box.querySelector(this.detailRootSelector) : null;\n if (explicit) return explicit; // 子应用/iframe 模式:容器内可能没有 detail-root 标记,\n // 回退到首个详情子节点(排除 AI 面板自身)\n\n var kids = Array.from(box.children).filter(function (c) {\n return !c.classList.contains(\"ai-assistant\");\n });\n return kids.length ? kids[0] : null;\n },\n togglePanel: function togglePanel() {\n var _this3 = this;\n\n // 拖拽后松手会触发一次 click,这里拦截,避免误展开/收起面板\n if (this.moved) {\n this.moved = false;\n return;\n }\n\n if (this.panelVisible) {\n this.closePanel();\n return;\n }\n\n this.panelVisible = true;\n this.$nextTick(function () {\n _this3.snapPanel();\n\n if (!_this3.messages.length) {\n _this3.greet();\n }\n\n _this3.scrollBottom();\n\n if (_this3.$refs.input) _this3.$refs.input.focus();\n });\n },\n closePanel: function closePanel() {\n this.panelVisible = false;\n },\n snapBtn: function snapBtn() {\n var box = this.getContainer();\n if (!box) return; // 默认贴右下角完整展开:右侧留 20px\n\n if (!this.tucked && !this.btnLeft) {\n this.btnLeft = Math.max(0, box.clientWidth - 20 - 46);\n } // 若按钮在可视范围外则修正\n\n\n this.btnLeft = Math.max(0, Math.min(this.btnLeft, box.clientWidth - 46));\n this.btnBottom = Math.max(this.barBottomOffset(), Math.min(this.btnBottom, box.clientHeight - 44 - 8));\n },\n snapPanel: function snapPanel() {\n var box = this.getContainer();\n var el = this.$el.querySelector(\".ai-panel\");\n if (!box || !el) return;\n var margin = 24; // 上下安全距离,避免遮挡\n\n var ph = el.clientHeight || 560;\n var desired = this.btnBottom + 62;\n this.panelBottom = Math.max(margin, Math.min(desired, box.clientHeight - ph - margin));\n },\n // 打开时 AI 先总结当前审批\n greet: function greet() {\n var _this4 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var summary, amount, res, base;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n _this4.collectDetail();\n\n summary = _this4.buildSummary();\n amount = _this4.findField([\"金额\", \"费用\", \"总计\", \"总金额\", \"合计\"]); // 已接入后端时,优先用 AI 生成的总结\n\n console.log(_this4.id, _this4.aiEnabled());\n\n if (!(_this4.aiEnabled() && _this4.id)) {\n _context.next = 9;\n break;\n }\n\n _context.next = 7;\n return aiApi.getSummary(_this4.id);\n\n case 7:\n res = _context.sent;\n\n if (res && res.data && res.data.summary) {\n summary = res.data.summary;\n }\n\n case 9:\n base = \"\\u4F60\\u597D\\uFF0C\\u6211\\u662F AI \\u5BA1\\u6279\\u52A9\\u624B\\uD83D\\uDC4B\\n\\n\\u5DF2\\u7ECF\\u4E3A\\u4F60\\u68B3\\u7406\\u4E86\\u5F53\\u524D\\u5BA1\\u6279\\u7684\\u6982\\u89C8\\uFF1A\\n\\n\".concat(summary); // const tail = this.fields.length\n // ? `\\n\\n这份单据的关键字段共 ${this.fields.length} 项${amount ? `,其中金额相关「${amount}」` : \"\"}。你可以继续问我,比如「这笔审批是做什么的」「申请人是谁」。`\n // : \"\\n\\n暂时未采集到该单据的明细字段。\"\n // this.push(\"assistant\", base + tail)\n\n _this4.push(\"assistant\", base);\n\n case 11:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n sendMessage: function sendMessage() {\n var _this5 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return;\n this.push(\"user\", text);\n this.inputText = \"\";\n this.sending = true;\n this.collectDetail(); // 直接走后端流式问答(已接入时);未接入则返回本地兜底\n\n var msg = null;\n\n if (this.aiEnabled() && this.id) {\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: \"\"\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk) {\n if (msg) {\n msg.content += chunk;\n\n _this5.followBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this5.fallbackReply(text);\n } else {\n _this5.push(\"assistant\", _this5.fallbackReply(text));\n }\n\n _this5.streaming = false;\n _this5.sending = false;\n\n _this5.followBottom();\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this6 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n payload = {\n context: {\n type: _this6.type,\n status: _this6.status,\n typeName: _this6.typeName,\n id: _this6.id,\n summary: _this6.buildSummary(),\n fields: _this6.fields,\n detailData: _this6.rawDetail\n },\n question: question\n };\n _context2.next = 3;\n return _this6.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context2.sent;\n return _context2.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this6.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 当前用户信息(来自 props,替代宿主 Vuex)\n userInfo: function userInfo() {\n return {\n no: this.userId || \"\",\n name: this.userName || \"\"\n };\n },\n // 创建/复用 AI 对话会话\n ensureSession: function ensureSession() {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n if (!_this7.sessionId) {\n _context3.next = 2;\n break;\n }\n\n return _context3.abrupt(\"return\", _this7.sessionId);\n\n case 2:\n u = _this7.userInfo();\n _context3.next = 5;\n return aiApi.createChatSession({\n approvalNo: _this7.id,\n userId: u.no,\n userName: u.name\n });\n\n case 5:\n res = _context3.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context3.next = 9;\n break;\n }\n\n _this7.sessionId = res.data.sessionId;\n return _context3.abrupt(\"return\", _this7.sessionId);\n\n case 9:\n return _context3.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this8 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (_this8.aiEnabled()) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 2:\n _context4.prev = 2;\n _context4.next = 5;\n return _this8.ensureSession();\n\n case 5:\n sessionId = _context4.sent;\n\n if (sessionId) {\n _context4.next = 8;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 8:\n text = \"\";\n failed = false;\n _context4.next = 12;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n userId: _this8.userInfo().no,\n userName: _this8.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\" || event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data);\n } else if (event === \"agent_result\") {\n text = data;\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 12:\n if (!failed) {\n _context4.next = 14;\n break;\n }\n\n return _context4.abrupt(\"return\", null);\n\n case 14:\n return _context4.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 17:\n _context4.prev = 17;\n _context4.t0 = _context4[\"catch\"](2);\n return _context4.abrupt(\"return\", null);\n\n case 20:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[2, 17]]);\n }))();\n },\n // 无法从数据中检索到时的兜底:诚实告知查询不到,不编造\n fallbackReply: function fallbackReply(question) {\n var q = String(question).trim();\n var list = '';\n return \"\\u6682\\u65F6\\u65E0\\u6CD5\\u67E5\\u8BE2\\u5230\\u4E0E\\u300C\".concat(q, \"\\u300D\\u5BF9\\u5E94\\u7684\\u4FE1\\u606F\\u3002\").concat(list);\n },\n findField: function findField(labels) {\n var _iterator = (0, _createForOfIteratorHelper2.default)(this.rawEntries()),\n _step;\n\n try {\n var _loop = function _loop() {\n var e = _step.value;\n if (labels.some(function (l) {\n return e.label.indexOf(l) > -1;\n })) return {\n v: e.value\n };\n };\n\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var _ret = _loop();\n\n if ((0, _typeof2.default)(_ret) === \"object\") return _ret.v;\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return \"\";\n },\n // 合并 DOM 采集字段 + 详情页完整原始业务对象,得到可检索的条目\n rawEntries: function rawEntries() {\n var _this9 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this9.stringify(value);\n\n if (!v) return;\n var k = \"\".concat(label, \"__\").concat(v);\n if (seen.has(k)) return;\n seen.add(k);\n list.push({\n label: label,\n value: v\n });\n };\n\n var _iterator2 = (0, _createForOfIteratorHelper2.default)(this.fields),\n _step2;\n\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var f = _step2.value;\n pushEntry(f.label, f.value);\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n\n var raw = this.rawDetail && this.rawDetail.data;\n\n if (raw && (0, _typeof2.default)(raw) === \"object\") {\n for (var _i = 0, _Object$keys = Object.keys(raw); _i < _Object$keys.length; _i++) {\n var key = _Object$keys[_i];\n pushEntry(key, raw[key]);\n }\n }\n\n return list;\n },\n stringify: function stringify(v) {\n if (v === null || v === undefined) return \"\";\n\n if ((0, _typeof2.default)(v) === \"object\") {\n try {\n return JSON.stringify(v);\n } catch (e) {\n return \"\";\n }\n }\n\n return String(v);\n },\n // 通用采集详情页数据:兼容 el-form-item / el-descriptions / 自定义 span标签:值 / 表格,\n // 并尽量读取详情页组件的原始业务对象(info/form) 作为完整数据\n collectDetail: function collectDetail() {\n var root = this.getDetailRoot();\n var seen = {};\n\n var push = function push(label, value) {\n label = normalizeLabel(label);\n var v = String(value === null || value === undefined ? \"\" : value).replace(/\\s+/g, \" \").trim();\n if (!label || !v || v === \"暂无\" || label === \"图片\" || label === \"操作\") return;\n if (seen[label] !== undefined) return;\n seen[label] = v;\n };\n\n if (root) {\n // 1) el-form-item\n root.querySelectorAll(\".el-form-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-form-item__label\");\n var contentEl = item.querySelector(\".el-form-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 2) el-descriptions 描述项\n\n root.querySelectorAll(\".el-descriptions-item\").forEach(function (item) {\n var labelEl = item.querySelector(\".el-descriptions-item__label\");\n var contentEl = item.querySelector(\".el-descriptions-item__content\");\n if (labelEl && contentEl) push(labelEl.textContent, contentEl.textContent);\n }); // 3) 自定义 label:value(span 以「:」/「:」结尾,值在大括号内剩余文本)\n\n root.querySelectorAll(\"span\").forEach(function (span) {\n var txt = (span.textContent || \"\").trim();\n var m = txt.match(/^(.+?)([::])$/);\n if (!m) return;\n var parent = span.parentElement;\n if (!parent) return;\n var value = (parent.textContent || \"\").replace(txt, \"\").replace(/\\s+/g, \" \").trim();\n push(m[1], value);\n }); // 4) 表格:表头 -> 逐行数据\n\n root.querySelectorAll(\"table\").forEach(function (table) {\n var rows = table.querySelectorAll(\"tr\");\n var headers = [];\n\n if (rows.length) {\n headers = Array.from(rows[0].querySelectorAll(\"th,td\")).map(function (c) {\n return normalizeLabel(c.textContent);\n });\n }\n\n rows.forEach(function (tr, idx) {\n if (idx === 0) return;\n var cells = Array.from(tr.querySelectorAll(\"th,td\")).map(function (c) {\n return (c.textContent || \"\").replace(/\\s+/g, \" \").trim();\n });\n if (!cells.length) return;\n\n if (headers.length) {\n headers.forEach(function (h, j) {\n return h && cells[j] !== undefined && push(h, cells[j]);\n });\n } else {\n cells.forEach(function (c, j) {\n return c && push(\"\\u7B2C\".concat(idx, \"\\u884C\\u5B57\\u6BB5\").concat(j + 1), c);\n });\n }\n });\n });\n } // 5) 读取详情组件的原始业务对象,作为完整结构化数据补全\n\n\n var raw = this.readRawDetail();\n this.rawDetail = raw;\n if (raw) this.flattenTo(seen, raw);\n this.fields = Object.keys(seen).map(function (label) {\n return {\n label: label,\n value: seen[label]\n };\n }).slice(0, 40);\n },\n // 寻找详情页组件的业务数据对象(info / form 等)\n readRawDetail: function readRawDetail() {\n var _this10 = this;\n\n var root = this.getDetailRoot();\n if (!root) return null;\n\n var _iterator3 = (0, _createForOfIteratorHelper2.default)(root.querySelectorAll(\"*\")),\n _step3;\n\n try {\n for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {\n var el = _step3.value;\n var vm = el.__vue__;\n if (!vm || !vm.$data || (0, _typeof2.default)(vm.$data) !== \"object\") continue;\n var d = vm.$data;\n\n var _loop2 = function _loop2() {\n var key = _arr[_i2];\n var obj = d[key];\n\n if (obj && (0, _typeof2.default)(obj) === \"object\" && !Array.isArray(obj)) {\n var keys = Object.keys(obj).filter(function (k) {\n return obj[k] !== \"\" && obj[k] !== null && obj[k] !== undefined;\n });\n\n if (keys.length) {\n return {\n v: {\n key: key,\n data: _this10.jsonSafe(obj)\n }\n };\n }\n }\n };\n\n for (var _i2 = 0, _arr = [\"info\", \"form\"]; _i2 < _arr.length; _i2++) {\n var _ret2 = _loop2();\n\n if ((0, _typeof2.default)(_ret2) === \"object\") return _ret2.v;\n }\n }\n } catch (err) {\n _iterator3.e(err);\n } finally {\n _iterator3.f();\n }\n\n return null;\n },\n jsonSafe: function jsonSafe(obj) {\n try {\n return JSON.parse(JSON.stringify(obj, function (k, v) {\n return typeof v === \"function\" ? undefined : v;\n }));\n } catch (e) {\n return obj;\n }\n },\n // 把原始业务对象摊平成 label:value,补进已采集集合\n flattenTo: function flattenTo(seen, raw) {\n var walk = function walk(obj, path) {\n if (obj === null || obj === undefined) return;\n\n if (Array.isArray(obj)) {\n obj.forEach(function (item, i) {\n return walk(item, path ? \"\".concat(path, \"[\").concat(i, \"]\") : \"[\".concat(i, \"]\"));\n });\n return;\n }\n\n if ((0, _typeof2.default)(obj) === \"object\") {\n Object.keys(obj).forEach(function (k) {\n return walk(obj[k], path ? \"\".concat(path, \".\").concat(k) : k);\n });\n return;\n }\n\n var label = path || \"\";\n var v = String(obj).replace(/\\s+/g, \" \").trim();\n if (label && v && seen[label] === undefined) seen[label] = v;\n };\n\n walk(raw.data, raw.key);\n },\n // 依据采集字段生成“当前审批是做什么的”概览(启发式)\n buildSummary: function buildSummary() {\n var hits = {};\n FIELD_HINTS.forEach(function (hint) {\n hits[hint.key] = \"\";\n });\n this.fields.forEach(function (f) {\n var _iterator4 = (0, _createForOfIteratorHelper2.default)(FIELD_HINTS),\n _step4;\n\n try {\n for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {\n var hint = _step4.value;\n if (hits[hint.key]) continue;\n\n if (hint.labels.some(function (l) {\n return f.label.indexOf(l) > -1;\n })) {\n hits[hint.key] = f.value;\n break;\n }\n }\n } catch (err) {\n _iterator4.e(err);\n } finally {\n _iterator4.f();\n }\n });\n var name = this.typeName || \"该单据\";\n var parts = [];\n if (hits.applicant) parts.push(\"\\u7533\\u8BF7\\u4EBA\\u300C\".concat(hits.applicant, \"\\u300D\"));\n if (hits.unit) parts.push(\"\\u6240\\u5C5E\\u5355\\u4F4D\\u300C\".concat(hits.unit, \"\\u300D\"));\n var verb = \"\".concat(name === \"该单据\" ? \"该审批\" : \"\\u8BE5\\u300C\".concat(name, \"\\u300D\\u5BA1\\u6279\"));\n var summary = \"\".concat(verb, \"\\u662F\\u4E00\\u7B14\\u4E1A\\u52A1\\u5BA1\\u6279\");\n if (parts.length) summary += \"\\uFF0C\\u7531\".concat(parts.join(\",\"), \"\\u53D1\\u8D77\");\n if (hits.reason) summary += \"\\uFF0C\\u4E3B\\u8981\\u4E8B\\u7531\\uFF1A\".concat(hits.reason);\n if (hits.amount) summary += \"\\uFF0C\\u6D89\\u53CA\\u91D1\\u989D\\u300C\".concat(hits.amount, \"\\u300D\");\n summary += \"\\u3002\\u5F53\\u524D\\u5BA1\\u6279\\u72B6\\u6001\\u4E3A\\u300C\".concat(this.statusText, \"\\u300D\\u3002\");\n return summary;\n },\n push: function push(role, content) {\n this.messages.push({\n id: ++msgSeq,\n role: role,\n content: content\n });\n },\n // 气泡渲染:助手消息转 Markdown,用户消息按纯文本处理(转义防 XSS)\n renderBubble: function renderBubble(m) {\n var text = m && m.content != null ? String(m.content) : \"\";\n if (m && m.role === \"user\") return this.escapeHtml(text);\n return md.render(text || \"\");\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this11 = this;\n\n this.$nextTick(function () {\n var el = _this11.$refs.chatBody;\n if (el) el.scrollTop = el.scrollHeight;\n });\n },\n // 用户手动滚动时记录是否仍停留底部;上滚离开底部即暂停自动跟随\n onChatScroll: function onChatScroll() {\n var el = this.$refs.chatBody;\n if (!el) return;\n var nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;\n this.autoFollow = nearBottom;\n },\n // 仅当用户停留在底部时才自动滚到底,避免回答过程中打断用户向上查看\n followBottom: function followBottom() {\n if (!this.autoFollow) return;\n this.scrollBottom();\n },\n startDrag: function startDrag(e) {\n if (e.button !== undefined && e.button !== 0) return;\n var point = this.point(e);\n if (!point) return;\n this.dragging = true;\n this.moved = false;\n this.tucked = false;\n this.startX = point.clientX;\n this.startY = point.clientY;\n this.startLeft = this.btnLeft;\n this.startBottom = this.btnBottom; // 拖动时防止文本/图片被选中导致跟手中断;\n // 仅鼠标 preventDefault,触摸靠 touch-action:none,避免吞掉点击(打开/收起面板)\n\n if (e.type === \"mousedown\") e.preventDefault();\n },\n onDrag: function onDrag(e) {\n if (!this.dragging) return;\n var point = this.point(e);\n if (!point) return;\n var dx = point.clientX - this.startX;\n var dy = point.clientY - this.startY;\n\n if (!this.moved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this.moved = true;\n }\n\n var box = this.getContainer();\n var maxW = box ? box.clientWidth - 46 : 9999;\n var maxH = box ? box.clientHeight - 44 : 9999;\n var minBottom = this.barBottomOffset();\n var left = Math.max(0, Math.min(this.startLeft + dx, Math.max(0, maxW)));\n var bottom = Math.max(minBottom, Math.min(this.startBottom - dy, Math.max(minBottom, maxH - 8))); // 直接写 DOM,图标即时跟手,不经过 Vue 重渲染\n\n var el = this.$refs.floatEl;\n\n if (el) {\n el.style.left = left + \"px\";\n el.style.bottom = bottom + \"px\";\n }\n\n if (this.panelVisible) {\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n if (!this.dragging) return;\n this.dragging = false;\n if (!this.moved) return; // 读取拖拽后的实际位置\n\n var el = this.$refs.floatEl;\n var left = el ? parseInt(el.style.left || \"0\", 10) : this.btnLeft;\n var bottom = el ? parseInt(el.style.bottom || \"0\", 10) : this.btnBottom;\n this.btnLeft = left;\n this.btnBottom = bottom; // 拖动过才吸附贴边;靠近右边时收成半角标\n\n var box = this.getContainer();\n if (!box) return;\n\n if (box.clientWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = box.clientWidth - 46;\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.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");
242
242
 
243
243
  /***/ }),
244
244
 
245
- /***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"09ea9ef8-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&":
245
+ /***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"02165064-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&":
246
246
  /*!*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
247
- !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"09ea9ef8-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true& ***!
247
+ !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"02165064-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true& ***!
248
248
  \*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
249
249
  /*! exports provided: render, staticRenderFns */
250
250
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
251
251
 
252
252
  "use strict";
253
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"ai-assistant\" },\n [\n _c(\"transition\", { attrs: { name: \"ai-fade\" } }, [\n _vm.panelVisible\n ? _c(\n \"div\",\n {\n staticClass: \"ai-panel\",\n style: { bottom: _vm.panelBottom + \"px\" },\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"ai-panel__header\" }, [\n _c(\"span\", { staticClass: \"ai-panel__icon\" }, [\n _c(\"img\", { attrs: { src: _vm.iconUrl, alt: \"AI\" } })\n ]),\n _c(\"span\", { staticClass: \"ai-panel__title\" }, [\n _vm._v(\"AI 审批助手\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"ai-panel__close\",\n on: { click: _vm.closePanel }\n },\n [_vm._v(\"✕\")]\n )\n ]),\n _c(\n \"div\",\n {\n ref: \"chatBody\",\n staticClass: \"ai-chat\",\n on: {\n \"&scroll\": function($event) {\n return _vm.onChatScroll.apply(null, arguments)\n }\n }\n },\n [\n _vm._l(_vm.messages, function(m) {\n return [\n _c(\n \"div\",\n {\n key: m.id,\n staticClass: \"msg\",\n class: \"msg--\" + m.role\n },\n [\n m.role === \"assistant\"\n ? _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ])\n : _vm._e(),\n _c(\"div\", {\n staticClass: \"msg__bubble\",\n domProps: {\n innerHTML: _vm._s(_vm.renderBubble(m))\n }\n })\n ]\n )\n ]\n }),\n _vm.sending && !_vm.streaming\n ? _c(\"div\", { staticClass: \"msg msg--assistant\" }, [\n _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]),\n _c(\n \"div\",\n { staticClass: \"msg__bubble msg__bubble--typing\" },\n [_c(\"span\"), _c(\"span\"), _c(\"span\")]\n )\n ])\n : _vm._e()\n ],\n 2\n ),\n _c(\"div\", { staticClass: \"ai-input\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"input\",\n staticClass: \"ai-input__box\",\n attrs: { rows: \"1\", placeholder: \"输入你的问题\" },\n domProps: { value: _vm.inputText },\n on: {\n keydown: function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.sendMessage.apply(null, arguments)\n },\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n }\n }\n }),\n _c(\n \"button\",\n {\n staticClass: \"ai-input__send\",\n attrs: { disabled: _vm.sending || !_vm.inputText.trim() },\n on: { click: _vm.sendMessage }\n },\n [_vm._v(\"发送\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]),\n _c(\n \"div\",\n {\n ref: \"floatEl\",\n staticClass: \"ai-float\",\n class: {\n tucked: _vm.tucked,\n \"is-open\": _vm.panelVisible,\n dragging: _vm.dragging\n },\n style: { left: _vm.btnLeft + \"px\", bottom: _vm.btnBottom + \"px\" },\n on: { mousedown: _vm.startDrag }\n },\n [\n _c(\n \"button\",\n {\n staticClass: \"ai-float__btn\",\n class: { \"is-active\": _vm.panelVisible },\n attrs: {\n title: _vm.panelVisible ? \"收起 AI 助手\" : \"打开 AI 助手\"\n },\n on: { click: _vm.togglePanel, mouseup: _vm.stopDrag }\n },\n [\n _c(\"img\", {\n staticClass: \"ai-float__img\",\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]\n )\n ]\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2209ea9ef8-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
253
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"ai-assistant\" },\n [\n _vm.panelVisible && _vm.isMobile\n ? _c(\"div\", { staticClass: \"ai-mask\", on: { click: _vm.closePanel } })\n : _vm._e(),\n _c(\"transition\", { attrs: { name: \"ai-fade\" } }, [\n _vm.panelVisible\n ? _c(\n \"div\",\n {\n staticClass: \"ai-panel\",\n style: { bottom: _vm.panelBottom + \"px\" },\n on: {\n click: function($event) {\n $event.stopPropagation()\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"ai-panel__header\" }, [\n _c(\"span\", { staticClass: \"ai-panel__icon\" }, [\n _c(\"img\", { attrs: { src: _vm.iconUrl, alt: \"AI\" } })\n ]),\n _c(\"span\", { staticClass: \"ai-panel__title\" }, [\n _vm._v(\"AI 审批助手\")\n ]),\n _c(\n \"span\",\n {\n staticClass: \"ai-panel__close\",\n on: { click: _vm.closePanel }\n },\n [_vm._v(\"✕\")]\n )\n ]),\n _c(\n \"div\",\n {\n ref: \"chatBody\",\n staticClass: \"ai-chat\",\n on: {\n \"&scroll\": function($event) {\n return _vm.onChatScroll.apply(null, arguments)\n }\n }\n },\n [\n _vm._l(_vm.messages, function(m) {\n return [\n _c(\n \"div\",\n {\n key: m.id,\n staticClass: \"msg\",\n class: \"msg--\" + m.role\n },\n [\n m.role === \"assistant\"\n ? _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ])\n : _vm._e(),\n _c(\"div\", {\n staticClass: \"msg__bubble\",\n domProps: {\n innerHTML: _vm._s(_vm.renderBubble(m))\n }\n })\n ]\n )\n ]\n }),\n _vm.sending && !_vm.streaming\n ? _c(\"div\", { staticClass: \"msg msg--assistant\" }, [\n _c(\"div\", { staticClass: \"msg__avatar\" }, [\n _c(\"img\", {\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]),\n _c(\n \"div\",\n { staticClass: \"msg__bubble msg__bubble--typing\" },\n [_c(\"span\"), _c(\"span\"), _c(\"span\")]\n )\n ])\n : _vm._e()\n ],\n 2\n ),\n _c(\"div\", { staticClass: \"ai-input\" }, [\n _c(\"textarea\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.inputText,\n expression: \"inputText\"\n }\n ],\n ref: \"input\",\n staticClass: \"ai-input__box\",\n attrs: { rows: \"1\", placeholder: \"输入你的问题\" },\n domProps: { value: _vm.inputText },\n on: {\n keydown: function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k(\n $event.keyCode,\n \"enter\",\n 13,\n $event.key,\n \"Enter\"\n )\n ) {\n return null\n }\n if (\n $event.ctrlKey ||\n $event.shiftKey ||\n $event.altKey ||\n $event.metaKey\n ) {\n return null\n }\n $event.preventDefault()\n return _vm.sendMessage.apply(null, arguments)\n },\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.inputText = $event.target.value\n }\n }\n }),\n _c(\n \"button\",\n {\n staticClass: \"ai-input__send\",\n attrs: { disabled: _vm.sending || !_vm.inputText.trim() },\n on: { click: _vm.sendMessage }\n },\n [_vm._v(\"发送\")]\n )\n ])\n ]\n )\n : _vm._e()\n ]),\n _c(\n \"div\",\n {\n ref: \"floatEl\",\n staticClass: \"ai-float\",\n class: {\n tucked: _vm.tucked,\n \"is-open\": _vm.panelVisible,\n dragging: _vm.dragging\n },\n style: { left: _vm.btnLeft + \"px\", bottom: _vm.btnBottom + \"px\" },\n on: {\n mousedown: _vm.startDrag,\n touchstart: _vm.startDrag,\n touchmove: _vm.onDrag,\n touchend: _vm.endDrag,\n touchcancel: _vm.endDrag\n }\n },\n [\n _c(\n \"button\",\n {\n staticClass: \"ai-float__btn\",\n class: { \"is-active\": _vm.panelVisible },\n attrs: {\n title: _vm.panelVisible ? \"收起 AI 助手\" : \"打开 AI 助手\"\n },\n on: { click: _vm.togglePanel, mouseup: _vm.stopDrag }\n },\n [\n _c(\"img\", {\n staticClass: \"ai-float__img\",\n attrs: { src: _vm.iconUrl, alt: \"AI\" }\n })\n ]\n )\n ]\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2202165064-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
254
254
 
255
255
  /***/ }),
256
256
 
@@ -261,7 +261,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
261
261
  /*! no static exports found */
262
262
  /***/ (function(module, exports, __webpack_require__) {
263
263
 
264
- eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"@charset \\\"UTF-8\\\";\\n.ai-assistant[data-v-5d775cd8] {\\n position: absolute;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n pointer-events: none;\\n z-index: 20;\\n}\\n.ai-float[data-v-5d775cd8] {\\n position: absolute;\\n pointer-events: auto;\\n z-index: 21;\\n transition: transform 0.25s ease;\\n}\\n.ai-float.dragging[data-v-5d775cd8] {\\n transition: none;\\n}\\n.ai-float.tucked[data-v-5d775cd8] {\\n transform: translateX(50%);\\n}\\n.ai-float.tucked[data-v-5d775cd8]:hover, .ai-float.tucked.is-open[data-v-5d775cd8] {\\n transform: translateX(0);\\n}\\n.ai-float__btn[data-v-5d775cd8] {\\n width: 46px;\\n height: 46px;\\n border-radius: 50%;\\n border: 1px solid #e4e7ed;\\n background: #fff;\\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n cursor: pointer;\\n color: #3067d9;\\n transition: box-shadow 0.2s, transform 0.2s;\\n}\\n.ai-float__btn[data-v-5d775cd8]:hover {\\n box-shadow: 0 4px 16px rgba(48, 103, 217, 0.3);\\n}\\n.ai-float__btn.is-active[data-v-5d775cd8] {\\n background: #fff;\\n color: #3067d9;\\n transform: none;\\n}\\n.ai-float__btn .ai-float__img[data-v-5d775cd8] {\\n width: 28px;\\n height: 28px;\\n border-radius: 50%;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n position: absolute;\\n right: 74px;\\n width: 384px;\\n height: 560px; /* 默认高度 */\\n max-height: calc(100% - 48px); /* 上下各留 24px 安全距离,容器不够高时自动压缩 */\\n max-width: calc(100% - 98px); /* 右侧留按钮位(74px)+左侧 24px 安全,窄容器不溢出遮挡 */\\n background: #fff;\\n border: 1px solid #e4e7ed;\\n border-radius: 10px;\\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);\\n pointer-events: auto;\\n z-index: 22;\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n}\\n.ai-panel__header[data-v-5d775cd8] {\\n height: 46px;\\n padding: 0 12px;\\n display: flex;\\n align-items: center;\\n background: #fafbfc;\\n border-bottom: 1px solid #f0f2f5;\\n gap: 8px;\\n}\\n.ai-panel__icon[data-v-5d775cd8] {\\n color: #3067d9;\\n display: flex;\\n}\\n.ai-panel__icon img[data-v-5d775cd8] {\\n width: 22px;\\n height: 22px;\\n border-radius: 50%;\\n}\\n.ai-panel__title[data-v-5d775cd8] {\\n font-size: 15px;\\n font-weight: 600;\\n color: #1f2329;\\n}\\n.ai-panel__status[data-v-5d775cd8] {\\n font-size: 12px;\\n padding: 1px 8px;\\n border-radius: 10px;\\n}\\n.ai-panel__status.status-ok[data-v-5d775cd8] {\\n color: #1a7f37;\\n background: #e6f4ea;\\n}\\n.ai-panel__status.status-no[data-v-5d775cd8] {\\n color: #d83931;\\n background: #fdeeee;\\n}\\n.ai-panel__status.status-go[data-v-5d775cd8] {\\n color: #3067d9;\\n background: #e8efff;\\n}\\n.ai-panel__close[data-v-5d775cd8] {\\n margin-left: auto;\\n cursor: pointer;\\n color: #86909c;\\n font-size: 16px;\\n line-height: 1;\\n padding: 0 2px;\\n}\\n.ai-panel__close[data-v-5d775cd8]:hover {\\n color: #1f2329;\\n}\\n.ai-chat[data-v-5d775cd8] {\\n flex: 1;\\n min-height: 120px;\\n padding: 14px;\\n overflow-y: auto;\\n background: #fff;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar {\\n width: 6px;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar-thumb {\\n background: #e4e7ed;\\n border-radius: 3px;\\n}\\n.msg[data-v-5d775cd8] {\\n display: flex;\\n margin-bottom: 14px;\\n}\\n.msg--assistant[data-v-5d775cd8] {\\n align-items: flex-start;\\n}\\n.msg--user[data-v-5d775cd8] {\\n justify-content: flex-end;\\n}\\n.msg__avatar[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n width: 30px;\\n height: 30px;\\n margin-right: 8px;\\n border-radius: 50%;\\n background: #e8efff;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\\n.msg__avatar img[data-v-5d775cd8] {\\n width: 30px;\\n height: 30px;\\n border-radius: 50%;\\n}\\n.msg__bubble[data-v-5d775cd8] {\\n max-width: 76%;\\n padding: 9px 12px;\\n border-radius: 10px;\\n font-size: 13px;\\n line-height: 1.7;\\n word-break: break-word;\\n color: #4e5969;\\n background: #f5f6f7;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n.msg__bubble h2[data-v-5d775cd8],\\n.msg__bubble h3[data-v-5d775cd8],\\n.msg__bubble h4[data-v-5d775cd8],\\n.msg__bubble h5[data-v-5d775cd8],\\n.msg__bubble h6[data-v-5d775cd8] {\\n font-size: 14px;\\n font-weight: 600;\\n color: #1f2329;\\n margin: 8px 0 4px;\\n}\\n.msg__bubble h1[data-v-5d775cd8]:first-child,\\n.msg__bubble h2[data-v-5d775cd8]:first-child,\\n.msg__bubble h3[data-v-5d775cd8]:first-child,\\n.msg__bubble h4[data-v-5d775cd8]:first-child,\\n.msg__bubble h5[data-v-5d775cd8]:first-child,\\n.msg__bubble h6[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble p[data-v-5d775cd8] {\\n margin: 4px 0;\\n}\\n.msg__bubble p[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble ul[data-v-5d775cd8],\\n.msg__bubble ol[data-v-5d775cd8] {\\n margin: 4px 0;\\n padding-left: 0;\\n list-style: none;\\n}\\n.msg__bubble li[data-v-5d775cd8] {\\n margin: 2px 0;\\n}\\n.msg__bubble strong[data-v-5d775cd8] {\\n color: #1f2329;\\n font-weight: 600;\\n}\\n.msg__bubble a[data-v-5d775cd8] {\\n color: #3067d9;\\n text-decoration: none;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n background: #f0f2f5;\\n color: #d83931;\\n padding: 1px 4px;\\n border-radius: 3px;\\n font-size: 12px;\\n}\\n.msg__bubble pre[data-v-5d775cd8] {\\n background: #f6f7f8;\\n padding: 8px;\\n border-radius: 6px;\\n overflow-x: auto;\\n margin: 6px 0;\\n}\\n.msg__bubble pre code[data-v-5d775cd8] {\\n background: none;\\n color: #1f2329;\\n padding: 0;\\n}\\n.msg--user .msg__bubble[data-v-5d775cd8] {\\n color: #fff;\\n background: #3067d9;\\n}\\n.msg__bubble--typing[data-v-5d775cd8] {\\n display: flex;\\n gap: 4px;\\n padding: 12px;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8] {\\n width: 6px;\\n height: 6px;\\n border-radius: 50%;\\n background: #86909c;\\n -webkit-animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(2) {\\n -webkit-animation-delay: 0.2s;\\n animation-delay: 0.2s;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(3) {\\n -webkit-animation-delay: 0.4s;\\n animation-delay: 0.4s;\\n}\\n@-webkit-keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n@keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n.ai-quick[data-v-5d775cd8] {\\n display: flex;\\n flex-wrap: wrap;\\n gap: 6px;\\n padding: 0 14px 10px;\\n background: #fff;\\n}\\n.ai-quick__item[data-v-5d775cd8] {\\n border: 1px solid #dce3f0;\\n background: #f7f9ff;\\n color: #3067d9;\\n font-size: 12px;\\n padding: 4px 10px;\\n border-radius: 14px;\\n cursor: pointer;\\n}\\n.ai-quick__item[data-v-5d775cd8]:hover {\\n border-color: #3067d9;\\n background: #eef3ff;\\n}\\n.ai-input[data-v-5d775cd8] {\\n display: flex;\\n align-items: flex-end;\\n gap: 8px;\\n padding: 10px 12px;\\n border-top: 1px solid #f0f2f5;\\n background: #fafbfc;\\n}\\n.ai-input__box[data-v-5d775cd8] {\\n flex: 1;\\n resize: none;\\n border: 1px solid #e4e7ed;\\n border-radius: 6px;\\n padding: 7px 10px;\\n font-size: 13px;\\n line-height: 1.5;\\n font-family: inherit;\\n color: #1f2329;\\n background: #fff;\\n outline: none;\\n max-height: 96px;\\n}\\n.ai-input__box[data-v-5d775cd8]:focus {\\n border-color: #3067d9;\\n}\\n.ai-input__send[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n border: none;\\n background: #3067d9;\\n color: #fff;\\n font-size: 13px;\\n padding: 7px 16px;\\n border-radius: 6px;\\n cursor: pointer;\\n}\\n.ai-input__send[data-v-5d775cd8]:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n.ai-fade-enter-active[data-v-5d775cd8],\\n.ai-fade-leave-active[data-v-5d775cd8] {\\n transition: opacity 0.2s, transform 0.2s;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n.ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 0;\\n transform: translateY(10px);\\n}\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
264
+ eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"@charset \\\"UTF-8\\\";\\n.ai-assistant[data-v-5d775cd8] {\\n position: absolute;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n pointer-events: none;\\n z-index: 20;\\n}\\n.ai-float[data-v-5d775cd8] {\\n position: absolute;\\n pointer-events: auto;\\n z-index: 21;\\n transition: transform 0.25s ease;\\n touch-action: none; /* 触摸拖拽时不触发页面滚动/手势,保证跟手 */\\n}\\n.ai-float.dragging[data-v-5d775cd8] {\\n transition: none;\\n}\\n.ai-float.tucked[data-v-5d775cd8] {\\n transform: translateX(50%);\\n}\\n.ai-float.tucked[data-v-5d775cd8]:hover, .ai-float.tucked.is-open[data-v-5d775cd8] {\\n transform: translateX(0);\\n}\\n.ai-float__btn[data-v-5d775cd8] {\\n width: 46px;\\n height: 46px;\\n border-radius: 50%;\\n border: 1px solid #e4e7ed;\\n background: #fff;\\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n cursor: pointer;\\n color: #3067d9;\\n transition: box-shadow 0.2s, transform 0.2s;\\n}\\n.ai-float__btn[data-v-5d775cd8]:hover {\\n box-shadow: 0 4px 16px rgba(48, 103, 217, 0.3);\\n}\\n.ai-float__btn.is-active[data-v-5d775cd8] {\\n background: #fff;\\n color: #3067d9;\\n transform: none;\\n}\\n.ai-float__btn .ai-float__img[data-v-5d775cd8] {\\n width: 28px;\\n height: 28px;\\n border-radius: 50%;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n position: absolute;\\n right: 74px;\\n width: 384px;\\n height: 560px; /* 默认高度 */\\n max-height: calc(100% - 48px); /* 上下各留 24px 安全距离,容器不够高时自动压缩 */\\n max-width: calc(100% - 98px); /* 右侧留按钮位(74px)+左侧 24px 安全,窄容器不溢出遮挡 */\\n background: #fff;\\n border: 1px solid #e4e7ed;\\n border-radius: 10px;\\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);\\n pointer-events: auto;\\n z-index: 22;\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n}\\n.ai-panel__header[data-v-5d775cd8] {\\n height: 46px;\\n padding: 0 12px;\\n display: flex;\\n align-items: center;\\n background: #fafbfc;\\n border-bottom: 1px solid #f0f2f5;\\n gap: 8px;\\n}\\n.ai-panel__icon[data-v-5d775cd8] {\\n color: #3067d9;\\n display: flex;\\n}\\n.ai-panel__icon img[data-v-5d775cd8] {\\n width: 22px;\\n height: 22px;\\n border-radius: 50%;\\n}\\n.ai-panel__title[data-v-5d775cd8] {\\n font-size: 15px;\\n font-weight: 600;\\n color: #1f2329;\\n}\\n.ai-panel__status[data-v-5d775cd8] {\\n font-size: 12px;\\n padding: 1px 8px;\\n border-radius: 10px;\\n}\\n.ai-panel__status.status-ok[data-v-5d775cd8] {\\n color: #1a7f37;\\n background: #e6f4ea;\\n}\\n.ai-panel__status.status-no[data-v-5d775cd8] {\\n color: #d83931;\\n background: #fdeeee;\\n}\\n.ai-panel__status.status-go[data-v-5d775cd8] {\\n color: #3067d9;\\n background: #e8efff;\\n}\\n.ai-panel__close[data-v-5d775cd8] {\\n margin-left: auto;\\n cursor: pointer;\\n color: #86909c;\\n font-size: 16px;\\n line-height: 1;\\n padding: 0 2px;\\n}\\n.ai-panel__close[data-v-5d775cd8]:hover {\\n color: #1f2329;\\n}\\n.ai-chat[data-v-5d775cd8] {\\n flex: 1;\\n min-height: 120px;\\n padding: 14px;\\n overflow-y: auto;\\n background: #fff;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar {\\n width: 6px;\\n}\\n.ai-chat[data-v-5d775cd8]::-webkit-scrollbar-thumb {\\n background: #e4e7ed;\\n border-radius: 3px;\\n}\\n.msg[data-v-5d775cd8] {\\n display: flex;\\n margin-bottom: 14px;\\n}\\n.msg--assistant[data-v-5d775cd8] {\\n align-items: flex-start;\\n}\\n.msg--user[data-v-5d775cd8] {\\n justify-content: flex-end;\\n}\\n.msg__avatar[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n width: 30px;\\n height: 30px;\\n margin-right: 8px;\\n border-radius: 50%;\\n background: #e8efff;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\\n.msg__avatar img[data-v-5d775cd8] {\\n width: 30px;\\n height: 30px;\\n border-radius: 50%;\\n}\\n.msg__bubble[data-v-5d775cd8] {\\n max-width: 76%;\\n padding: 9px 12px;\\n border-radius: 10px;\\n font-size: 13px;\\n line-height: 1.7;\\n word-break: break-word;\\n color: #4e5969;\\n background: #f5f6f7;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n.msg__bubble h2[data-v-5d775cd8],\\n.msg__bubble h3[data-v-5d775cd8],\\n.msg__bubble h4[data-v-5d775cd8],\\n.msg__bubble h5[data-v-5d775cd8],\\n.msg__bubble h6[data-v-5d775cd8] {\\n font-size: 14px;\\n font-weight: 600;\\n color: #1f2329;\\n margin: 8px 0 4px;\\n}\\n.msg__bubble h1[data-v-5d775cd8]:first-child,\\n.msg__bubble h2[data-v-5d775cd8]:first-child,\\n.msg__bubble h3[data-v-5d775cd8]:first-child,\\n.msg__bubble h4[data-v-5d775cd8]:first-child,\\n.msg__bubble h5[data-v-5d775cd8]:first-child,\\n.msg__bubble h6[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble p[data-v-5d775cd8] {\\n margin: 4px 0;\\n}\\n.msg__bubble p[data-v-5d775cd8]:first-child {\\n margin-top: 0;\\n}\\n.msg__bubble ul[data-v-5d775cd8],\\n.msg__bubble ol[data-v-5d775cd8] {\\n margin: 4px 0;\\n padding-left: 0;\\n list-style: none;\\n}\\n.msg__bubble li[data-v-5d775cd8] {\\n margin: 2px 0;\\n}\\n.msg__bubble strong[data-v-5d775cd8] {\\n color: #1f2329;\\n font-weight: 600;\\n}\\n.msg__bubble a[data-v-5d775cd8] {\\n color: #3067d9;\\n text-decoration: none;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n background: #f0f2f5;\\n color: #d83931;\\n padding: 1px 4px;\\n border-radius: 3px;\\n font-size: 12px;\\n}\\n.msg__bubble pre[data-v-5d775cd8] {\\n background: #f6f7f8;\\n padding: 8px;\\n border-radius: 6px;\\n overflow-x: auto;\\n margin: 6px 0;\\n}\\n.msg__bubble pre code[data-v-5d775cd8] {\\n background: none;\\n color: #1f2329;\\n padding: 0;\\n}\\n.msg--user .msg__bubble[data-v-5d775cd8] {\\n color: #fff;\\n background: #3067d9;\\n}\\n.msg__bubble--typing[data-v-5d775cd8] {\\n display: flex;\\n gap: 4px;\\n padding: 12px;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8] {\\n width: 6px;\\n height: 6px;\\n border-radius: 50%;\\n background: #86909c;\\n -webkit-animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n animation: ai-blink-data-v-5d775cd8 1.2s infinite;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(2) {\\n -webkit-animation-delay: 0.2s;\\n animation-delay: 0.2s;\\n}\\n.msg__bubble--typing span[data-v-5d775cd8]:nth-child(3) {\\n -webkit-animation-delay: 0.4s;\\n animation-delay: 0.4s;\\n}\\n@-webkit-keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n@keyframes ai-blink-data-v-5d775cd8 {\\n0%, 60%, 100% {\\n opacity: 0.35;\\n transform: translateY(0);\\n}\\n30% {\\n opacity: 1;\\n transform: translateY(-3px);\\n}\\n}\\n.ai-quick[data-v-5d775cd8] {\\n display: flex;\\n flex-wrap: wrap;\\n gap: 6px;\\n padding: 0 14px 10px;\\n background: #fff;\\n}\\n.ai-quick__item[data-v-5d775cd8] {\\n border: 1px solid #dce3f0;\\n background: #f7f9ff;\\n color: #3067d9;\\n font-size: 12px;\\n padding: 4px 10px;\\n border-radius: 14px;\\n cursor: pointer;\\n}\\n.ai-quick__item[data-v-5d775cd8]:hover {\\n border-color: #3067d9;\\n background: #eef3ff;\\n}\\n.ai-input[data-v-5d775cd8] {\\n display: flex;\\n align-items: flex-end;\\n gap: 8px;\\n padding: 10px 12px;\\n border-top: 1px solid #f0f2f5;\\n background: #fafbfc;\\n}\\n.ai-input__box[data-v-5d775cd8] {\\n flex: 1;\\n resize: none;\\n border: 1px solid #e4e7ed;\\n border-radius: 6px;\\n padding: 7px 10px;\\n font-size: 13px;\\n line-height: 1.5;\\n font-family: inherit;\\n color: #1f2329;\\n background: #fff;\\n outline: none;\\n max-height: 96px;\\n}\\n.ai-input__box[data-v-5d775cd8]:focus {\\n border-color: #3067d9;\\n}\\n.ai-input__send[data-v-5d775cd8] {\\n flex: 0 0 auto;\\n border: none;\\n background: #3067d9;\\n color: #fff;\\n font-size: 13px;\\n padding: 7px 16px;\\n border-radius: 6px;\\n cursor: pointer;\\n}\\n.ai-input__send[data-v-5d775cd8]:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n.ai-fade-enter-active[data-v-5d775cd8],\\n.ai-fade-leave-active[data-v-5d775cd8] {\\n transition: opacity 0.2s, transform 0.2s;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n.ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 0;\\n transform: translateY(10px);\\n}\\n\\n/* ============ H5 兼容:点击 AI 后从底部弹出,占 80% 高度、宽度占满 ============ */\\n/* 仅影响窄屏移动端,不影响 PC 端逻辑(PC 宽度 > 480px 仍走上方右侧悬浮面板) */\\n@media (max-width: 480px) {\\n /* 整层提到宿主底部操作栏(通常 z-index 高)之上,保证浮球/面板/遮罩不被遮挡 */\\n.ai-assistant[data-v-5d775cd8] {\\n z-index: 3000;\\n}\\n /* 审批面板打开时的阴影遮罩层 */\\n.ai-mask[data-v-5d775cd8] {\\n position: fixed;\\n top: 0;\\n right: 0;\\n bottom: 0;\\n left: 0;\\n background: rgba(0, 0, 0, 0.45);\\n pointer-events: auto;\\n z-index: 999;\\n}\\n /* 浮球改为视口固定:在可滚动的 H5 长详情页上始终贴在当前视口,并保持置顶 */\\n.ai-float[data-v-5d775cd8] {\\n position: fixed;\\n z-index: 1000;\\n}\\n.ai-panel[data-v-5d775cd8] {\\n /* 脱离容器,固定到视口底部,宽度占满、高度占视口 80% */\\n position: fixed;\\n top: auto;\\n left: 0;\\n right: 0;\\n bottom: 0 !important; /* 覆盖内联面板 bottom 绑定,始终贴底 */\\n width: 100%;\\n max-width: 100%;\\n height: 85%;\\n max-height: 85%; /* 兼容个别不允许 vh 的旧内核 */\\n border-radius: 16px 16px 0 0;\\n z-index: 1001; /* 位于遮罩层之上 */\\n}\\n /* 底部弹出/收起动画:从视口底部滑入 */\\n.ai-fade-enter-active[data-v-5d775cd8],\\n .ai-fade-leave-active[data-v-5d775cd8] {\\n transition: transform 0.28s ease;\\n}\\n.ai-fade-enter[data-v-5d775cd8],\\n .ai-fade-leave-to[data-v-5d775cd8] {\\n opacity: 1;\\n transform: translateY(100%);\\n}\\n /* H5 放大审批详情/聊天文字,提升可读性 */\\n.msg__bubble[data-v-5d775cd8] {\\n font-size: 15px;\\n line-height: 1.8;\\n padding: 11px 14px;\\n}\\n.msg__bubble h1[data-v-5d775cd8],\\n .msg__bubble h2[data-v-5d775cd8],\\n .msg__bubble h3[data-v-5d775cd8],\\n .msg__bubble h4[data-v-5d775cd8],\\n .msg__bubble h5[data-v-5d775cd8],\\n .msg__bubble h6[data-v-5d775cd8] {\\n font-size: 17px;\\n}\\n.msg__bubble code[data-v-5d775cd8] {\\n font-size: 14px;\\n}\\n /* 输入区针对全面屏 Home 指示条预留安全距离 */\\n.ai-input[data-v-5d775cd8] {\\n padding-bottom: calc(10px + env(safe-area-inset-bottom));\\n}\\n}\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
265
265
 
266
266
  /***/ }),
267
267
 
@@ -1234,7 +1234,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nod
1234
1234
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1235
1235
 
1236
1236
  "use strict";
1237
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"09ea9ef8-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=5d775cd8&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"09ea9ef8-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_09ea9ef8_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?");
1237
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"02165064-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=5d775cd8&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"02165064-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/lib/components/approvalAi/index.vue?vue&type=template&id=5d775cd8&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_02165064_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_5d775cd8_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/index.vue?");
1238
1238
 
1239
1239
  /***/ }),
1240
1240
 
package/lib/lib.zip ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zj-plugin-ai-assistant",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.2.0-beta.2",
4
4
  "main": "lib/ZjPluginAiAssistant.umd.min.js",
5
5
  "private": false,
6
6
  "scripts": {