修复了一些问题

This commit is contained in:
TermiNexus committed 2026-06-25 17:56:08 +08:00
1 parent f3cf68a51a
commit a77df5da0f
114 files changed
+5183 -1664

No files matched your search

@@ -0,0 +1,244 @@
<script src="/assets/gerp/CompanyCommon.js?v=20260625d"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<a href="javascript:void(0)" class="cp-back-btn" onclick="goBack()"><i class="fas fa-arrow-left"></i> 返回</a>
<h1 class="PageTitle">
<i class="fas fa-building icon-primary icon-left-lg"></i>
<span id="page-title">公司详情</span>
</h1>
<button class="GERPBtn Primary" onclick="handleEdit()" style="margin-left:auto;"><i class="fas fa-edit"></i> 编辑</button>
</div>
<div class="cp-container" id="detailContainer" style="overflow:auto;">
<div class="cp-loading"><i class="fas fa-spinner fa-spin"></i> 加载中...</div>
</div>
</div>
</div>
<script>
const currentServer = CompanyUtils.getServer();
const companyUUID = new URLSearchParams(window.location.search).get('CompanyUUID');
CompanyUtils.setPageTitle('公司详情');
function goBack() {
if (window.history.length > 1) {
window.history.back();
} else {
GERP.Navigate('/pages/Company/company.html' + (currentServer !== 'gvsds-master' ? `?server=${encodeURIComponent(currentServer)}` : ''), '公司管理');
}
}
let currentData = null;
let deptTable = null;
let wgTable = null;
let empTable = null;
function buildApiUrl(path, params = {}) {
const allParams = {};
if (currentServer !== 'gvsds-master') allParams.server = currentServer;
Object.assign(allParams, params);
const qs = new URLSearchParams(allParams).toString();
return qs ? `${path}?${qs}` : path;
}
async function init() {
const containerEl = document.getElementById('detailContainer');
if (!companyUUID) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-triangle" style="margin-right:6px;"></i> 缺少 CompanyUUID 参数,无法加载公司详情。</div>`;
return;
}
try {
const dataResp = await fetch(buildApiUrl('/API/FormAPI/Get/Company', { CompanyUUID: companyUUID, PageSize: '1' }), { credentials: 'include' });
const dataResult = await dataResp.json();
if (dataResult.code !== 0) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 加载失败: ${CompanyUtils.esc(dataResult.msg || '未知错误')}</div>`;
return;
}
const list = dataResult.data || [];
const data = Array.isArray(list) ? (list.find(c => c.CompanyUUID === companyUUID) || list[0]) : list;
if (!data) {
containerEl.innerHTML = `<div class="cp-error">未找到公司数据</div>`;
return;
}
currentData = data;
CompanyUtils.setPageTitle(data.CompanyName || '公司详情');
// 解析 UUID 字段为名称
const uuidMap = {};
if (data.ParentCompanyUUID) uuidMap.parentCompany = { table: 'Company', uuids: [data.ParentCompanyUUID] };
if (data.TenantUUID) uuidMap.tenant = { table: 'Tenant', uuids: [data.TenantUUID] };
const resolved = await CompanyUtils.resolveUUIDs(uuidMap);
// 渲染基本信息 + 表格容器
containerEl.innerHTML = `
<div class="cp-detail-section">
<h3><i class="fas fa-building"></i> 基本信息</h3>
<div class="cp-info-grid">
<div class="cp-info-item"><div class="label">公司名称</div><div class="value">${CompanyUtils.esc(data.CompanyName) || '-'}</div></div>
<div class="cp-info-item"><div class="label">公司简称</div><div class="value">${CompanyUtils.esc(data.CompanyShort) || '-'}</div></div>
<div class="cp-info-item"><div class="label">公司状态</div><div class="value">${CompanyUtils.statusTag(data.CompanyStatus)}</div></div>
<div class="cp-info-item"><div class="label">上级公司</div><div class="value">${CompanyUtils.esc(resolved.parentCompany?.[data.ParentCompanyUUID] || data.ParentCompanyUUID) || '-'}</div></div>
<div class="cp-info-item"><div class="label">租户</div><div class="value">${CompanyUtils.esc(resolved.tenant?.[data.TenantUUID] || data.TenantUUID) || '-'}</div></div>
<div class="cp-info-item"><div class="label">创建时间</div><div class="value">${CompanyUtils.formatTime(data.CompanyCreateTime)}</div></div>
</div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-sitemap"></i> 部门</h3>
<div id="dept-table-container" style="height:400px;"></div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-users-cog"></i> 工作组</h3>
<div id="wg-table-container" style="height:400px;"></div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-user-friends"></i> 员工</h3>
<div id="emp-table-container" style="height:400px;"></div>
</div>`;
// 初始化 TableEngine 实例
initDeptTable();
initWorkgroupTable();
initEmployeeTable();
} catch (e) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 网络错误: ${CompanyUtils.esc(e.message)}</div>`;
}
}
function initDeptTable() {
deptTable = new TableEngine({
container: '#dept-table-container',
height: '400px',
tableName: 'Department',
columns: [
{ field: 'DeptUUID', title: '部门UUID', primaryKey: true, editable: false, hideInForm: true },
{ field: 'DeptName', title: '部门名称' },
{ field: 'DeptStatus', title: '状态', type: 'select', options: [{ value: 0, label: '禁用' }, { value: 1, label: '启用' }] },
{ field: 'DeptSort', title: '排序', type: 'number' },
{ field: 'ParentDeptUUID_label', title: '上级部门' },
{ field: 'DeptCreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/Department'),
add: buildApiUrl('/API/FormAPI/Add/Department'),
edit: buildApiUrl('/API/FormAPI/Modify/Department/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/Department/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/Department/<id>'),
},
onView: (row) => {
let url = `/pages/Company/department-detail.html?DeptUUID=${encodeURIComponent(row.DeptUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.DeptName || '部门详情');
},
});
}
function initWorkgroupTable() {
wgTable = new TableEngine({
container: '#wg-table-container',
tableName: 'Workgroup',
columns: [
{ field: 'WorkgroupUUID', title: '工作组UUID', primaryKey: true, editable: false, hideInForm: true },
{ field: 'WorkgroupName', title: '工作组名称' },
{ field: 'WorkgroupStatus', title: '状态', type: 'select', options: [{ value: 0, label: '禁用' }, { value: 1, label: '启用' }] },
{ field: 'WorkgroupCreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/Workgroup'),
add: buildApiUrl('/API/FormAPI/Add/Workgroup'),
edit: buildApiUrl('/API/FormAPI/Modify/Workgroup/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/Workgroup/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/Workgroup/<id>'),
},
onView: (row) => {
let url = `/pages/Company/workgroup-detail.html?WorkgroupUUID=${encodeURIComponent(row.WorkgroupUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.WorkgroupName || '工作组详情');
},
});
}
function initEmployeeTable() {
empTable = new TableEngine({
container: '#emp-table-container',
height: '400px',
tableName: 'UserRole',
columns: [
{ field: 'UserRoleUUID', title: '关联UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('UserUUID', '用户名', 'User', { editable: false }),
CompanyUtils.epColumn('RoleUUID', '角色名', 'Role'),
CompanyUtils.epColumn('CompanyUUID', '公司', 'Company', { editable: false, hideInForm: true }),
CompanyUtils.epColumn('DeptUUID', '部门', 'Department'),
CompanyUtils.epColumn('WorkgroupUUID', '工作组', 'Workgroup'),
{ field: 'RoleType', title: '角色类型', type: 'select', options: [{ value: 0, label: '组员' }, { value: 1, label: '组长' }] },
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserRole'),
add: buildApiUrl('/API/FormAPI/Add/UserRole'),
edit: buildApiUrl('/API/FormAPI/Modify/UserRole/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserRole/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserRole/<id>'),
},
onView: (row) => {
let url = `/pages/Company/employee-detail.html?UserUUID=${encodeURIComponent(row.UserUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.UserUUID_label || '员工详情');
},
});
}
/** 编辑按钮:弹出编辑对话框 */
async function handleEdit() {
if (!currentData) return;
CompanyUtils.showEditDialog({
tableName: 'Company',
data: currentData,
onSave: () => {
if (window.GERPUI && typeof window.GERPUI.ShowToast === 'function') {
window.GERPUI.ShowToast({ message: '保存成功', type: 'success' });
}
init();
}
});
}
init();
</script>
+38
View File
@@ -0,0 +1,38 @@
<script src="/assets/gerp/CompanyCommon.js"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<h1 class="PageTitle">
<i class="fas fa-building icon-primary icon-left-lg"></i>
<span id="page-title">公司管理</span>
</h1>
</div>
<div class="cp-container" id="TableContainer"></div>
</div>
</div>
<script>
const page = new CompanyTablePage({
tableName: 'Company',
title: '公司管理',
pkField: 'CompanyUUID',
defaultColumns: () => [
{ field: 'CompanyUUID', title: '公司UUID', width: 140, primaryKey: true, editable: false, hideInForm: true },
{ field: 'CompanyName', title: '公司全称', width: 200, type: 'text' },
{ field: 'CompanyShort', title: '公司简称', width: 120, type: 'text' },
{ field: 'CompanyStatus', title: '状态', width: 70, type: 'select', options: [{value:0,label:'禁用'},{value:1,label:'启用'}] },
{ field: 'ParentCompanyUUID', title: '上级公司', width: 140, type: 'text' },
{ field: 'CreateTime', title: '创建时间', width: 160, type: 'datetime', editable: false },
],
onView: (row) => {
console.log('[company] onView row:', row);
const server = new URLSearchParams(window.location.search).get('server');
let url = `/pages/Company/company-detail.html?CompanyUUID=${encodeURIComponent(row.CompanyUUID || '')}`;
if (server && server !== 'gvsds-master') url += `&server=${encodeURIComponent(server)}`;
GERP.Navigate(url, row.CompanyName || '公司详情');
},
});
page.init();
</script>
@@ -0,0 +1,152 @@
<script src="/assets/gerp/CompanyCommon.js?v=20260625d"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<a href="javascript:void(0)" class="cp-back-btn" onclick="goBack()"><i class="fas fa-arrow-left"></i> 返回</a>
<h1 class="PageTitle">
<i class="fas fa-sitemap icon-primary icon-left-lg"></i>
<span id="page-title">部门详情</span>
</h1>
<button class="GERPBtn Primary" onclick="handleEdit()" style="margin-left:auto;"><i class="fas fa-edit"></i> 编辑</button>
</div>
<div class="cp-container" id="detailContainer" style="overflow:auto;">
<div class="cp-loading"><i class="fas fa-spinner fa-spin"></i> 加载中...</div>
</div>
</div>
</div>
<script>
const currentServer = CompanyUtils.getServer();
const deptUUID = new URLSearchParams(window.location.search).get('DeptUUID');
CompanyUtils.setPageTitle('部门详情');
function goBack() {
if (window.history.length > 1) {
window.history.back();
} else {
GERP.Navigate('/pages/Company/department.html' + (currentServer !== 'gvsds-master' ? `?server=${encodeURIComponent(currentServer)}` : ''), '部门管理');
}
}
let currentData = null;
let empTable = null;
function buildApiUrl(path, params = {}) {
const allParams = {};
if (currentServer !== 'gvsds-master') allParams.server = currentServer;
Object.assign(allParams, params);
const qs = new URLSearchParams(allParams).toString();
return qs ? `${path}?${qs}` : path;
}
async function init() {
const containerEl = document.getElementById('detailContainer');
if (!deptUUID) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-triangle" style="margin-right:6px;"></i> 缺少 DeptUUID 参数,无法加载部门详情。</div>`;
return;
}
try {
const dataResp = await fetch(buildApiUrl('/API/FormAPI/Get/Department', { DeptUUID: deptUUID, PageSize: '1' }), { credentials: 'include' });
const dataResult = await dataResp.json();
if (dataResult.code !== 0) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 加载失败: ${CompanyUtils.esc(dataResult.msg || '未知错误')}</div>`;
return;
}
const list = dataResult.data || [];
const data = Array.isArray(list) ? (list.find(d => d.DeptUUID === deptUUID) || list[0]) : list;
if (!data) {
containerEl.innerHTML = `<div class="cp-error">未找到部门数据</div>`;
return;
}
currentData = data;
CompanyUtils.setPageTitle(data.DeptName || '部门详情');
// 解析 UUID 字段为名称
const uuidMap = {};
if (data.ParentDeptUUID) uuidMap.parentDept = { table: 'Department', uuids: [data.ParentDeptUUID] };
const resolved = await CompanyUtils.resolveUUIDs(uuidMap);
// 渲染基本信息 + 表格容器
containerEl.innerHTML = `
<div class="cp-detail-section">
<h3><i class="fas fa-sitemap"></i> 基本信息</h3>
<div class="cp-info-grid">
<div class="cp-info-item"><div class="label">部门名称</div><div class="value">${CompanyUtils.esc(data.DeptName) || '-'}</div></div>
<div class="cp-info-item"><div class="label">部门状态</div><div class="value">${CompanyUtils.statusTag(data.DeptStatus)}</div></div>
<div class="cp-info-item"><div class="label">上级部门</div><div class="value">${CompanyUtils.esc(resolved.parentDept?.[data.ParentDeptUUID] || data.ParentDeptUUID) || '-'}</div></div>
<div class="cp-info-item"><div class="label">排序</div><div class="value">${data.DeptSort ?? '-'}</div></div>
<div class="cp-info-item"><div class="label">创建时间</div><div class="value">${CompanyUtils.formatTime(data.DeptCreateTime)}</div></div>
</div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-users"></i> 部门员工</h3>
<div id="emp-table-container" style="height:400px;"></div>
</div>`;
// 初始化 TableEngine 实例
initEmployeeTable();
} catch (e) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 网络错误: ${CompanyUtils.esc(e.message)}</div>`;
}
}
function initEmployeeTable() {
empTable = new TableEngine({
container: '#emp-table-container',
height: '400px',
tableName: 'UserRole',
columns: [
{ field: 'UserRoleUUID', title: '关联UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('UserUUID', '用户名', 'User', { editable: false }),
CompanyUtils.epColumn('RoleUUID', '角色名', 'Role'),
{ field: 'RoleType', title: '角色类型', type: 'select', options: [{ value: 0, label: '员工' }, { value: 1, label: '负责人' }] },
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserRole', { DeptUUID: deptUUID }),
add: buildApiUrl('/API/FormAPI/Add/UserRole'),
edit: buildApiUrl('/API/FormAPI/Modify/UserRole/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserRole/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserRole/<id>'),
},
onView: (row) => {
let url = `/pages/Company/employee-detail.html?UserUUID=${encodeURIComponent(row.UserUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.UserUUID_label || '员工详情');
},
});
}
/** 编辑按钮:弹出编辑对话框 */
async function handleEdit() {
if (!currentData) return;
CompanyUtils.showEditDialog({
tableName: 'Department',
data: currentData,
onSave: () => {
if (window.GERPUI && typeof window.GERPUI.ShowToast === 'function') {
window.GERPUI.ShowToast({ message: '保存成功', type: 'success' });
}
init();
}
});
}
init();
</script>
@@ -0,0 +1,39 @@
<script src="/assets/gerp/CompanyCommon.js"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<h1 class="PageTitle">
<i class="fas fa-sitemap icon-primary icon-left-lg"></i>
<span id="page-title">部门管理</span>
</h1>
</div>
<div class="cp-container" id="TableContainer"></div>
</div>
</div>
<script>
const page = new CompanyTablePage({
tableName: 'Department',
title: '部门管理',
pkField: 'DeptUUID',
defaultColumns: () => [
{ field: 'DeptUUID', title: '部门UUID', width: 140, primaryKey: true, editable: false, hideInForm: true },
{ field: 'DeptName', title: '部门名称', width: 160, type: 'text' },
{ field: 'DeptSort', title: '排序', width: 80, type: 'number' },
{ field: 'DeptStatus', title: '状态', width: 80, type: 'select', options: [{value:0,label:'禁用'},{value:1,label:'启用'}] },
{ field: 'CompanyUUID', title: '所属公司', width: 160, type: 'text', editable: false, hideInForm: true },
{ field: 'ParentDeptUUID', title: '上级部门', width: 160, type: 'text' },
{ field: 'CreateTime', title: '创建时间', width: 160, type: 'datetime', editable: false },
],
onView: (row) => {
console.log('[department] onView row:', row);
const server = new URLSearchParams(window.location.search).get('server');
let url = `/pages/Company/department-detail.html?DeptUUID=${encodeURIComponent(row.DeptUUID || '')}`;
if (server && server !== 'gvsds-master') url += `&server=${encodeURIComponent(server)}`;
GERP.Navigate(url, row.DeptName || '部门详情');
},
});
page.init();
</script>
@@ -0,0 +1,187 @@
<script src="/assets/gerp/CompanyCommon.js?v=20260625d"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<a href="javascript:void(0)" class="cp-back-btn" onclick="goBack()"><i class="fas fa-arrow-left"></i> 返回</a>
<h1 class="PageTitle">
<i class="fas fa-user-circle icon-primary icon-left-lg"></i>
<span id="page-title">员工详情</span>
</h1>
<button class="GERPBtn Primary" onclick="handleEdit()" style="margin-left:auto;"><i class="fas fa-edit"></i> 编辑</button>
</div>
<div class="cp-container" id="detailContainer" style="overflow:auto;">
<div class="cp-loading"><i class="fas fa-spinner fa-spin"></i> 加载中...</div>
</div>
</div>
</div>
<script>
const currentServer = CompanyUtils.getServer();
const userUUID = new URLSearchParams(window.location.search).get('UserUUID');
CompanyUtils.setPageTitle('员工详情');
function goBack() {
if (window.history.length > 1) {
window.history.back();
} else {
GERP.Navigate('/pages/system/users.html' + (currentServer !== 'gvsds-master' ? `?server=${encodeURIComponent(currentServer)}` : ''), '用户管理');
}
}
let currentData = null;
let roleTable = null;
let deptTable = null;
function buildApiUrl(path, params = {}) {
const allParams = {};
if (currentServer !== 'gvsds-master') allParams.server = currentServer;
Object.assign(allParams, params);
const qs = new URLSearchParams(allParams).toString();
return qs ? `${path}?${qs}` : path;
}
async function init() {
const containerEl = document.getElementById('detailContainer');
if (!userUUID) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-triangle" style="margin-right:6px;"></i> 缺少 UserUUID 参数,无法加载员工详情。</div>`;
return;
}
try {
const dataResp = await fetch(buildApiUrl('/API/FormAPI/Get/User', { UserUUID: userUUID, PageSize: '1' }), { credentials: 'include' });
const dataResult = await dataResp.json();
if (dataResult.code !== 0) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 加载失败: ${CompanyUtils.esc(dataResult.msg || '未知错误')}</div>`;
return;
}
const list = dataResult.data || [];
const data = Array.isArray(list) ? (list.find(u => u.UserUUID === userUUID) || list[0]) : list;
if (!data) {
containerEl.innerHTML = `<div class="cp-error">未找到用户数据</div>`;
return;
}
currentData = data;
CompanyUtils.setPageTitle(data.UserName || '员工详情');
// 渲染基本信息 + 表格容器
containerEl.innerHTML = `
<div class="cp-detail-section">
<h3><i class="fas fa-user-circle"></i> 基本信息</h3>
<div class="cp-info-grid">
<div class="cp-info-item"><div class="label">用户名</div><div class="value">${CompanyUtils.esc(data.UserName) || '-'}</div></div>
<div class="cp-info-item"><div class="label">真实姓名</div><div class="value">${CompanyUtils.esc(data.UserRealName) || '-'}</div></div>
<div class="cp-info-item"><div class="label">邮箱</div><div class="value">${CompanyUtils.esc(data.UserEmail) || '-'}</div></div>
<div class="cp-info-item"><div class="label">电话</div><div class="value">${CompanyUtils.esc(data.UserTele) || '-'}</div></div>
<div class="cp-info-item"><div class="label">账号状态</div><div class="value">${CompanyUtils.statusTag(data.UserAccountLicense)}</div></div>
<div class="cp-info-item"><div class="label">是否员工</div><div class="value">${data.IsStaff ? '<span style="color:#166534;">是</span>' : '<span style="color:#991b1b;">否</span>'}</div></div>
<div class="cp-info-item"><div class="label">创建时间</div><div class="value">${CompanyUtils.formatTime(data.UserCreateTime)}</div></div>
</div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-shield-alt"></i> 角色与组织</h3>
<div id="role-table-container" style="height:400px;"></div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-sitemap"></i> 部门关联</h3>
<div id="dept-table-container" style="height:400px;"></div>
</div>`;
// 初始化 TableEngine 实例
initRoleTable();
initDeptTable();
} catch (e) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 网络错误: ${CompanyUtils.esc(e.message)}</div>`;
}
}
function initRoleTable() {
roleTable = new TableEngine({
container: '#role-table-container',
height: '400px',
tableName: 'UserRole',
columns: [
{ field: 'UserRoleUUID', title: '角色UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('RoleUUID', '角色名', 'Role'),
CompanyUtils.epColumn('CompanyUUID', '公司', 'Company', { editable: false, hideInForm: true }),
CompanyUtils.epColumn('DeptUUID', '部门', 'Department'),
CompanyUtils.epColumn('WorkgroupUUID', '工作组', 'Workgroup'),
{ field: 'RoleType', title: '角色类型', type: 'select', options: [{ value: 0, label: '组员' }, { value: 1, label: '组长' }] },
{ field: 'RoleUUID_RoleStatus', title: '角色状态', type: 'select', options: [{ value: 0, label: '禁用' }, { value: 1, label: '启用' }], editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserRole', { UserUUID: userUUID }),
add: buildApiUrl('/API/FormAPI/Add/UserRole'),
edit: buildApiUrl('/API/FormAPI/Modify/UserRole/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserRole/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserRole/<id>'),
},
});
}
function initDeptTable() {
deptTable = new TableEngine({
container: '#dept-table-container',
height: '400px',
tableName: 'UserDept',
columns: [
{ field: 'UserDeptUUID', title: '关联UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('DeptUUID', '部门名称', 'Department'),
{ field: 'IsMainDept', title: '主部门', type: 'select', options: [{ value: 0, label: '兼职' }, { value: 1, label: '主部门' }] },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserDept', { UserUUID: userUUID }),
add: buildApiUrl('/API/FormAPI/Add/UserDept'),
edit: buildApiUrl('/API/FormAPI/Modify/UserDept/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserDept/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserDept/<id>'),
},
onView: (row) => {
let url = `/pages/Company/department-detail.html?DeptUUID=${encodeURIComponent(row.DeptUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.DeptUUID_label || '部门详情');
},
});
}
/** 编辑按钮:弹出编辑对话框 */
async function handleEdit() {
if (!currentData) return;
CompanyUtils.showEditDialog({
tableName: 'User',
data: currentData,
onSave: () => {
if (window.GERPUI && typeof window.GERPUI.ShowToast === 'function') {
window.GERPUI.ShowToast({ message: '保存成功', type: 'success' });
}
init();
}
});
}
init();
</script>
@@ -0,0 +1,145 @@
<script src="/assets/gerp/CompanyCommon.js?v=20260625d"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<a href="javascript:void(0)" class="cp-back-btn" onclick="goBack()"><i class="fas fa-arrow-left"></i> 返回</a>
<h1 class="PageTitle">
<i class="fas fa-users-cog icon-primary icon-left-lg"></i>
<span id="page-title">工作组详情</span>
</h1>
<button class="GERPBtn Primary" onclick="handleEdit()" style="margin-left:auto;"><i class="fas fa-edit"></i> 编辑</button>
</div>
<div class="cp-container" id="detailContainer" style="overflow:auto;">
<div class="cp-loading"><i class="fas fa-spinner fa-spin"></i> 加载中...</div>
</div>
</div>
</div>
<script>
const currentServer = CompanyUtils.getServer();
const workgroupUUID = new URLSearchParams(window.location.search).get('WorkgroupUUID');
CompanyUtils.setPageTitle('工作组详情');
function goBack() {
if (window.history.length > 1) {
window.history.back();
} else {
GERP.Navigate('/pages/Company/workgroup.html' + (currentServer !== 'gvsds-master' ? `?server=${encodeURIComponent(currentServer)}` : ''), '工作组管理');
}
}
let currentData = null;
let memberTable = null;
function buildApiUrl(path, params = {}) {
const allParams = {};
if (currentServer !== 'gvsds-master') allParams.server = currentServer;
Object.assign(allParams, params);
const qs = new URLSearchParams(allParams).toString();
return qs ? `${path}?${qs}` : path;
}
async function init() {
const containerEl = document.getElementById('detailContainer');
if (!workgroupUUID) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-triangle" style="margin-right:6px;"></i> 缺少 WorkgroupUUID 参数,无法加载工作组详情。</div>`;
return;
}
try {
const dataResp = await fetch(buildApiUrl('/API/FormAPI/Get/Workgroup', { WorkgroupUUID: workgroupUUID, PageSize: '1' }), { credentials: 'include' });
const dataResult = await dataResp.json();
if (dataResult.code !== 0) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 加载失败: ${CompanyUtils.esc(dataResult.msg || '未知错误')}</div>`;
return;
}
const list = dataResult.data || [];
const data = Array.isArray(list) ? (list.find(w => w.WorkgroupUUID === workgroupUUID) || list[0]) : list;
if (!data) {
containerEl.innerHTML = `<div class="cp-error">未找到工作组数据</div>`;
return;
}
currentData = data;
CompanyUtils.setPageTitle(data.WorkgroupName || '工作组详情');
// 渲染基本信息 + 表格容器
containerEl.innerHTML = `
<div class="cp-detail-section">
<h3><i class="fas fa-users-cog"></i> 基本信息</h3>
<div class="cp-info-grid">
<div class="cp-info-item"><div class="label">工作组名称</div><div class="value">${CompanyUtils.esc(data.WorkgroupName) || '-'}</div></div>
<div class="cp-info-item"><div class="label">工作组状态</div><div class="value">${CompanyUtils.statusTag(data.WorkgroupStatus)}</div></div>
<div class="cp-info-item"><div class="label">创建时间</div><div class="value">${CompanyUtils.formatTime(data.WorkgroupCreateTime)}</div></div>
</div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-user-friends"></i> 工作组成员</h3>
<div id="member-table-container" style="height:400px;"></div>
</div>`;
// 初始化 TableEngine 实例
initMemberTable();
} catch (e) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 网络错误: ${CompanyUtils.esc(e.message)}</div>`;
}
}
function initMemberTable() {
memberTable = new TableEngine({
container: '#member-table-container',
height: '400px',
tableName: 'UserRole',
columns: [
{ field: 'UserRoleUUID', title: '关联UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('UserUUID', '用户名', 'User', { editable: false }),
CompanyUtils.epColumn('RoleUUID', '角色名', 'Role'),
{ field: 'RoleType', title: '角色类型', type: 'select', options: [{ value: 0, label: '组员' }, { value: 1, label: '组长' }] },
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserRole', { WorkgroupUUID: workgroupUUID }),
add: buildApiUrl('/API/FormAPI/Add/UserRole'),
edit: buildApiUrl('/API/FormAPI/Modify/UserRole/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserRole/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserRole/<id>'),
},
onView: (row) => {
let url = `/pages/Company/employee-detail.html?UserUUID=${encodeURIComponent(row.UserUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.UserUUID_label || '员工详情');
},
});
}
/** 编辑按钮:弹出编辑对话框 */
async function handleEdit() {
if (!currentData) return;
CompanyUtils.showEditDialog({
tableName: 'Workgroup',
data: currentData,
onSave: () => {
if (window.GERPUI && typeof window.GERPUI.ShowToast === 'function') {
window.GERPUI.ShowToast({ message: '保存成功', type: 'success' });
}
init();
}
});
}
init();
</script>
@@ -0,0 +1,35 @@
<script src="/assets/gerp/CompanyCommon.js"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<h1 class="PageTitle">
<i class="fas fa-users-cog icon-primary icon-left-lg"></i>
<span id="page-title">工作组管理</span>
</h1>
</div>
<div class="cp-container" id="TableContainer"></div>
</div>
</div>
<script>
const page = new CompanyTablePage({
tableName: 'Workgroup',
title: '工作组管理',
defaultColumns: () => [
{ field: 'WorkgroupUUID', title: '工作组UUID', width: 140, primaryKey: true, editable: false, hideInForm: true },
{ field: 'WorkgroupName', title: '工作组名称', width: 160, type: 'text' },
{ field: 'WorkgroupEnglishName', title: '英文名称', width: 160, type: 'text' },
{ field: 'WorkgroupIntroduction', title: '工作组介绍', width: 240, type: 'textarea' },
],
onView: (row) => {
console.log('[workgroup] onView row:', row);
const server = new URLSearchParams(window.location.search).get('server');
let url = `/pages/Company/workgroup-detail.html?WorkgroupUUID=${encodeURIComponent(row.WorkgroupUUID || '')}`;
if (server && server !== 'gvsds-master') url += `&server=${encodeURIComponent(server)}`;
GERP.Navigate(url, row.WorkgroupName || '工作组详情');
},
});
page.init();
</script>
+261
View File
@@ -0,0 +1,261 @@
<script src="/assets/gerp/CompanyCommon.js?v=20260625d"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<a href="javascript:void(0)" class="cp-back-btn" onclick="goBack()"><i class="fas fa-arrow-left"></i> 返回</a>
<h1 class="PageTitle">
<i class="fas fa-user-tag icon-primary icon-left-lg"></i>
<span id="page-title">角色详情</span>
</h1>
<span id="admin-badge" style="display:none;margin-left:8px;padding:2px 10px;border-radius:4px;background:#1e40af;color:#fff;font-size:12px;font-weight:600;"><i class="fas fa-shield-alt"></i> 特权模式</span>
<button class="GERPBtn Primary" onclick="handleEdit()" style="margin-left:auto;"><i class="fas fa-edit"></i> 编辑</button>
</div>
<div class="cp-container" id="detailContainer" style="overflow:auto;">
<div class="cp-loading"><i class="fas fa-spinner fa-spin"></i> 加载中...</div>
</div>
</div>
</div>
<script>
const currentServer = CompanyUtils.getServer();
const roleUUID = new URLSearchParams(window.location.search).get('RoleUUID');
CompanyUtils.setPageTitle('角色详情');
function goBack() {
if (window.history.length > 1) {
window.history.back();
} else {
GERP.Navigate('/pages/system/roles.html' + (currentServer !== 'gvsds-master' ? `?server=${encodeURIComponent(currentServer)}` : ''), '角色管理');
}
}
let currentData = null;
let userRoleTable = null;
let permTable = null;
let adminTable = null;
let isAdminMode = false;
/** 检查当前用户是否具有管理员特权 */
async function checkAdmin() {
try {
const resp = await fetch(buildApiUrl('/API/FormAPI/CheckAdmin'), { credentials: 'include' });
const result = await resp.json();
return result.code === 0 && result.data && result.data.is_admin;
} catch (e) {
return false;
}
}
function buildApiUrl(path, params = {}) {
const allParams = {};
if (currentServer !== 'gvsds-master') allParams.server = currentServer;
if (isAdminMode) allParams.admin_verify = '1';
Object.assign(allParams, params);
const qs = new URLSearchParams(allParams).toString();
return qs ? `${path}?${qs}` : path;
}
function roleTypeText(t) {
if (t === 0 || t === '0') return '组员';
if (t === 1 || t === '1') return '组长';
return '-';
}
function assignScopeText(s) {
if (s === 0 || s === '0') return '<span style="color:#166534;">全租户可分发</span>';
if (s === 1 || s === '1') return '<span style="color:#1e40af;">指定公司可分发</span>';
if (s === 2 || s === '2') return '<span style="color:#6d28d9;">指定部门可分发</span>';
return '-';
}
async function init() {
const containerEl = document.getElementById('detailContainer');
if (!roleUUID) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-triangle" style="margin-right:6px;"></i> 缺少 RoleUUID 参数,无法加载角色详情。</div>`;
return;
}
// 先检查管理员特权
isAdminMode = await checkAdmin();
const adminBadge = document.getElementById('admin-badge');
if (isAdminMode) {
adminBadge.style.display = 'inline-block';
}
try {
const dataResp = await fetch(buildApiUrl('/API/FormAPI/Get/Role', { RoleUUID: roleUUID, PageSize: '1' }), { credentials: 'include' });
const dataResult = await dataResp.json();
if (dataResult.code !== 0) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 加载失败: ${CompanyUtils.esc(dataResult.msg || '未知错误')}</div>`;
return;
}
const list = dataResult.data || [];
const data = Array.isArray(list) ? (list.find(r => r.RoleUUID === roleUUID) || list[0]) : list;
if (!data) {
containerEl.innerHTML = `<div class="cp-error">未找到角色数据</div>`;
return;
}
currentData = data;
CompanyUtils.setPageTitle(data.RoleName || '角色详情');
const isolationNotice = isAdminMode
? `<div style="margin-bottom:12px;padding:8px 12px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:4px;color:#1e40af;font-size:13px;"><i class="fas fa-info-circle"></i> 特权模式:显示该角色在所有公司的关联记录(无公司隔离)</div>`
: `<div style="margin-bottom:12px;padding:8px 12px;background:#f9fafb;border:1px solid #e5e7eb;border-radius:4px;color:#6b7280;font-size:13px;"><i class="fas fa-info-circle"></i> 仅显示当前公司数据。如需查看所有公司记录,需管理员特权。</div>`;
containerEl.innerHTML = `
<div class="cp-detail-section">
<h3><i class="fas fa-user-tag"></i> 基本信息</h3>
<div class="cp-info-grid">
<div class="cp-info-item"><div class="label">角色名称</div><div class="value">${CompanyUtils.esc(data.RoleName) || '-'}</div></div>
<div class="cp-info-item"><div class="label">角色类型</div><div class="value">${roleTypeText(data.RoleType)}</div></div>
<div class="cp-info-item"><div class="label">角色状态</div><div class="value">${CompanyUtils.statusTag(data.RoleStatus)}</div></div>
<div class="cp-info-item"><div class="label">分配范围</div><div class="value">${assignScopeText(data.AssignScope)}</div></div>
<div class="cp-info-item"><div class="label">角色描述</div><div class="value">${CompanyUtils.esc(data.RoleDesc) || '-'}</div></div>
<div class="cp-info-item"><div class="label">创建时间</div><div class="value">${CompanyUtils.formatTime(data.CreateTime)}</div></div>
</div>
</div>
${isolationNotice}
<div class="cp-detail-section">
<h3><i class="fas fa-users"></i> 关联用户</h3>
<div id="userrole-table-container" style="height:400px;"></div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-key"></i> 角色权限</h3>
<div id="perm-table-container" style="height:400px;"></div>
<div style="margin-top:8px;font-size:12px;color:#6b7280;"><i class="fas fa-info-circle"></i> 添加 <code>admin:super</code> 权限可使该角色成为超级管理员</div>
</div>`;
initUserRoleTable();
initPermTable();
initAdminTable();
} catch (e) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 网络错误: ${CompanyUtils.esc(e.message)}</div>`;
}
}
function initUserRoleTable() {
userRoleTable = new TableEngine({
container: '#userrole-table-container',
height: '400px',
tableName: 'UserRole',
columns: [
{ field: 'UserRoleUUID', title: '关联UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('UserUUID', '用户名', 'User'),
CompanyUtils.epColumn('CompanyUUID', '公司', 'Company', { editable: false }),
CompanyUtils.epColumn('DeptUUID', '部门', 'Department'),
CompanyUtils.epColumn('WorkgroupUUID', '工作组', 'Workgroup'),
{ field: 'RoleType', title: '角色类型', type: 'select', options: [{ value: 0, label: '组员' }, { value: 1, label: '组长' }] },
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserRole', { RoleUUID: roleUUID }),
add: buildApiUrl('/API/FormAPI/Add/UserRole'),
edit: buildApiUrl('/API/FormAPI/Modify/UserRole/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserRole/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserRole/<id>'),
},
onView: (row) => {
let url = `/pages/system/user-detail.html?UserUUID=${encodeURIComponent(row.UserUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.UserUUID_label || '用户详情');
},
});
}
function initPermTable() {
permTable = new TableEngine({
container: '#perm-table-container',
height: '400px',
tableName: 'RolePermission',
columns: [
{ field: 'RolePermUUID', title: '关联UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('PermUUID', '权限名称', 'Permission'),
CompanyUtils.epColumn('CompanyUUID', '公司', 'Company', { editable: false, hideInForm: true }),
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/RolePermission', { RoleUUID: roleUUID }),
add: buildApiUrl('/API/FormAPI/Add/RolePermission'),
edit: buildApiUrl('/API/FormAPI/Modify/RolePermission/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/RolePermission/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/RolePermission/<id>'),
},
onView: (row) => {
// 权限没有独立详情页,暂不跳转
},
});
}
function initAdminTable() {
adminTable = new TableEngine({
container: '#admin-table-container',
height: '200px',
tableName: 'RoleAdmin',
columns: [
{ field: 'RoleAdminUUID', title: '主键UUID', primaryKey: true, editable: false, hideInForm: true },
{ field: 'RoleUUID', title: '角色UUID', editable: false, hideInForm: true },
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/RoleAdmin', { RoleUUID: roleUUID }),
add: buildApiUrl('/API/FormAPI/Add/RoleAdmin'),
edit: buildApiUrl('/API/FormAPI/Modify/RoleAdmin/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/RoleAdmin/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/RoleAdmin/<id>'),
},
onView: (row) => {
// 无需跳转
},
});
}
/** 编辑按钮:弹出编辑对话框 */
async function handleEdit() {
if (!currentData) return;
CompanyUtils.showEditDialog({
tableName: 'Role',
data: currentData,
extraParams: isAdminMode ? { admin_verify: '1' } : {},
onSave: () => init()
});
}
init();
</script>
+118
View File
@@ -0,0 +1,118 @@
<style>
.GERPPage { padding: 24px; max-width: 1400px; margin: 0 auto; height: 100%; display: flex; flex-direction: column; box-sizing: border-box; }
.role-page { padding: 20px; flex: 1; min-height: 0; display: flex; flex-direction: column; box-sizing: border-box; }
.role-header { display: flex; align-items: center; margin-bottom: 16px; gap: 12px; flex-shrink: 0; }
.role-header h1 { margin: 0; flex: 1; }
.role-container { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
</style>
<div class="GERPPage">
<div class="role-page">
<div class="role-header">
<h1 class="PageTitle">
<i class="fas fa-user-tag icon-primary icon-left-lg"></i>
<span id="page-title">角色管理</span>
</h1>
</div>
<div class="role-container" id="TableContainer"></div>
</div>
</div>
<script>
const urlParams = new URLSearchParams(window.location.search);
const currentServer = urlParams.get('server') || 'gvsds-master';
let _tableInstance = null;
let _isAdminMode = false;
GERP.SetTitle(currentServer === 'gvsds-master' ? '角色管理' : `${currentServer} 角色管理`);
document.getElementById('page-title').textContent = currentServer === 'gvsds-master' ? '角色管理' : `${currentServer} - 角色管理`;
function buildApiUrl(path, params = {}) {
const allParams = {};
if (currentServer !== 'gvsds-master') allParams.server = currentServer;
if (_isAdminMode) allParams.admin_verify = '1';
Object.assign(allParams, params);
const qs = new URLSearchParams(allParams).toString();
return qs ? `${path}?${qs}` : path;
}
async function checkAdmin() {
try {
const resp = await fetch(buildApiUrl('/API/FormAPI/CheckAdmin'), { credentials: 'include' });
const result = await resp.json();
return result.code === 0 && result.data && result.data.is_admin;
} catch (e) {
return false;
}
}
async function initTable() {
const containerEl = document.getElementById('TableContainer');
// 先检查管理员特权
_isAdminMode = await checkAdmin();
try {
let columns;
if (currentServer === 'gvsds-master') {
const columnsResp = await fetch(buildApiUrl('/API/FormAPI/Columns/Role'), { credentials: 'include' });
const columnsResult = await columnsResp.json();
if (columnsResult.code !== 0) {
containerEl.innerHTML = `<div style="color:var(--DangerColor);padding:40px;text-align:center;">加载列配置失败: ${columnsResult.msg || ''}</div>`;
return;
}
columns = columnsResult.data || [];
} else {
columns = getDefaultColumns();
}
_tableInstance = new TableEngine({
container: '#TableContainer',
tableName: 'Role',
columns: columns,
data: [],
pageSize: 20,
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
columnDistribution: 'equal',
api: {
get: buildApiUrl('/API/FormAPI/Get/Role'),
add: buildApiUrl('/API/FormAPI/Add/Role'),
edit: buildApiUrl('/API/FormAPI/Modify/Role/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/Role/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/Role/<id>'),
columns: buildApiUrl('/API/FormAPI/Columns/Role'),
},
onView: (row) => {
let url = `/pages/system/role-detail.html?RoleUUID=${encodeURIComponent(row.RoleUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.RoleName || '角色详情');
},
});
} catch (error) {
console.error('initTable error:', error);
containerEl.innerHTML = `<div style="color:var(--DangerColor);padding:40px;text-align:center;">初始化失败: ${error.message}</div>`;
}
}
function getDefaultColumns() {
return [
{ field: 'RoleUUID', title: '角色UUID', primaryKey: true, editable: false, hideInForm: true },
{ field: 'RoleName', title: '角色名称', type: 'text' },
{ field: 'RoleType', title: '角色类型', type: 'select', options: [{value:0,label:'组员'},{value:1,label:'组长'},{value:2,label:'其他'}] },
{ field: 'RoleStatus', title: '状态', type: 'select', options: [{value:0,label:'禁用'},{value:1,label:'启用'}] },
{ field: 'RoleDesc', title: '描述', type: 'text' },
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
];
}
initTable();
</script>
+227
View File
@@ -0,0 +1,227 @@
<script src="/assets/gerp/CompanyCommon.js?v=20260625d"></script>
<div class="GERPPage">
<div class="cp-page">
<div class="cp-header">
<a href="javascript:void(0)" class="cp-back-btn" onclick="goBack()"><i class="fas fa-arrow-left"></i> 返回</a>
<h1 class="PageTitle">
<i class="fas fa-user-circle icon-primary icon-left-lg"></i>
<span id="page-title">用户详情</span>
</h1>
<span id="admin-badge" style="display:none;margin-left:8px;padding:2px 10px;border-radius:4px;background:#1e40af;color:#fff;font-size:12px;font-weight:600;"><i class="fas fa-shield-alt"></i> 特权模式</span>
<button class="GERPBtn Primary" onclick="handleEdit()" style="margin-left:auto;"><i class="fas fa-edit"></i> 编辑</button>
</div>
<div class="cp-container" id="detailContainer" style="overflow:auto;">
<div class="cp-loading"><i class="fas fa-spinner fa-spin"></i> 加载中...</div>
</div>
</div>
</div>
<script>
const currentServer = CompanyUtils.getServer();
const userUUID = new URLSearchParams(window.location.search).get('UserUUID');
CompanyUtils.setPageTitle('用户详情');
function goBack() {
if (window.history.length > 1) {
window.history.back();
} else {
GERP.Navigate('/pages/system/users.html' + (currentServer !== 'gvsds-master' ? `?server=${encodeURIComponent(currentServer)}` : ''), '用户管理');
}
}
let currentData = null;
let roleTable = null;
let deptTable = null;
let isAdminMode = false;
/** 检查当前用户是否具有管理员特权 */
async function checkAdmin() {
try {
const resp = await fetch(buildApiUrl('/API/FormAPI/CheckAdmin'), { credentials: 'include' });
const result = await resp.json();
return result.code === 0 && result.data && result.data.is_admin;
} catch (e) {
return false;
}
}
function buildApiUrl(path, params = {}) {
const allParams = {};
if (currentServer !== 'gvsds-master') allParams.server = currentServer;
if (isAdminMode) allParams.admin_verify = '1';
Object.assign(allParams, params);
const qs = new URLSearchParams(allParams).toString();
return qs ? `${path}?${qs}` : path;
}
function genderText(g) {
if (g === 1 || g === '1') return '男';
if (g === 0 || g === '0') return '女';
return '-';
}
function yesNoText(val) {
return val ? '<span style="color:#166534;">是</span>' : '<span style="color:#991b1b;">否</span>';
}
async function init() {
const containerEl = document.getElementById('detailContainer');
if (!userUUID) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-triangle" style="margin-right:6px;"></i> 缺少 UserUUID 参数,无法加载用户详情。</div>`;
return;
}
// 先检查管理员特权,决定是否启用无隔离模式
isAdminMode = await checkAdmin();
const adminBadge = document.getElementById('admin-badge');
if (isAdminMode) {
adminBadge.style.display = 'inline-block';
}
try {
const dataResp = await fetch(buildApiUrl('/API/FormAPI/Get/User', { UserUUID: userUUID, PageSize: '1' }), { credentials: 'include' });
const dataResult = await dataResp.json();
if (dataResult.code !== 0) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 加载失败: ${CompanyUtils.esc(dataResult.msg || '未知错误')}</div>`;
return;
}
const list = dataResult.data || [];
const data = Array.isArray(list) ? (list.find(u => u.UserUUID === userUUID) || list[0]) : list;
if (!data) {
containerEl.innerHTML = `<div class="cp-error">未找到用户数据</div>`;
return;
}
currentData = data;
CompanyUtils.setPageTitle(data.UserName || '用户详情');
// 渲染基本信息 + 表格容器
const isolationNotice = isAdminMode
? `<div style="margin-bottom:12px;padding:8px 12px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:4px;color:#1e40af;font-size:13px;"><i class="fas fa-info-circle"></i> 特权模式:显示该用户在所有公司、所有部门的关联记录(无公司隔离)</div>`
: `<div style="margin-bottom:12px;padding:8px 12px;background:#f9fafb;border:1px solid #e5e7eb;border-radius:4px;color:#6b7280;font-size:13px;"><i class="fas fa-info-circle"></i> 仅显示当前公司数据。如需查看所有公司记录,需管理员特权。</div>`;
containerEl.innerHTML = `
<div class="cp-detail-section">
<h3><i class="fas fa-user-circle"></i> 基本信息</h3>
<div class="cp-info-grid">
<div class="cp-info-item"><div class="label">用户名</div><div class="value">${CompanyUtils.esc(data.UserName) || '-'}</div></div>
<div class="cp-info-item"><div class="label">邮箱</div><div class="value">${CompanyUtils.esc(data.UserEmail) || '-'}</div></div>
<div class="cp-info-item"><div class="label">电话</div><div class="value">${CompanyUtils.esc(data.UserTele) || '-'}</div></div>
<div class="cp-info-item"><div class="label">性别</div><div class="value">${genderText(data.UserGender)}</div></div>
<div class="cp-info-item"><div class="label">账号状态</div><div class="value">${CompanyUtils.statusTag(data.UserAccountLicense)}</div></div>
<div class="cp-info-item"><div class="label">是否员工</div><div class="value">${yesNoText(data.IsStaff)}</div></div>
<div class="cp-info-item"><div class="label">是否超级用户</div><div class="value">${yesNoText(data.IsSuperuser)}</div></div>
<div class="cp-info-item"><div class="label">创建时间</div><div class="value">${CompanyUtils.formatTime(data.UserCreateTime)}</div></div>
<div class="cp-info-item"><div class="label">最后登录</div><div class="value">${CompanyUtils.formatTime(data.UserLastLoginDate)}</div></div>
</div>
</div>
${isolationNotice}
<div class="cp-detail-section">
<h3><i class="fas fa-shield-alt"></i> 角色与组织</h3>
<div id="role-table-container" style="height:400px;"></div>
</div>
<div class="cp-detail-section">
<h3><i class="fas fa-sitemap"></i> 部门关联</h3>
<div id="dept-table-container" style="height:400px;"></div>
</div>`;
// 初始化 TableEngine 实例
initRoleTable();
initDeptTable();
} catch (e) {
containerEl.innerHTML = `<div class="cp-error"><i class="fas fa-exclamation-circle" style="margin-right:6px;"></i> 网络错误: ${CompanyUtils.esc(e.message)}</div>`;
}
}
function initRoleTable() {
roleTable = new TableEngine({
container: '#role-table-container',
height: '400px',
tableName: 'UserRole',
columns: [
{ field: 'UserRoleUUID', title: '角色UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('RoleUUID', '角色名', 'Role'),
CompanyUtils.epColumn('CompanyUUID', '公司', 'Company', { editable: false }),
CompanyUtils.epColumn('DeptUUID', '部门', 'Department'),
CompanyUtils.epColumn('WorkgroupUUID', '工作组', 'Workgroup'),
{ field: 'RoleType', title: '角色类型', type: 'select', options: [{ value: 0, label: '组员' }, { value: 1, label: '组长' }] },
{ field: 'CreateTime', title: '创建时间', type: 'datetime', editable: false },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserRole', { UserUUID: userUUID }),
add: buildApiUrl('/API/FormAPI/Add/UserRole'),
edit: buildApiUrl('/API/FormAPI/Modify/UserRole/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserRole/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserRole/<id>'),
},
onView: (row) => {
let url = `/pages/system/role-detail.html?RoleUUID=${encodeURIComponent(row.RoleUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.RoleUUID_label || '角色详情');
},
});
}
function initDeptTable() {
deptTable = new TableEngine({
container: '#dept-table-container',
height: '400px',
tableName: 'UserDept',
columns: [
{ field: 'UserDeptUUID', title: '关联UUID', primaryKey: true, editable: false, hideInForm: true },
CompanyUtils.epColumn('DeptUUID', '部门名称', 'Department'),
{ field: 'IsMainDept', title: '主部门', type: 'select', options: [{ value: 0, label: '兼职' }, { value: 1, label: '主部门' }] },
],
data: [],
pageSize: 20,
columnDistribution: 'equal',
showPagination: true,
showCheckbox: true,
editable: true,
sortable: true,
filterable: true,
striped: true,
hoverable: true,
bordered: true,
api: {
get: buildApiUrl('/API/FormAPI/Get/UserDept', { UserUUID: userUUID }),
add: buildApiUrl('/API/FormAPI/Add/UserDept'),
edit: buildApiUrl('/API/FormAPI/Modify/UserDept/<id>'),
modify: buildApiUrl('/API/FormAPI/ModifyItem/UserDept/<id>'),
delete: buildApiUrl('/API/FormAPI/Delete/UserDept/<id>'),
},
onView: (row) => {
let url = `/pages/Company/department-detail.html?DeptUUID=${encodeURIComponent(row.DeptUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.DeptUUID_label || '部门详情');
},
});
}
/** 编辑按钮:弹出编辑对话框 */
async function handleEdit() {
if (!currentData) return;
CompanyUtils.showEditDialog({
tableName: 'User',
data: currentData,
extraParams: isAdminMode ? { admin_verify: '1' } : {},
onSave: () => init()
});
}
init();
</script>
+15 -197
View File
@@ -4,24 +4,6 @@
.user-header { display: flex; align-items: center; margin-bottom: 16px; gap: 12px; flex-shrink: 0; }
.user-header h1 { margin: 0; flex: 1; }
.user-container { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
/* 详情面板内样式 */
.user-detail-section { margin-bottom: 20px; }
.user-detail-section h3 { font-size: 14px; font-weight: 600; margin: 0 0 10px 0; padding-bottom: 8px; border-bottom: 1px solid var(--BorderColor); display: flex; align-items: center; gap: 8px; }
.user-detail-section h3 i { color: var(--PrimaryColor); }
.user-info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }
.user-info-item { font-size: 13px; }
.user-info-item .label { color: var(--TextSecondary); font-size: 12px; }
.user-info-item .value { color: var(--TextPrimary); font-weight: 500; word-break: break-all; }
.user-detail-list { list-style: none; padding: 0; margin: 0; }
.user-detail-list li { padding: 8px 12px; border: 1px solid var(--BorderColor); border-radius: 6px; margin-bottom: 6px; font-size: 13px; display: flex; align-items: center; gap: 8px; }
.user-detail-list li .item-name { font-weight: 500; flex: 1; }
.user-detail-list li .item-desc { color: var(--TextSecondary); font-size: 12px; }
.user-order-item { display: flex; align-items: center; gap: 12px; width: 100%; }
.user-order-item .order-no { font-weight: 600; color: var(--PrimaryColor); }
.user-order-item .order-amount { font-weight: 600; }
.user-order-item .order-status { margin-left: auto; }
.user-empty { text-align: center; padding: 20px; color: var(--TextSecondary); font-size: 13px; }
</style>
<div class="GERPPage">
@@ -40,9 +22,7 @@
<script>
const urlParams = new URLSearchParams(window.location.search);
const currentServer = urlParams.get('server') || 'gvsds-master';
const API_BASE = '/api/gerp/users/';
let _tableInstance = null;
let _drawerClose = null;
GERP.SetTitle(currentServer === 'gvsds-master' ? '用户管理' : `${currentServer} 用户管理`);
document.getElementById('page-title').textContent = currentServer === 'gvsds-master' ? '用户管理' : `${currentServer} - 用户管理`;
@@ -78,17 +58,20 @@
striped: true,
hoverable: true,
bordered: true,
columnDistribution: 'equal',
api: {
get: `${API_BASE}list/?server=${currentServer}`,
get: '/API/FormAPI/Get/User',
add: '/API/FormAPI/Add/User',
edit: '/API/FormAPI/Modify/User/<id>',
modify: '/API/FormAPI/ModifyItem/User/<id>',
delete: '/API/FormAPI/Delete/User/<id>',
columns: '/API/FormAPI/Columns/User'
},
onRowClick: (row) => openDetail(row.UserUUID),
onDelete: (row) => {},
onEdit: (row) => {},
onView: (row) => {
let url = `/pages/system/user-detail.html?UserUUID=${encodeURIComponent(row.UserUUID || '')}`;
if (currentServer !== 'gvsds-master') url += `&server=${encodeURIComponent(currentServer)}`;
GERP.Navigate(url, row.UserName || '用户详情');
},
});
} catch (error) {
@@ -99,181 +82,16 @@
function getDefaultColumns() {
return [
{ field: 'UserUUID', title: '用户UUID', width: 140, primaryKey: true, editable: false, hideInForm: true },
{ field: 'UserName', title: '用户名', width: 120, type: 'text' },
{ field: 'UserEmail', title: '邮箱', width: 180, type: 'text' },
{ field: 'UserTele', title: '电话', width: 120, type: 'text' },
{ field: 'UserGender', title: '性别', width: 60, type: 'select', options: [{value:0,label:'女'},{value:1,label:'男'}] },
{ field: 'UserAccountLicense', title: '状态', width: 70, type: 'select', options: [{value:0,label:'禁用'},{value:1,label:'启用'}] },
{ field: 'UserCreateTime', title: '创建时间', width: 160, type: 'datetime', editable: false },
{ field: 'UserLastLoginDate', title: '最后登录', width: 160, type: 'datetime', editable: false },
{ field: 'UserUUID', title: '用户UUID', primaryKey: true, editable: false, hideInForm: true },
{ field: 'UserName', title: '用户名', type: 'text' },
{ field: 'UserEmail', title: '邮箱', type: 'text' },
{ field: 'UserTele', title: '电话', type: 'text' },
{ field: 'UserGender', title: '性别', type: 'select', options: [{value:0,label:'女'},{value:1,label:'男'}] },
{ field: 'UserAccountLicense', title: '状态', type: 'select', options: [{value:0,label:'禁用'},{value:1,label:'启用'}] },
{ field: 'UserCreateTime', title: '创建时间', type: 'datetime', editable: false },
{ field: 'UserLastLoginDate', title: '最后登录', type: 'datetime', editable: false },
];
}
// === 用户详情抽屉 ===
function openDetail(userUUID) {
if (_drawerClose) _drawerClose();
WaitForUI((UI) => {
const { element, close } = UI.OpenDrawer({
title: '加载中...',
placement: 'right',
width: '520px',
content: '<div style="text-align:center;padding:40px;color:var(--TextSecondary);"><i class="fas fa-spinner fa-spin"></i> 加载中...</div>',
});
_drawerClose = close;
const params = new URLSearchParams({ server: currentServer, UserUUID: userUUID });
fetch(`${API_BASE}detail/?${params}`, { credentials: 'include' })
.then(resp => resp.json())
.then(json => {
if (json.code !== 0) {
element.querySelector('.GERPDrawerBody').innerHTML = `<div class="user-empty" style="color:var(--DangerColor);">${json.msg || '加载失败'}</div>`;
return;
}
const d = json.data;
element.querySelector('.GERPDrawerTitle').textContent = d.User.UserName || userUUID;
element.querySelector('.GERPDrawerBody').innerHTML = renderDetail(d);
})
.catch(e => {
element.querySelector('.GERPDrawerBody').innerHTML = `<div class="user-empty" style="color:var(--DangerColor);">网络错误: ${e.message}</div>`;
});
});
}
function WaitForUI(callback) {
if (window.GERPUI && typeof window.GERPUI.OpenDrawer === 'function') {
callback(window.GERPUI);
return;
}
document.addEventListener('GERPUIReady', () => callback(window.GERPUI), { once: true });
}
function renderDetail(d) {
const u = d.User;
let html = '';
html += `<div class="user-detail-section">
<h3><i class="fas fa-id-card"></i> 基本信息</h3>
<div class="user-info-grid">
<div class="user-info-item"><div class="label">用户名</div><div class="value">${esc(u.UserName)}</div></div>
<div class="user-info-item"><div class="label">邮箱</div><div class="value">${esc(u.UserEmail) || '-'}</div></div>
<div class="user-info-item"><div class="label">电话</div><div class="value">${esc(u.UserTele) || '-'}</div></div>
<div class="user-info-item"><div class="label">性别</div><div class="value">${genderText(u.UserGender)}</div></div>
<div class="user-info-item"><div class="label">状态</div><div class="value">${statusTag(u.UserAccountLicense)}</div></div>
<div class="user-info-item"><div class="label">职位状态</div><div class="value">${u.UserPositionStatus === 1 ? '在职' : '离职'}</div></div>
<div class="user-info-item"><div class="label">创建时间</div><div class="value">${formatTime(u.UserCreateTime)}</div></div>
<div class="user-info-item"><div class="label">最后登录</div><div class="value">${formatTime(u.UserLastLoginDate)}</div></div>
<div class="user-info-item"><div class="label">UUID</div><div class="value" style="font-size:11px;font-family:monospace;">${esc(u.UserUUID)}</div></div>
<div class="user-info-item"><div class="label">标签</div><div class="value">${u.IsSuperuser ? '<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:#fef3c7;color:#92400e;">超级用户</span> ' : ''}${u.IsStaff ? '<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:#dbeafe;color:#1e40af;">员工</span>' : ''}</div></div>
</div>
</div>`;
html += `<div class="user-detail-section">
<h3><i class="fas fa-shield-alt"></i> 角色 (${d.Roles.length}) <a href="javascript:void(0)" onclick="openTable('Role')" style="font-size:12px;font-weight:400;margin-left:auto;">管理</a></h3>`;
if (d.Roles.length === 0) {
html += `<div class="user-empty">暂无角色</div>`;
} else {
html += `<ul class="user-detail-list">`;
d.Roles.forEach(r => {
html += `<li>
<i class="fas fa-user-tag" style="color:var(--PrimaryColor);"></i>
<span class="item-name">${esc(r.RoleName)}</span>
${r.CompanyName ? `<span class="item-desc">${esc(r.CompanyName)}${r.CompanyShort ? ` (${esc(r.CompanyShort)})` : ''}</span>` : ''}
${assignScopeTag(r.AssignScope)}
${r.RoleStatus === 1 ? '<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:#dcfce7;color:#166534;">启用</span>' : '<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:#fee2e2;color:#991b1b;">禁用</span>'}
</li>`;
});
html += `</ul>`;
}
html += `</div>`;
html += `<div class="user-detail-section">
<h3><i class="fas fa-key"></i> 权限 (${d.Permissions.length}) <a href="javascript:void(0)" onclick="openTable('Permission')" style="font-size:12px;font-weight:400;margin-left:auto;">管理</a></h3>`;
if (d.Permissions.length === 0) {
html += `<div class="user-empty">暂无权限</div>`;
} else {
html += `<ul class="user-detail-list">`;
d.Permissions.forEach(p => {
html += `<li>
<i class="fas fa-lock" style="color:#f59e0b;"></i>
<span class="item-name">${esc(p.PermName)}</span>
${p.PermCategory ? `<span style="display:inline-block;padding:2px 6px;border-radius:4px;font-size:10px;background:#f3f4f6;color:#6b7280;">${esc(p.PermCategory)}</span>` : ''}
<span class="item-desc" style="font-family:monospace;font-size:11px;">${esc(p.PermCode)}</span>
</li>`;
});
html += `</ul>`;
}
html += `</div>`;
html += `<div class="user-detail-section">
<h3><i class="fas fa-receipt"></i> 订单 (${d.Orders.length}) <a href="javascript:void(0)" onclick="openTable('Order')" style="font-size:12px;font-weight:400;margin-left:auto;">管理</a></h3>`;
if (d.Orders.length === 0) {
html += `<div class="user-empty">暂无订单</div>`;
} else {
html += `<ul class="user-detail-list">`;
d.Orders.forEach(o => {
html += `<li>
<div class="user-order-item">
<i class="fas fa-file-invoice" style="color:#10b981;"></i>
<span class="order-no">${esc(o.OrderNO)}</span>
<span class="item-name">${esc(o.OrderTitle)}</span>
<span class="order-amount">${o.Currency} ${o.PayAmount}</span>
<span class="order-status">${orderStatusText(o.OrderStatus)}</span>
</div>
</li>`;
});
html += `</ul>`;
}
html += `</div>`;
return html;
}
// === 工具函数 ===
function openTable(tableName) {
if (currentServer === 'gvsds-master') {
GERP.Navigate(`/pages/system/dbtable.html?table=${encodeURIComponent(tableName)}`, tableName);
} else {
GERP.Navigate(`/pages/system/sub-dbtable.html?server=${encodeURIComponent(currentServer)}&table=${encodeURIComponent(tableName)}`, `${currentServer} / ${tableName}`);
}
}
function esc(s) { if (s == null) return ''; const d = document.createElement('div'); d.textContent = String(s); return d.innerHTML; }
function assignScopeTag(scope) {
const map = {
0: { label: '全租户', bg: '#ede9fe', color: '#6d28d9' },
1: { label: '公司级', bg: '#dbeafe', color: '#1e40af' },
2: { label: '部门级', bg: '#fef3c7', color: '#92400e' },
};
const s = map[scope];
if (!s) return '';
return `<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:${s.bg};color:${s.color};">${s.label}</span>`;
}
function genderText(g) {
if (g === 1 || g === '1') return '男';
if (g === 0 || g === '0') return '女';
return '未知';
}
function statusTag(license) {
if (license === 1 || license === '1') return '<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:#dcfce7;color:#166534;">启用</span>';
return '<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:#fee2e2;color:#991b1b;">禁用</span>';
}
function formatTime(t) {
if (!t) return '-';
try { const d = new Date(t + (t.endsWith('Z') ? '' : 'Z')); return d.toLocaleString(); } catch { return t; }
}
function orderStatusText(s) {
const map = { 0:'待确认', 1:'已确认', 2:'已支付', 3:'处理中', 4:'已完成', 5:'已取消', 6:'退款中', 7:'已退款', 8:'已关闭' };
return map[s] || String(s);
}
initTable();
</script>