[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>工资条查询系统</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
success: '#00B42A',
danger: '#F53F3F',
}
}
}
}
</script>
<style type="text/tailwindcss">
[url=home.php?mod=space&uid=1688376]@layer[/url] utilities {
.card { [url=home.php?mod=space&uid=101791]@apply[/url] bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden; }
.card-header { @apply px-6 py-4 border-b border-gray-200 font-semibold text-gray-800; }
.card-body { @apply p-6; }
.btn { @apply px-4 py-2 rounded-lg font-medium transition-all duration-200; }
.btn-primary { @apply bg-primary text-white hover:bg-primary/90; }
.btn-outline { @apply border border-gray-300 text-gray-700 hover:bg-gray-50; }
.btn-danger { @apply bg-danger text-white hover:bg-danger/90; }
.btn-sm { @apply px-2 py-1 text-xs rounded-lg border transition; }
.input-control { @apply w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/30 focus:border-primary outline-none transition; }
.fade-in { animation: fadeIn 0.3s ease forwards; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
}
</style>
</head>
<body class="bg-gradient-to-br from-slate-900 via-blue-900 to-slate-800 min-h-screen text-gray-800">
<div class="max-w-5xl mx-auto px-4 py-10 pb-20 relative z-10">
<div class="mb-6 text-center">
<h1 class="text-[clamp(1.5rem,3vw,2.2rem)] font-bold text-white mb-1">工资条查询系统</h1>
<p class="text-blue-200/80 text-sm">Salary Inquiry System</p>
</div>
<!-- 员工查询面板 -->
<div id="employeePanel" class="block">
<div class="card max-w-md mx-auto">
<div class="card-header text-center">员工工资查询</div>
<div class="card-body">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">身份证后6位</label>
<input type="text" id="idLast6" maxlength="6" class="input-control" placeholder="请输入6位数字">
</div>
<div class="mb-5">
<label class="block text-sm font-medium text-gray-700 mb-1">查询月份</label>
<select id="monthSelect" class="input-control">
<option value="latest">最新月份</option>
</select>
</div>
<button id="queryBtn" class="btn btn-primary w-full mb-4">查询工资</button>
<div id="resultBox" class="hidden fade-in">
<!-- 工资标题居中 -->
<div class="text-center font-semibold text-lg mb-3" id="salaryTitle"></div>
<div id="salaryItems" class="grid grid-cols-1 gap-2"></div>
</div>
<div id="emptyTip" class="hidden text-center text-danger text-sm">未查询到工资信息</div>
</div>
</div>
</div>
<!-- 管理员界面 -->
<div id="adminPanel" class="hidden">
<div id="adminLogin" class="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
<div class="card w-full max-w-sm mx-4">
<div class="card-header text-center">管理员登录</div>
<div class="card-body">
<div class="mb-3"><label class="text-sm">账号</label><input type="text" id="adminName" class="input-control"></div>
<div class="mb-5"><label class="text-sm">密码</label><input type="password" id="adminPwd" class="input-control"></div>
<div class="flex gap-3">
<button id="loginBtn" class="btn btn-primary flex-1">登录</button>
<button id="backBtn" class="btn btn-outline flex-1">返回查询</button>
</div>
</div>
</div>
</div>
<div id="adminDashboard" class="card fade-in">
<div class="card-header flex justify-between items-center">
<span>管理员控制台</span>
<div class="flex gap-3">
<button id="backToQueryBtn" class="text-sm text-primary hover:text-primary/80">返回查询</button>
<button id="openChangeAdminBtn" class="text-sm text-primary hover:text-primary/80">修改账号</button>
<button id="logoutBtn" class="text-sm text-gray-500 hover:text-danger">退出登录</button>
</div>
</div>
<div class="card-body grid md:grid-cols-2 gap-6">
<div class="p-5 border rounded-lg bg-gray-50">
<h3 class="font-medium mb-2">工资表模板</h3>
<p class="text-sm text-gray-500 mb-3">下载标准Excel模板</p>
<button id="downloadTemplate" class="btn btn-primary">下载模板</button>
</div>
<div class="p-5 border rounded-lg bg-gray-50">
<h3 class="font-medium mb-2">工资数据导入</h3>
<div class="space-y-2 mb-3">
<div class="flex gap-2">
<input type="number" id="year" placeholder="年份" value="2025" class="input-control flex-1">
<input type="number" id="month" placeholder="月份" min="1" max="12" class="input-control flex-1">
</div>
<input type="file" id="fileUpload" accept=".xlsx" class="text-sm w-full">
</div>
<button id="importData" class="btn btn-primary">确认导入</button>
</div>
<div class="md:col-span-2 p-5 border rounded-lg bg-gray-50">
<h3 class="font-medium mb-3">已上传工资单列表</h3>
<div id="salaryList" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2 max-h-48 overflow-y-auto pr-2"></div>
</div>
<div class="p-5 border rounded-lg bg-gray-50">
<h3 class="font-medium mb-2">清空所有数据</h3>
<p class="text-sm text-gray-500 mb-3">不可恢复,请谨慎操作</p>
<button id="clearData" class="btn btn-danger w-full">清空所有数据</button>
</div>
</div>
<div id="importSuccess" class="hidden mx-6 mb-3 text-success text-center">✅ 数据导入成功</div>
<div id="importFail" class="hidden mx-6 mb-3 text-danger text-center">❌ 导入失败</div>
<div id="clearSuccess" class="hidden mx-6 mb-3 text-success text-center">✅ 已清空所有数据</div>
</div>
</div>
<!-- 修改账号弹窗 -->
<div id="changeAdminModal" class="hidden fixed inset-0 bg-black/40 flex items-center justify-center z-50">
<div class="card w-full max-w-sm mx-4">
<div class="card-header text-center">修改管理员账号密码</div>
<div class="card-body">
<div class="mb-3">
<label class="text-sm block mb-1">新管理员账号</label>
<input type="text" id="newUser" class="input-control">
</div>
<div class="mb-5">
<label class="text-sm block mb-1">新管理员密码</label>
<input type="password" id="newPwd" class="input-control">
</div>
<div class="flex gap-3">
<button id="saveAdminBtn" class="btn btn-primary flex-1">保存并重新登录</button>
<button id="closeAdminModalBtn" class="btn btn-outline flex-1">取消</button>
</div>
</div>
</div>
</div>
</div>
<!-- 固定在底部的管理员入口按钮 -->
<div class="fixed bottom-6 left-1/2 -translate-x-1/2 z-50">
<button id="tabAdmin" class="btn-sm border-blue-300/50 bg-blue-900/40 text-blue-100 hover:bg-blue-800/50">
管理员入口
</button>
</div>
<script>
const ADMIN_KEY = 'adminConfig';
const SALARY_KEY = 'salaryData';
function getAdmin() {
const def = { user: 'admin', pwd: '123456' };
try { const l = localStorage.getItem(ADMIN_KEY); return l ? JSON.parse(l) : def; } catch { return def; }
}
function saveAdmin(user, pwd) { localStorage.setItem(ADMIN_KEY, JSON.stringify({ user, pwd })); }
let ADMIN = getAdmin();
const tabAdmin = document.getElementById('tabAdmin');
const employeePanel = document.getElementById('employeePanel');
const adminPanel = document.getElementById('adminPanel');
const changeAdminModal = document.getElementById('changeAdminModal');
const monthSelect = document.getElementById('monthSelect');
const salaryList = document.getElementById('salaryList');
function loadMonthList() {
const store = JSON.parse(localStorage.getItem(SALARY_KEY)) || {};
const months = Object.keys(store).sort((a,b)=>new Date(b)-new Date(a));
monthSelect.innerHTML = '<option value="latest">最新月份</option>';
months.forEach(m=>{
const o=document.createElement('option');
o.value=m; o.textContent=m;
monthSelect.appendChild(o);
});
}
function loadSalaryList() {
const store = JSON.parse(localStorage.getItem(SALARY_KEY)) || {};
const months = Object.keys(store).sort((a,b)=>new Date(b)-new Date(a));
salaryList.innerHTML = '';
months.forEach(m=>{
const d = document.createElement('div');
d.className = 'flex justify-between items-center bg-white px-3 py-2 rounded border';
d.innerHTML = `<span class="text-sm">${m}</span><button data-month="${m}" class="delMonth text-xs text-danger">删除</button>`;
salaryList.appendChild(d);
});
document.querySelectorAll('.delMonth').forEach(el=>{
el.onclick = function(){
const m = this.dataset.month;
if(!confirm(`确定删除 ${m} 工资数据?`)) return;
const s = JSON.parse(localStorage.getItem(SALARY_KEY))||{};
delete s[m];
localStorage.setItem(SALARY_KEY,JSON.stringify(s));
loadMonthList(); loadSalaryList();
}
});
}
tabAdmin.onclick = () => {
employeePanel.classList.add('hidden');
adminPanel.classList.remove('hidden');
changeAdminModal.classList.add('hidden');
document.getElementById('adminName').value = ADMIN.user;
document.getElementById('adminPwd').value = '';
document.getElementById('adminLogin').classList.remove('hidden');
document.getElementById('adminDashboard').classList.add('hidden');
};
document.getElementById('backBtn').onclick = () => {
employeePanel.classList.remove('hidden');
adminPanel.classList.add('hidden');
};
document.getElementById('backToQueryBtn').onclick = () => {
employeePanel.classList.remove('hidden');
adminPanel.classList.add('hidden');
changeAdminModal.classList.add('hidden');
};
document.getElementById('openChangeAdminBtn').onclick = () => {
changeAdminModal.classList.remove('hidden');
document.getElementById('newUser').value = ADMIN.user;
document.getElementById('newPwd').value = '';
};
document.getElementById('closeAdminModalBtn').onclick = () => {
changeAdminModal.classList.add('hidden');
};
document.getElementById('loginBtn').onclick = () => {
const u = document.getElementById('adminName').value.trim();
const p = document.getElementById('adminPwd').value.trim();
if(u===ADMIN.user && p===ADMIN.pwd){
document.getElementById('adminLogin').classList.add('hidden');
document.getElementById('adminDashboard').classList.remove('hidden');
loadSalaryList();
}else{
alert('账号或密码错误');
}
};
document.getElementById('logoutBtn').onclick = () => {
document.getElementById('adminDashboard').classList.add('hidden');
document.getElementById('adminLogin').classList.remove('hidden');
};
document.getElementById('saveAdminBtn').onclick = () => {
const u = document.getElementById('newUser').value.trim();
const p = document.getElementById('newPwd').value.trim();
if(!u||!p){ alert('请输入完整信息!'); return; }
saveAdmin(u,p);
ADMIN = getAdmin();
alert('修改成功,请使用新账号密码登录');
changeAdminModal.classList.add('hidden');
tabAdmin.click();
};
document.getElementById('downloadTemplate').onclick = () => {
const h = ['姓名','身份证号码','基本工资','加班工资','绩效奖','养老保险','医疗保险','住房公积金','失业金','其他补助','所得税','其他扣款','其他补助备注','其他扣款备注'];
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.aoa_to_sheet([h]);
XLSX.utils.book_append_sheet(wb,ws,'模板');
XLSX.writeFile(wb,'工资表模板.xlsx');
};
document.getElementById('importData').onclick = () => {
const f = document.getElementById('fileUpload').files[0];
const y = document.getElementById('year').value;
const m = document.getElementById('month').value;
if(!f||!y||!m){ alert('请完善信息'); return; }
const r = new FileReader();
r.onload = e=>{
try{
const d = new Uint8Array(e.target.result);
const j = XLSX.read(d,{type:'array'});
const s = XLSX.utils.sheet_to_json(j.Sheets[j.SheetNames[0]]);
if(!s.length) throw new Error();
const store = JSON.parse(localStorage.getItem(SALARY_KEY))||{};
store[`${y}年${m}月`] = s.map(i=>({...i,idEncrypt:i['身份证号码']?.slice(-6)||''}));
localStorage.setItem(SALARY_KEY,JSON.stringify(store));
document.getElementById('importSuccess').classList.remove('hidden');
setTimeout(()=>document.getElementById('importSuccess').classList.add('hidden'),2000);
loadMonthList(); loadSalaryList();
}catch(e){
document.getElementById('importFail').classList.remove('hidden');
setTimeout(()=>document.getElementById('importFail').classList.add('hidden'),2000);
}
};
r.readAsArrayBuffer(f);
};
document.getElementById('clearData').onclick = () => {
if(!confirm('确定清空所有工资数据?不可恢复!')) return;
localStorage.removeItem(SALARY_KEY);
loadMonthList(); loadSalaryList();
document.getElementById('clearSuccess').classList.remove('hidden');
setTimeout(()=>document.getElementById('clearSuccess').classList.add('hidden'),2000);
};
document.getElementById('queryBtn').onclick = () => {
const v = document.getElementById('idLast6').value.trim();
const t = monthSelect.value;
if(v.length!==6||isNaN(Number(v))){ alert('请输入6位数字'); return; }
const store = JSON.parse(localStorage.getItem(SALARY_KEY))||{};
const ms = Object.keys(store).sort((a,b)=>new Date(b)-new Date(a));
if(!ms.length){
document.getElementById('resultBox').classList.add('hidden');
document.getElementById('emptyTip').classList.remove('hidden');
return;
}
const cur = t==='latest'?ms[0]:t;
const user = store[cur]?.find(i=>i.idEncrypt===v);
if(user){
document.getElementById('emptyTip').classList.add('hidden');
document.getElementById('resultBox').classList.remove('hidden');
document.getElementById('salaryTitle').textContent = cur+' 工资明细';
const items = document.getElementById('salaryItems');
items.innerHTML='';
const fields = [
{l:'姓名',k:'姓名'},{l:'基本工资',k:'基本工资'},{l:'加班工资',k:'加班工资'},{l:'绩效奖',k:'绩效奖'},
{l:'养老保险',k:'养老保险'},{l:'医疗保险',k:'医疗保险'},{l:'住房公积金',k:'住房公积金'},{l:'失业金',k:'失业金'},
{l:'其他补助',k:'其他补助'},{l:'所得税',k:'所得税'},{l:'其他扣款',k:'其他扣款'},
{l:'补助备注',k:'其他补助备注'},{l:'扣款备注',k:'其他扣款备注'}
];
fields.forEach(f=>{
const d=document.createElement('div');
// 工资明细项全部居中
d.className='flex justify-between p-2 bg-gray-50 rounded text-center';
d.innerHTML=`<span class="text-gray-600 w-1/2">${f.l}</span><span class="font-medium w-1/2">${user[f.k]||'-'}</span>`;
items.appendChild(d);
});
}else{
document.getElementById('resultBox').classList.add('hidden');
document.getElementById('emptyTip').classList.remove('hidden');
}
};
window.onload = () => { loadMonthList(); };
</script>
</body>
</html>