2025-12-20 16:53:57 +08:00
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html lang="zh-CN">
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
<title>许可管理与筛选 - LawRisk</title>
|
|
|
|
|
|
<style>
|
|
|
|
|
|
* {
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
body {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
font-family: "Microsoft YaHei", "PingFang SC", -apple-system, BlinkMacSystemFont, sans-serif;
|
|
|
|
|
|
background: linear-gradient(135deg, #f7fafc 0%, #e2e8f0 100%);
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
color: #1f2937;
|
|
|
|
|
|
}
|
|
|
|
|
|
.page {
|
|
|
|
|
|
max-width: 1600px;
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
padding: 32px 20px 60px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.header {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
|
padding: 28px;
|
|
|
|
|
|
box-shadow: 0 20px 60px rgba(79, 70, 229, 0.12);
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
.title {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.title h1 {
|
|
|
|
|
|
font-size: 30px;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
color: #111827;
|
|
|
|
|
|
}
|
|
|
|
|
|
.title p {
|
|
|
|
|
|
margin: 6px 0 0;
|
|
|
|
|
|
color: #4b5563;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-section {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
|
padding: 28px;
|
|
|
|
|
|
box-shadow: 0 20px 60px rgba(79, 70, 229, 0.12);
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
|
|
|
|
gap: 20px;
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-group {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-label {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-select, .filter-input {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
border: 1px solid #d1d5db;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-select:focus, .filter-input:focus {
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
border-color: #6366f1;
|
|
|
|
|
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-select:disabled {
|
|
|
|
|
|
background: #f3f4f6;
|
|
|
|
|
|
color: #9ca3af;
|
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.btn {
|
|
|
|
|
|
padding: 12px 24px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.btn-primary {
|
|
|
|
|
|
background: #6366f1;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.btn-primary:hover {
|
|
|
|
|
|
background: #4f46e5;
|
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
|
box-shadow: 0 10px 20px rgba(99, 102, 241, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.btn-secondary {
|
|
|
|
|
|
background: #e5e7eb;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
}
|
|
|
|
|
|
.btn-secondary:hover {
|
|
|
|
|
|
background: #d1d5db;
|
|
|
|
|
|
}
|
|
|
|
|
|
.btn:disabled {
|
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
transform: none !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.results-section {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
|
padding: 28px;
|
|
|
|
|
|
box-shadow: 0 20px 60px rgba(79, 70, 229, 0.12);
|
|
|
|
|
|
}
|
|
|
|
|
|
.results-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
padding-bottom: 16px;
|
|
|
|
|
|
border-bottom: 2px solid #f3f4f6;
|
|
|
|
|
|
}
|
|
|
|
|
|
.results-count {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
color: #4b5563;
|
|
|
|
|
|
}
|
|
|
|
|
|
.results-count strong {
|
|
|
|
|
|
color: #6366f1;
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.loading {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 60px 20px;
|
|
|
|
|
|
color: #6b7280;
|
|
|
|
|
|
}
|
|
|
|
|
|
.loading-spinner {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
border: 4px solid #f3f4f6;
|
|
|
|
|
|
border-top-color: #6366f1;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
animation: spin 0.8s linear infinite;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
|
to { transform: rotate(360deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.error-message {
|
|
|
|
|
|
background: #fee2e2;
|
|
|
|
|
|
border: 1px solid #fecaca;
|
|
|
|
|
|
color: #991b1b;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.permits-table {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
|
}
|
|
|
|
|
|
.permits-table th {
|
|
|
|
|
|
background: #f9fafb;
|
|
|
|
|
|
padding: 14px 16px;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
border-bottom: 2px solid #e5e7eb;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.permits-table td {
|
|
|
|
|
|
padding: 14px 16px;
|
|
|
|
|
|
border-bottom: 1px solid #f3f4f6;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #4b5563;
|
|
|
|
|
|
}
|
|
|
|
|
|
.permits-table tr:hover {
|
|
|
|
|
|
background: #f9fafb;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-state {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 80px 20px;
|
|
|
|
|
|
color: #9ca3af;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-icon {
|
|
|
|
|
|
font-size: 48px;
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
|
|
<div class="page">
|
|
|
|
|
|
<!-- 页面头部 -->
|
|
|
|
|
|
<div class="header">
|
|
|
|
|
|
<div class="title">
|
|
|
|
|
|
<h1>许可管理与筛选</h1>
|
|
|
|
|
|
<p>使用过滤器快速定位所需许可文件</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 筛选器区域 -->
|
|
|
|
|
|
<div class="filter-section">
|
|
|
|
|
|
<div class="filter-grid">
|
|
|
|
|
|
<!-- 市级单位筛选 -->
|
|
|
|
|
|
<div class="filter-group">
|
|
|
|
|
|
<label class="filter-label">市级单位</label>
|
|
|
|
|
|
<select id="municipalDept" class="filter-select">
|
|
|
|
|
|
<option value="">请选择市级单位</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 区级单位筛选 -->
|
|
|
|
|
|
<div class="filter-group">
|
|
|
|
|
|
<label class="filter-label">区级单位</label>
|
|
|
|
|
|
<select id="districtDept" class="filter-select" disabled>
|
|
|
|
|
|
<option value="">请先选择市级单位</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 行政区域筛选 -->
|
|
|
|
|
|
<div class="filter-group">
|
|
|
|
|
|
<label class="filter-label">行政区域</label>
|
|
|
|
|
|
<select id="regionFilter" class="filter-select">
|
|
|
|
|
|
<option value="">全部区域</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 搜索关键词 -->
|
|
|
|
|
|
<div class="filter-group">
|
|
|
|
|
|
<label class="filter-label">搜索关键词</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
id="searchText"
|
|
|
|
|
|
class="filter-input"
|
|
|
|
|
|
placeholder="输入许可名称关键词..."
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 操作按钮 -->
|
|
|
|
|
|
<div class="filter-actions">
|
|
|
|
|
|
<button id="applyBtn" class="btn btn-primary">应用筛选</button>
|
|
|
|
|
|
<button id="resetBtn" class="btn btn-secondary">重置筛选</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 结果展示区域 -->
|
|
|
|
|
|
<div class="results-section">
|
|
|
|
|
|
<div class="results-header">
|
|
|
|
|
|
<div class="results-count">
|
|
|
|
|
|
共找到 <strong id="resultCount">0</strong> 个许可文件
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div id="loading" class="loading" style="display: none;">
|
|
|
|
|
|
<div class="loading-spinner"></div>
|
|
|
|
|
|
<div>正在加载许可数据...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div id="errorMessage" class="error-message" style="display: none;"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<div id="resultsTable">
|
|
|
|
|
|
<div class="empty-state">
|
|
|
|
|
|
<div class="empty-icon">📋</div>
|
|
|
|
|
|
<div>请选择筛选条件并点击"应用筛选"</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
// 全局变量
|
|
|
|
|
|
let municipalTree = [];
|
|
|
|
|
|
let regions = [];
|
|
|
|
|
|
|
|
|
|
|
|
// DOM元素
|
|
|
|
|
|
const municipalSelect = document.getElementById('municipalDept');
|
|
|
|
|
|
const districtSelect = document.getElementById('districtDept');
|
|
|
|
|
|
const regionSelect = document.getElementById('regionFilter');
|
|
|
|
|
|
const searchInput = document.getElementById('searchText');
|
|
|
|
|
|
const applyBtn = document.getElementById('applyBtn');
|
|
|
|
|
|
const resetBtn = document.getElementById('resetBtn');
|
|
|
|
|
|
const resultsTable = document.getElementById('resultsTable');
|
|
|
|
|
|
const resultCount = document.getElementById('resultCount');
|
|
|
|
|
|
const loading = document.getElementById('loading');
|
|
|
|
|
|
const errorMessage = document.getElementById('errorMessage');
|
|
|
|
|
|
|
|
|
|
|
|
// 页面初始化
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
loadInitialData();
|
|
|
|
|
|
bindEvents();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定事件
|
|
|
|
|
|
function bindEvents() {
|
|
|
|
|
|
// 市级单位选择变更 - 联动加载区级单位
|
|
|
|
|
|
municipalSelect.addEventListener('change', handleMunicipalChange);
|
|
|
|
|
|
|
|
|
|
|
|
// 应用筛选
|
|
|
|
|
|
applyBtn.addEventListener('click', applyFilter);
|
|
|
|
|
|
|
|
|
|
|
|
// 重置筛选
|
|
|
|
|
|
resetBtn.addEventListener('click', resetFilter);
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索框回车筛选
|
|
|
|
|
|
searchInput.addEventListener('keypress', function(e) {
|
|
|
|
|
|
if (e.key === 'Enter') {
|
|
|
|
|
|
applyFilter();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载初始数据
|
|
|
|
|
|
async function loadInitialData() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await Promise.all([
|
|
|
|
|
|
loadMunicipalDepartments(),
|
|
|
|
|
|
loadRegions()
|
|
|
|
|
|
]);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
showError('加载初始数据失败: ' + error.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载市级单位树
|
|
|
|
|
|
async function loadMunicipalDepartments() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch('/fs-ai-asistant/api/workflow/lawrisk/admin/service-departments/tree');
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!data.success) {
|
|
|
|
|
|
throw new Error(data.message || '加载市级单位失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
municipalTree = data.data.tree || [];
|
|
|
|
|
|
renderMunicipalOptions(municipalTree);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载市级单位失败:', error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染市级单位选项
|
|
|
|
|
|
function renderMunicipalOptions(tree) {
|
|
|
|
|
|
municipalSelect.innerHTML = '<option value="">请选择市级单位</option>';
|
|
|
|
|
|
|
|
|
|
|
|
tree.forEach(dept => {
|
|
|
|
|
|
const option = document.createElement('option');
|
|
|
|
|
|
option.value = dept.id;
|
|
|
|
|
|
option.textContent = `${dept.name} (${dept.code})`;
|
|
|
|
|
|
municipalSelect.appendChild(option);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载行政区域
|
|
|
|
|
|
async function loadRegions() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch('/fs-ai-asistant/api/workflow/lawrisk/v2/regions');
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!data.success) {
|
|
|
|
|
|
throw new Error(data.message || '加载区域失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
regions = data.data.regions || [];
|
|
|
|
|
|
renderRegionOptions(regions);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载区域失败:', error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染区域选项
|
|
|
|
|
|
function renderRegionOptions(regionList) {
|
|
|
|
|
|
regionSelect.innerHTML = '<option value="">全部区域</option>';
|
|
|
|
|
|
|
|
|
|
|
|
regionList.forEach(region => {
|
|
|
|
|
|
const option = document.createElement('option');
|
|
|
|
|
|
option.value = region.id || region;
|
|
|
|
|
|
option.textContent = region.name || region;
|
|
|
|
|
|
regionSelect.appendChild(option);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理市级单位选择变更
|
|
|
|
|
|
async function handleMunicipalChange() {
|
|
|
|
|
|
const municipalId = municipalSelect.value;
|
|
|
|
|
|
|
|
|
|
|
|
// 重置区级单位
|
|
|
|
|
|
districtSelect.innerHTML = '<option value="">请选择区级单位</option>';
|
|
|
|
|
|
districtSelect.disabled = !municipalId;
|
|
|
|
|
|
|
|
|
|
|
|
if (!municipalId) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载区级单位
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch(
|
|
|
|
|
|
`/fs-ai-asistant/api/workflow/lawrisk/admin/departments/children?parent_id=${municipalId}`
|
|
|
|
|
|
);
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!data.success) {
|
|
|
|
|
|
throw new Error(data.message || '加载区级单位失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const children = data.data.units || [];
|
|
|
|
|
|
|
|
|
|
|
|
districtSelect.innerHTML = '<option value="">全部区级单位</option>';
|
|
|
|
|
|
children.forEach(dept => {
|
|
|
|
|
|
const option = document.createElement('option');
|
|
|
|
|
|
option.value = dept.id;
|
|
|
|
|
|
option.textContent = `${dept.name} (${dept.code})`;
|
|
|
|
|
|
districtSelect.appendChild(option);
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载区级单位失败:', error);
|
|
|
|
|
|
showError('加载区级单位失败: ' + error.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 应用筛选
|
|
|
|
|
|
async function applyFilter() {
|
|
|
|
|
|
const filters = {
|
|
|
|
|
|
municipal_dept_id: municipalSelect.value || null,
|
|
|
|
|
|
district_dept_id: districtSelect.value || null,
|
|
|
|
|
|
region: regionSelect.value || null,
|
|
|
|
|
|
search_text: searchInput.value.trim() || null
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 显示加载状态
|
|
|
|
|
|
showLoading(true);
|
|
|
|
|
|
hideError();
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch('/fs-ai-asistant/api/workflow/lawrisk/admin/permits/filter', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify(filters)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!data.success) {
|
|
|
|
|
|
throw new Error(data.message || '筛选失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
renderResults(data.data.permits || []);
|
|
|
|
|
|
resultCount.textContent = data.data.pagination?.total || 0;
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('筛选失败:', error);
|
|
|
|
|
|
showError('筛选失败: ' + error.message);
|
|
|
|
|
|
renderResults([]);
|
|
|
|
|
|
resultCount.textContent = 0;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
showLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染筛选结果
|
|
|
|
|
|
function renderResults(permits) {
|
|
|
|
|
|
if (!permits || permits.length === 0) {
|
|
|
|
|
|
resultsTable.innerHTML = `
|
|
|
|
|
|
<div class="empty-state">
|
|
|
|
|
|
<div class="empty-icon">📂</div>
|
|
|
|
|
|
<div>未找到符合条件的许可文件</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let html = `
|
|
|
|
|
|
<table class="permits-table">
|
|
|
|
|
|
<thead>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th>许可名称</th>
|
|
|
|
|
|
<th>所属单位</th>
|
|
|
|
|
|
<th>行政区域</th>
|
|
|
|
|
|
<th>上传时间</th>
|
|
|
|
|
|
<th>操作</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
permits.forEach(permit => {
|
|
|
|
|
|
html += `
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td>${escapeHtml(permit.name || '未知许可')}</td>
|
|
|
|
|
|
<td>${escapeHtml(permit.department_name || '未知单位')}</td>
|
|
|
|
|
|
<td>${escapeHtml(permit.region_name || '未知区域')}</td>
|
|
|
|
|
|
<td>${formatDate(permit.created_at)}</td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<button class="btn btn-primary" style="padding: 6px 12px; font-size: 13px;"
|
|
|
|
|
|
onclick="viewPermit('${permit.id}')">
|
|
|
|
|
|
查看详情
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
`;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
html += `
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
resultsTable.innerHTML = html;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置筛选
|
|
|
|
|
|
function resetFilter() {
|
|
|
|
|
|
municipalSelect.value = '';
|
|
|
|
|
|
districtSelect.value = '';
|
|
|
|
|
|
districtSelect.disabled = true;
|
|
|
|
|
|
districtSelect.innerHTML = '<option value="">请先选择市级单位</option>';
|
|
|
|
|
|
regionSelect.value = '';
|
|
|
|
|
|
searchInput.value = '';
|
|
|
|
|
|
|
|
|
|
|
|
renderResults([]);
|
|
|
|
|
|
resultCount.textContent = '0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示/隐藏加载状态
|
|
|
|
|
|
function showLoading(show) {
|
|
|
|
|
|
loading.style.display = show ? 'block' : 'none';
|
|
|
|
|
|
applyBtn.disabled = show;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示错误信息
|
|
|
|
|
|
function showError(message) {
|
|
|
|
|
|
errorMessage.textContent = message;
|
|
|
|
|
|
errorMessage.style.display = 'block';
|
|
|
|
|
|
setTimeout(() => hideError(), 5000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏错误信息
|
|
|
|
|
|
function hideError() {
|
|
|
|
|
|
errorMessage.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HTML转义
|
|
|
|
|
|
function escapeHtml(text) {
|
|
|
|
|
|
if (!text) return '';
|
|
|
|
|
|
const div = document.createElement('div');
|
|
|
|
|
|
div.textContent = text;
|
|
|
|
|
|
return div.innerHTML;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 日期格式化
|
|
|
|
|
|
function formatDate(dateString) {
|
|
|
|
|
|
if (!dateString) return '-';
|
|
|
|
|
|
const date = new Date(dateString);
|
|
|
|
|
|
return date.toLocaleDateString('zh-CN', {
|
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
|
month: '2-digit',
|
|
|
|
|
|
day: '2-digit',
|
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
|
minute: '2-digit'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查看许可详情
|
|
|
|
|
|
function viewPermit(permitId) {
|
|
|
|
|
|
// TODO: 实现许可详情查看功能
|
|
|
|
|
|
alert('查看许可详情功能待实现,许可ID: ' + permitId);
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|