diff --git a/public/static/plugs/ulthon-admin/admin-table.js b/public/static/plugs/ulthon-admin/admin-table.js index 9ee9f74..66b1064 100644 --- a/public/static/plugs/ulthon-admin/admin-table.js +++ b/public/static/plugs/ulthon-admin/admin-table.js @@ -181,7 +181,8 @@ // (b) 模板引擎优先级解析:tplFunction > tplLay > tplVue > tplLayId > tplVueId, // 提供一种即生效;五种全无且非 table 条目 → console.warn 并回退 table。 - // lay/layId 引擎挂载于 renderLayoutForm(任务 16,layui.laytpl);vue/vueId 挂载于任务 17。 + // lay/layId(任务 16,layui.laytpl)与 vue/vueId(任务 17,全局 Vue 2.6.10 + // 运行时编译)均挂载于 renderLayoutForm。 var resolveLayoutEngine = function (entry) { if (entry.name === 'table') { return {kind: 'table', entry: entry}; @@ -231,22 +232,76 @@ return resolveLayoutEngine(entry); }; - // 模板管线失败回退(tplLayId 节点缺失 / laytpl 渲染错误 / Vue 引擎未挂载): - // 当前形态归位 'table'(与"无钩子条目回退"同语义,幂等、不写 localStorage - // 记忆,用户重新切换可再次触发),容器清空隐藏。调用方据 false 返回值做 - // 引擎纯渲染,随后的 syncLayoutVisibility 按 table kind 复显引擎数据区, - // 避免"容器残留上一形态旧内容 + 引擎数据被隐藏"的视觉错位。 + // 模板管线失败回退(tplLayId/tplVueId 节点缺失 / laytpl 渲染错误 / Vue 未引入 + // 或挂载渲染失败):当前形态归位 'table'(与"无钩子条目回退"同语义,幂等、 + // 不写 localStorage 记忆,用户重新切换可再次触发),容器销毁 Vue 实例后清空 + // 隐藏。调用方据 false 返回值做引擎纯渲染,随后的 syncLayoutVisibility 按 + // table kind 复显引擎数据区,避免"容器残留上一形态旧内容 + 引擎数据被隐藏" + // 的视觉错位。 var fallbackLayoutToTable = function (tableId, $container) { var layout = controlledLayouts[tableId]; if (layout) { layout.current = 'table'; } + destroyLayoutVueInstances(tableId); if ($container && $container.length) { $container.addClass('layui-hide').empty(); } return false; }; + // ========== vue/vueId 引擎(任务 17,全局 Vue 2.6.10 运行时编译)========== + // 渲染模式为列表级单实例:模板内 v-for="d in rows" 循环行,一次渲染只挂载 + // 一个实例(逐行 new Vue 的成本随行数线性膨胀,销毁纪律也难以收口)。 + // 实例存活表:tableId → 当前实例数组。 + var layoutVueInstances = {}; + + // 销毁本表全部存活实例(渲染入口 / fallbackLayoutToTable / + // syncLayoutVisibility 三处统一调用,幂等)。$destroy 只拆 watcher 与事件, + // 不动 DOM,容器清空由调用方完成。任何重渲路径(store done 管线/搜索/ + // 切换/回退)都必须先销毁旧实例再挂新实例——销毁重建式是"实例存活期禁止 + // 响应式数据变更"约束(约束 2)的落地方式:form.render 美化插入的 DOM + // (div.layui-form-switch 等)不受 Vue 虚拟 DOM 管理,存活期 re-patch 会 + // 清除它们。 + var destroyLayoutVueInstances = function (tableId) { + var list = layoutVueInstances[tableId] || []; + $.each(list, function (i, vm) { + try { + vm.$destroy(); + } catch (e) {} + }); + layoutVueInstances[tableId] = []; + }; + + // Vue 生产构建中模板编译错误不抛异常也不打日志(dev 告警被裁剪),坏模板 + // 只会在挂载渲染时经全局 errorHandler 暴露——链式接管 Vue.config.errorHandler: + // 本框架实例(vm.__uaLayout 标记)按形态名 console.error 并记录 + // lastVueLayoutError,由 renderLayoutForm 探测后回退;其余实例 + // (propertyInput/tagInput 等页面自有 Vue)透传既有处理器或复刻默认行为。 + var lastVueLayoutError = null; + var vueErrorHandlerInstalled = false; + var installVueErrorHandler = function () { + if (vueErrorHandlerInstalled) { + return; + } + vueErrorHandlerInstalled = true; + var previous = window.Vue.config.errorHandler; + window.Vue.config.errorHandler = function (err, vm, info) { + if (vm && vm.__uaLayout) { + var meta = vm.__uaLayout; + lastVueLayoutError = {tableId: meta.tableId, name: meta.name}; + console.error('[ua.table] Vue 模板渲染失败(形态 "' + meta.name + '"):' + + ((err && err.message) || err) + ',本次回退 table 形态'); + return; + } + if (typeof previous === 'function') { + previous(err, vm, info); + return; + } + console.error(err); + }; + }; + // 形态可见性同步:引擎数据区(表头+表体+合计行)与模板容器互斥显隐, // 引擎视图的工具栏(刷新/新增/批量删除等)两形态共用保持可见,搜索区 // 本就挂在表格元素之前不受影响。非 table 形态下引擎根本不渲染行数据 @@ -261,6 +316,7 @@ var $total = $view.find('.layui-table-total').first(); var $container = $('#ua-layout-' + tableId); if (resolveCurrentLayout(tableId).kind === 'table') { + destroyLayoutVueInstances(tableId); $box.add($total).removeClass('layui-hide'); $container.addClass('layui-hide').empty(); } else { @@ -279,8 +335,9 @@ $layoutContainer.addClass('ua-layout-loading'); opts.done = function (res) { // 受控渲染分发:table 形态走引擎纯渲染(现状),非 table 形态走 - // 模板管线(渲染上下文 + 行级循环);模板管线不可用(引擎未挂载, - // Vue 待任务 17;tplLayId 节点缺失/laytpl 渲染错误)时回退引擎纯渲染兜底 + // 模板管线(渲染上下文 + 行级循环);模板管线不可用(Vue 未引入/ + // 挂载渲染失败;tplLayId/tplVueId 节点缺失/laytpl 渲染错误)时 + // 回退引擎纯渲染兜底 var engineRender = function () { originalTableReloadData.call(table, tableId, { data: res.data, @@ -416,6 +473,25 @@ // - laytpl 编译/运行错误不抛异常,render() 返回 "Laytpl Error: ..." 字符串 // (laytpl 内部已 console.error 细节),框架探测到后按条目名 console.error // 并返回 false,调用方回退引擎纯渲染 + // 【Vue 调用约定(tplVue / tplVueId,任务 17)】 + // 列表级单实例:框架把模板包进单一根节点