471 lines
19 KiB
HTML
471 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>字典管理 - OA系统</title>
|
||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||
<!-- 统一布局样式 -->
|
||
<link rel="stylesheet" href="unified-layout.css">
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body {
|
||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
|
||
background: #f0f2f5;
|
||
overflow: hidden;
|
||
}
|
||
.layout-container {
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.main-container {
|
||
flex: 1;
|
||
display: flex;
|
||
overflow: hidden;
|
||
}
|
||
.app-main {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: #f0f2f5;
|
||
}
|
||
.sidebar {
|
||
width: 210px;
|
||
background: #fff;
|
||
border-right: 1px solid #e6e6e6;
|
||
overflow-y: auto;
|
||
}
|
||
.menu-item { padding: 12px 24px; cursor: pointer; transition: background 0.3s; color: #303133; }
|
||
.menu-item:hover {
|
||
background: #ecf5ff;
|
||
color: #409EFF;
|
||
}
|
||
.menu-item.active { background: #409EFF; color: #fff; }
|
||
.content {
|
||
flex: 1;
|
||
padding: 20px;
|
||
overflow-y: auto;
|
||
background: #fff;
|
||
margin: 10px;
|
||
border-radius: 4px;
|
||
}
|
||
.page-header {
|
||
margin-bottom: 24px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.page-title { font-size: 20px; font-weight: 500; color: #303133; }
|
||
.search-form {
|
||
background: #fafafa;
|
||
padding: 16px;
|
||
border-radius: 4px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.form-row {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.form-item {
|
||
flex: 1;
|
||
}
|
||
.form-input, .form-select {
|
||
width: 100%;
|
||
padding: 6px 12px;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
}
|
||
.btn {
|
||
padding: 6px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
.btn-primary {
|
||
background: #409EFF;
|
||
color: white;
|
||
}
|
||
.btn-primary:hover {
|
||
background: #66b1ff;
|
||
}
|
||
.btn-default {
|
||
background: white;
|
||
color: #606266;
|
||
border: 1px solid #dcdfe6;
|
||
}
|
||
.btn-default:hover {
|
||
color: #409EFF;
|
||
border-color: #409EFF;
|
||
}
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
th, td {
|
||
padding: 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
th {
|
||
background: #fafafa;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
}
|
||
.action-link {
|
||
color: #409EFF;
|
||
cursor: pointer;
|
||
margin-right: 12px;
|
||
}
|
||
.dict-content {
|
||
display: flex;
|
||
gap: 20px;
|
||
}
|
||
.dict-type-list {
|
||
width: 300px;
|
||
background: #fafafa;
|
||
border-radius: 4px;
|
||
padding: 16px;
|
||
}
|
||
.dict-type-item {
|
||
padding: 12px;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
margin-bottom: 8px;
|
||
cursor: pointer;
|
||
border: 1px solid #e6e6e6;
|
||
transition: all 0.3s;
|
||
}
|
||
.dict-type-item:hover {
|
||
border-color: #409EFF;
|
||
}
|
||
.dict-type-item.active {
|
||
background: #ecf5ff;
|
||
border-color: #409EFF;
|
||
}
|
||
.dict-type-name {
|
||
font-weight: 500;
|
||
color: #303133;
|
||
margin-bottom: 4px;
|
||
}
|
||
.dict-type-code {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
.dict-data-table {
|
||
flex: 1;
|
||
}
|
||
</style>
|
||
<!-- Vue.js -->
|
||
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
|
||
<!-- Element UI JS -->
|
||
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<div class="layout-container">
|
||
<div class="main-container">
|
||
<div class="sidebar">
|
||
<div class="menu-item" onclick="window.location.href='settings-org.html'">组织架构</div>
|
||
<div class="menu-item" onclick="window.location.href='settings-user.html'">账号管理</div>
|
||
<div class="menu-item" onclick="window.location.href='settings-role.html'">权限配置</div>
|
||
<div class="menu-item" onclick="window.location.href='settings-workflow.html'">流程配置</div>
|
||
<div class="menu-item" onclick="window.location.href='settings-notice.html'">系统公告</div>
|
||
<div class="menu-item active" onclick="window.location.href='settings-dict.html'">字典管理</div>
|
||
<div class="menu-item" onclick="window.location.href='settings-param.html'">参数配置</div>
|
||
<div class="menu-item" onclick="window.location.href='settings-log.html'">操作日志</div>
|
||
</div>
|
||
|
||
<div class="app-main">
|
||
<div class="content">
|
||
<div class="page-header">
|
||
<div class="page-title">字典管理</div>
|
||
<button class="btn btn-primary" onclick="addDictType()">+ 新增字典类型</button>
|
||
</div>
|
||
|
||
<div class="dict-content">
|
||
<div class="dict-type-list">
|
||
<div class="dict-type-item active" onclick="selectDictType('service_type')">
|
||
<div class="dict-type-name">服务内容</div>
|
||
<div class="dict-type-code">service_type</div>
|
||
</div>
|
||
<div class="dict-type-item" onclick="selectDictType('project_status')">
|
||
<div class="dict-type-name">项目状态</div>
|
||
<div class="dict-type-code">project_status</div>
|
||
</div>
|
||
<div class="dict-type-item" onclick="selectDictType('output_type')">
|
||
<div class="dict-type-name">成果类型</div>
|
||
<div class="dict-type-code">output_type</div>
|
||
</div>
|
||
<div class="dict-type-item" onclick="selectDictType('approval_status')">
|
||
<div class="dict-type-name">审核状态</div>
|
||
<div class="dict-type-code">approval_status</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="dict-data-table">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
||
<div style="font-weight: 500; color: #303133;">字典数据列表</div>
|
||
<button class="btn btn-primary" onclick="addDictData()">+ 新增</button>
|
||
</div>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>字典标签</th>
|
||
<th>字典值</th>
|
||
<th>排序</th>
|
||
<th>状态</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<!-- 字典数据将动态渲染 -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="common.js"></script>
|
||
<script>
|
||
// 字典数据
|
||
let dictData = {
|
||
'service_type': [
|
||
{ id: 1, label: '结算审计', value: '1', sort: 1, status: '启用' },
|
||
{ id: 2, label: '工程咨询', value: '2', sort: 2, status: '启用' },
|
||
{ id: 3, label: '跟踪审计', value: '3', sort: 3, status: '启用' }
|
||
],
|
||
'project_status': [
|
||
{ id: 4, label: '已立项', value: '1', sort: 1, status: '启用' },
|
||
{ id: 5, label: '投标中', value: '2', sort: 2, status: '启用' },
|
||
{ id: 6, label: '已签合同', value: '3', sort: 3, status: '启用' },
|
||
{ id: 7, label: '已启动', value: '4', sort: 4, status: '启用' }
|
||
],
|
||
'output_type': [
|
||
{ id: 8, label: '初稿', value: '1', sort: 1, status: '启用' },
|
||
{ id: 9, label: '对账', value: '2', sort: 2, status: '启用' },
|
||
{ id: 10, label: '最终成果', value: '3', sort: 3, status: '启用' }
|
||
],
|
||
'approval_status': [
|
||
{ id: 11, label: '待审核', value: '1', sort: 1, status: '启用' },
|
||
{ id: 12, label: '已通过', value: '2', sort: 2, status: '启用' },
|
||
{ id: 13, label: '已驳回', value: '3', sort: 3, status: '启用' }
|
||
]
|
||
};
|
||
|
||
let currentDictType = 'service_type';
|
||
|
||
function selectDictType(typeCode) {
|
||
document.querySelectorAll('.dict-type-item').forEach(item => {
|
||
item.classList.remove('active');
|
||
});
|
||
event.currentTarget.classList.add('active');
|
||
currentDictType = typeCode;
|
||
renderDictData();
|
||
}
|
||
|
||
function renderDictData() {
|
||
const data = dictData[currentDictType] || [];
|
||
const tbody = document.querySelector('.dict-data-table tbody');
|
||
if (!tbody) return;
|
||
|
||
tbody.innerHTML = data.map(item => `
|
||
<tr>
|
||
<td>${item.label}</td>
|
||
<td>${item.value}</td>
|
||
<td>${item.sort}</td>
|
||
<td>${item.status}</td>
|
||
<td>
|
||
<span class="action-link" onclick="editDictData(${item.id})">编辑</span>
|
||
<span class="action-link" onclick="deleteDictData(${item.id})" style="color: #f56c6c;">删除</span>
|
||
</td>
|
||
</tr>
|
||
`).join('');
|
||
}
|
||
|
||
function addDictType() {
|
||
const content = `
|
||
<form style="padding: 10px 0;">
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">字典类型名称 <span style="color: #f56c6c;">*</span></label>
|
||
<input type="text" id="dict-type-name" class="form-input" placeholder="如:服务内容" required>
|
||
</div>
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">字典类型编码 <span style="color: #f56c6c;">*</span></label>
|
||
<input type="text" id="dict-type-code" class="form-input" placeholder="如:service_type" required>
|
||
</div>
|
||
</form>
|
||
`;
|
||
|
||
CommonUtils.createModal('新增字典类型', content, () => {
|
||
const name = document.getElementById('dict-type-name').value.trim();
|
||
const code = document.getElementById('dict-type-code').value.trim();
|
||
|
||
if (!name || !code) {
|
||
CommonUtils.showMessage('请填写必填项', 'error');
|
||
return false;
|
||
}
|
||
|
||
// 添加到字典类型列表(实际应该添加到DOM)
|
||
CommonUtils.showMessage('字典类型添加成功');
|
||
return true;
|
||
});
|
||
}
|
||
|
||
function addDictData() {
|
||
const content = `
|
||
<form style="padding: 10px 0;">
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">字典标签 <span style="color: #f56c6c;">*</span></label>
|
||
<input type="text" id="dict-label" class="form-input" placeholder="如:结算审计" required>
|
||
</div>
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">字典值 <span style="color: #f56c6c;">*</span></label>
|
||
<input type="text" id="dict-value" class="form-input" placeholder="如:1" required>
|
||
</div>
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">排序</label>
|
||
<input type="number" id="dict-sort" class="form-input" placeholder="数字,越小越靠前" value="1">
|
||
</div>
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">状态</label>
|
||
<select id="dict-status" class="form-select">
|
||
<option>启用</option>
|
||
<option>禁用</option>
|
||
</select>
|
||
</div>
|
||
</form>
|
||
`;
|
||
|
||
CommonUtils.createModal('新增字典数据', content, () => {
|
||
const label = document.getElementById('dict-label').value.trim();
|
||
const value = document.getElementById('dict-value').value.trim();
|
||
const sort = parseInt(document.getElementById('dict-sort').value) || 1;
|
||
const status = document.getElementById('dict-status').value;
|
||
|
||
if (!label || !value) {
|
||
CommonUtils.showMessage('请填写必填项', 'error');
|
||
return false;
|
||
}
|
||
|
||
if (!dictData[currentDictType]) {
|
||
dictData[currentDictType] = [];
|
||
}
|
||
|
||
const maxId = dictData[currentDictType].length > 0
|
||
? Math.max(...dictData[currentDictType].map(d => d.id))
|
||
: 0;
|
||
|
||
dictData[currentDictType].push({
|
||
id: maxId + 1,
|
||
label: label,
|
||
value: value,
|
||
sort: sort,
|
||
status: status
|
||
});
|
||
|
||
// 按排序排序
|
||
dictData[currentDictType].sort((a, b) => a.sort - b.sort);
|
||
|
||
renderDictData();
|
||
localStorage.setItem('dictData', JSON.stringify(dictData));
|
||
CommonUtils.showMessage('字典数据添加成功');
|
||
return true;
|
||
});
|
||
}
|
||
|
||
function editDictData(id) {
|
||
const data = dictData[currentDictType] || [];
|
||
const item = data.find(d => d.id === id);
|
||
if (!item) return;
|
||
|
||
const content = `
|
||
<form style="padding: 10px 0;">
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">字典标签 <span style="color: #f56c6c;">*</span></label>
|
||
<input type="text" id="dict-label" class="form-input" value="${item.label}" required>
|
||
</div>
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">字典值 <span style="color: #f56c6c;">*</span></label>
|
||
<input type="text" id="dict-value" class="form-input" value="${item.value}" required>
|
||
</div>
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">排序</label>
|
||
<input type="number" id="dict-sort" class="form-input" value="${item.sort}">
|
||
</div>
|
||
<div style="margin-bottom: 16px;">
|
||
<label style="display: block; margin-bottom: 8px; color: #606266;">状态</label>
|
||
<select id="dict-status" class="form-select">
|
||
<option ${item.status === '启用' ? 'selected' : ''}>启用</option>
|
||
<option ${item.status === '禁用' ? 'selected' : ''}>禁用</option>
|
||
</select>
|
||
</div>
|
||
</form>
|
||
`;
|
||
|
||
CommonUtils.createModal('编辑字典数据', content, () => {
|
||
const label = document.getElementById('dict-label').value.trim();
|
||
const value = document.getElementById('dict-value').value.trim();
|
||
const sort = parseInt(document.getElementById('dict-sort').value) || 1;
|
||
const status = document.getElementById('dict-status').value;
|
||
|
||
if (!label || !value) {
|
||
CommonUtils.showMessage('请填写必填项', 'error');
|
||
return false;
|
||
}
|
||
|
||
Object.assign(item, { label, value, sort, status });
|
||
dictData[currentDictType].sort((a, b) => a.sort - b.sort);
|
||
renderDictData();
|
||
localStorage.setItem('dictData', JSON.stringify(dictData));
|
||
CommonUtils.showMessage('字典数据更新成功');
|
||
return true;
|
||
});
|
||
}
|
||
|
||
function deleteDictData(id) {
|
||
CommonUtils.createModal('确认删除', '确定要删除该字典数据吗?删除后无法恢复。', () => {
|
||
const data = dictData[currentDictType] || [];
|
||
const index = data.findIndex(d => d.id === id);
|
||
if (index > -1) {
|
||
data.splice(index, 1);
|
||
renderDictData();
|
||
localStorage.setItem('dictData', JSON.stringify(dictData));
|
||
CommonUtils.showMessage('字典数据已删除');
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
|
||
// 初始化
|
||
window.addEventListener('DOMContentLoaded', () => {
|
||
const saved = localStorage.getItem('dictData');
|
||
if (saved) {
|
||
try {
|
||
dictData = JSON.parse(saved);
|
||
} catch (e) {
|
||
console.error('加载字典数据失败', e);
|
||
}
|
||
}
|
||
renderDictData();
|
||
});
|
||
</script>
|
||
<script src="unified-layout.js"></script>
|
||
<script>
|
||
initUnifiedLayout('settings');
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|