411 lines
14 KiB
JavaScript
411 lines
14 KiB
JavaScript
(function () {
|
||
'use strict';
|
||
|
||
// ---------- 状态 ----------
|
||
var isLoggedIn = false; // 页面1 登录态,静态演示默认未登录
|
||
var currentView = 'list'; // list | detail
|
||
var currentRole = 'market'; // market | admin | engineer
|
||
var currentProjectId = null; // 当前详情/编辑的项目 ID,null 表示新建
|
||
var projects = [];
|
||
var listFiltered = [];
|
||
|
||
// ---------- Mock 数据 ----------
|
||
function getMockProjects() {
|
||
return [
|
||
{
|
||
id: '1',
|
||
name: '城东 110kV 输变电工程',
|
||
contractCode: 'HT-2024-001',
|
||
progress: '进行中',
|
||
costStatus: '正常',
|
||
ownerUnit: '市供电公司',
|
||
},
|
||
{
|
||
id: '2',
|
||
name: '高新区配电改造项目',
|
||
contractCode: 'HT-2024-002',
|
||
progress: '已竣工',
|
||
costStatus: '正常',
|
||
ownerUnit: '高新区管委会',
|
||
},
|
||
{
|
||
id: '3',
|
||
name: '西区 35kV 线路迁改',
|
||
contractCode: 'HT-2024-003',
|
||
progress: '筹备中',
|
||
costStatus: '节余',
|
||
ownerUnit: '西区建设局',
|
||
},
|
||
];
|
||
}
|
||
|
||
function getMockLogs(projectId) {
|
||
return [
|
||
{ operator: '张三', time: '2025-01-28 14:30', summary: '修改合同信息' },
|
||
{ operator: '李四', time: '2025-01-27 10:00', summary: '修改成本控制' },
|
||
{ operator: '王五', time: '2025-01-26 16:45', summary: '修改应收款' },
|
||
];
|
||
}
|
||
|
||
// ---------- DOM ----------
|
||
var roleSelect = document.getElementById('roleSelect');
|
||
var btnNewProject = document.getElementById('btnNewProject');
|
||
var viewList = document.getElementById('view-list');
|
||
var viewDetail = document.getElementById('view-detail');
|
||
var searchType = document.getElementById('searchType');
|
||
var searchInput = document.getElementById('searchInput');
|
||
var btnSearch = document.getElementById('btnSearch');
|
||
var filterProgress = document.getElementById('filterProgress');
|
||
var filterCost = document.getElementById('filterCost');
|
||
var listLoading = document.getElementById('listLoading');
|
||
var listEmpty = document.getElementById('listEmpty');
|
||
var projectTable = document.getElementById('projectTable');
|
||
var projectTableBody = document.getElementById('projectTableBody');
|
||
var pagination = document.getElementById('pagination');
|
||
var btnBack = document.getElementById('btnBack');
|
||
var detailTitle = document.getElementById('detailTitle');
|
||
var detailLoading = document.getElementById('detailLoading');
|
||
var detailContent = document.getElementById('detailContent');
|
||
var btnViewLog = document.getElementById('btnViewLog');
|
||
var btnSave = document.getElementById('btnSave');
|
||
var logOverlay = document.getElementById('logOverlay');
|
||
var logDrawer = document.getElementById('logDrawer');
|
||
var logDrawerClose = document.getElementById('logDrawerClose');
|
||
var logLoading = document.getElementById('logLoading');
|
||
var logEmpty = document.getElementById('logEmpty');
|
||
var logList = document.getElementById('logList');
|
||
var toast = document.getElementById('toast');
|
||
var navList = document.getElementById('navList');
|
||
var viewLogin = document.getElementById('view-login');
|
||
var appMain = document.getElementById('appMain');
|
||
var loginForm = document.getElementById('loginForm');
|
||
var loginError = document.getElementById('loginError');
|
||
var btnLoginSubmit = document.getElementById('btnLoginSubmit');
|
||
var btnLogout = document.querySelector('.btn-logout');
|
||
|
||
// ---------- 页面1:登录 / 退出 ----------
|
||
function showLogin() {
|
||
isLoggedIn = false;
|
||
if (viewLogin) viewLogin.classList.remove('hidden');
|
||
if (appMain) appMain.style.display = 'none';
|
||
}
|
||
|
||
function showApp() {
|
||
isLoggedIn = true;
|
||
if (viewLogin) viewLogin.classList.add('hidden');
|
||
if (appMain) appMain.style.display = 'flex';
|
||
}
|
||
|
||
if (loginForm) {
|
||
loginForm.addEventListener('submit', function (e) {
|
||
e.preventDefault();
|
||
var account = document.getElementById('loginAccount');
|
||
var password = document.getElementById('loginPassword');
|
||
if (!account || !password) return;
|
||
if (loginError) loginError.style.display = 'none';
|
||
if (btnLoginSubmit) btnLoginSubmit.disabled = true;
|
||
|
||
// 静态演示:任意非空账号密码视为成功;可改为模拟失败(如 password === 'fail')
|
||
setTimeout(function () {
|
||
if (btnLoginSubmit) btnLoginSubmit.disabled = false;
|
||
if (password.value === 'fail') {
|
||
if (loginError) {
|
||
loginError.textContent = '账号或密码错误,请重试';
|
||
loginError.style.display = 'block';
|
||
}
|
||
return;
|
||
}
|
||
showApp();
|
||
renderList();
|
||
}, 400);
|
||
});
|
||
}
|
||
|
||
if (btnLogout) {
|
||
btnLogout.addEventListener('click', function () {
|
||
showLogin();
|
||
});
|
||
}
|
||
|
||
// ---------- 权限:是否市场部 ----------
|
||
function isMarketRole() {
|
||
return currentRole === 'market';
|
||
}
|
||
|
||
function updateRoleUI() {
|
||
if (btnNewProject) btnNewProject.style.display = isMarketRole() ? '' : 'none';
|
||
}
|
||
|
||
if (roleSelect) {
|
||
roleSelect.addEventListener('change', function () {
|
||
currentRole = roleSelect.value;
|
||
updateRoleUI();
|
||
});
|
||
}
|
||
|
||
// ---------- 视图切换 ----------
|
||
function showList() {
|
||
currentView = 'list';
|
||
if (viewList) viewList.classList.add('active');
|
||
if (viewDetail) viewDetail.classList.remove('active');
|
||
document.querySelectorAll('.nav-item').forEach(function (n) { n.classList.remove('active'); });
|
||
if (navList) navList.classList.add('active');
|
||
}
|
||
|
||
function showDetail(projectId) {
|
||
currentView = 'detail';
|
||
currentProjectId = projectId;
|
||
if (viewList) viewList.classList.remove('active');
|
||
if (viewDetail) viewDetail.classList.add('active');
|
||
document.querySelectorAll('.nav-item').forEach(function (n) { n.classList.remove('active'); });
|
||
|
||
if (detailTitle) detailTitle.textContent = projectId ? '项目详情' : '新建项目';
|
||
if (detailLoading) detailLoading.style.display = 'block';
|
||
if (detailContent) detailContent.style.display = 'none';
|
||
|
||
if (projectId) {
|
||
// 模拟加载详情
|
||
setTimeout(function () {
|
||
if (detailLoading) detailLoading.style.display = 'none';
|
||
if (detailContent) detailContent.style.display = 'block';
|
||
fillDetailForm(projectId);
|
||
}, 400);
|
||
} else {
|
||
if (detailLoading) detailLoading.style.display = 'none';
|
||
if (detailContent) detailContent.style.display = 'block';
|
||
clearDetailForm();
|
||
}
|
||
}
|
||
|
||
function fillDetailForm(projectId) {
|
||
var p = projects.find(function (x) { return x.id === projectId; });
|
||
if (!p) return;
|
||
var nameInput = detailContent.querySelector('input[name="projectName"]');
|
||
if (nameInput) nameInput.value = p.name || '';
|
||
var codeInput = detailContent.querySelector('input[name="contractCode"]');
|
||
if (codeInput) codeInput.value = p.contractCode || '';
|
||
var ownerInput = detailContent.querySelector('input[name="ownerUnit"]');
|
||
if (ownerInput) ownerInput.value = p.ownerUnit || '';
|
||
}
|
||
|
||
function clearDetailForm() {
|
||
var inputs = detailContent ? detailContent.querySelectorAll('input, select, textarea') : [];
|
||
inputs.forEach(function (el) {
|
||
if (el.type === 'checkbox' || el.type === 'radio') el.checked = false;
|
||
else el.value = '';
|
||
});
|
||
}
|
||
|
||
// ---------- Tab 切换 ----------
|
||
function bindTabs() {
|
||
var tabs = document.querySelectorAll('.tab');
|
||
var panels = document.querySelectorAll('.tab-panel');
|
||
tabs.forEach(function (tab) {
|
||
tab.addEventListener('click', function () {
|
||
var t = tab.getAttribute('data-tab');
|
||
tabs.forEach(function (x) { x.classList.remove('active'); });
|
||
panels.forEach(function (p) {
|
||
var pid = p.id;
|
||
if (pid === 'panel-' + t) p.classList.add('active');
|
||
else p.classList.remove('active');
|
||
});
|
||
tab.classList.add('active');
|
||
});
|
||
});
|
||
}
|
||
|
||
// ---------- 列表:渲染 ----------
|
||
function filterList() {
|
||
var type = searchType && searchType.value ? searchType.value : 'name';
|
||
var keyword = (searchInput && searchInput.value || '').trim().toLowerCase();
|
||
var progressVal = filterProgress && filterProgress.value ? filterProgress.value : '';
|
||
var costVal = filterCost && filterCost.value ? filterCost.value : '';
|
||
|
||
listFiltered = projects.filter(function (p) {
|
||
if (keyword) {
|
||
var name = (p.name || '').toLowerCase();
|
||
var code = (p.contractCode || '').toLowerCase();
|
||
if (type === 'name' && name.indexOf(keyword) === -1) return false;
|
||
if (type === 'code' && code.indexOf(keyword) === -1) return false;
|
||
}
|
||
if (progressVal) {
|
||
if ((p.progressKey || '').toLowerCase() !== progressVal.toLowerCase()) return false;
|
||
}
|
||
if (costVal) {
|
||
if ((p.costKey || '').toLowerCase() !== costVal.toLowerCase()) return false;
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
|
||
function mapProgressToKey(p) {
|
||
var map = { '筹备中': 'planning', '进行中': 'ongoing', '已竣工': 'completed', '已结算': 'settled' };
|
||
p.progressKey = map[p.progress] || p.progress;
|
||
return p;
|
||
}
|
||
|
||
function mapCostToKey(p) {
|
||
var map = { '正常': 'normal', '超支': 'over', '节余': 'under' };
|
||
p.costKey = map[p.costStatus] || p.costStatus;
|
||
return p;
|
||
}
|
||
|
||
function renderList() {
|
||
if (listLoading) listLoading.style.display = 'block';
|
||
if (listEmpty) listEmpty.style.display = 'none';
|
||
if (projectTable) projectTable.style.visibility = 'hidden';
|
||
|
||
setTimeout(function () {
|
||
filterList();
|
||
if (listLoading) listLoading.style.display = 'none';
|
||
|
||
if (listFiltered.length === 0) {
|
||
if (listEmpty) listEmpty.style.display = 'flex';
|
||
if (projectTable) projectTable.style.visibility = 'hidden';
|
||
} else {
|
||
if (listEmpty) listEmpty.style.display = 'none';
|
||
if (projectTable) projectTable.style.visibility = 'visible';
|
||
var html = listFiltered.map(function (p) {
|
||
return (
|
||
'<tr>' +
|
||
'<td>' + (p.name || '—') + '</td>' +
|
||
'<td>' + (p.contractCode || '—') + '</td>' +
|
||
'<td>' + (p.progress || '—') + '</td>' +
|
||
'<td>' + (p.costStatus || '—') + '</td>' +
|
||
'<td>' + (p.ownerUnit || '—') + '</td>' +
|
||
'<td class="cell-actions">' +
|
||
'<button type="button" class="link-action" data-id="' + p.id + '">查看</button> ' +
|
||
'<button type="button" class="link-action" data-id="' + p.id + '">编辑</button>' +
|
||
'</td>' +
|
||
'</tr>'
|
||
);
|
||
}).join('');
|
||
if (projectTableBody) projectTableBody.innerHTML = html;
|
||
|
||
projectTableBody.querySelectorAll('.link-action').forEach(function (btn) {
|
||
btn.addEventListener('click', function () {
|
||
var id = btn.getAttribute('data-id');
|
||
showDetail(id);
|
||
});
|
||
});
|
||
}
|
||
|
||
var totalEl = pagination && pagination.querySelector('.pagination-info');
|
||
if (totalEl) totalEl.textContent = '共 ' + listFiltered.length + ' 条';
|
||
}, 300);
|
||
}
|
||
|
||
// ---------- 搜索与筛选 ----------
|
||
if (btnSearch) btnSearch.addEventListener('click', renderList);
|
||
if (searchInput) searchInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') renderList(); });
|
||
if (filterProgress) filterProgress.addEventListener('change', renderList);
|
||
if (filterCost) filterCost.addEventListener('change', renderList);
|
||
|
||
// ---------- 新建项目 ----------
|
||
if (btnNewProject) {
|
||
btnNewProject.addEventListener('click', function () {
|
||
showDetail(null);
|
||
});
|
||
}
|
||
|
||
// ---------- 返回列表 ----------
|
||
if (btnBack) {
|
||
btnBack.addEventListener('click', function () {
|
||
showList();
|
||
});
|
||
}
|
||
|
||
if (navList) {
|
||
navList.addEventListener('click', function (e) {
|
||
e.preventDefault();
|
||
showList();
|
||
});
|
||
}
|
||
|
||
// ---------- 保存 ----------
|
||
if (btnSave) {
|
||
btnSave.addEventListener('click', function () {
|
||
var textSpan = btnSave.querySelector('.btn-text');
|
||
var loadingSpan = btnSave.querySelector('.btn-loading');
|
||
if (loadingSpan && loadingSpan.style.display === 'inline') return;
|
||
|
||
if (textSpan) textSpan.style.display = 'none';
|
||
if (loadingSpan) loadingSpan.style.display = 'inline';
|
||
btnSave.disabled = true;
|
||
|
||
setTimeout(function () {
|
||
if (textSpan) textSpan.style.display = 'inline';
|
||
if (loadingSpan) loadingSpan.style.display = 'none';
|
||
btnSave.disabled = false;
|
||
showToast('保存成功', 'success');
|
||
}, 800);
|
||
});
|
||
}
|
||
|
||
// ---------- 操作日志抽屉 ----------
|
||
function openLogDrawer() {
|
||
if (logOverlay) logOverlay.classList.add('visible');
|
||
if (logDrawer) logDrawer.classList.add('visible');
|
||
if (logOverlay) logOverlay.setAttribute('aria-hidden', 'false');
|
||
|
||
if (logLoading) logLoading.style.display = 'block';
|
||
if (logEmpty) logEmpty.style.display = 'none';
|
||
if (logList) logList.innerHTML = '';
|
||
|
||
setTimeout(function () {
|
||
if (logLoading) logLoading.style.display = 'none';
|
||
var logs = getMockLogs(currentProjectId);
|
||
if (logs.length === 0) {
|
||
if (logEmpty) logEmpty.style.display = 'flex';
|
||
} else {
|
||
logList.innerHTML = logs.map(function (l) {
|
||
return (
|
||
'<li class="log-item">' +
|
||
'<div class="log-meta">' + l.operator + ' · ' + l.time + '</div>' +
|
||
'<div class="log-desc">' + l.summary + '</div>' +
|
||
'</li>'
|
||
);
|
||
}).join('');
|
||
}
|
||
}, 400);
|
||
}
|
||
|
||
function closeLogDrawer() {
|
||
if (logOverlay) logOverlay.classList.remove('visible');
|
||
if (logDrawer) logDrawer.classList.remove('visible');
|
||
if (logOverlay) logOverlay.setAttribute('aria-hidden', 'true');
|
||
}
|
||
|
||
if (btnViewLog) btnViewLog.addEventListener('click', openLogDrawer);
|
||
if (logDrawerClose) logDrawerClose.addEventListener('click', closeLogDrawer);
|
||
if (logOverlay) logOverlay.addEventListener('click', closeLogDrawer);
|
||
|
||
// ---------- Toast ----------
|
||
function showToast(message, type) {
|
||
if (!toast) return;
|
||
toast.textContent = message;
|
||
toast.className = 'toast visible' + (type ? ' ' + type : '');
|
||
clearTimeout(toast._tid);
|
||
toast._tid = setTimeout(function () {
|
||
toast.classList.remove('visible');
|
||
}, 2500);
|
||
}
|
||
|
||
// ---------- 初始化 ----------
|
||
projects = getMockProjects().map(function (p) {
|
||
mapProgressToKey(p);
|
||
mapCostToKey(p);
|
||
return p;
|
||
});
|
||
|
||
bindTabs();
|
||
updateRoleUI();
|
||
|
||
// 未登录时只显示登录页;已登录则显示主应用(可通过 hash 或 localStorage 模拟,此处默认显示登录页)
|
||
if (viewLogin && appMain) {
|
||
showLogin();
|
||
} else {
|
||
renderList();
|
||
}
|
||
})();
|