Files
ulthon_admin/public/static/plugs/ulthon-admin/admin-table.js
2026-09-11 11:16:12 +00:00

2026 lines
94 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 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 等无字段列跳过(勾选语义由勾位图标承接)
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;
}
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;
};
// 内置 card 勾选交互:点卡片任意处切换选中(操作按钮/表单控件等交互元素除外),
// 禁用行(_disabled)整卡不可勾选。选择态落 store(与引擎勾选同一事实源,
// 批量删除经 ua.table.checkStatus 分发即取到),随后重渲模板管线回显勾位图标。
// 自定义模板形态的统一行选中委托是任务 18 范围,本委托只认 .ua-data-card。
$(document).on('click', '.ua-data-card', function (e) {
var $card = $(this);
if ($card.hasClass('ua-data-card-disabled')) {
return;
}
if ($(e.target).closest('a,button,input,select,textarea,label,.layui-form-switch,.layui-form-checkbox,.layui-form-radio').length) {
return;
}
var tableId = $card.attr('data-table-id');
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;
}
window.UA_TABLE_STORE.selection.set(tableId, row);
ua.table.renderLayoutForm(tableId);
});
// (b) 模板引擎优先级解析:tplFunction > tplLay > tplVue > tplLayId > tplVueId,
// 提供一种即生效;五种全无且非 table 条目 → console.warn 并回退 table。
// lay/layId 引擎挂载于 renderLayoutForm(任务 16,layui.laytpl);vue/vueId 挂载于任务 17。
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 节点缺失 / laytpl 渲染错误 / Vue 引擎未挂载):
// 当前形态归位 'table'(与"无钩子条目回退"同语义,幂等、不写 localStorage
// 记忆,用户重新切换可再次触发),容器清空隐藏。调用方据 false 返回值做
// 引擎纯渲染,随后的 syncLayoutVisibility 按 table kind 复显引擎数据区,
// 避免"容器残留上一形态旧内容 + 引擎数据被隐藏"的视觉错位。
var fallbackLayoutToTable = function (tableId, $container) {
var layout = controlledLayouts[tableId];
if (layout) {
layout.current = 'table';
}
if ($container && $container.length) {
$container.addClass('layui-hide').empty();
}
return false;
};
// 形态可见性同步:引擎数据区(表头+表体+合计行)与模板容器互斥显隐,
// 引擎视图的工具栏(刷新/新增/批量删除等)两形态共用保持可见,搜索区
// 本就挂在表格元素之前不受影响。非 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);
if (resolveCurrentLayout(tableId).kind === 'table') {
$box.add($total).removeClass('layui-hide');
$container.addClass('layui-hide').empty();
} else {
$box.add($total).addClass('layui-hide');
$container.removeClass('layui-hide');
}
};
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;
// 模板形态的加载态容器标记(容器内加载样式由任务 14 的内置形态接入)
var $layoutContainer = $('#ua-layout-' + tableId);
$layoutContainer.addClass('ua-layout-loading');
opts.done = function (res) {
// 受控渲染分发:table 形态走引擎纯渲染(现状),非 table 形态走
// 模板管线(渲染上下文 + 行级循环);模板管线不可用(引擎未挂载,
// Vue 待任务 17;tplLayId 节点缺失/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 标记。
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;
}
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
};
$.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}
// - 返回值 = 该行的 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,调用方回退引擎纯渲染
// 【两条硬约束】
// 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;
}
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);
} else if (resolved.kind !== 'function') {
// vue/vueId 分发点:引擎挂载于任务 17(Vue)
console.warn('[ua.table] 模板引擎未挂载:' + resolved.kind + '(任务 17 实现),本次回退 table 形态');
return fallbackLayoutToTable(tableId, $container);
}
var context = ua.table.buildLayoutContext(tableId);
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);
$container.html(html);
// 容器恒为 .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 节点缺失 / laytpl 渲染错误,均已在 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);
originalTableReloadData.call(table, tableId, {
data: state ? state.currentPageData : [],
totalRow: state && state.lastResponse ? state.lastResponse.totalRow : undefined
});
}
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 = [];
if (options.controlled === 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 + '"'
});
}
}
if (options.defaultToolbar === undefined) {
options.defaultToolbar = defaultToolbar;
}
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;
}
if (options.controlled === true) {
// 受控灰度模式:数据请求与分页职责移交 UA_TABLE_STORE,引擎只做
// 纯渲染(空数据起步、关闭引擎分页,由 store 的 laypage 自绘)。
// 选择态注入(rowDisableCallback / URL selectedIds / LAY_CHECKED
// 跨页回显)由 store.fetch 的 injectSelectionMarks 承接,故不装
// 引擎 parseData。默认关闭——未显式 controlled:true 的页面保持
// 引擎 url 模式,行为与历史版本完全一致。
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;
};
}
// 初始化表格
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 承接),此处无需逐点改造。
if (options.controlled === true) {
// 分页容器挂在引擎视图容器之后(表格容器外),不与引擎分页共存。
// 注意 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>'
);
// 形态注册:layoutList 已在 defaultToolbar 段规范化(视图按钮
// 注入共用)。运行期切形态经 ua.table.setLayoutForm。
// options.cols 此时已过 formatCols/renderOperat/renderTrueHide,
// 渲染器族与 operat 缺省值已就绪,构建上下文直接消费
var layoutByName = {};
$.each(layoutEntries, function (i, entry) {
layoutByName[entry.name] = entry;
});
// 默认形态解析(任务 15 (a)):优先级 localStorage 按页记忆 >
// 显式 layoutDefault / 手机端自动检测 > 'table'。手机端默认
// card 仅当 layoutList 含 card 条目;记忆值不在 layoutList 内
// 时忽略(防脏数据);未配 layoutList 的页面恒 table,行为
// 零变化。layoutDefault=true 旧协议即"自动判断",与新默认
// 行为一致,无需单独分支。
var defaultLayoutName = 'table';
if (layoutEntries.length > 0) {
if (options.layoutDefault && layoutByName[options.layoutDefault]) {
defaultLayoutName = options.layoutDefault;
} else if (ua.checkMobile() && layoutByName['card']) {
defaultLayoutName = 'card';
}
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);
});
// 初始形态可见性: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.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) {
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;
}
});
},
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;
}
};
})();