Commit 1cb480b4 authored by Data Governance Dev's avatar Data Governance Dev

feat(web2): 左侧菜单改为「身份证合规验证」+ 顶部独立规则描述面板

UI/UX 调整:
- 左侧菜单 + 路由 meta + 面板标题统一改为「身份证合规验证」
- 左侧菜单字号加大(14→17px)、行高加高(52px)

IdCardTool.vue 顶部新增独立 textarea 面板:
- 与下方 5 张规则卡片完全分离(互不联动)
- 默认填充 5 句规则 summary(每行一条),用户可自由编辑
- 自有 localStorage key(idcard:rule-text-v1)持久化
- 提供「清空」按钮

5 张规则卡片样式还原 a5990444(图标 + 标题 + ❓,hover tooltip 看详细):
- 每条规则拆成 summary(一句话)+ detail(详细列表)
- tooltip 展示 detail 多条 li(恢复 a5990444 详细版本)
- 顶部 textarea 默认值取 summary

涉及踩坑:
- 后端 FastAPI 直接托管 web2/dist/(web2/backend/app.py:93),
  源码改动后浏览器不更新,必须 npm run build 重新生成 dist/
  才能让 192.168.101.126:8766 服务拿到新版本
parent 581b3e16
......@@ -57,7 +57,7 @@ const menuItems = [
{
path: '/id-card',
name: 'IdCardTool',
title: '身份证治理工具',
title: '身份证合规验证',
icon: 'IdCard',
},
]
......@@ -112,6 +112,15 @@ const activeMenu = computed(() => route.path)
border-right: none;
height: 100%;
}
/* 菜单项字号加大 */
.layout-menu :deep(.el-menu-item) {
font-size: 17px;
height: 52px;
line-height: 52px;
}
.layout-menu :deep(.el-menu-item .el-icon) {
font-size: 19px;
}
/* 内容区 */
.layout-main {
......
......@@ -13,7 +13,7 @@ const routes = [
path: 'id-card',
name: 'IdCardTool',
component: IdCardTool,
meta: { title: '身份证治理工具', icon: 'IdCard' },
meta: { title: '身份证合规验证', icon: 'IdCard' },
},
],
},
......
<template>
<div class="idcard-page">
<!-- 顶部独立 textarea:自由文本,与下方 5 张卡片互不联动 -->
<el-card class="idcard-page__header" shadow="never">
<h2 class="idcard-page__title">身份证治理工具</h2>
<p class="idcard-page__desc">
依据 <strong>GB 11643-1999《公民身份号码》</strong>、<strong>GB/T 2260《中华人民共和国行政区划代码》</strong>、
<strong>GB/T 7408《数据元和交换格式 信息交换 日期和时间表示法》</strong>,
对数据库内身份证字段进行合规性校验。每条规则 hover ❓ 查看详情。
</p>
<div class="idcard-page__head-bar">
<span class="idcard-page__head-hint">自由文本(与下方卡片互不联动)</span>
<el-button text type="primary" size="small" @click="clearRuleText">
清空
</el-button>
</div>
<el-input
v-model="ruleText"
type="textarea"
:autosize="{ minRows: 5, maxRows: 12 }"
resize="vertical"
placeholder="可在此自由记录..."
class="idcard-page__rule-textarea"
/>
</el-card>
<!-- 规则说明卡片 -->
<!-- 规则说明卡片(每张卡片展示一条规则的一句话描述) -->
<el-row :gutter="12" class="idcard-page__grid">
<el-col
v-for="rule in rules"
......@@ -220,7 +229,7 @@
</template>
<script setup>
import { reactive, ref, computed } from 'vue'
import { reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Document,
......@@ -533,12 +542,17 @@ async function pollOnce(sid) {
}
}
/* ──── 身份证合规性规则(前端展示用) ──── */
const rules = [
/* ──── 身份证合规性规则(前端展示) ──── */
// 每条规则三段:
// - title 卡片标题(写死)
// - summary 一句话摘要(用于顶部 textarea 的默认填充)
// - detail 详细规则列表(用于卡片 hover tooltip)
const DEFAULT_RULES = [
{
title: '长度与字符集',
icon: Document,
color: '#409eff',
summary: '身份证号必须为 18 位(前 17 位数字 + 第 18 位数字或大写 X),早期 15 位老号需先升位。',
detail: [
'标准长度为 18 位',
'早期(1999 年前)有 15 位老号(无校验位、年份仅 2 位)',
......@@ -552,6 +566,7 @@ const rules = [
title: '地址码(前 6 位)',
icon: Location,
color: '#67c23a',
summary: '前 6 位必须是 GB/T 2260 中真实存在的行政区划代码,且不能出现全 0。',
detail: [
'依据 GB/T 2260《中华人民共和国行政区划代码》',
'前 2 位不能为 00(省/自治区/直辖市必须存在)',
......@@ -565,6 +580,7 @@ const rules = [
title: '出生日期(第 7–14 位)',
icon: Calendar,
color: '#e6a23c',
summary: '第 7–14 位必须为合法日期(YYYYMMDD),不晚于今天且不早于 1900 年。',
detail: [
'依据 GB/T 7408《数据元和交换格式 日期和时间表示法》',
'格式 YYYYMMDD,共 8 位数字',
......@@ -578,6 +594,7 @@ const rules = [
title: '顺序码(第 15–17 位)',
icon: Histogram,
color: '#f56c6c',
summary: '第 15–17 位为顺序码(001–999),其中第 17 位奇数男、偶数女。',
detail: [
'同一地区、同一天出生的人按顺序编号',
'第 17 位奇数 = 男,偶数 = 女',
......@@ -589,6 +606,7 @@ const rules = [
title: '校验位(第 18 位)',
icon: Lock,
color: '#909399',
summary: '第 18 位必须等于前 17 位按 GB 11643 加权取模计算得到的校验码。',
detail: [
'依据 GB 11643-1999 §6',
'加权和:每位 × 加权因子(7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2),求和 S',
......@@ -599,22 +617,79 @@ const rules = [
],
},
]
const RULETEXT_LS_KEY = 'idcard:rule-text-v1'
// 顶部 textarea 的默认填充:把 5 条规则的 summary 拼起来(一行一句)
const DEFAULT_RULETEXT = DEFAULT_RULES.map((r) => r.summary).join('\n')
/**
* 顶部 textarea 是独立的自由文本控件,与下方 5 张卡片互不联动
* - 不再 split 同步到 rules.detail
* - 自有 localStorage key 持久化
* - 首次访问(LS 无 key)时填充 5 句默认规则描述
* - 卡片仍走 DEFAULT_RULES(不可改的常量,detail 仅用于 tooltip)
*/
const rules = DEFAULT_RULES
function loadRuleText() {
try {
const raw = localStorage.getItem(RULETEXT_LS_KEY)
return raw ?? DEFAULT_RULETEXT
} catch (e) {
console.warn('[rule-text] 读取 localStorage 失败:', e.message)
return DEFAULT_RULETEXT
}
}
const ruleText = ref(loadRuleText())
// 用户编辑后实时持久化
watch(
ruleText,
(val) => {
try {
localStorage.setItem(RULETEXT_LS_KEY, val ?? '')
} catch (e) {
console.warn('[rule-text] 保存到 localStorage 失败:', e.message)
}
},
{ flush: 'post' }
)
// 清空按钮
function clearRuleText() {
ElMessageBox.confirm('确定清空顶部文本框?', '清空', {
confirmButtonText: '清空',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
ruleText.value = ''
ElMessage.success('已清空')
})
.catch(() => {})
}
</script>
<style scoped>
/* 顶部规则描述面板 */
.idcard-page__header {
margin-bottom: 12px;
}
.idcard-page__title {
margin: 0 0 4px;
font-size: 18px;
color: #303133;
.idcard-page__head-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.idcard-page__desc {
margin: 0;
color: #606266;
.idcard-page__head-hint {
font-size: 12px;
color: #909399;
}
.idcard-page__rule-textarea :deep(.el-textarea__inner) {
line-height: 1.7;
font-size: 13px;
line-height: 1.6;
}
/* 查询表单 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment