587 lines
19 KiB
HTML
587 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>许可管理与筛选 - 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>
|