638 lines
20 KiB
HTML
638 lines
20 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;
|
||
}
|
||
.pagination {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
}
|
||
.pagination-btn {
|
||
padding: 8px 16px;
|
||
border: 1px solid #d1d5db;
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: all 0.2s;
|
||
}
|
||
.pagination-btn:hover:not(:disabled) {
|
||
border-color: #6366f1;
|
||
color: #6366f1;
|
||
}
|
||
.pagination-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
.pagination-btn.active {
|
||
background: #6366f1;
|
||
color: #fff;
|
||
border-color: #6366f1;
|
||
}
|
||
.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;
|
||
}
|
||
.permit-name {
|
||
font-weight: 600;
|
||
color: #111827;
|
||
margin-bottom: 4px;
|
||
}
|
||
.permit-themes {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
margin-top: 6px;
|
||
}
|
||
.theme-tag {
|
||
display: inline-block;
|
||
padding: 2px 8px;
|
||
background: #eef2ff;
|
||
color: #4c1d95;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
}
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 80px 20px;
|
||
color: #9ca3af;
|
||
}
|
||
.empty-icon {
|
||
font-size: 48px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
.btn-small {
|
||
padding: 6px 12px;
|
||
font-size: 13px;
|
||
}
|
||
.btn-outline {
|
||
background: #fff;
|
||
border: 1px solid #6366f1;
|
||
color: #6366f1;
|
||
}
|
||
.btn-outline:hover {
|
||
background: #6366f1;
|
||
color: #fff;
|
||
}
|
||
</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="regionFilter" class="filter-select">
|
||
<option value="">全部区域</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 主题筛选 -->
|
||
<div class="filter-group">
|
||
<label class="filter-label">主题</label>
|
||
<select id="themeFilter" class="filter-select">
|
||
<option value="">全部主题</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 关联部门筛选 -->
|
||
<div class="filter-group">
|
||
<label class="filter-label">关联部门</label>
|
||
<select id="departmentFilter" 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 class="pagination" id="pagination" style="display: none;">
|
||
<button id="prevPage" class="pagination-btn">上一页</button>
|
||
<span id="pageInfo">第 1 页 / 共 1 页</span>
|
||
<button id="nextPage" class="pagination-btn">下一页</button>
|
||
</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 currentPage = 0;
|
||
let pageSize = 50;
|
||
let totalPages = 0;
|
||
let filterOptions = {
|
||
regions: [],
|
||
themes: [],
|
||
departments: []
|
||
};
|
||
|
||
// DOM元素
|
||
const regionSelect = document.getElementById('regionFilter');
|
||
const themeSelect = document.getElementById('themeFilter');
|
||
const departmentSelect = document.getElementById('departmentFilter');
|
||
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 pagination = document.getElementById('pagination');
|
||
const pageInfo = document.getElementById('pageInfo');
|
||
const prevPageBtn = document.getElementById('prevPage');
|
||
const nextPageBtn = document.getElementById('nextPage');
|
||
const loading = document.getElementById('loading');
|
||
const errorMessage = document.getElementById('errorMessage');
|
||
|
||
// 页面初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
loadFilterOptions();
|
||
bindEvents();
|
||
});
|
||
|
||
// 绑定事件
|
||
function bindEvents() {
|
||
// 应用筛选
|
||
applyBtn.addEventListener('click', function() {
|
||
currentPage = 0;
|
||
applyFilter();
|
||
});
|
||
|
||
// 重置筛选
|
||
resetBtn.addEventListener('click', resetFilter);
|
||
|
||
// 搜索框回车筛选
|
||
searchInput.addEventListener('keypress', function(e) {
|
||
if (e.key === 'Enter') {
|
||
currentPage = 0;
|
||
applyFilter();
|
||
}
|
||
});
|
||
|
||
// 分页按钮
|
||
prevPageBtn.addEventListener('click', function() {
|
||
if (currentPage > 0) {
|
||
currentPage--;
|
||
applyFilter();
|
||
}
|
||
});
|
||
|
||
nextPageBtn.addEventListener('click', function() {
|
||
if (currentPage < totalPages - 1) {
|
||
currentPage++;
|
||
applyFilter();
|
||
}
|
||
});
|
||
}
|
||
|
||
// 加载筛选选项
|
||
async function loadFilterOptions() {
|
||
try {
|
||
const response = await fetch('/fs-ai-asistant/api/workflow/lawrisk/admin/permits/filter-options');
|
||
const data = await response.json();
|
||
|
||
if (!data.success) {
|
||
throw new Error(data.message || '加载筛选选项失败');
|
||
}
|
||
|
||
filterOptions = data.data || {};
|
||
renderFilterOptions();
|
||
} catch (error) {
|
||
console.error('加载筛选选项失败:', error);
|
||
showError('加载筛选选项失败: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// 渲染筛选选项
|
||
function renderFilterOptions() {
|
||
// 渲染区域选项
|
||
regionSelect.innerHTML = '<option value="">全部区域</option>';
|
||
filterOptions.regions.forEach(region => {
|
||
const option = document.createElement('option');
|
||
option.value = region.id || region;
|
||
option.textContent = region.name || region;
|
||
regionSelect.appendChild(option);
|
||
});
|
||
|
||
// 渲染主题选项
|
||
themeSelect.innerHTML = '<option value="">全部主题</option>';
|
||
filterOptions.themes.forEach(theme => {
|
||
const option = document.createElement('option');
|
||
option.value = theme.id;
|
||
option.textContent = theme.name;
|
||
themeSelect.appendChild(option);
|
||
});
|
||
|
||
// 渲染部门选项
|
||
departmentSelect.innerHTML = '<option value="">全部部门</option>';
|
||
filterOptions.departments.forEach(dept => {
|
||
const option = document.createElement('option');
|
||
option.value = dept.id;
|
||
option.textContent = `${dept.name} (${dept.code})`;
|
||
departmentSelect.appendChild(option);
|
||
});
|
||
}
|
||
|
||
// 应用筛选
|
||
async function applyFilter() {
|
||
const filters = {
|
||
region: regionSelect.value || null,
|
||
theme: themeSelect.value || null,
|
||
department: departmentSelect.value || null,
|
||
search_text: searchInput.value.trim() || null,
|
||
limit: pageSize,
|
||
offset: currentPage * pageSize
|
||
};
|
||
|
||
// 显示加载状态
|
||
showLoading(true);
|
||
hideError();
|
||
|
||
try {
|
||
const params = new URLSearchParams();
|
||
Object.keys(filters).forEach(key => {
|
||
if (filters[key]) {
|
||
params.append(key, filters[key]);
|
||
}
|
||
});
|
||
|
||
const response = await fetch(`/fs-ai-asistant/api/workflow/lawrisk/admin/permits/advanced-filter?${params}`);
|
||
const data = await response.json();
|
||
|
||
if (!data.success) {
|
||
throw new Error(data.message || '筛选失败');
|
||
}
|
||
|
||
renderResults(data.data.permits || []);
|
||
updatePagination(data.data.pagination || {});
|
||
|
||
} catch (error) {
|
||
console.error('筛选失败:', error);
|
||
showError('筛选失败: ' + error.message);
|
||
renderResults([]);
|
||
updatePagination({});
|
||
} 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 style="width: 40%;">许可事项</th>
|
||
<th style="width: 20%;">行政区域</th>
|
||
<th style="width: 20%;">主题</th>
|
||
<th style="width: 10%;">风险数</th>
|
||
<th style="width: 10%;">操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
`;
|
||
|
||
permits.forEach(permit => {
|
||
const themesHtml = (permit.themes || [])
|
||
.map(t => `<span class="theme-tag">${escapeHtml(t.name)}</span>`)
|
||
.join('');
|
||
|
||
html += `
|
||
<tr>
|
||
<td>
|
||
<div class="permit-name">${escapeHtml(permit.name || '未知许可')}</div>
|
||
<div class="permit-themes">${themesHtml}</div>
|
||
</td>
|
||
<td>${escapeHtml(permit.region?.name || '-')}</td>
|
||
<td>${permit.theme_count || 0} 个主题</td>
|
||
<td>${permit.risk_count || 0}</td>
|
||
<td>
|
||
<div class="action-buttons">
|
||
<button class="btn btn-small btn-outline" onclick="viewPermit('${permit.id}')">
|
||
查看
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
`;
|
||
});
|
||
|
||
html += `
|
||
</tbody>
|
||
</table>
|
||
`;
|
||
|
||
resultsTable.innerHTML = html;
|
||
}
|
||
|
||
// 更新分页信息
|
||
function updatePagination(pagination) {
|
||
if (!pagination || pagination.total === 0) {
|
||
pagination.style.display = 'none';
|
||
resultCount.textContent = '0';
|
||
return;
|
||
}
|
||
|
||
const total = pagination.total || 0;
|
||
totalPages = Math.ceil(total / pageSize);
|
||
|
||
resultCount.textContent = total;
|
||
pageInfo.textContent = `第 ${currentPage + 1} 页 / 共 ${totalPages} 页`;
|
||
|
||
// 更新按钮状态
|
||
prevPageBtn.disabled = currentPage === 0;
|
||
nextPageBtn.disabled = currentPage >= totalPages - 1;
|
||
|
||
// 显示分页
|
||
pagination.style.display = 'flex';
|
||
}
|
||
|
||
// 重置筛选
|
||
function resetFilter() {
|
||
regionSelect.value = '';
|
||
themeSelect.value = '';
|
||
departmentSelect.value = '';
|
||
searchInput.value = '';
|
||
currentPage = 0;
|
||
|
||
renderResults([]);
|
||
updatePagination({});
|
||
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 viewPermit(permitId) {
|
||
// TODO: 实现许可详情查看功能
|
||
alert('查看许可详情功能待实现,许可ID: ' + permitId);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|