zj-plugin-ai-assistant 0.3.0-beta.4 → 0.3.0-beta.5
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.
|
@@ -120,17 +120,6 @@ eval("var unsupportedIterableToArray = __webpack_require__(/*! ./unsupportedIter
|
|
|
120
120
|
|
|
121
121
|
/***/ }),
|
|
122
122
|
|
|
123
|
-
/***/ "./node_modules/@babel/runtime/helpers/defineProperty.js":
|
|
124
|
-
/*!***************************************************************!*\
|
|
125
|
-
!*** ./node_modules/@babel/runtime/helpers/defineProperty.js ***!
|
|
126
|
-
\***************************************************************/
|
|
127
|
-
/*! no static exports found */
|
|
128
|
-
/***/ (function(module, exports) {
|
|
129
|
-
|
|
130
|
-
eval("function _defineProperty(obj, key, value) {\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n\n return obj;\n}\n\nmodule.exports = _defineProperty;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./node_modules/@babel/runtime/helpers/defineProperty.js?");
|
|
131
|
-
|
|
132
|
-
/***/ }),
|
|
133
|
-
|
|
134
123
|
/***/ "./node_modules/@babel/runtime/helpers/interopRequireDefault.js":
|
|
135
124
|
/*!**********************************************************************!*\
|
|
136
125
|
!*** ./node_modules/@babel/runtime/helpers/interopRequireDefault.js ***!
|
|
@@ -153,17 +142,6 @@ eval("var _typeof = __webpack_require__(/*! @babel/runtime/helpers/typeof */ \".
|
|
|
153
142
|
|
|
154
143
|
/***/ }),
|
|
155
144
|
|
|
156
|
-
/***/ "./node_modules/@babel/runtime/helpers/objectSpread2.js":
|
|
157
|
-
/*!**************************************************************!*\
|
|
158
|
-
!*** ./node_modules/@babel/runtime/helpers/objectSpread2.js ***!
|
|
159
|
-
\**************************************************************/
|
|
160
|
-
/*! no static exports found */
|
|
161
|
-
/***/ (function(module, exports, __webpack_require__) {
|
|
162
|
-
|
|
163
|
-
eval("var defineProperty = __webpack_require__(/*! ./defineProperty.js */ \"./node_modules/@babel/runtime/helpers/defineProperty.js\");\n\nfunction ownKeys(object, enumerableOnly) {\n var keys = Object.keys(object);\n\n if (Object.getOwnPropertySymbols) {\n var symbols = Object.getOwnPropertySymbols(object);\n\n if (enumerableOnly) {\n symbols = symbols.filter(function (sym) {\n return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n });\n }\n\n keys.push.apply(keys, symbols);\n }\n\n return keys;\n}\n\nfunction _objectSpread2(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i] != null ? arguments[i] : {};\n\n if (i % 2) {\n ownKeys(Object(source), true).forEach(function (key) {\n defineProperty(target, key, source[key]);\n });\n } else if (Object.getOwnPropertyDescriptors) {\n Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n } else {\n ownKeys(Object(source)).forEach(function (key) {\n Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n });\n }\n }\n\n return target;\n}\n\nmodule.exports = _objectSpread2;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./node_modules/@babel/runtime/helpers/objectSpread2.js?");
|
|
164
|
-
|
|
165
|
-
/***/ }),
|
|
166
|
-
|
|
167
145
|
/***/ "./node_modules/@babel/runtime/helpers/typeof.js":
|
|
168
146
|
/*!*******************************************************!*\
|
|
169
147
|
!*** ./node_modules/@babel/runtime/helpers/typeof.js ***!
|
|
@@ -229,7 +207,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n// This file is imported into
|
|
|
229
207
|
/***/ (function(module, exports, __webpack_require__) {
|
|
230
208
|
|
|
231
209
|
"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//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\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}); // AI 输出的 Markdown 常不符合 CommonMark(#/- 后缺空格、标题与上文粘连同行),\n// markdown-it 严格解析会把 ### 等标记原样输出,渲染前统一规范化\n\nfunction normalizeMd(text) {\n return String(text) // 粘连在正文后的标题先断行(审批节点###审批事由);限定 ## 及以上且前一字符不是 # 或 /,避免误伤 URL 锚点\n .replace(/([^\\n#/])(#{2,6})(?=[^\\s#])/g, \"$1\\n$2\") // 标题 # 序列后补空格(###审批事由 -> ### 审批事由)\n .replace(/(^|\\n)(#{1,6})(?=[^\\s#])/g, \"$1$2 \") // 无序列表 - 后补空格(-项目名称 -> - 项目名称);排除 --- 分隔线、- 后数字(负数/金额)\n .replace(/(^|\\n)-(?=[^\\s-\\d])/g, \"$1- \") // 有序列表 1. 后补空格(1.内容 -> 1. 内容);点后为数字(小数/版本号)不处理\n .replace(/(^|\\n)(\\d{1,2})\\.(?=[^\\s\\d])/g, \"$1$2. \");\n} // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\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 // 当前登录态 token:init 接口(Authorization 头)用于读取 low-code 表单快照,服务端不落库保存\n authToken: {\n type: String,\n default: \"\"\n },\n // AI 助手入口类型(后端 3.11 入口开关按类型维护):custom=自定义表单 | qgb=企管帮\n entryType: {\n type: String,\n default: \"custom\"\n },\n // 是否向后端查询入口开关;false 时直接展示悬浮入口(后端未上线/无该接口时可关闭)\n entryStatusCheck: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n // 悬浮按钮右上角红点:初始化显示,进入面板后取消\n showRedDot: true,\n // 入口开关(后端 3.11):默认展示,查询明确返回 isEnabled=false 时隐藏整个悬浮入口\n entryEnabled: true,\n entryStatusLoaded: false,\n panelVisible: false,\n // 首次打开时审批详情/概览仍在加载中的提示开关\n greetingLoading: false,\n // 加载提示文案(拉历史 / 生成概览两个阶段文案不同)\n greetingLoadingText: \"正在加载审批详情\",\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n // 会话历史分页状态(GET /chat/history + 上拉加载更早消息)\n histPageNum: 1,\n histPageSize: 20,\n histTotal: 0,\n histHasMore: false,\n loadingHistory: false,\n histLoadedIds: [],\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 340,\n // 弹窗头部拖动:面板位置(PC 用 left + bottom 定位;H5 底部弹出仍走 CSS 固定)\n panelLeft: 0,\n panelBottom: 88,\n panelDragging: false,\n // 用户手动拖动过面板后,再次打开保持该位置(不再吸附到按钮附近)\n panelMoved: false,\n // 全页面拖拽:首次拖动后浮球/面板切换为视口 fixed 定位(初始渲染位置不变)\n floatFixed: false,\n panelFixed: false,\n tucked: false,\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 // 面板定位:PC 用 left + bottom 自由拖动;H5 底部弹出仅绑 bottom,\n // left/right/width 交给媒体查询,避免内联 left 覆盖掉 H5 的全宽定位\n panelStyle: function panelStyle() {\n var s = {\n bottom: this.panelBottom + \"px\"\n };\n if (!this.isMobile) s.left = this.panelLeft + \"px\";\n return s;\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() {\n // 注入 AI 服务地址\n aiApi.configureAI({\n baseUrl: this.baseUrl,\n authToken: this.authToken\n });\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.checkEntryStatus();\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 this.histPageNum = 1;\n this.histPageSize = 20;\n this.histTotal = 0;\n this.histHasMore = false;\n this.loadingHistory = false;\n this.histLoadedIds = [];\n this.greetingLoading = false; // 切换审批单时重置面板位置(恢复到默认吸附)\n\n this.panelLeft = 0;\n this.panelBottom = 88;\n this.panelMoved = false;\n this.panelFixed = false;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val,\n authToken: this.authToken\n });\n },\n // 登录态 token 变化时同步注入(保留当前 baseUrl)\n authToken: function authToken(val) {\n aiApi.configureAI({\n baseUrl: this.baseUrl,\n authToken: val\n });\n },\n // 入口类型变化时重新查询入口开关\n entryType: function entryType() {\n this.checkEntryStatus();\n },\n // 入口开关查询开关变化:关闭查询时直接展示入口\n entryStatusCheck: function entryStatusCheck(val) {\n if (val) this.checkEntryStatus();else this.entryEnabled = true;\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\n this.showRedDot = false;\n this.$nextTick(function () {\n _this3.snapPanel();\n\n if (!_this3.messages.length && !_this3.greetingLoading) {\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 // 将浮球切换为视口 fixed 定位:按当前视觉位置换算坐标,切换瞬间位置不变\n toFixedFloat: function toFixedFloat() {\n if (this.floatFixed) return;\n var el = this.$refs.floatEl;\n var rect = el && el.getBoundingClientRect();\n if (!rect) return;\n this.floatFixed = true;\n this.btnLeft = rect.left;\n this.btnBottom = window.innerHeight - rect.bottom;\n },\n // 将面板切换为视口 fixed 定位:按当前视觉位置换算坐标,切换瞬间位置不变\n toFixedPanel: function toFixedPanel() {\n if (this.panelFixed || this.isMobile) return;\n var el = this.$el.querySelector(\".ai-panel\");\n var rect = el && el.getBoundingClientRect();\n if (!rect) return;\n this.panelFixed = true;\n this.panelLeft = rect.left;\n this.panelBottom = window.innerHeight - rect.bottom;\n },\n // 面板吸附:打开时始终贴在浮球旁边(默认左侧,空间不足翻到右侧)。\n // 位置以浮球当前坐标实时计算,避免浮球移动后面板停在旧位置或跑到容器左上角\n snapPanel: function snapPanel() {\n // 面板被手动拖过:浮球没动则保持其位置;浮球移动后重新吸附到旁边\n if (this.panelMoved) {\n if (this.btnLeft === this._snapAtLeft && this.btnBottom === this._snapAtBottom) return;\n this.panelMoved = false;\n }\n\n var el = this.$el.querySelector(\".ai-panel\");\n if (!el) return;\n var box = this.getContainer();\n var inViewport = this.floatFixed || this.panelFixed;\n if (inViewport) this.toFixedPanel();\n var vw = inViewport ? window.innerWidth : box ? box.clientWidth : window.innerWidth;\n var vh = inViewport ? window.innerHeight : box ? box.clientHeight : window.innerHeight;\n var margin = 24; // 四周安全距离\n\n var gap = 12; // 面板与浮球之间的间距\n\n var floatW = 46; // 浮球直径\n // 面板尺寸与 CSS 约束一致:420x560,容器/视口不足时按 100%-98px、100%-48px 压缩\n\n var pw = Math.min(420, Math.max(200, vw - 98));\n var ph = Math.min(560, Math.max(200, vh - 48)); // 浮球位置(与面板同一坐标系;面板打开时 tucked 位移已取消,left 即视觉左缘)\n\n var fl = this.btnLeft;\n var fb = this.btnBottom; // 水平:优先贴浮球左侧;左侧放不下则翻到右侧;都放不下时夹紧且不与浮球重叠\n\n var leftSide = fl - pw - gap;\n var rightSide = fl + floatW + gap;\n var left = leftSide;\n if (left < margin) left = rightSide;\n left = Math.max(0, Math.min(left, Math.max(0, vw - pw - margin)));\n if (left < fl + floatW && left + pw > fl && leftSide >= 0) left = leftSide; // 垂直:面板底边在浮球顶边上方 gap;上方空间不足时整体下移并夹紧\n\n var bottom = fb + floatW + gap;\n if (bottom + ph > vh - margin) bottom = vh - ph - margin;\n if (bottom < margin) bottom = margin;\n this.panelLeft = left;\n this.panelBottom = bottom; // 记录吸附时的浮球坐标,用于判断后续是否需要重新吸附\n\n this._snapAtLeft = fl;\n this._snapAtBottom = fb;\n },\n // 弹窗头部拖拽开始(PC 端可自由拖动;H5 底部弹层保持原定位不参与)\n startPanelDrag: function startPanelDrag(e) {\n if (this.isMobile) return;\n if (e.button !== undefined && e.button !== 0) return;\n var el = this.$el.querySelector(\".ai-panel\");\n if (!el) return;\n var p = this.point(e);\n if (!p) return; // 先切换为视口 fixed 定位(视觉位置不变),再按视口坐标记录拖拽基准\n\n this.toFixedPanel();\n this.panelDragging = true;\n this._pdMoved = false;\n this._pdStartX = p.clientX;\n this._pdStartY = p.clientY;\n this._pdStartLeft = this.panelLeft || 0;\n this._pdStartBottom = this.panelBottom; // 拖动时防止选中 Header 文字导致跟手中断;仅鼠标 preventDefault\n\n if (e.type === \"mousedown\") e.preventDefault();\n },\n // 弹窗拖拽移动:随鼠标更新 left/bottom,并夹在视口安全边界内\n onPanelDrag: function onPanelDrag(e) {\n if (!this.panelDragging) return;\n var el = this.$el.querySelector(\".ai-panel\");\n var pw = el ? el.clientWidth : 420;\n var ph = el ? el.clientHeight : 560;\n var p = this.point(e) || {};\n var dx = (p.clientX || 0) - this._pdStartX;\n var dy = (p.clientY || 0) - this._pdStartY; // 超过阈值才算真正拖动过,避免单击头部把面板位置锁死\n\n if (!this._pdMoved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) this._pdMoved = true; // 全页面拖拽:以视口为边界\n\n var maxLeft = Math.max(0, window.innerWidth - pw - 24);\n var maxBottom = Math.max(24, window.innerHeight - ph - 24);\n this.panelLeft = Math.max(0, Math.min(this._pdStartLeft + dx, maxLeft));\n this.panelBottom = Math.max(24, Math.min(this._pdStartBottom - dy, maxBottom));\n },\n // 打开时初始化会话:优先拉取历史问答回显;无历史再走 SSE 总结(本地兜底)\n greet: function greet() {\n var _this4 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var hasSummary, data, intro, initSummary, initRes, _intro;\n\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n if (!_this4.greetingLoading) {\n _context.next = 2;\n break;\n }\n\n return _context.abrupt(\"return\");\n\n case 2:\n _this4.greetingLoading = true;\n _this4.greetingLoadingText = \"正在加载审批详情\";\n _context.prev = 4;\n\n _this4.collectDetail();\n\n hasSummary = false;\n\n if (!(_this4.aiEnabled() && _this4.id)) {\n _context.next = 14;\n break;\n }\n\n _context.next = 10;\n return _this4.loadHistory(1);\n\n case 10:\n data = _context.sent;\n if (data && data.sessionId) _this4.sessionId = data.sessionId; // 先回显历史问答记录\n\n if (data && Array.isArray(data.records) && data.records.length) {\n data.records.forEach(function (r) {\n _this4.messages.push({\n id: ++msgSeq,\n role: r.role === \"user\" ? \"user\" : \"assistant\",\n content: r.content || \"\",\n thinking: r.thinking || \"\" // 历史记录新增 thinking 字段:反显为可折叠思考过程\n\n });\n\n if (r.id != null && _this4.histLoadedIds.indexOf(r.id) < 0) {\n _this4.histLoadedIds.push(r.id);\n }\n });\n } // 关键:history 接口返回的 summary 是后端共享总结缓存\n // 只要 summary 非空(无论 records 是否为空),说明已有总结,跳过 init + SSE\n\n\n if (data && data.summary && String(data.summary).trim()) {\n intro = \"\\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\";\n\n _this4.messages.unshift({\n id: ++msgSeq,\n role: \"assistant\",\n content: intro + data.summary\n });\n\n hasSummary = true;\n }\n\n case 14:\n if (!hasSummary) {\n _context.next = 19;\n break;\n }\n\n // 已有共享总结:内容已就绪,关闭 loading\n _this4.greetingLoading = false;\n\n _this4.scrollBottom();\n\n _context.next = 35;\n break;\n\n case 19:\n // history 无总结(接口异常 / 存量单据未入库 / 后端缓存 miss):\n // 先调 /init(幂等),根据返回 initialized 决定后续路径——\n // initialized=false → 已有缓存,summary 非空,直接用,跳过 SSE\n // initialized=true → 刚初始化,summary 为 null,继续走 SSE 流式总结\n initSummary = null;\n\n if (!(_this4.aiEnabled() && _this4.id)) {\n _context.next = 26;\n break;\n }\n\n _this4.greetingLoadingText = \"正在初始化审批数据\";\n _context.next = 24;\n return aiApi.initInstance(_this4.id);\n\n case 24:\n initRes = _context.sent;\n\n if (initRes && initRes.initialized === false && initRes.summary && String(initRes.summary).trim()) {\n initSummary = String(initRes.summary).trim();\n }\n\n case 26:\n if (!initSummary) {\n _context.next = 33;\n break;\n }\n\n // init 返回已有缓存总结,直接置顶开场白\n _intro = \"\\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\";\n\n _this4.messages.unshift({\n id: ++msgSeq,\n role: \"assistant\",\n content: _intro + initSummary\n });\n\n _this4.greetingLoading = false;\n\n _this4.scrollBottom();\n\n _context.next = 35;\n break;\n\n case 33:\n _context.next = 35;\n return _this4.greetSummary();\n\n case 35:\n _context.prev = 35;\n // 兜底关闭 loading(总结流内部也会在内容就绪时提前关闭)\n _this4.greetingLoading = false;\n\n _this4.scrollBottom();\n\n return _context.finish(35);\n\n case 39:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee, null, [[4,, 35, 39]]);\n }))();\n },\n // 无历史时生成开场总结:接入后端走 SSE 流式;否则本地概览兜底。\n // 生成期间保持 loading 提示,首个内容到达后再展示开场语,避免出现只有半句的空气泡\n greetSummary: function greetSummary() {\n var _this5 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var localSummary, intro, msg, got;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n localSummary = _this5.buildSummary();\n intro = \"\\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\";\n\n if (!(_this5.aiEnabled() && _this5.id)) {\n _context2.next = 11;\n break;\n }\n\n _this5.greetingLoadingText = \"正在生成审批概览\";\n msg = null;\n got = false;\n _context2.next = 8;\n return aiApi.streamSummary(_this5.id, {\n onEvent: function onEvent(event, data) {\n if (event === \"answer\" || event === \"agent_result\") {\n got = true;\n\n if (!msg) {\n // 首个增量到达:隐藏 loading,推送开场语气泡,随后增量追加\n _this5.greetingLoading = false;\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: intro\n };\n\n _this5.messages.push(msg);\n }\n\n if (event === \"agent_result\") {\n msg.content = intro + (data || \"\");\n } else {\n msg.content += data;\n }\n\n _this5.followBottom();\n }\n }\n });\n\n case 8:\n // 流式未返回任何内容(失败/空)时回退到本地概览,避免空白\n if (!got) {\n _this5.greetingLoading = false;\n\n _this5.push(\"assistant\", intro + localSummary);\n\n _this5.followBottom();\n }\n\n _context2.next = 14;\n break;\n\n case 11:\n _this5.greetingLoading = false;\n\n _this5.push(\"assistant\", intro + localSummary);\n\n _this5.followBottom();\n\n case 14:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // 拉取会话历史(含分页信息回填)\n loadHistory: function loadHistory(pageNum) {\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 u = _this6.userInfo();\n _context3.next = 3;\n return aiApi.getChatHistory({\n approvalNo: _this6.id,\n userId: u.no,\n userName: u.name,\n pageNum: pageNum,\n pageSize: _this6.histPageSize\n });\n\n case 3:\n res = _context3.sent;\n\n if (!(res && res.data)) {\n _context3.next = 10;\n break;\n }\n\n _this6.histPageNum = res.data.pageNum || pageNum;\n _this6.histPageSize = res.data.pageSize || _this6.histPageSize;\n _this6.histTotal = res.data.total || 0;\n _this6.histHasMore = _this6.histPageNum * _this6.histPageSize < _this6.histTotal;\n return _context3.abrupt(\"return\", res.data);\n\n case 10:\n return _context3.abrupt(\"return\", null);\n\n case 11:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 上拉分页加载更早的历史消息(前插,并保持当前视口位置)\n loadPrevHistory: function loadPrevHistory() {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var el, prevHeight, data, older;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (!(_this7.loadingHistory || !_this7.histHasMore)) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\");\n\n case 2:\n if (!(!_this7.aiEnabled() || !_this7.id)) {\n _context4.next = 4;\n break;\n }\n\n return _context4.abrupt(\"return\");\n\n case 4:\n _this7.loadingHistory = true;\n el = _this7.$refs.chatBody;\n prevHeight = el ? el.scrollHeight : 0;\n _context4.prev = 7;\n _context4.next = 10;\n return _this7.loadHistory(_this7.histPageNum + 1);\n\n case 10:\n data = _context4.sent;\n\n if (data && Array.isArray(data.records) && data.records.length) {\n // 前插更早记录并按其服务端 id 去重,防止后端重叠页/重复 id 造成重复展示\n older = [];\n data.records.forEach(function (r) {\n if (r.id != null && _this7.histLoadedIds.indexOf(r.id) >= 0) return;\n if (r.id != null) _this7.histLoadedIds.push(r.id);\n older.push({\n id: ++msgSeq,\n role: r.role === \"user\" ? \"user\" : \"assistant\",\n content: r.content || \"\",\n thinking: r.thinking || \"\" // 历史记录新增 thinking 字段:反显为可折叠思考过程\n\n });\n });\n if (older.length) _this7.messages = older.concat(_this7.messages);\n }\n\n _this7.$nextTick(function () {\n var el2 = _this7.$refs.chatBody;\n if (el2) el2.scrollTop = el2.scrollHeight - prevHeight;\n });\n\n case 13:\n _context4.prev = 13;\n _this7.loadingHistory = false;\n return _context4.finish(13);\n\n case 16:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[7,, 13, 16]]);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n // 审批详情气泡内的链接禁止跳转(markdown 渲染出的 <a> 不触发页面跳转)\n onBubbleClick: function onBubbleClick(e) {\n var el = e.target;\n var link = el && el.closest ? el.closest(\"a\") : null;\n if (!link) return;\n e.preventDefault();\n e.stopPropagation();\n },\n sendMessage: function sendMessage() {\n var _this8 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return; // 审批概览生成期间禁止提问:避免总结流与问答流并发,\n // 造成消息顺序错乱、滚动相互抢占,以及后端同单总结未完成时问答排队/上下文不完整\n\n if (this.greetingLoading) 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 thinking: \"\",\n _thinkingOpen: true,\n _thinkingLoading: false,\n _thinkingSegs: []\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk, isReasoning, isReplace) {\n if (msg) {\n if (isReasoning) {\n // 思考过程:写入独立字段并切分为段落,逐段淡入上浮展示\n msg.thinking += chunk;\n if (chunk) msg._thinkingSegs.push(chunk);\n msg._thinkingLoading = true;\n msg._thinkingOpen = true;\n } else if (isReplace) {\n // 终稿全量覆盖,避免与 answer 增量内容重复展示\n msg.content = chunk;\n } else {\n // 正式回答:正常追加\n msg.content += chunk;\n }\n\n _this8.followBottom();\n\n _this8.scrollThinkingToBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this8.fallbackReply(text); // 思考过程结束:停止滚动跟随并自动折叠\n\n msg._thinkingLoading = false;\n msg._thinkingOpen = false;\n } else {\n _this8.push(\"assistant\", _this8.fallbackReply(text));\n }\n\n _this8.streaming = false;\n _this8.sending = false;\n\n _this8.followBottom();\n });\n },\n // 思考内容是否已拆分成段落(含分段数组时按段渲染;历史/未分段消息退化为整体一段)\n hasThinkingSegs: function hasThinkingSegs(m) {\n return !!(m && m._thinkingSegs && m._thinkingSegs.length);\n },\n // details 原生折叠状态与数据同步,展开时滚动贴底\n syncThinkingOpen: function syncThinkingOpen(m, e) {\n var open = !!e.target.open;\n\n if (m._thinkingOpen !== open) {\n m._thinkingOpen = open;\n if (open) this.scrollThinkingToBottom();\n }\n },\n // DeepSeek 移动端风格:让思考框内容向上滚动,始终保持最新内容贴底可见\n scrollThinkingToBottom: function scrollThinkingToBottom() {\n var _this9 = this;\n\n this.$nextTick(function () {\n var container = _this9.$refs.chatBody;\n if (!container) return;\n var bodies = container.querySelectorAll(\".thinking-body.thinking-scroll\");\n var last = bodies[bodies.length - 1];\n if (last) last.scrollTop = last.scrollHeight;\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this10 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee5$(_context5) {\n while (1) {\n switch (_context5.prev = _context5.next) {\n case 0:\n payload = {\n context: {\n type: _this10.type,\n status: _this10.status,\n typeName: _this10.typeName,\n id: _this10.id,\n summary: _this10.buildSummary(),\n fields: _this10.fields,\n detailData: _this10.rawDetail\n },\n question: question\n };\n _context5.next = 3;\n return _this10.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context5.sent;\n return _context5.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this10.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context5.stop();\n }\n }\n }, _callee5);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 查询 AI 助手入口开关(后端 3.11):仅明确返回 isEnabled=false 时隐藏悬浮入口;\n // 未配置 baseUrl、未指定 entryType 或接口异常时保持展示(fail-open,避免后端抖动导致入口消失)\n checkEntryStatus: function checkEntryStatus() {\n var _this11 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() {\n var res, enabled;\n return regeneratorRuntime.wrap(function _callee6$(_context6) {\n while (1) {\n switch (_context6.prev = _context6.next) {\n case 0:\n if (!(!_this11.entryStatusCheck || !_this11.entryType)) {\n _context6.next = 2;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 2:\n if (aiApi.aiConfigured()) {\n _context6.next = 4;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 4:\n _context6.next = 6;\n return aiApi.getEntryStatus(_this11.entryType);\n\n case 6:\n res = _context6.sent;\n\n if (!(!res || !res.data)) {\n _context6.next = 9;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 9:\n _this11.entryStatusLoaded = true;\n enabled = res.data.isEnabled !== false;\n\n if (enabled) {\n _context6.next = 15;\n break;\n }\n\n // 开关关闭:收起已展开的面板并隐藏悬浮入口\n _this11.entryEnabled = false;\n _this11.panelVisible = false;\n return _context6.abrupt(\"return\");\n\n case 15:\n _this11.entryEnabled = true;\n\n _this11.$nextTick(function () {\n return _this11.snapBtn();\n });\n\n case 17:\n case \"end\":\n return _context6.stop();\n }\n }\n }, _callee6);\n }))();\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 _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee7() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee7$(_context7) {\n while (1) {\n switch (_context7.prev = _context7.next) {\n case 0:\n if (!_this12.sessionId) {\n _context7.next = 2;\n break;\n }\n\n return _context7.abrupt(\"return\", _this12.sessionId);\n\n case 2:\n u = _this12.userInfo();\n _context7.next = 5;\n return aiApi.createChatSession({\n approvalNo: _this12.id,\n userId: u.no,\n userName: u.name\n });\n\n case 5:\n res = _context7.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context7.next = 9;\n break;\n }\n\n _this12.sessionId = res.data.sessionId;\n return _context7.abrupt(\"return\", _this12.sessionId);\n\n case 9:\n return _context7.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context7.stop();\n }\n }\n }, _callee7);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() {\n var sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee8$(_context8) {\n while (1) {\n switch (_context8.prev = _context8.next) {\n case 0:\n if (_this13.aiEnabled()) {\n _context8.next = 2;\n break;\n }\n\n return _context8.abrupt(\"return\", null);\n\n case 2:\n _context8.prev = 2;\n _context8.next = 5;\n return _this13.ensureSession();\n\n case 5:\n sessionId = _context8.sent;\n\n if (sessionId) {\n _context8.next = 8;\n break;\n }\n\n return _context8.abrupt(\"return\", null);\n\n case 8:\n text = \"\";\n failed = false;\n _context8.next = 12;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n userId: _this13.userInfo().no,\n userName: _this13.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\") {\n // 思考过程:单独回调(isReasoning=true),不写入回答文本\n if (onDelta) onDelta(data, true);\n } else if (event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data, false);\n } else if (event === \"agent_result\") {\n // agent_result 携带完整终稿:覆盖已流式追加的 answer 内容,\n // 若按追加处理同一答案会展示两遍\n text = data;\n if (onDelta) onDelta(data, false, true);\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 12:\n if (!failed) {\n _context8.next = 14;\n break;\n }\n\n return _context8.abrupt(\"return\", null);\n\n case 14:\n return _context8.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 17:\n _context8.prev = 17;\n _context8.t0 = _context8[\"catch\"](2);\n return _context8.abrupt(\"return\", null);\n\n case 20:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8, 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 _this14 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this14.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 _this15 = 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: _this15.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(normalizeMd(text || \"\"));\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this16 = this;\n\n this.$nextTick(function () {\n var el = _this16.$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 if (!this.loadingHistory && this.histHasMore && el.scrollTop < 60) {\n this.loadPrevHistory();\n }\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; // 先切换为视口 fixed 定位(视觉位置不变),再按视口坐标记录拖拽基准;\n // 面板打开时一并切换,保证浮球/面板坐标基准一致\n\n this.toFixedFloat();\n if (this.panelVisible) this.toFixedPanel();\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 // 若正在拖动弹窗面板,走面板拖拽逻辑\n if (this.panelDragging) {\n this.onPanelDrag(e);\n return;\n }\n\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\n var maxW = window.innerWidth - 46;\n var maxH = window.innerHeight - 44;\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 // 同步浮球坐标到数据层,面板吸附才能实时跟随(关闭状态不写入,保持拖拽性能)\n this.btnLeft = left;\n this.btnBottom = bottom;\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n // 结束弹窗拖拽:真正移动过才记录,再次打开保持该位置\n if (this.panelDragging) {\n this.panelDragging = false;\n if (this._pdMoved) this.panelMoved = true;\n return;\n }\n\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; // 拖动过才吸附贴边;靠近右边时收成半角标(fixed 定位下以视口宽度判断)\n\n if (window.innerWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = window.innerWidth - 46;\n } // 贴边收起会修正 btnLeft,面板需按新坐标重新吸附,避免打开时错位\n\n\n if (this.panelVisible) this.snapPanel();\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");
|
|
210
|
+
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//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\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}); // AI 输出或表单字段可能残留 <br>/ 等 HTML 标记。markdown-it 以 html:false 渲染时\n// 会把这些标签按字面转义显示(用户看到 <br>、 等代码),渲染前统一清洗为纯文本。\n// 循环两轮以覆盖 <br> 这类双重编码。\n\nfunction cleanHtml(text) {\n var s = String(text);\n\n for (var i = 0; i < 2; i++) {\n s = s.replace(/<br\\s*\\/?>/gi, \"\\n\").replace(/<\\/p>/gi, \"\\n\").replace(/<[^>]+>/g, \"\").replace(/ | |\\u00a0/gi, \" \").replace(/&/gi, \"&\").replace(/</gi, \"<\").replace(/>/gi, \">\").replace(/"/gi, '\"').replace(/'|'/gi, \"'\");\n }\n\n return s;\n} // AI 输出的 Markdown 常不符合 CommonMark(#/- 后缺空格、标题与上文粘连同行),\n// markdown-it 严格解析会把 ### 等标记原样输出,渲染前统一规范化\n\n\nfunction normalizeMd(text) {\n return String(cleanHtml(text)) // 粘连在正文后的标题先断行(审批节点###审批事由);限定 ## 及以上且前一字符不是 # 或 /,避免误伤 URL 锚点\n .replace(/([^\\n#/])(#{2,6})(?=[^\\s#])/g, \"$1\\n$2\") // 标题 # 序列后补空格(###审批事由 -> ### 审批事由)\n .replace(/(^|\\n)(#{1,6})(?=[^\\s#])/g, \"$1$2 \") // 无序列表 - 后补空格(-项目名称 -> - 项目名称);排除 --- 分隔线、- 后数字(负数/金额)\n .replace(/(^|\\n)-(?=[^\\s-\\d])/g, \"$1- \") // 有序列表 1. 后补空格(1.内容 -> 1. 内容);点后为数字(小数/版本号)不处理\n .replace(/(^|\\n)(\\d{1,2})\\.(?=[^\\s\\d])/g, \"$1$2. \");\n} // 已知英文编码 -> 中文名称(进包时内嵌,避免依赖宿主项目的 approvalType 常量)\n\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 // 当前登录态 token:init 接口(Authorization 头)用于读取 low-code 表单快照,服务端不落库保存\n authToken: {\n type: String,\n default: \"\"\n },\n // AI 助手入口类型(后端 3.11 入口开关按类型维护):custom=自定义表单 | qgb=企管帮\n entryType: {\n type: String,\n default: \"custom\"\n },\n // 是否向后端查询入口开关;false 时直接展示悬浮入口(后端未上线/无该接口时可关闭)\n entryStatusCheck: {\n type: Boolean,\n default: true\n },\n // 宿主注入:每次问答/首次总结前调用,返回页面最新实时上下文(含 approvalLog、表单、附件)。\n // 返回结构:{ dataId, contextBase64, context }(context 为 summary 对象;contextBase64 为其 Base64,二选一)\n contextProvider: {\n type: Function,\n default: null\n }\n },\n data: function data() {\n return {\n defaultIcon: _aiAssistant.default,\n // 悬浮按钮右上角红点:初始化显示,进入面板后取消\n showRedDot: true,\n // 入口开关(后端 3.11):默认展示,查询明确返回 isEnabled=false 时隐藏整个悬浮入口\n entryEnabled: true,\n entryStatusLoaded: false,\n panelVisible: false,\n // 首次打开时审批详情/概览仍在加载中的提示开关\n greetingLoading: false,\n // 加载提示文案(拉历史 / 生成概览两个阶段文案不同)\n greetingLoadingText: \"正在加载审批详情\",\n sending: false,\n streaming: false,\n autoFollow: true,\n sessionId: \"\",\n // 会话历史分页状态(GET /chat/history + 上拉加载更早消息)\n histPageNum: 1,\n histPageSize: 20,\n histTotal: 0,\n histHasMore: false,\n loadingHistory: false,\n histLoadedIds: [],\n messages: [],\n fields: [],\n rawDetail: null,\n inputText: \"\",\n btnLeft: 0,\n btnBottom: 340,\n // 弹窗头部拖动:面板位置(PC 用 left + bottom 定位;H5 底部弹出仍走 CSS 固定)\n panelLeft: 0,\n panelBottom: 88,\n panelDragging: false,\n // 用户手动拖动过面板后,再次打开保持该位置(不再吸附到按钮附近)\n panelMoved: false,\n // 全页面拖拽:首次拖动后浮球/面板切换为视口 fixed 定位(初始渲染位置不变)\n floatFixed: false,\n panelFixed: false,\n tucked: false,\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 // 面板定位:PC 用 left + bottom 自由拖动;H5 底部弹出仅绑 bottom,\n // left/right/width 交给媒体查询,避免内联 left 覆盖掉 H5 的全宽定位\n panelStyle: function panelStyle() {\n var s = {\n bottom: this.panelBottom + \"px\"\n };\n if (!this.isMobile) s.left = this.panelLeft + \"px\";\n return s;\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() {\n // 注入 AI 服务地址\n aiApi.configureAI({\n baseUrl: this.baseUrl,\n authToken: this.authToken\n });\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.checkEntryStatus();\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 this.histPageNum = 1;\n this.histPageSize = 20;\n this.histTotal = 0;\n this.histHasMore = false;\n this.loadingHistory = false;\n this.histLoadedIds = [];\n this.greetingLoading = false; // 切换审批单时重置面板位置(恢复到默认吸附)\n\n this.panelLeft = 0;\n this.panelBottom = 88;\n this.panelMoved = false;\n this.panelFixed = false;\n },\n // baseUrl 变化时同步注入 AI 服务\n baseUrl: function baseUrl(val) {\n aiApi.configureAI({\n baseUrl: val,\n authToken: this.authToken\n });\n },\n // 登录态 token 变化时同步注入(保留当前 baseUrl)\n authToken: function authToken(val) {\n aiApi.configureAI({\n baseUrl: this.baseUrl,\n authToken: val\n });\n },\n // 入口类型变化时重新查询入口开关\n entryType: function entryType() {\n this.checkEntryStatus();\n },\n // 入口开关查询开关变化:关闭查询时直接展示入口\n entryStatusCheck: function entryStatusCheck(val) {\n if (val) this.checkEntryStatus();else this.entryEnabled = true;\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\n this.showRedDot = false;\n this.$nextTick(function () {\n _this3.snapPanel();\n\n if (!_this3.messages.length && !_this3.greetingLoading) {\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 // 将浮球切换为视口 fixed 定位:按当前视觉位置换算坐标,切换瞬间位置不变\n toFixedFloat: function toFixedFloat() {\n if (this.floatFixed) return;\n var el = this.$refs.floatEl;\n var rect = el && el.getBoundingClientRect();\n if (!rect) return;\n this.floatFixed = true;\n this.btnLeft = rect.left;\n this.btnBottom = window.innerHeight - rect.bottom;\n },\n // 将面板切换为视口 fixed 定位:按当前视觉位置换算坐标,切换瞬间位置不变\n toFixedPanel: function toFixedPanel() {\n if (this.panelFixed || this.isMobile) return;\n var el = this.$el.querySelector(\".ai-panel\");\n var rect = el && el.getBoundingClientRect();\n if (!rect) return;\n this.panelFixed = true;\n this.panelLeft = rect.left;\n this.panelBottom = window.innerHeight - rect.bottom;\n },\n // 面板吸附:打开时始终贴在浮球旁边(默认左侧,空间不足翻到右侧)。\n // 位置以浮球当前坐标实时计算,避免浮球移动后面板停在旧位置或跑到容器左上角\n snapPanel: function snapPanel() {\n // 面板被手动拖过:浮球没动则保持其位置;浮球移动后重新吸附到旁边\n if (this.panelMoved) {\n if (this.btnLeft === this._snapAtLeft && this.btnBottom === this._snapAtBottom) return;\n this.panelMoved = false;\n }\n\n var el = this.$el.querySelector(\".ai-panel\");\n if (!el) return;\n var box = this.getContainer();\n var inViewport = this.floatFixed || this.panelFixed;\n if (inViewport) this.toFixedPanel();\n var vw = inViewport ? window.innerWidth : box ? box.clientWidth : window.innerWidth;\n var vh = inViewport ? window.innerHeight : box ? box.clientHeight : window.innerHeight;\n var margin = 24; // 四周安全距离\n\n var gap = 12; // 面板与浮球之间的间距\n\n var floatW = 46; // 浮球直径\n // 面板尺寸与 CSS 约束一致:420x560,容器/视口不足时按 100%-98px、100%-48px 压缩\n\n var pw = Math.min(420, Math.max(200, vw - 98));\n var ph = Math.min(560, Math.max(200, vh - 48)); // 浮球位置(与面板同一坐标系;面板打开时 tucked 位移已取消,left 即视觉左缘)\n\n var fl = this.btnLeft;\n var fb = this.btnBottom; // 水平:优先贴浮球左侧;左侧放不下则翻到右侧;都放不下时夹紧且不与浮球重叠\n\n var leftSide = fl - pw - gap;\n var rightSide = fl + floatW + gap;\n var left = leftSide;\n if (left < margin) left = rightSide;\n left = Math.max(0, Math.min(left, Math.max(0, vw - pw - margin)));\n if (left < fl + floatW && left + pw > fl && leftSide >= 0) left = leftSide; // 垂直:面板底边在浮球顶边上方 gap;上方空间不足时整体下移并夹紧\n\n var bottom = fb + floatW + gap;\n if (bottom + ph > vh - margin) bottom = vh - ph - margin;\n if (bottom < margin) bottom = margin;\n this.panelLeft = left;\n this.panelBottom = bottom; // 记录吸附时的浮球坐标,用于判断后续是否需要重新吸附\n\n this._snapAtLeft = fl;\n this._snapAtBottom = fb;\n },\n // 弹窗头部拖拽开始(PC 端可自由拖动;H5 底部弹层保持原定位不参与)\n startPanelDrag: function startPanelDrag(e) {\n if (this.isMobile) return;\n if (e.button !== undefined && e.button !== 0) return;\n var el = this.$el.querySelector(\".ai-panel\");\n if (!el) return;\n var p = this.point(e);\n if (!p) return; // 先切换为视口 fixed 定位(视觉位置不变),再按视口坐标记录拖拽基准\n\n this.toFixedPanel();\n this.panelDragging = true;\n this._pdMoved = false;\n this._pdStartX = p.clientX;\n this._pdStartY = p.clientY;\n this._pdStartLeft = this.panelLeft || 0;\n this._pdStartBottom = this.panelBottom; // 拖动时防止选中 Header 文字导致跟手中断;仅鼠标 preventDefault\n\n if (e.type === \"mousedown\") e.preventDefault();\n },\n // 弹窗拖拽移动:随鼠标更新 left/bottom,并夹在视口安全边界内\n onPanelDrag: function onPanelDrag(e) {\n if (!this.panelDragging) return;\n var el = this.$el.querySelector(\".ai-panel\");\n var pw = el ? el.clientWidth : 420;\n var ph = el ? el.clientHeight : 560;\n var p = this.point(e) || {};\n var dx = (p.clientX || 0) - this._pdStartX;\n var dy = (p.clientY || 0) - this._pdStartY; // 超过阈值才算真正拖动过,避免单击头部把面板位置锁死\n\n if (!this._pdMoved && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) this._pdMoved = true; // 全页面拖拽:以视口为边界\n\n var maxLeft = Math.max(0, window.innerWidth - pw - 24);\n var maxBottom = Math.max(24, window.innerHeight - ph - 24);\n this.panelLeft = Math.max(0, Math.min(this._pdStartLeft + dx, maxLeft));\n this.panelBottom = Math.max(24, Math.min(this._pdStartBottom - dy, maxBottom));\n },\n // 打开时初始化会话:优先拉取历史问答回显;无历史再走 SSE 总结(本地兜底)\n greet: function greet() {\n var _this4 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {\n var hasSummary, data, intro;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n if (!_this4.greetingLoading) {\n _context.next = 2;\n break;\n }\n\n return _context.abrupt(\"return\");\n\n case 2:\n _this4.greetingLoading = true;\n _this4.greetingLoadingText = \"正在加载审批详情\";\n _context.prev = 4;\n\n _this4.collectDetail();\n\n hasSummary = false;\n\n if (!(_this4.aiEnabled() && _this4.id)) {\n _context.next = 14;\n break;\n }\n\n _context.next = 10;\n return _this4.loadHistory(1);\n\n case 10:\n data = _context.sent;\n if (data && data.sessionId) _this4.sessionId = data.sessionId; // 先回显历史问答记录\n\n if (data && Array.isArray(data.records) && data.records.length) {\n data.records.forEach(function (r) {\n // 总结消息(msgKey=summary_*)已通过下方 summary 字段带开场语反显,这里跳过避免重复展示\n if (r.msgKey && String(r.msgKey).indexOf(\"summary_\") === 0) return;\n\n _this4.messages.push({\n id: ++msgSeq,\n role: r.role === \"user\" ? \"user\" : \"assistant\",\n content: r.content || \"\",\n thinking: r.thinking || \"\" // 历史记录新增 thinking 字段:反显为可折叠思考过程\n\n });\n\n if (r.id != null && _this4.histLoadedIds.indexOf(r.id) < 0) {\n _this4.histLoadedIds.push(r.id);\n }\n });\n } // 关键:history 接口返回的是后端基于最新上下文的共享总结缓存\n // summaryReady=true 且 summary 非空,说明已有总结,跳过 SSE 流式\n\n\n if (data && data.summaryReady && data.summary && String(data.summary).trim()) {\n intro = \"\\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\";\n\n _this4.messages.unshift({\n id: ++msgSeq,\n role: \"assistant\",\n content: intro + data.summary\n });\n\n hasSummary = true;\n }\n\n case 14:\n if (!hasSummary) {\n _context.next = 19;\n break;\n }\n\n // 已有共享总结:内容已就绪,关闭 loading\n _this4.greetingLoading = false;\n\n _this4.scrollBottom();\n\n _context.next = 21;\n break;\n\n case 19:\n _context.next = 21;\n return _this4.greetSummary();\n\n case 21:\n _context.prev = 21;\n // 兜底关闭 loading(总结流内部也会在内容就绪时提前关闭)\n _this4.greetingLoading = false;\n\n _this4.scrollBottom();\n\n return _context.finish(21);\n\n case 25:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee, null, [[4,, 21, 25]]);\n }))();\n },\n // 无历史时生成开场总结:接入后端走 SSE 流式;否则本地概览兜底。\n // 生成期间保持 loading 提示,首个内容到达后再展示开场语,避免出现只有半句的空气泡\n greetSummary: function greetSummary() {\n var _this5 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {\n var localSummary, intro, rt, msg, got;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n localSummary = _this5.buildSummary();\n intro = \"\\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\";\n\n if (!(_this5.aiEnabled() && _this5.id)) {\n _context2.next = 14;\n break;\n }\n\n _this5.greetingLoadingText = \"正在生成审批概览\"; // 首次总结携带页面最新上下文:后端会等待附件(ZIP/图片/PDF/Markdown)解析完成后再生成\n\n _context2.next = 6;\n return _this5.collectRealtimeContext();\n\n case 6:\n rt = _context2.sent;\n msg = null;\n got = false;\n _context2.next = 11;\n return aiApi.streamSummary({\n dataId: rt && rt.dataId ? rt.dataId : _this5.id,\n context: rt ? rt.context : null,\n contextBase64: rt ? rt.contextBase64 : \"\",\n userId: _this5.userInfo().no,\n userName: _this5.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"answer\" || event === \"agent_result\") {\n got = true;\n\n if (!msg) {\n // 首个增量到达:隐藏 loading,推送开场语气泡,随后增量追加\n _this5.greetingLoading = false;\n msg = {\n id: ++msgSeq,\n role: \"assistant\",\n content: intro\n };\n\n _this5.messages.push(msg);\n }\n\n if (event === \"agent_result\") {\n msg.content = intro + (data || \"\");\n } else {\n msg.content += data;\n }\n\n _this5.followBottom();\n }\n }\n });\n\n case 11:\n // 流式未返回任何内容(失败/空)时回退到本地概览,避免空白\n if (!got) {\n _this5.greetingLoading = false;\n\n _this5.push(\"assistant\", intro + localSummary);\n\n _this5.followBottom();\n }\n\n _context2.next = 17;\n break;\n\n case 14:\n _this5.greetingLoading = false;\n\n _this5.push(\"assistant\", intro + localSummary);\n\n _this5.followBottom();\n\n case 17:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }))();\n },\n // 拉取会话历史(含分页信息回填)\n loadHistory: function loadHistory(pageNum) {\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 u = _this6.userInfo();\n _context3.next = 3;\n return aiApi.getChatHistory({\n dataId: _this6.id,\n userId: u.no,\n pageNum: pageNum,\n pageSize: _this6.histPageSize\n });\n\n case 3:\n res = _context3.sent;\n\n if (!(res && res.data)) {\n _context3.next = 10;\n break;\n }\n\n _this6.histPageNum = res.data.pageNum || pageNum;\n _this6.histPageSize = res.data.pageSize || _this6.histPageSize;\n _this6.histTotal = res.data.total || 0;\n _this6.histHasMore = _this6.histPageNum * _this6.histPageSize < _this6.histTotal;\n return _context3.abrupt(\"return\", res.data);\n\n case 10:\n return _context3.abrupt(\"return\", null);\n\n case 11:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }))();\n },\n // 上拉分页加载更早的历史消息(前插,并保持当前视口位置)\n loadPrevHistory: function loadPrevHistory() {\n var _this7 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {\n var el, prevHeight, data, older;\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n if (!(_this7.loadingHistory || !_this7.histHasMore)) {\n _context4.next = 2;\n break;\n }\n\n return _context4.abrupt(\"return\");\n\n case 2:\n if (!(!_this7.aiEnabled() || !_this7.id)) {\n _context4.next = 4;\n break;\n }\n\n return _context4.abrupt(\"return\");\n\n case 4:\n _this7.loadingHistory = true;\n el = _this7.$refs.chatBody;\n prevHeight = el ? el.scrollHeight : 0;\n _context4.prev = 7;\n _context4.next = 10;\n return _this7.loadHistory(_this7.histPageNum + 1);\n\n case 10:\n data = _context4.sent;\n\n if (data && Array.isArray(data.records) && data.records.length) {\n // 前插更早记录并按其服务端 id 去重,防止后端重叠页/重复 id 造成重复展示\n older = [];\n data.records.forEach(function (r) {\n if (r.id != null && _this7.histLoadedIds.indexOf(r.id) >= 0) return;\n if (r.id != null) _this7.histLoadedIds.push(r.id);\n older.push({\n id: ++msgSeq,\n role: r.role === \"user\" ? \"user\" : \"assistant\",\n content: r.content || \"\",\n thinking: r.thinking || \"\" // 历史记录新增 thinking 字段:反显为可折叠思考过程\n\n });\n });\n if (older.length) _this7.messages = older.concat(_this7.messages);\n }\n\n _this7.$nextTick(function () {\n var el2 = _this7.$refs.chatBody;\n if (el2) el2.scrollTop = el2.scrollHeight - prevHeight;\n });\n\n case 13:\n _context4.prev = 13;\n _this7.loadingHistory = false;\n return _context4.finish(13);\n\n case 16:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4, null, [[7,, 13, 16]]);\n }))();\n },\n quickAsk: function quickAsk(q) {\n this.inputText = q;\n this.sendMessage();\n },\n // 审批详情气泡内的链接禁止跳转(markdown 渲染出的 <a> 不触发页面跳转)\n onBubbleClick: function onBubbleClick(e) {\n var el = e.target;\n var link = el && el.closest ? el.closest(\"a\") : null;\n if (!link) return;\n e.preventDefault();\n e.stopPropagation();\n },\n sendMessage: function sendMessage() {\n var _this8 = this;\n\n var text = (this.inputText || \"\").trim();\n if (!text || this.sending) return; // 审批概览生成期间禁止提问:避免总结流与问答流并发,\n // 造成消息顺序错乱、滚动相互抢占,以及后端同单总结未完成时问答排队/上下文不完整\n\n if (this.greetingLoading) 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 thinking: \"\",\n _thinkingOpen: true,\n _thinkingLoading: false,\n _thinkingSegs: []\n };\n this.messages.push(msg);\n this.streaming = true;\n this.scrollBottom();\n }\n\n this.getReply(text, function (chunk, isReasoning, isReplace) {\n if (msg) {\n if (isReasoning) {\n // 思考过程:写入独立字段并切分为段落,逐段淡入上浮展示\n msg.thinking += chunk;\n if (chunk) msg._thinkingSegs.push(chunk);\n msg._thinkingLoading = true;\n msg._thinkingOpen = true;\n } else if (isReplace) {\n // 终稿全量覆盖,避免与 answer 增量内容重复展示\n msg.content = chunk;\n } else {\n // 正式回答:正常追加\n msg.content += chunk;\n }\n\n _this8.followBottom();\n\n _this8.scrollThinkingToBottom();\n }\n }).then(function (reply) {\n if (msg) {\n if (!msg.content) msg.content = reply || _this8.fallbackReply(text); // 思考过程结束:停止滚动跟随并自动折叠\n\n msg._thinkingLoading = false;\n msg._thinkingOpen = false;\n } else {\n _this8.push(\"assistant\", _this8.fallbackReply(text));\n }\n\n _this8.streaming = false;\n _this8.sending = false;\n\n _this8.followBottom();\n });\n },\n // 思考内容是否已拆分成段落(含分段数组时按段渲染;历史/未分段消息退化为整体一段)\n hasThinkingSegs: function hasThinkingSegs(m) {\n return !!(m && m._thinkingSegs && m._thinkingSegs.length);\n },\n // details 原生折叠状态与数据同步,展开时滚动贴底\n syncThinkingOpen: function syncThinkingOpen(m, e) {\n var open = !!e.target.open;\n\n if (m._thinkingOpen !== open) {\n m._thinkingOpen = open;\n if (open) this.scrollThinkingToBottom();\n }\n },\n // DeepSeek 移动端风格:让思考框内容向上滚动,始终保持最新内容贴底可见\n scrollThinkingToBottom: function scrollThinkingToBottom() {\n var _this9 = this;\n\n this.$nextTick(function () {\n var container = _this9.$refs.chatBody;\n if (!container) return;\n var bodies = container.querySelectorAll(\".thinking-body.thinking-scroll\");\n var last = bodies[bodies.length - 1];\n if (last) last.scrollTop = last.scrollHeight;\n });\n },\n // 获取 AI 回复:已接入后端走流式接口;未接入时返回本地兜底\n getReply: function getReply(question, onDelta) {\n var _this10 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() {\n var payload, aiReply;\n return regeneratorRuntime.wrap(function _callee5$(_context5) {\n while (1) {\n switch (_context5.prev = _context5.next) {\n case 0:\n payload = {\n context: {\n type: _this10.type,\n status: _this10.status,\n typeName: _this10.typeName,\n id: _this10.id,\n summary: _this10.buildSummary(),\n fields: _this10.fields,\n detailData: _this10.rawDetail\n },\n question: question\n };\n _context5.next = 3;\n return _this10.requestAi(payload, onDelta);\n\n case 3:\n aiReply = _context5.sent;\n return _context5.abrupt(\"return\", aiReply && String(aiReply).trim() ? String(aiReply) : _this10.fallbackReply(question));\n\n case 5:\n case \"end\":\n return _context5.stop();\n }\n }\n }, _callee5);\n }))();\n },\n // AI 后端是否可用\n aiEnabled: function aiEnabled() {\n return aiApi.aiConfigured() && Boolean(this.id);\n },\n // 查询 AI 助手入口开关(后端 3.11):仅明确返回 isEnabled=false 时隐藏悬浮入口;\n // 未配置 baseUrl、未指定 entryType 或接口异常时保持展示(fail-open,避免后端抖动导致入口消失)\n checkEntryStatus: function checkEntryStatus() {\n var _this11 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() {\n var res, enabled;\n return regeneratorRuntime.wrap(function _callee6$(_context6) {\n while (1) {\n switch (_context6.prev = _context6.next) {\n case 0:\n if (!(!_this11.entryStatusCheck || !_this11.entryType)) {\n _context6.next = 2;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 2:\n if (aiApi.aiConfigured()) {\n _context6.next = 4;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 4:\n _context6.next = 6;\n return aiApi.getEntryStatus(_this11.entryType);\n\n case 6:\n res = _context6.sent;\n\n if (!(!res || !res.data)) {\n _context6.next = 9;\n break;\n }\n\n return _context6.abrupt(\"return\");\n\n case 9:\n _this11.entryStatusLoaded = true;\n enabled = res.data.isEnabled !== false;\n\n if (enabled) {\n _context6.next = 15;\n break;\n }\n\n // 开关关闭:收起已展开的面板并隐藏悬浮入口\n _this11.entryEnabled = false;\n _this11.panelVisible = false;\n return _context6.abrupt(\"return\");\n\n case 15:\n _this11.entryEnabled = true;\n\n _this11.$nextTick(function () {\n return _this11.snapBtn();\n });\n\n case 17:\n case \"end\":\n return _context6.stop();\n }\n }\n }, _callee6);\n }))();\n },\n // 采集页面最新实时上下文:优先调用宿主注入的 contextProvider,\n // 每次问答/首次总结前采集,保证回答基于页面(含 approvalLog)的最新状态\n collectRealtimeContext: function collectRealtimeContext() {\n var _this12 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee7() {\n var res, contextBase64, context;\n return regeneratorRuntime.wrap(function _callee7$(_context7) {\n while (1) {\n switch (_context7.prev = _context7.next) {\n case 0:\n if (!(typeof _this12.contextProvider !== \"function\")) {\n _context7.next = 2;\n break;\n }\n\n return _context7.abrupt(\"return\", null);\n\n case 2:\n _context7.prev = 2;\n _context7.next = 5;\n return _this12.contextProvider();\n\n case 5:\n res = _context7.sent;\n\n if (res) {\n _context7.next = 8;\n break;\n }\n\n return _context7.abrupt(\"return\", null);\n\n case 8:\n contextBase64 = res.contextBase64 || res.base64 || \"\";\n context = res.context || res.summary || null;\n\n if (!(!contextBase64 && !context)) {\n _context7.next = 12;\n break;\n }\n\n return _context7.abrupt(\"return\", null);\n\n case 12:\n return _context7.abrupt(\"return\", {\n dataId: res.dataId || _this12.id,\n contextBase64: contextBase64,\n context: context\n });\n\n case 15:\n _context7.prev = 15;\n _context7.t0 = _context7[\"catch\"](2);\n console.warn(\"[AI审批助手] 采集前端实时上下文失败\", _context7.t0);\n return _context7.abrupt(\"return\", null);\n\n case 19:\n case \"end\":\n return _context7.stop();\n }\n }\n }, _callee7, null, [[2, 15]]);\n }))();\n },\n // 当前用户信息(来自 props,替代宿主 Vuex)\n userInfo: function userInfo() {\n return {\n no: this.userId || \"\",\n name: this.userName || \"\"\n };\n },\n // 创建/复用 AI 对话会话(携带前端实时上下文,缺失时后端按 dataId 复用已上报上下文)\n ensureSession: function ensureSession(rt) {\n var _this13 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() {\n var u, res;\n return regeneratorRuntime.wrap(function _callee8$(_context8) {\n while (1) {\n switch (_context8.prev = _context8.next) {\n case 0:\n if (!_this13.sessionId) {\n _context8.next = 2;\n break;\n }\n\n return _context8.abrupt(\"return\", _this13.sessionId);\n\n case 2:\n u = _this13.userInfo();\n _context8.next = 5;\n return aiApi.createChatSession({\n dataId: rt && rt.dataId ? rt.dataId : _this13.id,\n userId: u.no,\n userName: u.name,\n context: rt ? rt.context : null,\n contextBase64: rt ? rt.contextBase64 : \"\"\n });\n\n case 5:\n res = _context8.sent;\n\n if (!(res && res.data && res.data.sessionId)) {\n _context8.next = 9;\n break;\n }\n\n _this13.sessionId = res.data.sessionId;\n return _context8.abrupt(\"return\", _this13.sessionId);\n\n case 9:\n return _context8.abrupt(\"return\", \"\");\n\n case 10:\n case \"end\":\n return _context8.stop();\n }\n }\n }, _callee8);\n }))();\n },\n // 对接后端 AI 流式问答接口:创建会话 + SSE 实时增量;异常时返回 null\n requestAi: function requestAi(payload, onDelta) {\n var _this14 = this;\n\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {\n var rt, sessionId, text, failed;\n return regeneratorRuntime.wrap(function _callee9$(_context9) {\n while (1) {\n switch (_context9.prev = _context9.next) {\n case 0:\n if (_this14.aiEnabled()) {\n _context9.next = 2;\n break;\n }\n\n return _context9.abrupt(\"return\", null);\n\n case 2:\n _context9.prev = 2;\n _context9.next = 5;\n return _this14.collectRealtimeContext();\n\n case 5:\n rt = _context9.sent;\n _context9.next = 8;\n return _this14.ensureSession(rt);\n\n case 8:\n sessionId = _context9.sent;\n\n if (sessionId) {\n _context9.next = 11;\n break;\n }\n\n return _context9.abrupt(\"return\", null);\n\n case 11:\n text = \"\";\n failed = false;\n _context9.next = 15;\n return aiApi.streamChat({\n sessionId: sessionId,\n message: payload.question,\n dataId: rt && rt.dataId ? rt.dataId : _this14.id,\n context: rt ? rt.context : null,\n contextBase64: rt ? rt.contextBase64 : \"\",\n userId: _this14.userInfo().no,\n userName: _this14.userInfo().name,\n onEvent: function onEvent(event, data) {\n if (event === \"reasoning\") {\n // 思考过程:单独回调(isReasoning=true),不写入回答文本\n if (onDelta) onDelta(data, true);\n } else if (event === \"answer\") {\n text += data;\n if (onDelta) onDelta(data, false);\n } else if (event === \"agent_result\") {\n // agent_result 携带完整终稿:覆盖已流式追加的 answer 内容,\n // 若按追加处理同一答案会展示两遍\n text = data;\n if (onDelta) onDelta(data, false, true);\n } else if (event === \"error\") {\n failed = true;\n }\n }\n });\n\n case 15:\n if (!failed) {\n _context9.next = 17;\n break;\n }\n\n return _context9.abrupt(\"return\", null);\n\n case 17:\n return _context9.abrupt(\"return\", text && text.trim() ? text : null);\n\n case 20:\n _context9.prev = 20;\n _context9.t0 = _context9[\"catch\"](2);\n return _context9.abrupt(\"return\", null);\n\n case 23:\n case \"end\":\n return _context9.stop();\n }\n }\n }, _callee9, null, [[2, 20]]);\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 _this15 = this;\n\n var list = [];\n var seen = new Set();\n\n var pushEntry = function pushEntry(label, value) {\n var v = _this15.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 _this16 = 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: _this16.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(normalizeMd(text || \"\"));\n },\n escapeHtml: function escapeHtml(str) {\n return String(str).replace(/[&<>\"']/g, function (c) {\n return {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\"\n }[c];\n });\n },\n scrollBottom: function scrollBottom() {\n var _this17 = this;\n\n this.$nextTick(function () {\n var el = _this17.$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 if (!this.loadingHistory && this.histHasMore && el.scrollTop < 60) {\n this.loadPrevHistory();\n }\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; // 先切换为视口 fixed 定位(视觉位置不变),再按视口坐标记录拖拽基准;\n // 面板打开时一并切换,保证浮球/面板坐标基准一致\n\n this.toFixedFloat();\n if (this.panelVisible) this.toFixedPanel();\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 // 若正在拖动弹窗面板,走面板拖拽逻辑\n if (this.panelDragging) {\n this.onPanelDrag(e);\n return;\n }\n\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\n var maxW = window.innerWidth - 46;\n var maxH = window.innerHeight - 44;\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 // 同步浮球坐标到数据层,面板吸附才能实时跟随(关闭状态不写入,保持拖拽性能)\n this.btnLeft = left;\n this.btnBottom = bottom;\n this.snapPanel();\n }\n },\n stopDrag: function stopDrag() {},\n endDrag: function endDrag() {\n // 结束弹窗拖拽:真正移动过才记录,再次打开保持该位置\n if (this.panelDragging) {\n this.panelDragging = false;\n if (this._pdMoved) this.panelMoved = true;\n return;\n }\n\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; // 拖动过才吸附贴边;靠近右边时收成半角标(fixed 定位下以视口宽度判断)\n\n if (window.innerWidth - (left + 46) <= 30) {\n this.tucked = true;\n this.btnLeft = window.innerWidth - 46;\n } // 贴边收起会修正 btnLeft,面板需按新坐标重新吸附,避免打开时错位\n\n\n if (this.panelVisible) this.snapPanel();\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
211
|
|
|
234
212
|
/***/ }),
|
|
235
213
|
|
|
@@ -252,7 +230,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
|
|
|
252
230
|
/*! no static exports found */
|
|
253
231
|
/***/ (function(module, exports, __webpack_require__) {
|
|
254
232
|
|
|
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, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\r\\n/* 审批气泡内 markdown 内容样式:v-html 注入的元素(h1-li-a-strong-code 等)不带 scoped data 属性,\\r\\n scoped 规则(.msg__bubble xxx[data-v-*])对其完全不生效,必须用非 scoped 样式。\\r\\n 用户气泡内容经 escapeHtml 转义不会产生真实标签,此规则仅作用于助手气泡的 markdown 渲染。 */\\r\\n\\r\\n/* Markdown 内容排版 */\\n.ai-panel .msg__bubble h1,\\r\\n.ai-panel .msg__bubble h2,\\r\\n.ai-panel .msg__bubble h3,\\r\\n.ai-panel .msg__bubble h4,\\r\\n.ai-panel .msg__bubble h5,\\r\\n.ai-panel .msg__bubble h6 {\\r\\n font-size: 14px;\\r\\n font-weight: 600;\\r\\n color: #1f2329;\\r\\n margin: 8px 0 4px;\\r\\n padding-left: 8px;\\r\\n border-left: 3px solid #3067d9;\\r\\n line-height: 1.5;\\n}\\n.ai-panel .msg__bubble h1:first-child,\\r\\n.ai-panel .msg__bubble h2:first-child,\\r\\n.ai-panel .msg__bubble h3:first-child,\\r\\n.ai-panel .msg__bubble h4:first-child,\\r\\n.ai-panel .msg__bubble h5:first-child,\\r\\n.ai-panel .msg__bubble h6:first-child {\\r\\n margin-top: 0;\\n}\\n.ai-panel .msg__bubble p {\\r\\n /* margin: 4px 0; */\\n}\\n.ai-panel .msg__bubble p:first-child {\\r\\n margin-top: 0;\\n}\\n.ai-panel .msg__bubble ul,\\r\\n.ai-panel .msg__bubble ol {\\r\\n margin: 6px 0;\\r\\n padding-left: 20px;\\r\\n list-style-position: outside;\\n}\\n.ai-panel .msg__bubble ul {\\r\\n list-style: disc;\\n}\\n.ai-panel .msg__bubble ol {\\r\\n list-style: decimal;\\n}\\n.ai-panel .msg__bubble li {\\r\\n margin: 3px 0;\\r\\n padding: 4px 2px;\\n}\\r\\n\\r\\n/* 紧凑 <li> 与 <li><p> 结构视觉一致:<p> 的间距统一转移到 li 内边距,避免条目高度不一 */\\n.ai-panel .msg__bubble li > p {\\r\\n margin: 0;\\n}\\n.ai-panel .msg__bubble li > p + p {\\r\\n margin-top: 4px;\\n}\\n.ai-panel .msg__bubble blockquote {\\r\\n margin: 8px 0;\\r\\n padding: 2px 12px;\\r\\n border-left: 3px solid #dde3ee;\\r\\n background: #fafbfc;\\r\\n border-radius: 4px;\\r\\n color: #86909c;\\n}\\n.ai-panel .msg__bubble hr {\\r\\n border: none;\\r\\n border-top: 1px dashed #e4e7ed;\\r\\n margin: 10px 0;\\n}\\n.ai-panel .msg__bubble strong {\\r\\n color: #1f2329;\\r\\n font-weight: 600;\\n}\\n.ai-panel .msg__bubble code {\\r\\n background: #f0f2f5;\\r\\n color: #d83931;\\r\\n padding: 1px 4px;\\r\\n border-radius: 3px;\\r\\n font-size: 12px;\\n}\\n.ai-panel .msg__bubble pre {\\r\\n background: #f6f7f8;\\r\\n padding: 8px;\\r\\n border-radius: 6px;\\r\\n overflow-x: auto;\\r\\n margin: 6px 0;\\n}\\n.ai-panel .msg__bubble pre code {\\r\\n background: none;\\r\\n color: #1f2329;\\r\\n padding: 0;\\n}\\r\\n\\r\\n/* 链接去样式化:与正文同色、无下划线、非手型,配合 onBubbleClick 完全禁用跳转 */\\n.ai-panel .msg__bubble a,\\r\\n.ai-panel .msg__bubble a:hover,\\r\\n.ai-panel .msg__bubble a:visited {\\r\\n color: inherit;\\r\\n text-decoration: none;\\r\\n cursor: default;\\n}\\r\\n\\r\\n/* H5 放大 markdown 标题/行内代码(同样需非 scoped 才能命中 v-html 内容) */\\n@media (max-width: 480px) {\\n.ai-panel .msg__bubble h1,\\r\\n .ai-panel .msg__bubble h2,\\r\\n .ai-panel .msg__bubble h3,\\r\\n .ai-panel .msg__bubble h4,\\r\\n .ai-panel .msg__bubble h5,\\r\\n .ai-panel .msg__bubble h6 {\\r\\n font-size: 17px;\\n}\\n.ai-panel .msg__bubble code {\\r\\n font-size: 14px;\\n}\\n}\\r\\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--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
233
|
+
eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\r\\n/* 审批气泡内 markdown 内容样式:v-html 注入的元素(h1-li-a-strong-code 等)不带 scoped data 属性,\\r\\n scoped 规则(.msg__bubble xxx[data-v-*])对其完全不生效,必须用非 scoped 样式。\\r\\n 用户气泡内容经 escapeHtml 转义不会产生真实标签,此规则仅作用于助手气泡的 markdown 渲染。 */\\r\\n\\r\\n/* Markdown 内容排版 */\\n.ai-panel .msg__bubble h1,\\r\\n.ai-panel .msg__bubble h2,\\r\\n.ai-panel .msg__bubble h3,\\r\\n.ai-panel .msg__bubble h4,\\r\\n.ai-panel .msg__bubble h5,\\r\\n.ai-panel .msg__bubble h6 {\\r\\n font-size: 14px;\\r\\n font-weight: 600;\\r\\n color: #1f2329;\\r\\n margin: 8px 0 4px;\\r\\n padding-left: 8px;\\r\\n border-left: 3px solid #3067d9;\\r\\n line-height: 1.5;\\n}\\n.ai-panel .msg__bubble h1:first-child,\\r\\n.ai-panel .msg__bubble h2:first-child,\\r\\n.ai-panel .msg__bubble h3:first-child,\\r\\n.ai-panel .msg__bubble h4:first-child,\\r\\n.ai-panel .msg__bubble h5:first-child,\\r\\n.ai-panel .msg__bubble h6:first-child {\\r\\n margin-top: 0;\\n}\\n.ai-panel .msg__bubble p {\\r\\n /* margin: 4px 0; */\\n}\\n.ai-panel .msg__bubble p:first-child {\\r\\n margin-top: 0;\\n}\\n.ai-panel .msg__bubble ul,\\r\\n.ai-panel .msg__bubble ol {\\r\\n margin: 6px 0;\\r\\n padding-left: 20px;\\r\\n list-style-position: outside;\\n}\\n.ai-panel .msg__bubble ul {\\r\\n list-style: disc;\\n}\\n.ai-panel .msg__bubble ol {\\r\\n list-style: decimal;\\n}\\n.ai-panel .msg__bubble li {\\r\\n margin: 3px 0;\\r\\n padding: 4px 2px;\\n}\\r\\n\\r\\n/* 紧凑 <li> 与 <li><p> 结构视觉一致:<p> 的间距统一转移到 li 内边距,避免条目高度不一 */\\n.ai-panel .msg__bubble li > p {\\r\\n margin: 0;\\n}\\n.ai-panel .msg__bubble li > p + p {\\r\\n margin-top: 4px;\\n}\\n.ai-panel .msg__bubble blockquote {\\r\\n margin: 8px 0;\\r\\n padding: 2px 12px;\\r\\n border-left: 3px solid #dde3ee;\\r\\n background: #fafbfc;\\r\\n border-radius: 4px;\\r\\n color: #86909c;\\n}\\n.ai-panel .msg__bubble hr {\\r\\n border: none;\\r\\n border-top: 1px dashed #e4e7ed;\\r\\n margin: 10px 0;\\n}\\n.ai-panel .msg__bubble strong {\\r\\n color: #1f2329;\\r\\n font-weight: 600;\\n}\\n.ai-panel .msg__bubble code {\\r\\n background: #f0f2f5;\\r\\n color: #d83931;\\r\\n padding: 1px 4px;\\r\\n border-radius: 3px;\\r\\n font-size: 12px;\\n}\\n.ai-panel .msg__bubble pre {\\r\\n background: #f6f7f8;\\r\\n padding: 8px;\\r\\n border-radius: 6px;\\r\\n overflow-x: auto;\\r\\n margin: 6px 0;\\n}\\n.ai-panel .msg__bubble pre code {\\r\\n background: none;\\r\\n color: #1f2329;\\r\\n padding: 0;\\n}\\r\\n\\r\\n/* 链接去样式化:与正文同色、无下划线、非手型,配合 onBubbleClick 完全禁用跳转 */\\n.ai-panel .msg__bubble a,\\r\\n.ai-panel .msg__bubble a:hover,\\r\\n.ai-panel .msg__bubble a:visited {\\r\\n color: inherit;\\r\\n text-decoration: none;\\r\\n cursor: default;\\n}\\r\\n\\r\\n/* H5 放大 markdown 标题/行内代码(同样需非 scoped 才能命中 v-html 内容) */\\n@media (max-width: 480px) {\\n.ai-panel .msg__bubble h1,\\r\\n .ai-panel .msg__bubble h2,\\r\\n .ai-panel .msg__bubble h3,\\r\\n .ai-panel .msg__bubble h4,\\r\\n .ai-panel .msg__bubble h5,\\r\\n .ai-panel .msg__bubble h6 {\\r\\n font-size: 17px;\\n}\\n.ai-panel .msg__bubble code {\\r\\n font-size: 14px;\\n}\\n}\\r\\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--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
|
256
234
|
|
|
257
235
|
/***/ }),
|
|
258
236
|
|
|
@@ -1199,7 +1177,7 @@ eval("module.exports = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAABYC
|
|
|
1199
1177
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1200
1178
|
|
|
1201
1179
|
"use strict";
|
|
1202
|
-
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.configureAI = configureAI;\nexports.aiConfigured = aiConfigured;\nexports.getInstance = getInstance;\nexports.getSummary = getSummary;\nexports.initInstance = initInstance;\nexports.extractApprovalInfo = extractApprovalInfo;\nexports.getRiskTips = getRiskTips;\nexports.generateOpinion = generateOpinion;\nexports.createChatSession = createChatSession;\nexports.getEntryStatus = getEntryStatus;\nexports.getEntryStatusList = getEntryStatusList;\nexports.getChatHistory = getChatHistory;\nexports.streamChat = streamChat;\nexports.streamSummary = streamSummary;\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\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\n/**\r\n * AI 审批助手 接口模块(外部系统 zj-ai-agent)\r\n * 对接文档:外部对接-AI审批助手API文档.md\r\n * 说明:\r\n * - 该服务为独立后端,基础地址通过 configureAI({ baseUrl }) 注入(示例:https://api.xxx.cn)\r\n * - 接口统一前缀 /api/v1/approval-ai 已在此拼接,只填 host\r\n * - 未配置 baseUrl 时返回 null,由调用方走本地兜底\r\n */\nvar BASE = \"https://ai.jybtech.cn\" || false;\nvar PREFIX = \"/api/v1/approval-ai\"; // 登录态 token:init 接口需要 Authorization 头读取 low-code 表单快照(服务端不落库),\n// 通过 configureAI({ authToken }) 注入,req() 统一携带\n\nvar AUTH_TOKEN = \"\";\n/** 注入 AI 服务地址(覆盖式,可多次调用) */\n\nfunction configureAI() {\n var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n baseUrl = _ref.baseUrl,\n authToken = _ref.authToken;\n\n // BASE = (baseUrl || \"\").replace(/\\/+$/, \"\")\n if (authToken !== undefined) AUTH_TOKEN = authToken;\n}\n/** 是否已配置 AI 服务地址 */\n\n\nfunction aiConfigured() {\n console.log(BASE);\n return Boolean(BASE);\n}\n\nfunction checkCode(data, ref) {\n if (data && (data.code === 200 || data.code === \"200\")) return data;\n if (ref && ref.data) ref.data = data;\n return null;\n}\n\nfunction req(_x) {\n return _req.apply(this, arguments);\n}\n/** 3.1 获取审批单完整数据 */\n\n\nfunction _req() {\n _req = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee(path) {\n var options,\n url,\n params,\n _options$method,\n method,\n body,\n q,\n qs,\n init,\n resp,\n text,\n json,\n _args = arguments;\n\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n options = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};\n\n if (BASE) {\n _context.next = 3;\n break;\n }\n\n return _context.abrupt(\"return\", null);\n\n case 3:\n url = BASE + PREFIX + path;\n params = options.params, _options$method = options.method, method = _options$method === void 0 ? \"GET\" : _options$method, body = options.body;\n\n if (params && (0, _typeof2.default)(params) === \"object\") {\n q = new URLSearchParams();\n Object.keys(params).forEach(function (k) {\n var v = params[k];\n if (v !== undefined && v !== null) q.append(k, v);\n });\n qs = q.toString();\n if (qs) url += (url.includes(\"?\") ? \"&\" : \"?\") + qs;\n }\n\n init = {\n method: method,\n headers: {\n Accept: \"application/json\"\n }\n };\n if (AUTH_TOKEN) init.headers[\"Authorization\"] = \"Bearer \".concat(AUTH_TOKEN);\n\n if (body !== undefined) {\n init.headers[\"Content-Type\"] = \"application/json\";\n init.body = typeof body === \"string\" ? body : JSON.stringify(body);\n }\n\n _context.prev = 9;\n _context.next = 12;\n return fetch(url, init);\n\n case 12:\n resp = _context.sent;\n _context.next = 15;\n return resp.text();\n\n case 15:\n text = _context.sent;\n json = null;\n\n try {\n json = text ? JSON.parse(text) : null;\n } catch (e) {\n json = null;\n }\n\n return _context.abrupt(\"return\", checkCode(json));\n\n case 21:\n _context.prev = 21;\n _context.t0 = _context[\"catch\"](9);\n return _context.abrupt(\"return\", null);\n\n case 24:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee, null, [[9, 21]]);\n }));\n return _req.apply(this, arguments);\n}\n\nfunction getInstance(approvalNo) {\n return req(\"/instances/\".concat(encodeURIComponent(approvalNo)));\n}\n/** 3.2 AI 生成审批单总结 */\n\n\nfunction getSummary(approvalNo) {\n return req(\"/instances/\".concat(encodeURIComponent(approvalNo), \"/summary\"), {\n method: \"POST\"\n });\n}\n/** 3.2.1 审批单初始化(存量单据入库 + 生成缓存总结):\r\n * 历史接口无总结时调用,按数据源重建落库并同步生成 AI 总结进共享缓存;幂等 */\n\n\nfunction initInstance(approvalNo) {\n return req(\"/instances/\".concat(encodeURIComponent(approvalNo), \"/init\"), {\n method: \"POST\"\n });\n}\n/** 3.3 AI 信息抽取(结构化) */\n\n\nfunction extractApprovalInfo(approvalNo) {\n return req(\"/instances/\".concat(encodeURIComponent(approvalNo), \"/extract\"), {\n method: \"POST\"\n });\n}\n/** 3.4 AI 风险提示 */\n\n\nfunction getRiskTips(approvalNo) {\n return req(\"/instances/\".concat(encodeURIComponent(approvalNo), \"/risk-tips\"), {\n method: \"POST\"\n });\n}\n/** 3.5 AI 生成审批意见 */\n\n\nfunction generateOpinion(approvalNo) {\n var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},\n action = _ref2.action,\n hint = _ref2.hint;\n\n return req(\"/instances/\".concat(encodeURIComponent(approvalNo), \"/generate-opinion\"), {\n method: \"POST\",\n body: {\n action: action,\n hint: hint\n }\n });\n}\n/** 3.6 创建 AI 对话会话 */\n\n\nfunction createChatSession(_ref3) {\n var approvalNo = _ref3.approvalNo,\n userId = _ref3.userId,\n userName = _ref3.userName;\n return req(\"/chat/sessions\", {\n method: \"POST\",\n body: {\n approvalNo: approvalNo,\n userId: userId,\n userName: userName\n }\n });\n}\n/**\r\n * 3.11 查询 AI 助手入口显示状态(指定类型)\r\n * @param {string} entryType custom=自定义表单 | qgb=企管帮\r\n * @returns {Promise<{entryType,isEnabled}|null>} 解析后取 res.data\r\n */\n\n\nfunction getEntryStatus(entryType) {\n return req(\"/entry-status/\".concat(encodeURIComponent(entryType)));\n}\n/**\r\n * 3.11 查询 AI 助手入口显示状态(全部类型)\r\n * @returns {Promise<Array<{entryType,entryName,isEnabled}>|null>} 解析后取 res.data\r\n */\n\n\nfunction getEntryStatusList() {\n return req(\"/entry-status\");\n}\n/**\r\n * 3.9 获取当前用户的会话与历史问答(一个接口完成会话初始化 + 历史回显)\r\n * 会话不存在时自动创建;summary 命中缓存直接返回、未命中为空串;\r\n * records 为空表示该用户还没有问答记录。\r\n * @param {object} params { approvalNo, userId, userName?, pageNum?, pageSize? }\r\n * 返回 { sessionId, summary, total, pageNum, pageSize, records[] }\r\n */\n\n\nfunction getChatHistory() {\n var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n approvalNo = _ref4.approvalNo,\n userId = _ref4.userId,\n userName = _ref4.userName,\n _ref4$pageNum = _ref4.pageNum,\n pageNum = _ref4$pageNum === void 0 ? 1 : _ref4$pageNum,\n _ref4$pageSize = _ref4.pageSize,\n pageSize = _ref4$pageSize === void 0 ? 20 : _ref4$pageSize;\n\n var query = new URLSearchParams({\n approvalNo: approvalNo,\n userId: userId\n });\n if (userName) query.set(\"userName\", userName);\n query.set(\"pageNum\", String(pageNum));\n query.set(\"pageSize\", String(pageSize));\n return req(\"/chat/history?\".concat(query.toString()), {\n method: \"GET\"\n });\n}\n/**\r\n * 通用 SSE 读取:fetch + ReadableStream,按空行切块并派发事件文本\r\n * @param {string} url\r\n * @param {(event: string, data: string) => void} onEvent\r\n */\n\n\nfunction readSSE(_x2, _x3) {\n return _readSSE.apply(this, arguments);\n}\n/**\r\n * 3.7 AI 流式问答(SSE)\r\n * @param {object} opts { sessionId, message, userId, userName, onEvent }\r\n * onEvent(event, data):event ∈ reasoning | answer | msgKey | agent_result | error\r\n * 返回 Promise,完成时 resolve。\r\n */\n\n\nfunction _readSSE() {\n _readSSE = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(url, onEvent) {\n var resp, reader, decoder, buf, _yield$reader$read, done, value, idx, block;\n\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n _context2.prev = 0;\n _context2.next = 3;\n return fetch(url, {\n headers: {\n Accept: \"text/event-stream\"\n }\n });\n\n case 3:\n resp = _context2.sent;\n\n if (!(!resp.ok || !resp.body)) {\n _context2.next = 7;\n break;\n }\n\n onEvent(\"error\", \"SSE \\u8BF7\\u6C42\\u5931\\u8D25\\uFF1AHTTP \".concat(resp.status));\n return _context2.abrupt(\"return\", false);\n\n case 7:\n reader = resp.body.getReader();\n decoder = new TextDecoder(\"utf-8\");\n buf = \"\";\n\n case 10:\n if (false) {}\n\n _context2.next = 13;\n return reader.read();\n\n case 13:\n _yield$reader$read = _context2.sent;\n done = _yield$reader$read.done;\n value = _yield$reader$read.value;\n\n if (!done) {\n _context2.next = 18;\n break;\n }\n\n return _context2.abrupt(\"break\", 23);\n\n case 18:\n buf += decoder.decode(value, {\n stream: true\n }); // SSE 事件以空行分隔\n\n idx = void 0;\n\n while ((idx = buf.indexOf(\"\\n\\n\")) >= 0) {\n block = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n emitBlock(normalizeBlock(block), onEvent);\n }\n\n _context2.next = 10;\n break;\n\n case 23:\n // 末尾残留块\n if (buf.trim()) emitBlock(normalizeBlock(buf), onEvent);\n return _context2.abrupt(\"return\", true);\n\n case 27:\n _context2.prev = 27;\n _context2.t0 = _context2[\"catch\"](0);\n onEvent(\"error\", \"SSE 连接异常\");\n return _context2.abrupt(\"return\", false);\n\n case 31:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2, null, [[0, 27]]);\n }));\n return _readSSE.apply(this, arguments);\n}\n\nfunction streamChat(_x4) {\n return _streamChat.apply(this, arguments);\n}\n/**\r\n * 3.3 AI 生成审批单总结(SSE 流式)\r\n * 命中缓存时一次性推送完整文本(agent_result);未命中时逐段推送增量文本(answer)。\r\n * @param {string} approvalNo 审批单号\r\n * @param {object} opts { onEvent }\r\n * onEvent(event, data):event ∈ answer | agent_result | error\r\n * 返回 Promise<boolean>,完成时 resolve。\r\n */\n\n\nfunction _streamChat() {\n _streamChat = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(_ref5) {\n var sessionId, message, userId, userName, _ref5$onEvent, onEvent, url;\n\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n sessionId = _ref5.sessionId, message = _ref5.message, userId = _ref5.userId, userName = _ref5.userName, _ref5$onEvent = _ref5.onEvent, onEvent = _ref5$onEvent === void 0 ? function () {} : _ref5$onEvent;\n\n if (BASE) {\n _context3.next = 4;\n break;\n }\n\n onEvent(\"error\", \"AI 服务未配置\");\n return _context3.abrupt(\"return\", false);\n\n case 4:\n url = BASE + PREFIX + \"/chat/stream?\" + new URLSearchParams((0, _objectSpread2.default)((0, _objectSpread2.default)({\n sessionId: sessionId,\n message: message\n }, userId ? {\n userId: userId\n } : {}), userName ? {\n userName: userName\n } : {})).toString();\n return _context3.abrupt(\"return\", readSSE(url, onEvent));\n\n case 6:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3);\n }));\n return _streamChat.apply(this, arguments);\n}\n\nfunction streamSummary(_x5) {\n return _streamSummary.apply(this, arguments);\n}\n\nfunction _streamSummary() {\n _streamSummary = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(approvalNo) {\n var _ref6,\n _ref6$onEvent,\n onEvent,\n url,\n _args4 = arguments;\n\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n _ref6 = _args4.length > 1 && _args4[1] !== undefined ? _args4[1] : {}, _ref6$onEvent = _ref6.onEvent, onEvent = _ref6$onEvent === void 0 ? function () {} : _ref6$onEvent;\n\n if (BASE) {\n _context4.next = 4;\n break;\n }\n\n onEvent(\"error\", \"AI 服务未配置\");\n return _context4.abrupt(\"return\", false);\n\n case 4:\n url = BASE + PREFIX + \"/instances/\".concat(encodeURIComponent(approvalNo), \"/summary/stream\");\n return _context4.abrupt(\"return\", readSSE(url, onEvent));\n\n case 6:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4);\n }));\n return _streamSummary.apply(this, arguments);\n}\n\nfunction normalizeBlock(block) {\n var event = \"message\";\n var data = [];\n\n var _iterator = (0, _createForOfIteratorHelper2.default)(block.split(\"\\n\")),\n _step;\n\n try {\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var line = _step.value;\n\n if (line.startsWith(\"event:\")) {\n event = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n data.push(line.slice(5).trim());\n }\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return {\n event: event,\n data: data.join(\"\\n\")\n };\n}\n\nfunction emitBlock(_ref7, onEvent) {\n var event = _ref7.event,\n data = _ref7.data;\n if (!data) return;\n onEvent(event, data);\n}\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/apiApprovalAi.js?");
|
|
1180
|
+
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.configureAI = configureAI;\nexports.aiConfigured = aiConfigured;\nexports.createChatSession = createChatSession;\nexports.getEntryStatus = getEntryStatus;\nexports.getEntryStatusList = getEntryStatusList;\nexports.getChatHistory = getChatHistory;\nexports.streamChat = streamChat;\nexports.streamSummary = streamSummary;\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\n/**\n * AI 审批助手 接口模块(外部系统 zj-ai-agent)\n * 对接文档:外部对接-AI审批助手-前端上下文版API文档.md\n * 说明:\n * - 该服务为独立后端,基础地址通过 configureAI({ baseUrl }) 注入(示例:https://api.xxx.cn)\n * - 接口统一前缀 /api/v1/approval-assistant 已在此拼接,只填 host\n * - 未配置 baseUrl 时返回 null,由调用方走本地兜底\n * - 问答/总结均以「前端实时上下文」为准:调用方通过 contextProvider 每次携带页面最新数据\n * (含 approvalLog、formLabelValueMap、comments、附件地址),后端据此解析附件并生成\n */\nvar BASE = \"https://ai.jybtech.cn\" || false;\nvar PREFIX = '/api/v1/approval-assistant'; // 登录态 token:下载鉴权附件用(服务端不落库),通过 configureAI({ authToken }) 注入,req() 统一携带\n\nvar AUTH_TOKEN = '';\n/** 注入 AI 服务地址(覆盖式,可多次调用) */\n\nfunction configureAI() {\n var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n baseUrl = _ref.baseUrl,\n authToken = _ref.authToken;\n\n if (baseUrl) BASE = String(baseUrl).replace(/\\/+$/, '');\n if (authToken !== undefined) AUTH_TOKEN = authToken;\n}\n/** 是否已配置 AI 服务地址 */\n\n\nfunction aiConfigured() {\n return Boolean(BASE);\n}\n\nfunction checkCode(data) {\n if (data && (data.code === 200 || data.code === '200')) return data;\n return null;\n}\n\nfunction req(_x) {\n return _req.apply(this, arguments);\n}\n/**\n * 3.6 创建/复用 AI 对话会话(同时携带前端实时上下文,缺失上下文时后端会校验上报)\n */\n\n\nfunction _req() {\n _req = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee(path) {\n var options,\n url,\n params,\n _options$method,\n method,\n body,\n q,\n qs,\n init,\n resp,\n text,\n json,\n _args = arguments;\n\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n options = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};\n\n if (BASE) {\n _context.next = 3;\n break;\n }\n\n return _context.abrupt(\"return\", null);\n\n case 3:\n url = BASE + PREFIX + path;\n params = options.params, _options$method = options.method, method = _options$method === void 0 ? 'GET' : _options$method, body = options.body;\n\n if (params && (0, _typeof2.default)(params) === 'object') {\n q = new URLSearchParams();\n Object.keys(params).forEach(function (k) {\n var v = params[k];\n if (v !== undefined && v !== null) q.append(k, v);\n });\n qs = q.toString();\n if (qs) url += (url.includes('?') ? '&' : '?') + qs;\n }\n\n init = {\n method: method,\n headers: {\n Accept: 'application/json'\n }\n };\n if (AUTH_TOKEN) init.headers['Authorization'] = \"Bearer \".concat(AUTH_TOKEN);\n\n if (body !== undefined) {\n init.headers['Content-Type'] = 'application/json';\n init.body = typeof body === 'string' ? body : JSON.stringify(body);\n }\n\n _context.prev = 9;\n _context.next = 12;\n return fetch(url, init);\n\n case 12:\n resp = _context.sent;\n _context.next = 15;\n return resp.text();\n\n case 15:\n text = _context.sent;\n json = null;\n\n try {\n json = text ? JSON.parse(text) : null;\n } catch (e) {\n json = null;\n }\n\n return _context.abrupt(\"return\", checkCode(json));\n\n case 21:\n _context.prev = 21;\n _context.t0 = _context[\"catch\"](9);\n return _context.abrupt(\"return\", null);\n\n case 24:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee, null, [[9, 21]]);\n }));\n return _req.apply(this, arguments);\n}\n\nfunction createChatSession() {\n var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n dataId = _ref2.dataId,\n userId = _ref2.userId,\n userName = _ref2.userName,\n context = _ref2.context,\n contextBase64 = _ref2.contextBase64;\n\n return req(\"/chat/sessions\", {\n method: 'POST',\n body: {\n dataId: dataId,\n userId: userId,\n userName: userName,\n context: context,\n contextBase64: contextBase64\n }\n });\n}\n/**\n * 3.11 查询 AI 助手入口显示状态(指定类型)\n * @param {string} entryType custom=自定义表单 | qgb=企管帮\n */\n\n\nfunction getEntryStatus(entryType) {\n return req(\"/entry-status/\".concat(encodeURIComponent(entryType)));\n}\n/**\n * 3.11 查询 AI 助手入口显示状态(全部类型)\n */\n\n\nfunction getEntryStatusList() {\n return req(\"/entry-status\");\n}\n/**\n * 3.9 获取当前用户的会话与历史问答(一个接口完成会话初始化 + 历史回显)\n * 会话不存在时自动创建;summary 命中缓存时 summaryReady=true 且 summary 非空;\n * records 为空表示该用户还没有问答记录。\n * @param {object} params { dataId, userId, pageNum?, pageSize? }\n * 返回 { sessionId, summaryReady, summary, total, pageNum, pageSize, records[] }\n */\n\n\nfunction getChatHistory() {\n var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n dataId = _ref3.dataId,\n userId = _ref3.userId,\n _ref3$pageNum = _ref3.pageNum,\n pageNum = _ref3$pageNum === void 0 ? 1 : _ref3$pageNum,\n _ref3$pageSize = _ref3.pageSize,\n pageSize = _ref3$pageSize === void 0 ? 20 : _ref3$pageSize;\n\n var query = new URLSearchParams({\n dataId: dataId,\n userId: userId || ''\n });\n query.set('pageNum', String(pageNum));\n query.set('pageSize', String(pageSize));\n return req(\"/chat/history?\".concat(query.toString()), {\n method: 'GET'\n });\n}\n/**\n * 通用 SSE 读取:fetch + ReadableStream,按空行切块并派发事件文本\n * @param {string} url\n * @param {(event: string, data: string) => void} onEvent\n */\n\n\nfunction readSSE(_x2, _x3) {\n return _readSSE.apply(this, arguments);\n}\n/** 通用 SSE POST:携带 JSON body(用于携带前端实时上下文) */\n\n\nfunction _readSSE() {\n _readSSE = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(url, onEvent) {\n var resp;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n _context2.prev = 0;\n _context2.next = 3;\n return fetch(url, {\n headers: {\n Accept: 'text/event-stream'\n }\n });\n\n case 3:\n resp = _context2.sent;\n\n if (!(!resp.ok || !resp.body)) {\n _context2.next = 7;\n break;\n }\n\n onEvent('error', \"SSE \\u8BF7\\u6C42\\u5931\\u8D25\\uFF1AHTTP \".concat(resp.status));\n return _context2.abrupt(\"return\", false);\n\n case 7:\n _context2.next = 9;\n return consumeSSE(resp, onEvent);\n\n case 9:\n return _context2.abrupt(\"return\", _context2.sent);\n\n case 12:\n _context2.prev = 12;\n _context2.t0 = _context2[\"catch\"](0);\n onEvent('error', 'SSE 连接异常');\n return _context2.abrupt(\"return\", false);\n\n case 16:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2, null, [[0, 12]]);\n }));\n return _readSSE.apply(this, arguments);\n}\n\nfunction readSSEPost(_x4, _x5, _x6) {\n return _readSSEPost.apply(this, arguments);\n}\n\nfunction _readSSEPost() {\n _readSSEPost = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(url, body, onEvent) {\n var headers, resp;\n return regeneratorRuntime.wrap(function _callee3$(_context3) {\n while (1) {\n switch (_context3.prev = _context3.next) {\n case 0:\n _context3.prev = 0;\n headers = {\n Accept: 'text/event-stream',\n 'Content-Type': 'application/json'\n };\n if (AUTH_TOKEN) headers['Authorization'] = \"Bearer \".concat(AUTH_TOKEN);\n _context3.next = 5;\n return fetch(url, {\n method: 'POST',\n headers: headers,\n body: JSON.stringify(body || {})\n });\n\n case 5:\n resp = _context3.sent;\n\n if (!(!resp.ok || !resp.body)) {\n _context3.next = 9;\n break;\n }\n\n onEvent('error', \"SSE \\u8BF7\\u6C42\\u5931\\u8D25\\uFF1AHTTP \".concat(resp.status));\n return _context3.abrupt(\"return\", false);\n\n case 9:\n _context3.next = 11;\n return consumeSSE(resp, onEvent);\n\n case 11:\n return _context3.abrupt(\"return\", _context3.sent);\n\n case 14:\n _context3.prev = 14;\n _context3.t0 = _context3[\"catch\"](0);\n onEvent('error', 'SSE 连接异常');\n return _context3.abrupt(\"return\", false);\n\n case 18:\n case \"end\":\n return _context3.stop();\n }\n }\n }, _callee3, null, [[0, 14]]);\n }));\n return _readSSEPost.apply(this, arguments);\n}\n\nfunction consumeSSE(_x7, _x8) {\n return _consumeSSE.apply(this, arguments);\n}\n/**\n * 3.7 AI 流式问答(SSE)\n * @param {object} opts { sessionId, message, dataId, contextId, context, contextBase64, userId, userName, onEvent }\n * onEvent(event, data):event ∈ reasoning | answer | msgKey | agent_result | error\n * 返回 Promise,完成时 resolve。\n */\n\n\nfunction _consumeSSE() {\n _consumeSSE = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(resp, onEvent) {\n var reader, decoder, buf, _yield$reader$read, done, value, idx, block;\n\n return regeneratorRuntime.wrap(function _callee4$(_context4) {\n while (1) {\n switch (_context4.prev = _context4.next) {\n case 0:\n reader = resp.body.getReader();\n decoder = new TextDecoder('utf-8');\n buf = '';\n\n case 3:\n if (false) {}\n\n _context4.next = 6;\n return reader.read();\n\n case 6:\n _yield$reader$read = _context4.sent;\n done = _yield$reader$read.done;\n value = _yield$reader$read.value;\n\n if (!done) {\n _context4.next = 11;\n break;\n }\n\n return _context4.abrupt(\"break\", 16);\n\n case 11:\n buf += decoder.decode(value, {\n stream: true\n });\n idx = void 0;\n\n while ((idx = buf.indexOf('\\n\\n')) >= 0) {\n block = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n emitBlock(normalizeBlock(block), onEvent);\n }\n\n _context4.next = 3;\n break;\n\n case 16:\n if (buf.trim()) emitBlock(normalizeBlock(buf), onEvent);\n return _context4.abrupt(\"return\", true);\n\n case 18:\n case \"end\":\n return _context4.stop();\n }\n }\n }, _callee4);\n }));\n return _consumeSSE.apply(this, arguments);\n}\n\nfunction streamChat() {\n return _streamChat.apply(this, arguments);\n}\n/**\n * 3.3 AI 生成审批单总结(SSE 流式)\n * 命中缓存时一次性推送完整文本(agent_result);未命中时逐段推送增量文本(answer)。\n * 此刻等待附件(ZIP/图片/PDF/Markdown)解析完成后才生成,内容会汇总进总结。\n * @param {object} opts { dataId, contextId, context, contextBase64, formName, onEvent }\n * onEvent(event, data):event ∈ answer | agent_result | error\n */\n\n\nfunction _streamChat() {\n _streamChat = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() {\n var _ref4,\n sessionId,\n message,\n dataId,\n contextId,\n context,\n contextBase64,\n userId,\n userName,\n _ref4$onEvent,\n onEvent,\n body,\n _args5 = arguments;\n\n return regeneratorRuntime.wrap(function _callee5$(_context5) {\n while (1) {\n switch (_context5.prev = _context5.next) {\n case 0:\n _ref4 = _args5.length > 0 && _args5[0] !== undefined ? _args5[0] : {}, sessionId = _ref4.sessionId, message = _ref4.message, dataId = _ref4.dataId, contextId = _ref4.contextId, context = _ref4.context, contextBase64 = _ref4.contextBase64, userId = _ref4.userId, userName = _ref4.userName, _ref4$onEvent = _ref4.onEvent, onEvent = _ref4$onEvent === void 0 ? function () {} : _ref4$onEvent;\n\n if (BASE) {\n _context5.next = 4;\n break;\n }\n\n onEvent('error', 'AI 服务未配置');\n return _context5.abrupt(\"return\", false);\n\n case 4:\n body = {\n sessionId: sessionId,\n message: message,\n dataId: dataId,\n contextId: contextId,\n userId: userId,\n userName: userName\n };\n if (context != null) body.context = context;\n if (contextBase64) body.contextBase64 = contextBase64;\n return _context5.abrupt(\"return\", readSSEPost(BASE + PREFIX + \"/chat/stream\", body, onEvent));\n\n case 8:\n case \"end\":\n return _context5.stop();\n }\n }\n }, _callee5);\n }));\n return _streamChat.apply(this, arguments);\n}\n\nfunction streamSummary() {\n return _streamSummary.apply(this, arguments);\n}\n\nfunction _streamSummary() {\n _streamSummary = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() {\n var _ref5,\n dataId,\n contextId,\n context,\n contextBase64,\n formName,\n userId,\n userName,\n _ref5$onEvent,\n onEvent,\n body,\n _args6 = arguments;\n\n return regeneratorRuntime.wrap(function _callee6$(_context6) {\n while (1) {\n switch (_context6.prev = _context6.next) {\n case 0:\n _ref5 = _args6.length > 0 && _args6[0] !== undefined ? _args6[0] : {}, dataId = _ref5.dataId, contextId = _ref5.contextId, context = _ref5.context, contextBase64 = _ref5.contextBase64, formName = _ref5.formName, userId = _ref5.userId, userName = _ref5.userName, _ref5$onEvent = _ref5.onEvent, onEvent = _ref5$onEvent === void 0 ? function () {} : _ref5$onEvent;\n\n if (BASE) {\n _context6.next = 4;\n break;\n }\n\n onEvent('error', 'AI 服务未配置');\n return _context6.abrupt(\"return\", false);\n\n case 4:\n body = {\n dataId: dataId,\n contextId: contextId,\n formName: formName,\n userId: userId,\n userName: userName\n };\n if (context != null) body.context = context;\n if (contextBase64) body.contextBase64 = contextBase64;\n return _context6.abrupt(\"return\", readSSEPost(BASE + PREFIX + \"/summary/stream\", body, onEvent));\n\n case 8:\n case \"end\":\n return _context6.stop();\n }\n }\n }, _callee6);\n }));\n return _streamSummary.apply(this, arguments);\n}\n\nfunction normalizeBlock(block) {\n var event = 'message';\n var data = [];\n\n var _iterator = (0, _createForOfIteratorHelper2.default)(block.split('\\n')),\n _step;\n\n try {\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var line = _step.value;\n\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n data.push(line.slice(5).trim());\n }\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n\n return {\n event: event,\n data: data.join('\\n')\n };\n}\n\nfunction emitBlock(_ref6, onEvent) {\n var event = _ref6.event,\n data = _ref6.data;\n if (!data) return;\n onEvent(event, data);\n}\n\n//# sourceURL=webpack://ZjPluginAiAssistant/./src/lib/components/approvalAi/apiApprovalAi.js?");
|
|
1203
1181
|
|
|
1204
1182
|
/***/ }),
|
|
1205
1183
|
|