Files
ulthon_admin/public/static/plugs/ulthon-admin/admin-table.js
augushong 21a5eceeda fix(table): 排序行图标右置并在排序组标题提供重置
- 排序行改两端布局:标题居左、方向图标贴右,未排序行右端留空对齐
- 「排序」标题右侧新增重置链接(仅排序生效时渲染),点击清除排序并就地刷新,面板保持打开
2026-10-05 11:34:58 +00:00

2627 lines
129 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
var ua = window.UA_ADMIN;
var form = window.UA_CORE.form,
layer = window.UA_CORE.layer,
table = window.UA_CORE.table,
laydate = window.UA_CORE.laydate,
laytpl = window.UA_CORE.laytpl,
util = window.UA_CORE.util;
var lastTableWhere = {};
var colToggledTimers = {};
// 卡片设置面板外点收起的原生捕获监听(单例,打开面板前先摘旧监听防重复绑定)
var cardSettingOutsideClose = null;
var selectMode, selectConfirmCallback;
// reloadData 引擎接线(加载期执行一次):受控表(UA_TABLE_STORE 已注册)的数据
// 重载分流到 store,非受控表透传原引擎调用。原引用只备份一次,防止重复加载时
// 把包装层当原引用再包一层;透传用 arguments 保真,reloadData(id, opts, type)
// 三参形态的 type 语义不变。
if (table.__uaOriginalReloadData === undefined) {
table.__uaOriginalReloadData = table.reloadData;
}
var originalTableReloadData = table.__uaOriginalReloadData;
table.reloadData = function (tableId, opts) {
if (window.UA_TABLE_STORE.has(tableId)) {
return window.UA_TABLE_STORE.reload(tableId, opts);
}
return originalTableReloadData.apply(this, arguments);
};
// 受控表渲染接线(加载期执行一次):store 的 fetch/reload 只负责取数与状态、
// 不触发渲染,这里在唯一的数据到达点(store.reload 的成功回调)注入引擎
// 纯渲染——首屏/搜索/排序/switch 回滚/删除刷新全部经 shim 汇入 reload。
// 渲染必须走备份的原引擎引用:走 table.reloadData 会被上方 shim 路由回
// store 造成请求循环。initSort 透传保持表头排序箭头(与引擎 url 模式的
// listenTableSort 行为一致)。受控表分页条由 render() 内的
// syncControlledPager 自管:store 的 pager.jump 在内部直呼 fetch,外部
// 注入不了渲染回调,故 register 时不传 pagerElem 使 store 分页静默跳过,
// laypage 的 jump 经 shim(table.reloadData)回 store.reload 形成闭环。
var syncControlledPager = function (tableId) {
var state = window.UA_TABLE_STORE.dataProvider.getState(tableId);
if (!state) {
return;
}
layui.laypage.render({
elem: 'ua-store-pager-' + tableId,
count: state.count,
curr: state.page.curr,
limit: state.page.limit,
limits: state.limits,
layout: ['count', 'prev', 'page', 'next', 'limit', 'skip'],
jump: function (obj, first) {
if (!first) {
table.reloadData(tableId, {
page: {curr: obj.curr, limit: obj.limit}
});
}
}
});
};
// ========== 多形态渲染(五级模板协议)==========
// 形态注册表:tableId → {elem, cols, init, entries, byName, current}。
// render() 受控分支写入;受控渲染 wrapper 与 ua.table.renderLayoutForm 消费。
var controlledLayouts = {};
// 已回退告警去重表(同一形态条目只 warn 一次)
var warnedLayouts = {};
// (a) layoutList 规范化:字符串 'table'/'card' 转对象 {name, title};自定义条目
// {name, title, icon?, tplFunction?, tplLay?, tplVue?, tplLayId?, tplVueId?} 原样保留
var normalizeLayoutList = function (layoutList) {
var stringTitles = {table: '表格', card: '卡片'};
var result = [];
$.each(layoutList || [], function (i, item) {
if (typeof item === 'string') {
result.push({name: item, title: stringTitles[item] || item});
} else if (item && typeof item === 'object' && item.name) {
result.push(item);
}
});
return result;
};
// ========== 内置 card 形态(name='card' 且无自定义钩子时启用)==========
// 结构参照旧寄生实现的 header/body/footer 骨架(代码全新):header(标题 +
// 勾位图标)、body(键值对字段列表)、footer(create_time + 操作按钮)。
// 数据全部来自渲染上下文(_cells/_operatHtml/_checked/_disabled),列元信息读
// controlledLayouts 的 cols(本闭包内),无任何业务字段硬编码。
//
// 【与 buildLayoutContext 同规则的单元格列筛选】操作列无 field,须先于
// field 判定识别;checkbox/radio 等无字段列跳过(勾选语义由勾位图标承接);
// 隐藏列(col.hide)跳过——不进卡片行、不产 _cells,与表格"隐藏列不渲染
// 单元格"语义一致
var builtinCardCellCols = function (tableId) {
var cols = (controlledLayouts[tableId] && controlledLayouts[tableId].cols[0]) || [];
var cellCols = [];
$.each(cols, function (i, col) {
if (!col) {
return;
}
if (col.templet === ua.table.tool) {
return;
}
if (!col.field) {
return;
}
if (col.hide) {
return;
}
cellCols.push(col);
});
return cellCols;
};
// 内置 card 单行模板(tplFunction 调用约定:this/第一参 = 行上下文,第二参 = 列表上下文)
// - 标题列 = 首个字段列(含 checkbox 列的表格即"第二列",无 checkbox 表格即"首字段")
// - 标题列与 create_time 列不重复进 body(分别在 header/footer 展示)
// - create_time 无对应列时 footer 时间留空,不回退原始字段值(避免裸时间戳)
var builtinCardTemplate = function (row, list) {
var cellCols = builtinCardCellCols(list.tableId);
var titleCol = cellCols.length > 0 ? cellCols[0] : null;
var html = '<div class="ua-data-card'
+ (row._checked ? ' ua-data-card-checked' : '')
+ (row._disabled ? ' ua-data-card-disabled' : '')
+ '" data-table-id="' + list.tableId + '" data-id="' + row.id + '">';
html += '<div class="ua-data-card-header">';
html += '<div class="ua-data-card-title">' + (titleCol ? (row._cells[titleCol.field] || '') : '') + '</div>';
html += '<div class="ua-data-card-check"><i class="layui-icon layui-icon-ok-circle"></i></div>';
html += '</div>';
html += '<div class="ua-data-card-body">';
$.each(cellCols, function (i, col) {
if (titleCol && col.field === titleCol.field) {
return;
}
if (col.field === 'create_time') {
return;
}
html += '<div class="ua-data-card-item">'
+ '<span class="ua-data-card-item-title">' + (col.title || col.field) + '</span>'
+ '<span class="ua-data-card-item-value">' + (row._cells[col.field] || '') + '</span>'
+ '</div>';
});
html += '</div>';
html += '<div class="ua-data-card-footer">';
html += '<div class="ua-data-card-time">' + (row._cells['create_time'] || '') + '</div>';
html += '<div class="ua-data-card-operat">' + (row._operatHtml || '') + '</div>';
html += '</div>';
html += '</div>';
return html;
};
// 统一行选中委托(任务 18):模板渲染容器内凡带 data-table-id + data-id 属性
// 的行元素——内置 card 的 .ua-data-card 与自定义 laytpl/Vue 模板的卡片/勾位
// 元素——仅当点击落在行内 .ua-data-card-check 勾位元素内才切换选中,全部走
// 本委托,无任何形态专用事件。模板作者契约:给行根元素(或勾位元素)加这两
// 属性、勾位区域挂 .ua-data-card-check 类即获得点选交互,无需页面级监听。
// 交互元素(按钮/表单控件/layui 美化层)豁免不触发勾选;禁用行
// (ua-data-card-disabled 标记或行 LAY_DISABLED)整元素不可勾选。选择态落
// store(与引擎勾选同一事实源,批量删除经 ua.table.checkStatus 分发即取到),
// 随后走快速路径按模式只改勾位卡片 class 回显(radio 替换 / checkbox 切换),
// 目标卡元素找不到时兜底整容器重渲模板管线回显勾位标记。
$(document).on('click', '[data-table-id][data-id]', function (e) {
var $card = $(this);
var tableId = $card.attr('data-table-id');
// 仅模板渲染容器内的行元素生效(页面其他区域的 data-id 元素不受影响)
if ($card.closest('#ua-layout-' + tableId).length === 0) {
return;
}
if ($card.hasClass('ua-data-card-disabled')) {
return;
}
// 仅勾位点击生效:点卡片正文不触发勾选(点正文零开销)
if ($(e.target).closest('.ua-data-card-check').length === 0) {
return;
}
if ($(e.target).closest('a,button,input,select,textarea,label,.layui-form-switch,.layui-form-checkbox,.layui-form-radio').length) {
return;
}
var state = window.UA_TABLE_STORE && window.UA_TABLE_STORE.dataProvider.getState(tableId);
if (!state) {
return;
}
var row = null;
var rowId = $card.attr('data-id');
$.each(state.currentPageData || [], function (i, item) {
if (String(item.id) === String(rowId)) {
row = item;
return false;
}
});
if (!row || row.LAY_DISABLED) {
return;
}
// 快速路径(pro#4):不做整容器重渲,仅按模式调整勾位卡片 class——
// radio 替换(清旧卡、点新卡,重复点同一卡净效果保持选中)、checkbox
// 切换(含再点取消);DOM 不重建,含图卡片不闪。自定义模板未按契约
// 输出勾位元素时兜底整容器重渲,不丢功能。
var prevRadioId = (state.selectMode === 'radio' && state.selectedIds && state.selectedIds.length)
? state.selectedIds[0]
: null;
window.UA_TABLE_STORE.selection.set(tableId, row);
var $target = $('#ua-layout-' + tableId).find('[data-id="' + rowId + '"]');
if ($target.length === 0) {
ua.table.renderLayoutForm(tableId);
return;
}
if (state.selectMode === 'radio') {
if (prevRadioId !== null && String(prevRadioId) !== String(rowId)) {
$('#ua-layout-' + tableId).find('[data-id="' + prevRadioId + '"]').removeClass('ua-data-card-checked');
}
$target.addClass('ua-data-card-checked');
} else {
$target.toggleClass('ua-data-card-checked');
}
});
// (b) 模板引擎优先级解析:tplFunction > tplLay > tplVue > tplLayId > tplVueId,
// 提供一种即生效;五种全无且非 table 条目 → console.warn 并回退 table。
// 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};
}
// 内置 card 形态:name='card' 且未提供任何模板钩子时挂内置模板;提供了
// 钩子(tplFunction/tplLay/tplVue/tplLayId/tplVueId 任一)则照常走五级
// 优先级,自定义实现完全覆盖内置形态。builtinCard 标记供模板管线给容器
// 挂网格布局类(.ua-layout-cards)
if (entry.name === 'card') {
var hasHook = typeof entry.tplFunction === 'function'
|| (typeof entry.tplLay === 'string' && entry.tplLay !== '')
|| (typeof entry.tplVue === 'string' && entry.tplVue !== '')
|| (typeof entry.tplLayId === 'string' && entry.tplLayId !== '')
|| (typeof entry.tplVueId === 'string' && entry.tplVueId !== '');
if (!hasHook) {
var builtinEntry = $.extend({}, entry, {tplFunction: builtinCardTemplate, builtinCard: true});
return {kind: 'function', entry: builtinEntry};
}
}
if (typeof entry.tplFunction === 'function') {
return {kind: 'function', entry: entry};
}
if (typeof entry.tplLay === 'string' && entry.tplLay !== '') {
return {kind: 'lay', entry: entry};
}
if (typeof entry.tplVue === 'string' && entry.tplVue !== '') {
return {kind: 'vue', entry: entry};
}
if (typeof entry.tplLayId === 'string' && entry.tplLayId !== '') {
return {kind: 'layId', entry: entry};
}
if (typeof entry.tplVueId === 'string' && entry.tplVueId !== '') {
return {kind: 'vueId', entry: entry};
}
if (!warnedLayouts[entry.name]) {
warnedLayouts[entry.name] = true;
console.warn('[ua.table] layoutList 条目 "' + entry.name + '" 未提供任何模板钩子(tplFunction/tplLay/tplVue/tplLayId/tplVueId),已回退 table 形态');
}
return {kind: 'table', entry: entry};
};
// 当前形态解析:先取 options.layoutDefault(缺省 'table');运行期切形态
// 经 ua.table.setLayoutForm 改写 current。条目缺失/未登记时恒 table。
var resolveCurrentLayout = function (tableId) {
var layout = controlledLayouts[tableId];
var entry = (layout && layout.byName[layout.current]) || {name: 'table', title: '表格'};
return resolveLayoutEngine(entry);
};
// 模板管线失败回退(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;
};
// 卡片操作按钮折叠收纳:模板管线渲染完成后扫描各卡片操作区,直接子级
// a/button 超过阈值 3 时从第 4 个起移入隐藏收纳容器,并在操作区末尾追加
// "更多"开关锚点(展开委托在 render() 受控接线创建模板容器处绑定)。
// 阈值写死不配置;调用点在 form.render 之前——按钮为锚点无逐节点事件,
// 先移 DOM 再美化安全。Vue 分支不经过本函数(模板自理)。
var applyCardOperatFold = function ($container) {
$container.find('.ua-data-card-operat').each(function () {
var $operat = $(this);
var $buttons = $operat.children('a, button');
if ($buttons.length <= 3) {
return;
}
var $moreWrap = $('<div class="ua-card-operat-more-wrap layui-hide"></div>');
$buttons.slice(3).appendTo($moreWrap);
$moreWrap.appendTo($operat);
$operat.append($('<a class="ua-card-operat-more"><i class="layui-icon layui-icon-more"></i></a>'));
});
};
// ========== 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);
};
};
// 形态可见性同步:引擎数据区(表头+表体+合计行)与模板容器互斥显隐,
// 引擎视图的工具栏(刷新/新增/批量删除等)两形态共用保持可见(print 与
// 卡片设置按钮除外,随形态互斥显隐),搜索区本就挂在表格元素之前不受影响。
// 非 table 形态下引擎根本不渲染行数据
// (wrapper 非 table 分支不走引擎渲染),并非旧寄生实现"引擎恒渲+CSS 隐藏"。
var syncLayoutVisibility = function (tableId) {
var layout = controlledLayouts[tableId];
if (!layout) {
return;
}
var $view = $(layout.elem).siblings('.layui-table-view').first();
var $box = $view.find('.layui-table-box').first();
var $total = $view.find('.layui-table-total').first();
var $container = $('#ua-layout-' + tableId);
var isTable = resolveCurrentLayout(tableId).kind === 'table';
if (isTable) {
destroyLayoutVueInstances(tableId);
$box.add($total).removeClass('layui-hide');
$container.addClass('layui-hide').empty();
} else {
$box.add($total).addClass('layui-hide');
$container.removeClass('layui-hide');
}
// 工具栏形态联动:print 仅表格形态有意义;卡片设置仅非表格形态有意义。
// filter(LAYTABLE_COLS)与视图切换(TABLE_LAYOUT)两形态常驻不动
$view.find('[lay-event="LAYTABLE_PRINT"]').toggleClass('layui-hide', !isTable);
$view.find('[lay-event="TABLE_CARD_SETTING"]').toggleClass('layui-hide', isTable);
};
// 卡片设置面板统一收起(外点收起/图标再点收起等路径共用):移除面板并摘除
// 外点捕获监听,保证不留孤儿监听
var closeCardSettingPanel = function (tableId) {
$('#ua-card-setting-' + tableId).remove();
if (cardSettingOutsideClose) {
document.removeEventListener('click', cardSettingOutsideClose, true);
cardSettingOutsideClose = null;
}
};
// 卡片设置面板开关(pro#5 迭代:入口为右侧 defaultToolbar 图标组最左侧的
// TABLE_CARD_SETTING 图标,走 layui toolbar 事件)。含面板创建/移除/开关/
// 排序/外点收起全套逻辑。面板 append 在本表的 .layui-table-tool 容器
//(layui 已 position:relative)而非图标元素内:layui.css 的
// .layui-btn:hover{opacity:.8} 会沿祖先链压暗整棵面板子树,断开父子
// 关系即根治。打开时用 getBoundingClientRect 内联定位(两 rect 同帧
// 相减与滚动无关),面板右缘对齐图标右缘、顶边贴图标下沿,向下向左
// 展开。面板内交互均阻断冒泡
var toggleCardSettingPanel = function (tableId) {
var cardSettingId = 'ua-card-setting-' + tableId;
var $view = $(controlledLayouts[tableId].elem).siblings('.layui-table-view').first();
var $toolbarSettingElem = $view.find('[lay-event="TABLE_CARD_SETTING"]');
var $toolElem = $view.find('.layui-table-tool').first();
var $cardSettingElem = $('#' + cardSettingId);
if ($cardSettingElem.length > 0) {
closeCardSettingPanel(tableId);
return;
}
// 配置段 + 排序段(面板每次点按钮重建,排序段随建随取
// store 当前态,表头排序/搜索后重开面板即最新)
var cardSettingHtml = '<div id="' + cardSettingId + '" class="ul-nav-block ua-card-setting-panel">';
var operatAlwaysOn = ua.getPageSetting('card-operat-always', '') === '1';
cardSettingHtml += '<div class="ua-card-setting-title">配置</div>';
cardSettingHtml += '<div class="ua-card-setting-item"><span>操作按钮始终显示</span>'
+ '<span class="ua-card-setting-switch' + (operatAlwaysOn ? ' on' : '') + '"></span></div>';
// 当前排序读 store 单一事实源(where.sort 形状为 {字段名: 'asc'|'desc'}
// 单键映射,与 store setSort/buildRequestData 同构)——须先于标题行
// 构建(重置链接的渲染判定要用)
var cardSortState = window.UA_TABLE_STORE.dataProvider.getState(tableId);
var cardSortCurrent = cardSortState && cardSortState.where ? cardSortState.where.sort : null;
// 「排序」标题 + 重置链接:以 cardSortCurrent 键数判定是否渲染
// (排序字段可能是已隐藏列,不在下方排序行清单里,不能用行命中判定)
cardSettingHtml += '<div class="ua-card-setting-title ua-card-setting-sort-title"><span>排序</span>'
+ (cardSortCurrent && Object.keys(cardSortCurrent).length > 0
? '<a class="ua-card-setting-sort-reset">重置</a>' : '')
+ '</div>';
$.each(((controlledLayouts[tableId] && controlledLayouts[tableId].cols[0]) || []), function (i, col) {
if (!col || !col.field || col.sort === false || col.hide) {
return;
}
var sortClass = '';
var sortIconClass = 'layui-icon layui-hide';
if (cardSortCurrent && cardSortCurrent[col.field]) {
sortClass = ' current';
sortIconClass = 'layui-icon layui-icon-' + (cardSortCurrent[col.field] === 'desc' ? 'down' : 'up');
}
cardSettingHtml += '<div class="ua-card-setting-sort-item item' + sortClass + '" data-field="' + col.field + '">'
+ '<span class="ua-card-setting-sort-label">' + (col.title || col.field) + '</span>'
+ '<i class="' + sortIconClass + ' ua-card-setting-sort-arrow"></i></div>';
});
cardSettingHtml += '</div>';
$cardSettingElem = $(cardSettingHtml).appendTo($toolElem);
// 内联定位:面板右缘对齐图标右缘、顶边贴图标下沿(200px 宽面板
// 自图标向左下展开,完全落在视口内)。iconRect/toolRect 同帧相减,
// 与页面滚动无关
var iconRect = $toolbarSettingElem[0].getBoundingClientRect();
var toolRect = $toolElem[0].getBoundingClientRect();
$cardSettingElem.css({
top: (iconRect.bottom - toolRect.top) + 'px',
right: (toolRect.right - iconRect.right) + 'px'
});
// 面板内条目点击不冒泡(排序段等后续 .item 条目沿用)
$cardSettingElem.on('click', '.item', function (event) {
event.stopPropagation();
});
// 开关:页级记忆 + 模板容器即时生效,不收起面板不重渲
$cardSettingElem.on('click', '.ua-card-setting-switch', function (event) {
var on = !$(this).hasClass('on');
ua.setPageSetting('card-operat-always', on ? '1' : '');
$(this).toggleClass('on', on);
$('#ua-layout-' + tableId).toggleClass('ua-card-operat-always', on);
event.stopPropagation();
});
// 排序状态就地刷新共用闭包:重读 store where.sort,遍历排序行更新
// current 高亮与方向图标(未排序行 i 保持 layui-hide,仅剩左侧标题),
// 并同步「重置」链接显隐——有排序(where.sort 有键,排序字段可能是
// 已隐藏列不在行清单里,不能用行命中判定)且无链接则补建,无排序则
// 移除。reloadData 调用链内 store setWhere 同步完成,调用即读即新
var refreshSortState = function () {
var sortState = window.UA_TABLE_STORE.dataProvider.getState(tableId);
var current = sortState && sortState.where ? sortState.where.sort : null;
$cardSettingElem.find('.ua-card-setting-sort-item').each(function () {
var sortType = current ? (current[$(this).data('field')] || null) : null;
$(this).toggleClass('current', !!sortType)
.find('i').attr('class', (sortType
? 'layui-icon layui-icon-' + (sortType === 'desc' ? 'down' : 'up')
: 'layui-icon layui-hide') + ' ua-card-setting-sort-arrow');
});
var $sortTitle = $cardSettingElem.find('.ua-card-setting-sort-title');
var $reset = $sortTitle.find('.ua-card-setting-sort-reset');
if (current && Object.keys(current).length > 0) {
if ($reset.length === 0) {
$sortTitle.append('<a class="ua-card-setting-sort-reset">重置</a>');
}
} else {
$reset.remove();
}
};
// 排序:循环 升 → 降 → 取消。与 listenTableSort 同构:
// 以 lastTableWhere 为基底就地改 sort,显式传 where
// (store.reload 仅在 opts.where 存在时经 setWhere 更新
// 请求参数,漏传排序到不了服务端);initSort 供表头
// 箭头(取消时省略);当前态以 store getState 为准
// (搜索路径会整体覆盖 lastTableWhere,admin-table.js
// 侧变量不可作判定依据)。点击后面板保持打开,refreshSortState
// 就地刷新高亮与重置链接,可连续点击
$cardSettingElem.on('click', '.ua-card-setting-sort-item', function (event) {
event.stopPropagation();
var field = $(this).data('field');
var sortState = window.UA_TABLE_STORE.dataProvider.getState(tableId);
var currentType = sortState && sortState.where && sortState.where.sort ? (sortState.where.sort[field] || null) : null;
var lastWhere = lastTableWhere[tableId] ? lastTableWhere[tableId] : {};
var reloadOpts = {where: lastWhere};
if (currentType === 'asc') {
lastWhere.sort = {};
lastWhere.sort[field] = 'desc';
reloadOpts.initSort = {field: field, type: 'desc'};
} else if (currentType === 'desc') {
delete lastWhere.sort;
} else {
lastWhere.sort = {};
lastWhere.sort[field] = 'asc';
reloadOpts.initSort = {field: field, type: 'asc'};
}
lastTableWhere[tableId] = lastWhere;
ua.table.reloadData(tableId, reloadOpts);
refreshSortState();
});
// 重置排序:清除 where.sort 后重载(不带 initSort,取消排序语义
// 与排序 handler 的取消分支一致);面板保持打开、高亮清空,重置
// 链接自身随 refreshSortState 消失。stopPropagation 防外点收起,
// preventDefault 抑制 a 元素默认行为
$cardSettingElem.on('click', '.ua-card-setting-sort-reset', function (event) {
event.stopPropagation();
event.preventDefault();
var lastWhere = lastTableWhere[tableId] ? lastTableWhere[tableId] : {};
delete lastWhere.sort;
lastTableWhere[tableId] = lastWhere;
ua.table.reloadData(tableId, {where: lastWhere});
refreshSortState();
});
// 点击面板外区域收起(原生捕获阶段监听,先于一切 stopPropagation——
// layui 内置 LAYTABLE_COLS/PRINT/EXPORT 等图标的点击处理带
// stopPropagation 挡冒泡,冒泡阶段监听点不到,捕获阶段不受影响);
// 面板已不在图标元素内,判定为「既不在图标内、也不在面板内」才收起,
// 否则点面板自身会被误判为外部点立刻收起。绑定前先摘旧监听防重复绑定
if (cardSettingOutsideClose) {
document.removeEventListener('click', cardSettingOutsideClose, true);
}
cardSettingOutsideClose = function (event) {
var $t = $(event.target);
if (!$t.closest($toolbarSettingElem).length && !$t.closest('#' + cardSettingId).length) {
closeCardSettingPanel(tableId);
}
};
document.addEventListener('click', cardSettingOutsideClose, true);
};
if (window.UA_TABLE_STORE) {
var storeReload = window.UA_TABLE_STORE.reload;
window.UA_TABLE_STORE.reload = function (tableId, opts) {
opts = opts || {};
var userDone = opts.done;
var userFail = opts.fail;
// 模板形态的加载态容器标记(容器内加载样式由任务 14 的内置形态接入)
var $layoutContainer = $('#ua-layout-' + tableId);
$layoutContainer.addClass('ua-layout-loading');
// 失败通道(res.code != 0 / HTTP error)不进 done,需经 fail 摘除加载态,
// 避免请求失败后容器残留半透明禁点状态
opts.fail = function () {
$layoutContainer.removeClass('ua-layout-loading');
if (typeof userFail === 'function') {
userFail.apply(null, arguments);
}
};
opts.done = function (res) {
// 受控渲染分发:table 形态走引擎纯渲染(现状),非 table 形态走
// 模板管线(渲染上下文 + 行级循环);模板管线不可用(Vue 未引入/
// 挂载渲染失败;tplLayId/tplVueId 节点缺失/laytpl 渲染错误)时
// 回退引擎纯渲染兜底
var engineRender = function () {
originalTableReloadData.call(table, tableId, {
data: res.data,
totalRow: res.totalRow,
initSort: opts.initSort
});
};
if (resolveCurrentLayout(tableId).kind === 'table' || !ua.table.renderLayoutForm(tableId)) {
engineRender();
}
$layoutContainer.removeClass('ua-layout-loading');
syncControlledPager(tableId);
syncLayoutVisibility(tableId);
if (typeof userDone === 'function') {
userDone(res);
}
};
return storeReload(tableId, opts);
};
}
ua.table = {
// 受控/非受控分发入口(后续任务直呼改造的定义点):非受控路径走备份的
// 原引擎引用,不经过上方已包装的 table.reloadData,避免递归
reloadData: function (tableId, opts) {
if (window.UA_TABLE_STORE.has(tableId)) {
return window.UA_TABLE_STORE.reload(tableId, opts);
}
return originalTableReloadData.apply(this, arguments);
},
checkStatus: function (tableId) {
if (window.UA_TABLE_STORE.has(tableId)) {
return window.UA_TABLE_STORE.checkStatus(tableId);
}
return table.checkStatus(tableId);
},
// ========== 多形态渲染管线(五级模板协议)==========
// (c) 渲染上下文构建器:对 store 当页每行产出
// {...row, _cells: {field: 列 templet 渲染后的 HTML}, _operatHtml: 操作按钮 HTML,
// _checked: 是否选中, _disabled: 是否禁选}
// _cells 复用 formatCols 之后的列渲染器族(image/switch/list/自定义 templet 与
// 引擎同行为),逐列注入 LAY_COL(与引擎 templet 调用同构:渲染器经 data.LAY_COL
// 读列配置);_operatHtml 走既有 ua.table.tool(按钮权限 data-* 属性原样输出,
// 事件靠页面级委托天然覆盖,无旧的"暂时隐藏按钮"逻辑);_checked/_disabled
// 数据源为 store 选中集合(selectedIds)与行 LAY_DISABLED 标记。
// 隐藏列(col.hide)跳过——不产 _cells、不进卡片行,与表格"隐藏列不渲染
// 单元格"语义一致。
buildLayoutContext: function (tableId) {
var layout = controlledLayouts[tableId];
var state = window.UA_TABLE_STORE.dataProvider.getState(tableId);
var rows = (state && state.currentPageData) || [];
var selectedIds = (state && state.selectedIds) || [];
var cols = (layout && layout.cols[0]) || [];
var operatCol = null;
var cellCols = [];
$.each(cols, function (i, col) {
if (!col) {
return;
}
// 操作列无 field,须先于 field 判定识别
if (col.templet === ua.table.tool) {
operatCol = col;
return;
}
if (!col.field) {
// checkbox/radio 等无字段列:勾选语义由 _checked/_disabled 与模板作者承接
return;
}
if (col.hide) {
return;
}
cellCols.push(col);
});
var context = {
tableId: tableId,
rows: [],
page: state ? state.page : {curr: 1, limit: 0},
count: state ? state.count : 0,
checkedAll: state ? window.UA_TABLE_STORE.selection.isAll(tableId) : false,
// 面板设置快照:模板经 d._list.settings / list.settings 只读
settings: {cardOperatAlways: ua.getPageSetting('card-operat-always', '') === '1'}
};
$.each(rows, function (i, row) {
var rowCtx = $.extend({}, row);
rowCtx._cells = {};
$.each(cellCols, function (j, col) {
var cellData = $.extend({}, row);
cellData.LAY_COL = col;
try {
if (typeof col.templet === 'function') {
rowCtx._cells[col.field] = col.templet(cellData) || '';
} else if (typeof col.templet === 'string') {
rowCtx._cells[col.field] = laytpl(col.templet).render(cellData) || '';
} else {
rowCtx._cells[col.field] = '';
}
} catch (e) {
rowCtx._cells[col.field] = '';
}
});
if (operatCol) {
var toolData = $.extend({}, row);
toolData.LAY_COL = operatCol;
rowCtx._operatHtml = ua.table.tool(toolData);
} else {
rowCtx._operatHtml = '';
}
rowCtx._checked = selectedIds.indexOf(String(row.id)) > -1;
rowCtx._disabled = !!row.LAY_DISABLED;
context.rows.push(rowCtx);
});
return context;
},
// (d) 行级循环渲染框架:模板只定义单行 HTML 生成函数,框架循环拼接并维护
// 空态容器;加载态由 store.fetch 的全局 loading 与容器的 ua-layout-loading
// 标记承接(内置形态的加载态样式在任务 14 接入)。
//
// 【tplFunction 调用约定】
// 单行 HTML = tplFunction.call(rowCtx, rowCtx, listCtx)
// - this/第一参 = 行上下文 {...row, _cells, _operatHtml, _checked, _disabled}
// - 第二参 = 列表上下文 {tableId, rows, page, count, checkedAll, settings}
// - 返回值 = 该行的 HTML 字符串(框架直接拼接,不做转义)
// 【laytpl 调用约定(tplLay / tplLayId)】
// 单行 HTML = laytpl(tplLay | $(tplLayId).html()).render(rowCtx)
// - d = 行上下文,与 tplFunction 第一参同构,另挂 d._list = 列表上下文
// (与 tplFunction 第二参对齐;laytpl 仅支持单数据对象)
// - 模板语法(layui 内置 laytpl v2.0.0 实测):{{ d.x }} 与 {{= d.x }}
// 均经 laytpl.escape 转义;{{- d.x }} 原样输出(_cells/_operatHtml 等
// HTML 内容必须用此形态);{{# }} 为 JS 语句(if/each 等)
// - tplLayId 是页面 <script type="text/html"> 节点选择器,节点必须位于
// 渲染容器之外(下方硬约束 2);script 内容为原始文本,laytpl 的 {{ }}
// 不会被浏览器解析,.html() 读取即可
// - laytpl 编译/运行错误不抛异常,render() 返回 "Laytpl Error: ..." 字符串
// (laytpl 内部已 console.error 细节),框架探测到后按条目名 console.error
// 并返回 false,调用方回退引擎纯渲染
// 【Vue 调用约定(tplVue / tplVueId,任务 17)】
// 列表级单实例:框架把模板包进单一根节点 <div class="ua-layout-vue">
// (Vue 2 实例模板根元素禁用 v-for 且生产构建该错误静默,包裹层规避
// 全部根约束问题,多根模板也因此可用),行循环由模板作者用
// v-for="d in rows" 承接:
// - rows = 行上下文数组,d 与 tplFunction/tplLay 的行上下文同构
// (d._cells.xxx / d._operatHtml / d._checked / d._disabled);
// - list = 列表上下文 {tableId, rows, page, count, checkedAll, settings}
// (laytpl 的 d._list 同物;Vue 实例对 _/$ 前缀 data 键不做代理,
// 裸标识符 _list 会 ReferenceError,故 Vue 形态改名为 list);
// - _cells/_operatHtml 含 HTML,必须用 v-html 输出,纯文本用 {{ }};
// - tplVueId 是页面 <script type="text/x-template"> 节点选择器,节点
// 必须位于渲染容器之外(下方硬约束 2),.html() 读取原始文本;
// - 挂载成功后一次性 form.render(约束 1);实例存活期禁止响应式数据
// 变更(约束 2):渲染上下文经 Object.freeze 冻结,数据不可变即不会
// re-patch,form.render 美化 DOM 不被清除,更新一律销毁重建;
// - 模板编译错误在 Vue 生产构建不抛异常,经链式全局 errorHandler 记录
// (installVueErrorHandler),框架探测后按条目名 console.error 并
// 返回 false,调用方回退引擎纯渲染。
// 【两条硬约束】
// 1. 模板容器恒带 class="layui-form"(form 控件渲染与事件受 .layui-form
// 祖先约束,缺失则模板里的 switch/checkbox 全部失效);
// 2. laytpl/Vue 模板节点(<script type="text/html"> / text/x-template)必须
// 位于渲染容器之外,防止被模板引擎编译时吞掉 {{ }} 插值(节点由页面
// 模板区提供)。
// 返回 false 表示管线不可渲染(table 形态或模板引擎未挂载/渲染失败),调用
// 方回退引擎纯渲染。
renderLayoutForm: function (tableId) {
if (controlledLayouts[tableId] === undefined) {
return false;
}
// 销毁重建式(约束 2):任何重渲路径进入本函数先销毁上一轮 Vue 实例,
// 容器被清空的其余路径由 fallbackLayoutToTable / syncLayoutVisibility
// 兜底同规则销毁
destroyLayoutVueInstances(tableId);
var resolved = resolveCurrentLayout(tableId);
if (resolved.kind === 'table') {
return false;
}
var $container = $('#ua-layout-' + tableId);
if ($container.length === 0) {
return false;
}
// laytpl 引擎(任务 16):tplLay = 模板字符串;tplLayId = 模板节点选择器
var laytplCompiled = null;
if (resolved.kind === 'lay' || resolved.kind === 'layId') {
var template;
if (resolved.kind === 'layId') {
var $tplNode = $(resolved.entry.tplLayId);
if ($tplNode.length === 0) {
console.error('[ua.table] tplLayId 节点不存在:' + resolved.entry.tplLayId
+ '(形态 "' + resolved.entry.name + '"),本次回退 table 形态');
return fallbackLayoutToTable(tableId, $container);
}
template = $tplNode.html();
} else {
template = resolved.entry.tplLay;
}
laytplCompiled = laytpl(template);
}
// vue/vueId 引擎(任务 17):tplVue = 模板字符串;tplVueId =
// <script type="text/x-template"> 节点选择器(节点须在渲染容器之外)
var vueTemplate = null;
if (resolved.kind === 'vue' || resolved.kind === 'vueId') {
if (typeof window.Vue === 'undefined') {
console.error('[ua.table] 全局 Vue 未引入(形态 "' + resolved.entry.name + '"),本次回退 table 形态');
return fallbackLayoutToTable(tableId, $container);
}
installVueErrorHandler();
if (resolved.kind === 'vueId') {
var $vueTplNode = $(resolved.entry.tplVueId);
if ($vueTplNode.length === 0) {
console.error('[ua.table] tplVueId 节点不存在:' + resolved.entry.tplVueId
+ '(形态 "' + resolved.entry.name + '"),本次回退 table 形态');
return fallbackLayoutToTable(tableId, $container);
}
vueTemplate = $vueTplNode.html();
} else {
vueTemplate = resolved.entry.tplVue;
}
}
var context = ua.table.buildLayoutContext(tableId);
if (vueTemplate !== null) {
// 自定义模板形态移除内置 card 网格类(与 laytpl 同规则)
$container.toggleClass('ua-layout-cards', resolved.entry.builtinCard === true);
// 常显开关类随渲染重挂(设置面板开关已写入页级 pageSetting,
// 切形态/重渲后保持;CSS 三态效果见 public.scss)
$container.toggleClass('ua-card-operat-always', ua.getPageSetting('card-operat-always', '') === '1');
if (context.rows.length === 0) {
$container.html('<div class="ua-layout-empty">无数据</div>');
form.render(null, 'ua-layout-' + tableId);
return true;
}
// 冻结渲染上下文(约束 2 的强制保证):顶层数据不可扩展即不会被
// observe,模板表达式改写失效、不触发 re-patch,form.render 插入
// 的美化 DOM 安全;数据更新只经销毁重建(本函数开头已销毁)
Object.freeze(context.rows);
$.each(context.rows, function (i, rowCtx) {
Object.freeze(rowCtx);
});
Object.freeze(context);
var vm = null;
try {
vm = new window.Vue({
template: '<div class="ua-layout-vue">' + vueTemplate + '</div>',
data: {rows: context.rows, list: context}
});
vm.__uaLayout = {tableId: tableId, name: resolved.entry.name};
vm.$mount();
} catch (e) {
// 挂载期同步异常(CSP Function 构造失败等);渲染期错误已由
// 全局 errorHandler 记录并告警,此处不再重复;此时实例尚未
// 注册进存活表,需就地销毁
if (lastVueLayoutError === null) {
console.error('[ua.table] Vue 模板挂载失败(形态 "' + resolved.entry.name + '"):'
+ ((e && e.message) || e) + ',本次回退 table 形态');
}
if (vm) {
try {
vm.$destroy();
} catch (ignored) {}
}
return fallbackLayoutToTable(tableId, $container);
}
// 编译/渲染错误探测(errorHandler 已按形态名 console.error);
// 同上,实例未注册,先就地销毁再归位
if (lastVueLayoutError && lastVueLayoutError.tableId === tableId) {
lastVueLayoutError = null;
try {
vm.$destroy();
} catch (ignored) {}
return fallbackLayoutToTable(tableId, $container);
}
lastVueLayoutError = null;
if (!vm || !vm.$el) {
console.error('[ua.table] Vue 模板渲染为空(形态 "' + resolved.entry.name + '"),本次回退 table 形态');
return fallbackLayoutToTable(tableId, $container);
}
layoutVueInstances[tableId] = [vm];
// $destroy 只拆实例不动 DOM(见 destroyLayoutVueInstances 注释),
// 上一轮实例的 DOM 残留必须在此显式清空,否则每次重渲都会累积
$container.empty().append(vm.$el);
// 约束 1:容器恒为 .layui-form(lay-filter 与容器 id 同名),
// 挂载后一次性 scoped form.render 美化模板产出的 switch/checkbox
form.render(null, 'ua-layout-' + tableId);
return true;
}
var html = '';
if (context.rows.length === 0) {
html = '<div class="ua-layout-empty">无数据</div>';
} else {
try {
$.each(context.rows, function (i, rowCtx) {
if (laytplCompiled) {
rowCtx._list = context;
var rowHtml = laytplCompiled.render(rowCtx) || '';
if (rowHtml.indexOf('Laytpl Error:') === 0) {
// laytpl 错误不抛异常(实测),探测返回值后主动抛出,
// 由外层 catch 统一告警并整体回退
throw new Error(rowHtml);
}
html += rowHtml;
} else {
html += resolved.entry.tplFunction.call(rowCtx, rowCtx, context);
}
});
} catch (e) {
console.error('[ua.table] 模板渲染失败(形态 "' + resolved.entry.name + '"):'
+ e.message + ',本次回退 table 形态');
return fallbackLayoutToTable(tableId, $container);
}
}
// 内置 card 形态容器挂网格布局类(样式见 public.scss 的 .ua-layout-cards),
// 自定义模板形态移除该标记,不受卡片网格影响
$container.toggleClass('ua-layout-cards', resolved.entry.builtinCard === true);
// 常显开关类随渲染重挂(设置面板开关已写入页级 pageSetting,
// 切形态/重渲后保持;CSS 三态效果见 public.scss)
$container.toggleClass('ua-card-operat-always', ua.getPageSetting('card-operat-always', '') === '1');
$container.html(html);
// 卡片操作按钮折叠收纳(function/laytpl 共用分支,见 applyCardOperatFold)
applyCardOperatFold($container);
// 容器恒为 .layui-form(lay-filter 与容器 id 同名),scoped form.render
// 美化模板产出的 switch/checkbox 控件
form.render(null, 'ua-layout-' + tableId);
return true;
},
// 当前形态名(任务 15 的切换 UI 消费)
getLayoutForm: function (tableId) {
return controlledLayouts[tableId] ? controlledLayouts[tableId].current : 'table';
},
// 切换当前形态:仅改写运行期状态并重渲染,不发起网络请求(数据取自
// store 当页状态)。目标条目不存在返回 false;模板管线不可用(无钩子
// 条目 / tplLayId/tplVueId 节点缺失 / laytpl 渲染错误 / Vue 未引入或
// 挂载渲染失败,均已在 renderLayoutForm 内归位 current='table')时走
// 引擎纯渲染兜底。
setLayoutForm: function (tableId, name) {
var layout = controlledLayouts[tableId];
if (!layout || !layout.byName[name]) {
return false;
}
layout.current = name;
if (!ua.table.renderLayoutForm(tableId)) {
// table 形态(含无钩子条目回退):引擎纯渲染当页数据,直呼备份
// 原引用不走 shim(防路由回 store 造成 fetch)
var state = window.UA_TABLE_STORE.dataProvider.getState(tableId);
var reloadOpts = {
data: state ? state.currentPageData : [],
totalRow: state && state.lastResponse ? state.lastResponse.totalRow : undefined
};
// 当前排序(store where.sort 单键映射)转引擎 initSort
// {field, type},切回表格形态时表头箭头与数据一致
var currentSort = state && state.where ? state.where.sort : null;
if (currentSort && Object.keys(currentSort).length > 0) {
var sortField = Object.keys(currentSort)[0];
var sortType = currentSort[sortField];
if (sortType === 'asc' || sortType === 'desc') {
reloadOpts.initSort = {field: sortField, type: sortType};
}
}
originalTableReloadData.call(table, tableId, reloadOpts);
}
syncLayoutVisibility(tableId);
syncControlledPager(tableId);
return true;
},
render: function (options) {
options.init = options.init || ua.init;
options.modifyReload = ua.parame(options.modifyReload, true);
options.elem = options.elem || options.init.tableElem;
options.id = options.id || options.init.tableRenderId;
options.scrollPos = options.scrollPos || 'fixed';
options.layFilter = options.id + '_LayFilter';
options.url = options.url || ua.url(options.init.indexUrl);
options.headers = ua.headers();
options.page = ua.parame(options.page, true);
options.search = ua.parame(options.search, true);
options.skin = options.skin || 'line';
options.autoSort = options.autoSort || false;
options.limit = options.limit || 15;
options.limits = options.limits || [10, 15, 20, 25, 50, 100];
options.cols = options.cols || [];
options.rowDisableCallback = options.rowDisableCallback || null;
var defaultToolbar = ['filter', 'print'];
if (options.search) {
defaultToolbar.push({
title: '搜索',
layEvent: 'TABLE_SEARCH',
icon: 'layui-icon-search',
extend: 'data-table-id="' + options.id + '"'
});
}
// 受控表形态清单规范化须先于引擎渲染:视图切换按钮的注入条件
// (条目数>1)依赖它,受控分支直接复用。非受控(逃生口)表不
// normalize。
var layoutEntries = [];
var autoInjected = false;
// 受控是默认数据流,仅显式 controlled:false 的逃生口页跳过
if (options.controlled !== false) {
// 未配置 layoutList 的受控页双端自动启用内置 card:内置
// card 无需页面模板(数据全来自渲染上下文),自动注入可行。
// 注入后条目数>1,双端工具栏自动出现"视图"按钮。显式配置
// layoutList 的页面仍优先不注入(含 usercard 示例页等存量配置)。
if (!options.layoutList || !options.layoutList.length) {
options.layoutList = ['table', 'card'];
autoInjected = true;
}
layoutEntries = normalizeLayoutList(options.layoutList);
if (layoutEntries.length > 1) {
// 视图切换按钮(任务 15 (b),交互结构参照旧 TABLE_LAYOUT
// 实现重建):下拉条目在点击时按 layoutEntries 动态构建
defaultToolbar.push({
title: '视图',
layEvent: 'TABLE_LAYOUT',
icon: 'layui-icon-template-1',
extend: 'data-table-id="' + options.id + '"'
});
}
// 卡片设置入口(pro#5):存在任何非 table 形态即注入(不依赖
// 条目数>1,单形态 card 页也需要打开设置面板)。头部插入使图标
// 渲染在工具图标组最左侧(layui.js 按数组序 map 拼 HTML 写入
// .layui-table-tool-self)。不写 extend 属性(layui 不渲染该
// 属性,面板定位走 lay-table-id 模式)
var hasNonTableLayout = false;
$.each(layoutEntries, function (i, entry) {
if (entry && entry.name !== 'table') {
hasNonTableLayout = true;
return false;
}
});
if (hasNonTableLayout) {
defaultToolbar.unshift({
title: '卡片设置',
layEvent: 'TABLE_CARD_SETTING',
icon: 'layui-icon-set'
});
}
}
if (options.defaultToolbar === undefined) {
options.defaultToolbar = defaultToolbar;
} else if (autoInjected) {
// 自动注入 card 且页面显式配置了 defaultToolbar 时,上方
// push 视图按钮的 defaultToolbar 数组被整体丢弃,"默认 card
// 且可切回 table"会落空,这里对最终配置去重追加(仅注入场景
// 触发;显式 layoutList 页均不受影响)
var hasLayoutBtn = false;
$.each(options.defaultToolbar, function (i, btn) {
if (btn && typeof btn === 'object' && btn.layEvent === 'TABLE_LAYOUT') {
hasLayoutBtn = true;
return false;
}
});
if (!hasLayoutBtn) {
options.defaultToolbar.push({
title: '视图',
layEvent: 'TABLE_LAYOUT',
icon: 'layui-icon-template-1',
extend: 'data-table-id="' + options.id + '"'
});
}
// 卡片设置入口与 TABLE_LAYOUT 同规则去重补投:autoInjected
// 即默认注入 card,条件恒成立;已有同名 layEvent 不重复加。
// 头部插入保持"图标组最左侧"与主注入一致
var hasCardSettingBtn = false;
$.each(options.defaultToolbar, function (i, btn) {
if (btn && typeof btn === 'object' && btn.layEvent === 'TABLE_CARD_SETTING') {
hasCardSettingBtn = true;
return false;
}
});
if (!hasCardSettingBtn) {
options.defaultToolbar.unshift({
title: '卡片设置',
layEvent: 'TABLE_CARD_SETTING',
icon: 'layui-icon-set'
});
}
}
var optionBefore = function () { };
if (options.before != undefined) {
optionBefore = options.before;
}
options.before = function (tableIns) {
optionBefore(tableIns);
};
var optionDone = function () { };
if (options.done != undefined) {
optionDone = options.done;
}
options.done = function (res, curr, count) {
optionDone(res, curr, count);
// 监听表格内的复制组件
ua.api.copyText('[lay-id=' + options.id + ']');
};
/**
* 为空时认为自己是普通状态,否则是单选、多选、子页面,此时添加等默认行为走弹框而不是新标签页
*/
selectMode = ua.getQueryVariable("select_mode");
selectConfirmCallback = ua.getQueryVariable('select_confirm_callback', 'onTableDataConfirm');
if (selectMode == 'checkbox') {
if (options.cols[0][0].type == 'radio') {
options.cols[0][0].type = 'checkbox';
} else if (options.cols[0][0].type != 'checkbox') {
options.cols[0].unshift({
type: 'checkbox'
});
}
} else if (selectMode == 'radio') {
if (options.cols[0][0].type == 'checkbox') {
options.cols[0][0].type = 'radio';
} else if (options.cols[0][0].type != 'radio') {
options.cols[0].unshift({
type: 'radio'
});
}
} else {
if(!ua.isParentIndex()){
selectMode = 'childPage'
}
}
// 判断元素对象是否有嵌套的
options.cols = ua.table.formatCols(options.cols, options.init);
// 初始化表格lay-filter
$(options.elem).attr('lay-filter', options.layFilter);
// 初始化表格搜索
if (options.search === true) {
options = ua.table.renderSearch(options.cols, options.elem, options.id, options);
}
// 初始化表格左上方工具栏
options.toolbar = options.toolbar || ['refresh', 'add', 'delete', 'export'];
if (selectMode == 'checkbox' || selectMode == 'radio') {
options.toolbar.unshift('selectConfirm');
options.height = 'full-85';
}
if (options.init.formFullScreen == true) {
options.init.formFullScreen = 'true';
} else {
options.init.formFullScreen = 'false';
}
options.toolbar = ua.table.renderToolbar(options.toolbar, options.elem, options.id, options.init);
// 判断是否有操作列表权限
options.cols = ua.table.renderOperat(options.cols, options.elem);
// 判断是否有操作列表权限
options.cols = ua.table.renderTrueHide(options.cols, options);
var parseData = function (res) { return res; };
if (typeof options.parseData === 'function') {
parseData = options.parseData;
}
// 受控是默认数据流(store 取数 + 引擎纯渲染),controlled:false
// 是逃生口:依赖引擎 url 模式行为(如自定义 parseData)的下游
// 页面可显式传 controlled:false 退回。
if (options.controlled !== false) {
// 受控数据流:数据请求与分页职责移交 UA_TABLE_STORE,引擎只做
// 纯渲染(空数据起步、关闭引擎分页,由 store 的 laypage 自绘)。
// 选择态注入(rowDisableCallback / URL selectedIds / LAY_CHECKED
// 跨页回显)由 store.fetch 的 injectSelectionMarks 承接,故不装
// 引擎 parseData。
delete options.url;
delete options.parseData;
options.data = [];
options.page = false;
} else {
options.parseData = function (res) {
// 初始化已经选择的值
if (selectMode == 'checkbox' || selectMode == 'radio') {
if (typeof options.rowDisableCallback == 'function') {
for (let index = 0; index < res.data.length; index++) {
const dataItem = res.data[index];
var rowDisableResult = options.rowDisableCallback(dataItem);
if (rowDisableResult === false || rowDisableResult === true) {
res.data[index].LAY_DISABLED = rowDisableResult;
}
}
}
var selectedIds = ua.getQueryVariable('selectedIds', '');
if (selectedIds.length > 0) {
var selectedIdArr = selectedIds.split(',');
for (let index = 0; index < res.data.length; index++) {
const dataItem = res.data[index];
if (selectedIdArr.indexOf(dataItem.id.toString()) > -1) {
res.data[index].LAY_DISABLED = true;
}
}
}
}
res = parseData(res);
return res;
};
}
// 列显隐记忆恢复:必须在引擎 table.render 首次渲染之前改写
// col.hide——cols 为同一引用(layui 浅拷贝 config.cols ===
// options.cols),引擎表格、列筛选面板勾选态、卡片收集三处自动
// 一致;controlledLayouts 注册晚于本行,同样消费改写后的引用。
// 记忆损坏时静默忽略,不中断渲染
var savedHiddenFields = ua.getPageSetting('table-cols-hide-' + options.id, '');
if (savedHiddenFields) {
try {
var hiddenFieldMap = {};
$.each(JSON.parse(savedHiddenFields), function (i, fieldName) {
hiddenFieldMap[fieldName] = true;
});
$.each(options.cols, function (i, row) {
$.each(row || [], function (j, col) {
if (col.field && hiddenFieldMap[col.field]) {
col.hide = true;
}
});
});
} catch (e) {
// 视为无记忆
}
}
// 初始化表格
var newTable = table.render(options);
// 监听表格搜索开关显示
ua.table.listenToolbar(options.layFilter, options.id);
// 监听表格瓶排序
ua.table.listenTableSort(options);
// 监听表格开关切换
ua.table.renderSwitch(options.cols, options.init, options.id, options.modifyReload);
// 监听表格开关切换
ua.table.listenEdit(options.init, options.layFilter, options.id, options.modifyReload);
// 监听导出事件
ua.table.listenExport(options);
// 监听表格选择器
ua.table.listenTableSelectConfirm(options);
// 受控接线(默认数据流,在共享监听挂载之后):注册 store、自建
// 分页容器、同步勾选事件、首屏取数。搜索/排序/switch/编辑等既有
// 监听的 table.reloadData 调用经 shim 自动分流到 store.reload(渲染
// 由顶部接线层注入的 done 承接),此处无需逐点改造。
// controlled:false 的逃生口页不走此分支,保持引擎 url 模式。
if (options.controlled !== false) {
// 分页容器挂在引擎视图容器之后(表格容器外),不与引擎分页共存。
// 注意 layui render 后原 table 元素被移出视图(成为 view 的兄弟
// 节点),不能用 closest 向上找 view
var pagerId = 'ua-store-pager-' + options.id;
$('#' + pagerId).remove();
$(options.elem).siblings('.layui-table-view').first().after(
'<div id="' + pagerId + '" class="ua-store-pager"></div>'
);
// 模板形态容器:与 ua-store-pager 并列(分页条两形态共用)。
// 两条硬约束:容器恒带 class="layui-form"(form 控件渲染与事件受
// .layui-form 祖先约束);laytpl/Vue 模板节点必须位于本容器之外
// (由页面模板区提供,本层不生成任何模板节点)
var layoutId = 'ua-layout-' + options.id;
$('#' + layoutId).remove();
$('#' + pagerId).before(
'<div id="' + layoutId + '" class="ua-layout-container layui-form" lay-filter="ua-layout-' + options.id + '"></div>'
);
// 卡片操作按钮"更多"展开委托:容器每次 render 经上方 remove 后
// 重建,容器级委托绑定一次即随容器生命周期,无重复绑定;展开
// 状态不记忆(重渲后收回)属预期
$('#' + layoutId).on('click', '.ua-card-operat-more', function (e) {
$(this).toggleClass('on').siblings('.ua-card-operat-more-wrap').toggleClass('layui-hide');
e.preventDefault();
e.stopPropagation();
});
// 形态注册:layoutList 已在 defaultToolbar 段规范化(视图按钮
// 注入共用)。运行期切形态经 ua.table.setLayoutForm。
// options.cols 此时已过 formatCols/renderOperat/renderTrueHide,
// 渲染器族与 operat 缺省值已就绪,构建上下文直接消费
var layoutByName = {};
$.each(layoutEntries, function (i, entry) {
layoutByName[entry.name] = entry;
});
// 默认形态解析(按端三层链):a.localStorage 按页记忆(最
// 高优先)> b.按端显式 layoutDefault > c.端缺省。layoutDefault
// 兼容三种写法:对象 {pc, mobile} 按当前端取值(端键缺省视为
// 未配置)、字符串旧写法双端同值、true 旧协议即"自动判断"视
// 为未配置。b/c 层取到的形态名不在 layoutByName 内一律忽略
// (防脏数据,与记忆同规)。端缺省:pc 取 table、无则首条目;
// mobile 取 card、无则 table、再无则首条目。外层空清单守卫
// 保持:非受控逃生口页或垃圾 layoutList 全无效时 layoutEntries
// 为空,恒 'table',任何分支不在空数组上取 [0]。
var defaultLayoutName = 'table';
if (layoutEntries.length > 0) {
var device = ua.checkMobile() ? 'mobile' : 'pc';
// layoutDefault 规格化为按端对象:字符串双端同值;true
// 旧协议与未配置均视为 null(走端缺省)
var layoutDefaultByDevice = options.layoutDefault;
if (typeof layoutDefaultByDevice === 'string') {
layoutDefaultByDevice = {pc: layoutDefaultByDevice, mobile: layoutDefaultByDevice};
} else if (layoutDefaultByDevice === null || typeof layoutDefaultByDevice !== 'object') {
layoutDefaultByDevice = null;
}
var configuredLayout = layoutDefaultByDevice ? (layoutDefaultByDevice[device] || null) : null;
if (layoutByName[configuredLayout]) {
// b 层:按端显式配置生效(脏值已被清单内校验拦截)
defaultLayoutName = configuredLayout;
} else if (device === 'pc') {
// c 层 pc 端缺省:table 优先,无则首条目
defaultLayoutName = layoutByName['table'] ? 'table' : layoutEntries[0].name;
} else if (layoutByName['card']) {
// c 层 mobile 端缺省:card 优先
defaultLayoutName = 'card';
} else if (layoutByName['table']) {
defaultLayoutName = 'table';
} else {
defaultLayoutName = layoutEntries[0].name;
}
// a 层:记忆最高优先,值不在清单内忽略(防脏数据)
var savedLayout = ua.getPageSetting('table-layout-default', '');
if (layoutByName[savedLayout]) {
defaultLayoutName = savedLayout;
}
}
controlledLayouts[options.id] = {
elem: options.elem,
cols: options.cols,
init: options.init,
entries: layoutEntries,
byName: layoutByName,
current: defaultLayoutName
};
window.UA_TABLE_STORE.register(options.id, {
indexUrl: options.init.indexUrl,
where: options.where,
page: {curr: 1, limit: options.limit},
limits: options.limits,
selectMode: selectMode,
rowDisableCallback: options.rowDisableCallback
// 不传 pagerElem:store 的 pager.jump 内部直呼 fetch 无法
// 触发引擎渲染,分页条由接线层 syncControlledPager 自管
});
// 引擎勾选事件 → store 选中集合:跨页勾选回显(LAY_CHECKED 注入)
// 的数据源。layui 2.10 事件回调 obj.data 是当次操作的单行对象
// (type=all 时不含可用行数据),全选场景以 store 的当页数据为准
//(selection.checkAll 缺省即当页数据,禁用行由其内部跳过)
table.on('checkbox(' + options.layFilter + ')', function (obj) {
if (obj.type === 'all') {
window.UA_TABLE_STORE.selection.checkAll(options.id, undefined, obj.checked);
} else {
window.UA_TABLE_STORE.selection.set(options.id, obj.data);
}
});
table.on('radio(' + options.layFilter + ')', function (obj) {
window.UA_TABLE_STORE.selection.set(options.id, obj.data);
});
// 列筛选勾选 → 卡片实时重渲:表格形态短路(列筛选行为完全
// 不变),卡片形态 debounce 100ms 后从 store 当页数据重渲
//(renderLayoutForm 零网络请求,连点不抖动)
table.on('colToggled(' + options.layFilter + ')', function (obj) {
// 列显隐按页记忆:表格/卡片两种形态都能从列筛选面板触发
// 本事件,保存必须放在形态短路之前。收集当前隐藏列的字段
// 名,全显时存空串即清除记忆
var hiddenFields = [];
$.each(controlledLayouts[options.id].cols, function (i, row) {
$.each(row || [], function (j, col) {
if (col.field && col.hide === true && hiddenFields.indexOf(col.field) === -1) {
hiddenFields.push(col.field);
}
});
});
ua.setPageSetting('table-cols-hide-' + options.id, hiddenFields.length ? JSON.stringify(hiddenFields) : '');
if (resolveCurrentLayout(options.id).kind === 'table') {
return;
}
clearTimeout(colToggledTimers[options.id]);
colToggledTimers[options.id] = setTimeout(function () {
ua.table.renderLayoutForm(options.id);
}, 100);
});
// 初始形态可见性:layoutDefault 非 table 时先隐引擎空视图(避免
// 空表头闪烁),首个 reload 完成后 wrapper 再次同步
syncLayoutVisibility(options.id);
// 首屏取数:经接线后的 store.reload 触发 fetch + 引擎纯渲染
window.UA_TABLE_STORE.reload(options.id);
}
return newTable;
},
renderToolbar: function (data, elem, tableId, init) {
data = data || [];
var toolbarHtml = '';
$.each(data, function (i, v) {
if (v === 'refresh') {
toolbarHtml += ' <button class="layui-btn layui-btn-sm layuimini-btn-primary" data-table-refresh="' + tableId + '"><i class="fa fa-refresh"></i> </button>\n';
} else if (v === 'add') {
if (ua.checkAuth('add', elem)) {
if(selectMode){
toolbarHtml += '<button class="layui-btn layui-btn-normal layui-btn-sm" data-open="' + init.addUrl + '" data-title="添加" data-full="' + init.formFullScreen + '"><i class="fa fa-plus"></i> 添加</button>\n';
}else{
toolbarHtml += '<button class="layui-btn layui-btn-normal layui-btn-sm" layuimini-content-href="' + init.addUrl + '" data-title="添加" data-full="' + init.formFullScreen + '"><i class="fa fa-plus"></i> 添加</button>\n';
}
}
} else if (v === 'delete') {
if (ua.checkAuth('delete', elem)) {
toolbarHtml += '<button class="layui-btn layui-btn-sm layui-btn-danger" data-url="' + init.deleteUrl + '" data-table-delete="' + tableId + '"><i class="fa fa-trash-o"></i> 删除</button>\n';
}
} else if (v === 'export') {
if (ua.checkAuth('export', elem)) {
toolbarHtml += '<button class="layui-btn layui-btn-sm layui-btn-success easyadmin-export-btn" data-url="' + init.exportUrl + '" data-table-export="' + tableId + '"><i class="fa fa-file-excel-o"></i> 导出</button>\n';
}
} else if (v === 'selectConfirm') {
toolbarHtml += '<button class="layui-btn layui-btn-sm layui-btn-success select-confirm" data-table-target="' + tableId + '"> 确定选择</button>\n';
} else if (typeof v === "object") {
$.each(v, function (ii, vv) {
vv.class = vv.class || '';
vv.icon = vv.icon || '';
vv.auth = vv.auth || 'add';
vv.url = vv.url || '';
vv.method = vv.method || 'open';
vv.title = vv.title || vv.text;
vv.text = vv.text || vv.title;
vv.extend = vv.extend || '';
vv.checkbox = vv.checkbox || false;
if (ua.checkAuth(vv.auth, elem)) {
toolbarHtml += ua.table.buildToolbarHtml(vv, tableId);
}
});
}
});
return '<div>' + toolbarHtml + '</div>';
},
renderSearch: function (cols, elem, tableId, options) {
// TODO 只初始化第一个table搜索字段,如果存在多个(绝少数需求),得自己去扩展
cols = cols[0] || {};
var newCols = [];
var formHtml = '';
var tabSetting = {};
var formatFilter = {},
formatOp = {};
$.each(cols, function (i, d) {
d.field = d.field || false;
d.fieldAlias = ua.parame(d.fieldAlias, d.field);
d.title = d.title || d.field || '';
d.selectList = d.selectList || {};
d.search = ua.parame(d.search, true);
d.searchTip = d.searchTip || '请输入' + d.title || '';
d.searchValue = d.searchValue || undefined;
d.searchHide = d.searchHide || '';
d.defaultSearchValue = d.defaultSearchValue;
d.searchOp = d.searchOp || '%*%';
d.timeType = d.timeType || 'datetime';
d.elemIdName = d.fieldAlias;
var a = '';
var b = '';
if (typeof d.fieldAlias == 'string') {
if (d.fieldAlias.indexOf('[') == 0) {
var fieldPlusArr = d.fieldAlias.replace('[').split(']');
d.elemIdName = fieldPlusArr.join('-');
}
d.elemIdName = d.elemIdName.replace('.', '-');
}
if (d.defaultSearchValue !== undefined) {
if (!d.searchValue || d.searchValue.length == 0) {
d.searchValue = d.defaultSearchValue;
}
} else {
if (d.field) {
d.defaultSearchValue = ua.getQueryVariable(d.field);
if (d.defaultSearchValue != undefined) {
d.searchValue = d.defaultSearchValue;
}
}
}
if (d.searchValue !== undefined) {
if (d.search == 'number_limit') {
var paramsArr = d.searchValue.split(',');
a = paramsArr[0];
b = paramsArr[1];
if (a) {
formatFilter['[' + d.field + ']min'] = a;
formatOp['[' + d.field + ']min'] = 'min';
}
if (b) {
formatFilter['[' + d.field + ']max'] = b;
formatOp['[' + d.field + ']max'] = 'max';
}
} else if (d.search == 'time_limit') {
var paramsArr = d.searchValue.split(',');
a = paramsArr[0];
b = paramsArr[1];
if (a) {
formatFilter['[' + d.field + ']min_date'] = a;
formatOp['[' + d.field + ']min_date'] = 'min_date';
}
if (b) {
formatFilter['[' + d.field + ']max_date'] = b;
formatOp['[' + d.field + ']max_date'] = 'max_date';
}
} else {
formatFilter[d.field] = d.searchValue;
formatOp[d.field] = d.searchOp;
}
}
var formSearchHideClass = '';
if (d.searchHide) {
formSearchHideClass = ' search-hide-item';
}
if (d.searchValue === undefined) {
d.searchValue = '';
}
if (d.field !== false) {
if (d.search !== false) {
switch (d.search) {
case true:
formHtml += '\t<div class="layui-form-item layui-inline ' + formSearchHideClass + ' ">\n' +
'<label class="layui-form-label">' + d.title + '</label>\n' +
'<div class="layui-input-inline">\n' +
'<input id="c-' + d.elemIdName + '" name="' + d.fieldAlias + '" data-search-op="' + d.searchOp + '" value="' + d.searchValue + '" placeholder="' + d.searchTip + '" class="layui-input">\n' +
'</div>\n' +
'</div>';
break;
case 'select':
d.searchOp = '=';
var selectHtml = '';
if (d.tab) {
tabSetting.field = d.field;
tabSetting.elemIdName = d.elemIdName;
tabSetting.list = d.selectList;
tabSetting.index = -1;
}
var selectListIndex = 0;
$.each(d.selectList, function (sI, sV) {
var selected = '';
if (sI === d.searchValue) {
selected = 'selected=""';
if (d.tab) {
tabSetting.index = selectListIndex;
}
}
selectHtml += '<option value="' + sI + '" ' + selected + '>' + sV + '</option>/n';
selectListIndex++;
});
if (d.tab) {
formHtml += '<input type="hidden" id="c-' + d.elemIdName + '" name="' + d.fieldAlias + '" value="' + d.searchValue + '">';
break;
}
formHtml += '\t<div class="layui-form-item layui-inline ' + formSearchHideClass + ' ">\n' +
'<label class="layui-form-label">' + d.title + '</label>\n' +
'<div class="layui-input-inline">\n' +
'<select class="layui-select" id="c-' + d.elemIdName + '" name="' + d.fieldAlias + '" data-search-op="' + d.searchOp + '" >\n' +
'<option value="">- 全部 -</option> \n' +
selectHtml +
'</select>\n' +
'</div>\n' +
'</div>';
break;
case 'range':
d.searchOp = 'range';
formHtml += '\t<div class="layui-form-item layui-inline ' + formSearchHideClass + ' ">\n' +
'<label class="layui-form-label">' + d.title + '</label>\n' +
'<div class="layui-input-inline">\n' +
'<input id="c-' + d.elemIdName + '" name="' + d.fieldAlias + '" data-search-op="' + d.searchOp + '" value="' + d.searchValue + '" placeholder="' + d.searchTip + '" class="layui-input">\n' +
'</div>\n' +
'</div>';
break;
case 'time':
d.searchOp = '=';
formHtml += '\t<div class="layui-form-item layui-inline ' + formSearchHideClass + ' ">\n' +
'<label class="layui-form-label">' + d.title + '</label>\n' +
'<div class="layui-input-inline">\n' +
'<input id="c-' + d.elemIdName + '" name="' + d.fieldAlias + '" data-search-op="' + d.searchOp + '" value="' + d.searchValue + '" placeholder="' + d.searchTip + '" class="layui-input">\n' +
'</div>\n' +
'</div>';
break;
case 'time_limit':
d.searchOp = '=';
formHtml += '\t<div class="layui-form-item form-item-time-limit layui-inline ' + formSearchHideClass + ' ">\n' +
'<label class="layui-form-label">' + d.title + '</label>\n' +
'<div class="layui-input-inline">\n' +
'<input id="c-' + d.elemIdName + '-min_date" name="[' + d.fieldAlias + ']min_date" data-search-op="min_date" value="' + a + '" placeholder="最小值" class="layui-input">\n' +
'<input id="c-' + d.elemIdName + '-max_date" name="[' + d.fieldAlias + ']max_date" data-search-op="max_date" value="' + b + '" placeholder="最大值" class="layui-input">\n' +
'</div>\n' +
'</div>';
break;
case 'number_limit':
d.searchOp = '=';
formHtml += '\t<div class="layui-form-item form-item-number-limit layui-inline ' + formSearchHideClass + ' ">\n' +
'<label class="layui-form-label">' + d.title + '</label>\n' +
'<div class="layui-input-inline">\n' +
'<input id="c-' + d.elemIdName + '-min" name="[' + d.fieldAlias + ']min" data-search-op="min" type="text" value="' + a + '" placeholder="最小值" class="layui-input">\n' +
'<input id="c-' + d.elemIdName + '-max" name="[' + d.fieldAlias + ']max" data-search-op="max" type="text" value="' + b + '" placeholder="最大值" class="layui-input">\n' +
'</div>\n' +
'</div>';
break;
}
newCols.push(d);
} else {
// TODO:支持更多种类型的初始化,比如number_limit、time_limit
formHtml += '<input type="hidden" id="c-' + d.elemIdName + '" name="' + d.fieldAlias + '" value="' + d.searchValue + '">';
}
}
});
if (tabSetting.field) {
var tabId = 'tabFieldset_' + tableId;
$(elem).before('<div id="' + tabId + '" class="search-tab" data-field="' + tabSetting.field + '"></div>');
var header = [
{
title: '全部',
key: '',
}
];
for (const tabSettingListKey in tabSetting.list) {
if (Object.prototype.hasOwnProperty.call(tabSetting.list, tabSettingListKey)) {
const tabItem = tabSetting.list[tabSettingListKey];
header.push({
title: tabItem,
key: tabSettingListKey,
});
}
}
layui.tabs.render({
elem: '#' + tabId,
header: header,
index: tabSetting.index + 1,
});
layui.tabs.on('afterChange(' + tabId + ')', function (data) {
var value = $(data.thisHeaderItem).attr('lay-key');
$('#c-' + tabSetting.elemIdName).val(value);
$('[lay-filter="' + tableId + '_filter"]').trigger('click');
});
}
if (formHtml !== '') {
$(elem).before('<fieldset id="searchFieldset_' + tableId + '" class="table-search-fieldset layui-hide">\n' +
'<legend>条件搜索</legend>\n' +
'<form class="layui-form layui-form-pane form-search" lay-filter="' + tableId + '_filter_form">\n' +
formHtml +
'<div class="layui-form-item layui-inline" style="margin-left: 115px">\n' +
'<button type="submit" class="layui-btn layui-btn-normal" data-type="tableSearch" data-table="' + tableId + '" lay-submit lay-filter="' + tableId + '_filter"> 搜 索</button>\n' +
'<button type="reset" class="layui-btn layui-btn-primary" data-table-reset="' + tableId + '"> 重 置 </button>\n' +
' </div>' +
'</form>' +
'</fieldset>');
ua.table.listenTableSearch(tableId);
// 初始化form表单
form.render();
$.each(newCols, function (ncI, ncV) {
if (ncV.search === 'range') {
laydate.render({ range: true, type: ncV.timeType, elem: '[name="' + ncV.fieldAlias + '"]' });
}
if (ncV.search === 'time') {
laydate.render({ type: ncV.timeType, elem: '[name="' + ncV.fieldAlias + '"]' });
}
if (ncV.search === 'time_limit') {
laydate.render({ type: ncV.timeType, elem: '[name="[' + ncV.fieldAlias + ']min_date"]' });
laydate.render({ type: ncV.timeType, elem: '[name="[' + ncV.fieldAlias + ']max_date"]' });
}
});
}
options.where = {
filter: JSON.stringify(formatFilter),
op: JSON.stringify(formatOp)
};
lastTableWhere[tableId] = options.where;
return options;
},
renderSwitch: function (cols, tableInit, tableId, modifyReload) {
tableInit.modifyUrl = tableInit.modifyUrl || false;
cols = cols[0] || {};
tableId = tableId || ua.init.tableRenderId;
if (cols.length > 0) {
$.each(cols, function (i, v) {
v.filter = v.filter || false;
if (v.filter !== false && tableInit.modifyUrl !== false) {
ua.table.listenSwitch({ filter: v.filter, url: tableInit.modifyUrl, tableId: tableId, modifyReload: modifyReload });
}
});
}
},
renderOperat(data, elem) {
for (dk in data) {
var col = data[dk];
var operat = col[col.length - 1].operat;
if (operat !== undefined) {
var check = false;
for (key in operat) {
var item = operat[key];
if (typeof item === 'string') {
if (ua.checkAuth(item, elem)) {
check = true;
break;
}
} else {
for (k in item) {
var v = item[k];
if (v.auth == undefined) {
v.auth = 'add';
}
if (ua.checkAuth(v.auth, elem)) {
check = true;
break;
}
}
}
}
if (!check) {
data[dk].pop();
}
}
}
return data;
},
renderTrueHide(data, options) {
var newData = [];
for (dk in data) {
var newCol = [];
var col = data[dk];
col.forEach(colItem => {
var trueHide = false;
if (typeof colItem.trueHide == 'function') {
trueHide = colItem.trueHide(colItem, col, options);
} else if (typeof colItem.trueHide == 'string') {
trueHide = !ua.checkAuth(colItem.trueHide, options.elem);
} else {
trueHide = colItem.trueHide;
}
if (!trueHide) {
newCol.push(colItem);
}
});
newData.push(newCol);
}
return newData;
},
buildToolbarHtml: function (toolbar, tableId) {
var html = '';
toolbar.class = toolbar.class || '';
toolbar.icon = toolbar.icon || '';
toolbar.auth = toolbar.auth || 'add';
toolbar.url = toolbar.url || '';
toolbar.extend = toolbar.extend || '';
toolbar.method = toolbar.method || 'open';
toolbar.field = toolbar.field || 'id';
toolbar.title = toolbar.title || toolbar.text;
toolbar.text = toolbar.text || toolbar.title;
toolbar.checkbox = toolbar.checkbox || false;
var formatToolbar = toolbar;
formatToolbar.icon = formatToolbar.icon !== '' ? '<i class="' + formatToolbar.icon + '"></i> ' : '';
formatToolbar.class = formatToolbar.class !== '' ? 'class="' + formatToolbar.class + '" ' : '';
if (toolbar.method === 'open') {
formatToolbar.method = formatToolbar.method !== '' ? 'data-open="' + formatToolbar.url + '" data-title="' + formatToolbar.title + '" ' : '';
} else if (toolbar.method === 'none') { // 常用于与extend配合,自定义监听按钮
formatToolbar.method = '';
} else if (toolbar.method === 'blank') {
formatToolbar.method = ' href="' + formatToolbar.url + '" target="_blank" ';
} else if (operat.method === 'tab') {
formatToolbar.method = ' layuimini-content-href="' + formatToolbar.url + '" data-title="' + formatToolbar.title + '"';
} else {
formatToolbar.method = formatToolbar.method !== '' ? 'data-request="' + formatToolbar.url + '" data-title="' + formatToolbar.title + '" ' : '';
}
formatToolbar.checkbox = toolbar.checkbox ? ' data-checkbox="true" ' : '';
formatToolbar.tableId = tableId !== undefined ? ' data-table="' + tableId + '" ' : '';
html = '<button ' + formatToolbar.class + formatToolbar.method + formatToolbar.extend + formatToolbar.checkbox + formatToolbar.tableId + '>' + formatToolbar.icon + formatToolbar.text + '</button>';
return html;
},
buildOperatHtml: function (operat, data) {
var html = '';
// 拷贝替代引用:后续默认值写入与格式化不得写回共享配置对象
operat = $.extend({}, operat);
operat.class = operat.class || '';
operat.icon = operat.icon || '';
operat.auth = operat.auth || 'add';
operat.url = operat.url || '';
operat.extend = operat.extend || '';
operat.method = operat.method || 'open';
operat.field = operat.field || 'id';
operat.data = operat.data || ['id'];
operat.titleField = operat.titleField || 'title';
operat.title = operat.title || operat.text;
operat.text = operat.text || operat.title;
var titleEndfix = '';
if (typeof operat.titleField == 'function') {
titleEndfix = operat.titleField(data, operat);
} else if (data[operat.titleField]) {
titleEndfix = '-' + data[operat.titleField];
}
if (typeof operat.text == 'function') {
operat.text = operat.text(data, operat);
}
if (typeof operat.title == 'function') {
operat.title = operat.title(data, operat);
}
var formatOperat = operat;
formatOperat.icon = formatOperat.icon !== '' ? '<i class="' + formatOperat.icon + '"></i> ' : '';
formatOperat.class = formatOperat.class !== '' ? 'class="' + formatOperat.class + '" ' : '';
if (operat.method === 'open') {
formatOperat.method = formatOperat.method !== '' ? 'data-open="' + formatOperat.url + '" data-title="' + formatOperat.title + titleEndfix + '" ' : '';
} else if (operat.method === 'none') { // 常用于与extend配合,自定义监听按钮
formatOperat.method = '';
} else if (operat.method === 'blank') {
formatOperat.method = ' href="' + formatOperat.url + '" target="_blank" ';
} else if (operat.method === 'tab') {
formatOperat.method = ' layuimini-content-href="' + formatOperat.url + '" data-title="' + formatOperat.title + titleEndfix + '"';
} else {
formatOperat.method = formatOperat.method !== '' ? 'data-request="' + formatOperat.url + '" data-title="' + formatOperat.title + titleEndfix + '" ' : '';
}
formatOperat.dataBind = ' ';
operat.data.forEach((item, index) => {
formatOperat.dataBind += 'data-' + item + '="' + data[item] + '" ';
});
html = '<a ' + formatOperat.class + formatOperat.method + formatOperat.extend + formatOperat.dataBind + '>' + formatOperat.icon + formatOperat.text + '</a>';
return html;
},
toolSpliceUrl(url, field, data) {
url = url.indexOf("?") !== -1 ? url + '&' + field + '=' + data[field] : url + '?' + field + '=' + data[field];
return url;
},
formatCols: function (cols, init) {
for (i in cols) {
var col = cols[i];
for (index in col) {
var val = col[index];
if (typeof val.hide == 'function') {
cols[i][index]['hide'] = val.hide(val, cols, init);
}
if (val['width'] == undefined && val['minWidth'] == undefined) {
var width = null;
if (val.title) {
width = val.title.length * 15 + 55;
}
if (width != null) {
cols[i][index]['minWidth'] = width;
}
}
// if str end with _time
if (val.field && val.field.indexOf('_time') !== -1) {
cols[i][index]['minWidth'] = 160;
}
if (val.sort === undefined) {
cols[i][index]['sort'] = true;
}
if (val.defaultValue === undefined) {
cols[i][index]['defaultValue'] = '';
}
// 判断是否包含初始化数据
if (val.init === undefined) {
cols[i][index]['init'] = init;
}
// 格式化列操作栏
if (val.templet === ua.table.tool && val.operat === undefined) {
cols[i][index]['operat'] = ['edit', 'delete'];
}
// 格式化列操作栏
if (val.templet === ua.table.tool) {
cols[i][index]['sort'] = false;
}
// 判断是否包含开关组件
if (val.templet === ua.table.switch && val.filter === undefined) {
cols[i][index]['filter'] = val.field;
}
// 判断是否含有搜索下拉列表
if (val.selectList !== undefined && val.search === undefined) {
cols[i][index]['search'] = 'select';
}
// 判断是否初始化对齐方式
if (val.align === undefined) {
cols[i][index]['align'] = 'left';
}
// 部分字段开启排序
var sortDefaultFields = ['id', 'sort'];
if (val.sort === undefined && sortDefaultFields.indexOf(val.field) >= 0) {
cols[i][index]['sort'] = true;
}
// 初始化图片高度
if (val.templet === ua.table.image && val.imageHeight === undefined) {
cols[i][index]['imageHeight'] = 26;
cols[i][index]['sort'] = false;
}
// 图片默认不搜索
if (val.templet === ua.table.image && val.search === undefined) {
cols[i][index]['search'] = false;
}
// 判断是否列表数据转换
if (val.selectList !== undefined && val.templet === undefined) {
cols[i][index]['templet'] = ua.table.list;
}
// 判断是否多层对象
if (val.field !== undefined && val.field.split(".").length > 1) {
if (val.templet === undefined) {
cols[i][index]['templet'] = ua.table.value;
}
}
// 如果未定义则默认使用value
if (cols[i][index]['templet'] === undefined) {
cols[i][index]['templet'] = ua.table.value;
} else {
// 针对特定的模板做数据调整
if (cols[i][index]['templet'] == ua.table.list) {
if (val.selectValue) {
var newSelectList = {};
val.selectList.map(item => {
newSelectList[item[val.selectValue]] = item[val.selectLabel];
});
cols[i][index]['selectList'] = newSelectList;
}
}
}
if (val.fieldFormat == undefined) {
switch (val.templet) {
case ua.table.image:
val.fieldFormat = 'image';
break;
case ua.table.date:
val.fieldFormat = 'date';
break;
default:
val.fieldFormat = 'text';
if (val.selectList !== undefined) {
val.fieldFormat = 'select';
}
break;
}
}
}
}
return cols;
},
tool: function (data) {
var option = data.LAY_COL;
option.operat = option.operat || ['edit', 'delete'];
var elem = option.init.tableElem || ua.init.tableElem;
var html = '';
$.each(option.operat, function (i, item) {
if (typeof item === 'string') {
switch (item) {
case 'edit':
var editMethod = 'tab';
if(selectMode){
editMethod = 'open';
}
var operat = {
class: 'layui-btn layui-btn-success layui-btn-xs',
method: editMethod,
field: 'id',
icon: '',
text: '编辑',
title: '编辑信息',
auth: 'edit',
url: option.init.editUrl,
extend: option.init.formFullScreen == 'true' ? ' data-full="true"' : ''
};
operat.url = ua.table.toolSpliceUrl(operat.url, operat.field, data);
if (ua.checkAuth(operat.auth, elem)) {
html += ua.table.buildOperatHtml(operat, data);
}
break;
case 'delete':
var operat = {
class: 'layui-btn layui-btn-danger layui-btn-xs',
method: 'get',
field: 'id',
icon: '',
text: '删除',
title: '确定删除?',
auth: 'delete',
url: option.init.deleteUrl,
extend: ""
};
operat.url = ua.table.toolSpliceUrl(operat.url, operat.field, data);
if (ua.checkAuth(operat.auth, elem)) {
html += ua.table.buildOperatHtml(operat, data);
}
break;
}
} else if (typeof item === 'object') {
$.each(item, function (i, operat) {
// 拷贝替代引用:默认值写入与 url 拼接不得写回共享配置对象
operat = $.extend({}, operat);
operat.class = operat.class || '';
operat.icon = operat.icon || '';
operat.auth = operat.auth || 'add';
operat.url = operat.url || '';
operat.method = operat.method || 'open';
operat.field = operat.field || 'id';
operat.title = operat.title || operat.text;
operat.text = operat.text || operat.title;
operat.extend = operat.extend || '';
operat._if = operat._if || function () { return true; };
if (typeof operat._if == 'function') {
if (operat._if(data, operat) !== true) {
return '';
}
} else if (typeof operat._if == 'string') {
var ifValue = ua.table.returnColumnValue(data, operat._if, false);
if (!ifValue) {
return '';
}
}
// 自定义表格opreat按钮的弹窗标题风格,extra是表格里的欲加入标题中的字段
operat.extra = operat.extra || '';
if (data[operat.extra] !== undefined) {
operat.title = data[operat.extra] + ' - ' + operat.title;
}
if (typeof operat.url == 'function') {
operat.url = operat.url(data, operat);
}
if (typeof operat.field != 'function') {
if (!ua.empty(operat.field)) {
operat.url = ua.table.toolSpliceUrl(operat.url, operat.field, data);
}
} else {
var fieldParam = operat.field(data, operat);
if (typeof fieldParam == 'string') {
operat.url = fieldParam;
} else {
var querys = '';
if (operat.url.indexOf("?") !== -1) {
if (operat.url.indexOf("?") !== operat.url.length - 1) {
querys = '&';
}
} else {
querys = '?';
}
operat.url += querys + $.param(fieldParam);
}
}
if (ua.checkAuth(operat.auth, elem)) {
html += ua.table.buildOperatHtml(operat, data);
}
});
}
});
return html;
},
list: function (data) {
var option = data.LAY_COL;
option.selectList = option.selectList || {};
var value = ua.table.returnColumnValue(data);
if (option.selectList[value] === undefined || option.selectList[value] === '' || option.selectList[value] === null) {
return value;
} else {
return option.selectList[value];
}
},
filePreview: function (data) {
var mimeName = data.mime_type.split('/')[0];
if (mimeName == 'image') {
return ua.table.image(data);
} else {
var value = ua.table.returnColumnValue(data);
var groupName = ua.getExtGroupName(data.file_ext);
return '<a href="' + value + '"><img style="height:40px;width:40px" src="/static/admin/images/upload-icons/' + groupName + '.png" /></a>';
}
},
image: function (data) {
var option = data.LAY_COL;
option.imageWidth = option.imageWidth || 200;
option.imageHeight = option.imageHeight || 26;
option.imageSplit = option.imageSplit || '|';
option.imageJoin = option.imageJoin || '<br>';
option.title = option.title || option.field;
var title = data[option.title];
var value = ua.table.returnColumnValue(data);
if (value === undefined || value === null) {
return '<img style="max-width: ' + option.imageWidth + 'px; max-height: ' + option.imageHeight + 'px;" src="' + value + '" data-image="' + title + '">';
} else {
var values = value.split(option.imageSplit),
valuesHtml = [];
values.forEach((value, index) => {
valuesHtml.push('<img style="max-width: ' + option.imageWidth + 'px; max-height: ' + option.imageHeight + 'px;" src="' + value + '" data-image="' + title + '">');
});
return valuesHtml.join(option.imageJoin);
}
},
url: function (data) {
var option = data.LAY_COL;
var urlNameField = option.urlNameField || '';
var value = ua.table.returnColumnValue(data);
if (ua.empty(value)) {
return '';
}
var urlName = value;
if (urlNameField != '') {
try {
var urlNameFieldType = typeof urlNameField;
if (urlNameFieldType == 'string') {
urlName = data[urlNameField];
} else if (urlNameFieldType == 'function') {
urlName = urlNameField(data);
}
} catch (e) {
}
}
return '<a class="layuimini-table-url" href="' + value + '" target="_blank" class="label bg-green">' + urlName + '</a>';
},
switch: function (data) {
var option = data.LAY_COL;
if (!ua.checkAuth('modify', option.init.tableElem)) {
return ua.table.list(data);
}
option.filter = option.filter || option.field || null;
option.checked = option.checked || 1;
var value = ua.table.returnColumnValue(data);
var checked = value === option.checked ? 'checked' : '';
return laytpl('<input type="checkbox" name="' + option.field + '" value="' + data.id + '" lay-skin="switch" lay-filter="' + option.filter + '" ' + checked + ' >').render(data);
},
price: function (data) {
var value = ua.table.returnColumnValue(data);
return '<span>¥' + value + '</span>';
},
percent: function (data) {
var value = ua.table.returnColumnValue(data);
return '<span>' + value + '%</span>';
},
icon: function (data) {
var value = ua.table.returnColumnValue(data);
return '<i class="' + value + '"></i>';
},
text: function (data) {
var value = ua.table.returnColumnValue(data);
return '<span class="line-limit-length">' + value + '</span>';
},
value: function (data) {
var value = ua.table.returnColumnValue(data);
return '<span>' + value + '</span>';
},
//时间戳转日期
date: function (data) {
var option = data.LAY_COL;
value = ua.table.returnColumnValue(data);
if (!ua.empty(value)) {
value = util.toDateString(value * 1000, option.format || 'yyyy-MM-dd HH:mm:ss');
}
return '<span>' + value + '</span>';
},
bytes: (data) => {
var size = ua.table.returnColumnValue(data);
sizeReadable = ua.bytes(size);
return sizeReadable;
},
copyText(data) {
var option = data.LAY_COL;
var value = ua.table.returnColumnValue(data);
var copyValue = value;
var copyText = option.copyText;
if (typeof copyText === 'function') {
copyValue = copyText(value, data);
}
return '<span data-toggle="copy-text" data-clipboard-text="' + copyValue + '"><i class="fa fa-copy"></i> ' + value + '</span>';
},
// 统一列返回数据处理
returnColumnValue(data, field, defaultValue) {
if (!data.LAY_COL) {
return '';
}
var option = data.LAY_COL;
field = field || option.field;
defaultValue = defaultValue || option.defaultValue;
var valueParser = option.valueParser;
var value = undefined;
try {
value = eval("data." + field);
} catch (e) {
}
if (typeof valueParser == 'function') {
value = valueParser(value, data);
}
if (value === null || value === undefined) {
value = defaultValue;
}
return value;
},
listenTableSearch: function (tableId) {
form.on('submit(' + tableId + '_filter)', function (data) {
var dataField = data.field;
var formatFilter = {},
formatOp = {};
$.each(dataField, function (key, val) {
if (val !== '') {
formatFilter[key] = val;
var elemId = ua.table.renderSearchFormItemElementId(key);
var op = $('#c-' + elemId).attr('data-search-op');
op = op || '%*%';
formatOp[key] = op;
}
});
var where = {
filter: JSON.stringify(formatFilter),
op: JSON.stringify(formatOp)
};
lastTableWhere[tableId] = where;
table.reloadData(tableId, {
page: {
curr: 1
}
, where: where
}, 'data');
return false;
});
},
listenSwitch: function (option, ok) {
option.filter = option.filter || '';
option.url = option.url || '';
option.field = option.field || option.filter || '';
option.tableId = option.tableId || ua.init.tableRenderId;
option.modifyReload = option.modifyReload || false;
form.on('switch(' + option.filter + ')', function (obj) {
var checked = obj.elem.checked ? 1 : 0;
if (typeof ok === 'function') {
return ok({
id: obj.value,
checked: checked,
});
} else {
var data = {
id: obj.value,
field: option.field,
value: checked,
};
ua.request.post({
url: option.url,
prefix: true,
data: data,
}, function (res) {
if (option.modifyReload) {
table.reloadData(option.tableId);
}
}, function (res) {
ua.msg.error(res.msg, function () {
table.reloadData(option.tableId);
});
}, function () {
table.reloadData(option.tableId);
});
}
});
},
listenTableSort(option) {
//触发排序事件
table.on('sort(' + option.layFilter + ')', function (obj) { //注:sort 是工具条事件名,test 是 table 原始容器的属性 lay-filter="对应的值"
var lastWhere = lastTableWhere[option.id] ? lastTableWhere[option.id] : {};
lastWhere.sort = {};
lastWhere.sort[obj.field] = obj.type;
table.reloadData(option.id, {
initSort: obj //记录初始排序,如果不设的话,将无法标记表头的排序状态。
, where: lastWhere
});
});
},
listenToolbar: function (layFilter, tableId) {
table.on('toolbar(' + layFilter + ')', function (obj) {
var options = obj.config;
var tableId = obj.config.id;
// 搜索表单的显示
switch (obj.event) {
case 'TABLE_SEARCH':
var searchFieldsetId = 'searchFieldset_' + tableId;
var _that = $("#" + searchFieldsetId);
if (_that.hasClass("layui-hide")) {
_that.removeClass('layui-hide');
} else {
_that.addClass('layui-hide');
}
break;
case 'TABLE_LAYOUT':
// 视图切换下拉(任务 15 (b),交互结构参照旧 TABLE_LAYOUT
// 实现重建):按钮仅在受控表 layoutList 条目>1 时注入,
// 条目清单来自 controlledLayouts 注册表。点击条目 =
// localStorage 按页记忆 + setLayoutForm 切换(数据取自
// store 当页状态,零网络请求)
var layoutEntryId = 'ua-layout-list-' + tableId;
var $toolbarLayoutElem = $('[lay-table-id="' + tableId + '"]').find('[lay-event="TABLE_LAYOUT"]');
var $layoutListElem = $('#' + layoutEntryId);
if ($layoutListElem.length > 0) {
$layoutListElem.remove();
break;
}
var layoutCurrent = ua.table.getLayoutForm(tableId);
var layoutIcons = {table: 'layui-icon-table', card: 'layui-icon-app'};
var layoutHtml = '<div id="' + layoutEntryId + '" class="ul-nav-block table-tpl-container">';
$.each(controlledLayouts[tableId].entries, function (i, item) {
var itemIcon = item.icon || layoutIcons[item.name] || 'layui-icon-template-1';
var itemClassName = item.name === layoutCurrent ? ' current' : '';
layoutHtml += '<div class="item' + itemClassName + '" data-name="' + item.name + '">'
+ '<div class="item-icon"><i class="layui-icon ' + itemIcon + '"></i></div>'
+ '<div class="item-title">' + item.title + '</div>'
+ '</div>';
});
layoutHtml += '</div>';
$layoutListElem = $(layoutHtml).appendTo($toolbarLayoutElem);
$layoutListElem.on('click', '.item', function (event) {
var layoutName = $(this).data('name');
ua.setPageSetting('table-layout-default', layoutName);
ua.table.setLayoutForm(tableId, layoutName);
$layoutListElem.remove();
$(document).off('click.ua-table-layout');
event.stopPropagation();
});
// 点击下拉外区域收起(命名空间事件,收起时解除)
$(document).off('click.ua-table-layout').on('click.ua-table-layout', function (event) {
if (!$(event.target).closest($toolbarLayoutElem).length) {
$('#' + layoutEntryId).remove();
$(document).off('click.ua-table-layout');
}
});
break;
case 'TABLE_CARD_SETTING':
// 卡片设置面板(开关/定位/外点收起全套在 toggleCardSettingPanel)
toggleCardSettingPanel(tableId);
break;
}
});
},
listenEdit: function (tableInit, layFilter, tableId, modifyReload) {
tableInit.modifyUrl = tableInit.modifyUrl || false;
tableId = tableId || ua.init.tableRenderId;
if (tableInit.modifyUrl !== false) {
table.on('edit(' + layFilter + ')', function (obj) {
var value = obj.value,
data = obj.data,
id = data.id,
field = obj.field;
var _data = {
id: id,
field: field,
value: value,
};
ua.request.post({
url: tableInit.modifyUrl,
prefix: true,
data: _data,
}, function (res) {
if (modifyReload) {
table.reloadData(tableId);
}
}, function (res) {
ua.msg.error(res.msg, function () {
table.reloadData(tableId);
});
}, function () {
table.reloadData(tableId);
});
});
}
},
listenTableSelectConfirm(options) {
$('.select-confirm').click(function () {
var checkStatus = ua.table.checkStatus(options.id);
if (checkStatus.data.length == 0) {
layer.msg('请选择数据');
return false;
}
parent.window[selectConfirmCallback](checkStatus.data);
});
},
listenExport: function (options) {
var exportFields = {};
var imageFields = [];
var selectFields = {};
var dateFields = [];
options.cols[0].forEach(col => {
if (col.field) {
exportFields[col.field] = col.title;
options.cols[0].forEach(col => {
if (col.field) {
exportFields[col.field] = col.title;
switch (col.fieldFormat) {
case 'image':
imageFields.push(col.field);
break;
case 'select':
if (Object.keys(col.selectList).length > 0) {
selectFields[col.field] = col.selectList;
}
break;
case 'date':
dateFields.push(col.field);
break;
default:
break;
}
}
});
}
});
// excel导出
$('body').on('click', '[data-table-export]', function () {
var tableId = $(this).attr('data-table-export'),
url = $(this).attr('data-url');
var searchVals = form.val(tableId + '_filter_form');
var dataField = searchVals;
var formatFilter = {},
formatOp = {};
$.each(dataField, function (key, val) {
if (val !== '') {
formatFilter[key] = val;
var elemId = ua.table.renderSearchFormItemElementId(key);
var op = $('#c-' + elemId).attr('data-search-op');
op = op || '%*%';
formatOp[key] = op;
}
});
var searchQuery = {
filter: JSON.stringify(formatFilter),
op: JSON.stringify(formatOp),
fields: JSON.stringify(exportFields),
image_fields: JSON.stringify(imageFields),
select_fields: JSON.stringify(selectFields),
date_fields: JSON.stringify(dateFields),
};
var query = $.param(searchQuery);
var index = ua.msg.confirm('根据查询进行导出,确定导出?', function () {
toUrl = ua.url(url);
if (toUrl.indexOf('?') < 0) {
toUrl += '?';
} else {
toUrl += '&';
}
toUrl += query;
window.open(toUrl);
layer.close(index);
});
});
},
renderSearchFormItemElementId(key) {
var elemId = key;
if (key.indexOf('[') == 0) {
var keyArr = key.replace('[', '').split(']');
elemId = keyArr[0] + '-' + keyArr[1];
}
elemId = elemId.replace('.', '-');
return elemId;
}
};
})();