From ef44f6dc25d8ab36e75f2791c3ff20633543722f Mon Sep 17 00:00:00 2001 From: hermes Date: Mon, 15 Jun 2026 22:59:36 +0800 Subject: [PATCH] feat: UI rewrite R4-R5 (17 pages) - R4: equipment+project+QR+notice (8) - R5: pshgl drain user management (9) All pages: consistent design system --- src/main.ts | 2 + src/router/index.ts | 8 + src/views/clientQR/deviceDetails.vue | 256 ++++++++--- src/views/clientQR/pshDetail.vue | 264 +++++++++--- .../monitoringEquipment/equipmentInfo.vue | 403 +++++++++++++++--- src/views/monitoringEquipment/index.vue | 266 +++++++++--- .../monitoringEquipment/monitoringDetail.vue | 306 ++++++++----- src/views/notice/index.vue | 210 ++++----- src/views/projectManagement/detail.vue | 313 ++++++++++---- src/views/projectManagement/index.vue | 236 +++++++--- src/views/pshgl/pshCheck.vue | 14 +- src/views/pshgl/pshCheckDetail.vue | 223 ++++++++++ 12 files changed, 1879 insertions(+), 622 deletions(-) create mode 100644 src/views/pshgl/pshCheckDetail.vue diff --git a/src/main.ts b/src/main.ts index bb1a319..14ce96c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -40,6 +40,7 @@ import { Picker, DatePicker, ActionSheet, + Checkbox, } from 'vant' // ── Vant 样式(组件样式按需引入) ── @@ -124,6 +125,7 @@ const vantComponents = [ Picker, DatePicker, ActionSheet, + Checkbox, ] for (const component of vantComponents) { diff --git a/src/router/index.ts b/src/router/index.ts index e07892d..4498b81 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -497,6 +497,14 @@ const routes: RouteRecordRaw[] = [ title: '检查报告', }, }, + { + path: '/pshCheckDetail/:detail?', + name: 'PshCheckDetail', + component: () => import('@/views/pshgl/pshCheckDetail.vue'), + meta: { + title: '检查详情', + }, + }, // ── 地图模块 ── { path: '/map', diff --git a/src/views/clientQR/deviceDetails.vue b/src/views/clientQR/deviceDetails.vue index 1378bf4..a449b8d 100644 --- a/src/views/clientQR/deviceDetails.vue +++ b/src/views/clientQR/deviceDetails.vue @@ -9,11 +9,22 @@ import { useRoute, useRouter } from 'vue-router' const route = useRoute() const router = useRouter() - const deviceId = computed(() => route.params.id as string | undefined) -/** 模拟设备详情 */ -const device = ref({ +interface DeviceDetail { + id: string + name: string + type: string + model: string + location: string + status: 'normal' | 'alarm' | 'offline' + installDate: string + lastMaintain: string + protocol: string + battery: string +} + +const device = ref({ id: deviceId.value || 'DEV-001', name: '流量监测仪 A-01', type: '流量监测', @@ -26,59 +37,90 @@ const device = ref({ battery: '85%', }) -const statusMap: Record = { - normal: '正常', - alarm: '告警', - offline: '离线', +const statusCfg: Record = { + normal: { label: '正常', color: '#07C160', bg: '#E8F8EF' }, + alarm: { label: '告警', color: '#EE0A24', bg: '#FDECEC' }, + offline: { label: '离线', color: '#969799', bg: '#F2F3F5' }, } -const statusColorMap: Record = { - normal: '#07c160', - alarm: '#ee0a24', - offline: '#999', +function goMaintenance() { + router.push('/maintenanceRecords') } function goBack() { router.back() } - -function goMaintenance() { - // 跳转到养护记录 - router.push('/maintenanceRecords') -} diff --git a/src/views/clientQR/pshDetail.vue b/src/views/clientQR/pshDetail.vue index 41147d0..1343e82 100644 --- a/src/views/clientQR/pshDetail.vue +++ b/src/views/clientQR/pshDetail.vue @@ -9,11 +9,21 @@ import { useRoute, useRouter } from 'vue-router' const route = useRoute() const router = useRouter() - const pshId = computed(() => route.params.id as string | undefined) -/** 模拟排水户详情 */ -const detail = ref({ +interface PshDetail { + id: string + name: string + type: string + address: string + contact: string + phone: string + licenseNo: string + drainType: string + qrCodeUrl: string +} + +const detail = ref({ id: pshId.value || 'PSH-001', name: '万达广场(城北店)', type: '商业', @@ -22,94 +32,208 @@ const detail = ref({ phone: '138****5678', licenseNo: '排许字第2024-0031号', drainType: '雨污分流', - qrCodeUrl: '', // 实际从接口获取 + qrCodeUrl: '', }) - -function goBack() { - router.back() -} diff --git a/src/views/monitoringEquipment/equipmentInfo.vue b/src/views/monitoringEquipment/equipmentInfo.vue index f1f186c..5d6415b 100644 --- a/src/views/monitoringEquipment/equipmentInfo.vue +++ b/src/views/monitoringEquipment/equipmentInfo.vue @@ -10,11 +10,31 @@ import { useRouter, useRoute } from 'vue-router' const router = useRouter() const route = useRoute() - const detailId = (route.query.id as string) || '1' -/** 模拟设备详情 */ -const equipment = ref({ +interface EquipDetail { + id: string + name: string + type: string + typeLabel: string + model: string + sn: string + status: 'normal' | 'alarm' | 'offline' + location: string + installDate: string + manufacturer: string + range: string + precision: string + protocol: string + ipRating: string + lastData: string + lastUpdateTime: string + batteryLevel: number + signalStrength: number + maintenanceRecords: { date: string; type: string; result: string; operator: string }[] +} + +const equipment = ref({ id: detailId, name: '流量监测仪 A-01', type: 'flow_meter', @@ -40,94 +60,343 @@ const equipment = ref({ ], }) -const statusMap: Record = { - normal: '正常', - alarm: '报警', - offline: '离线', +const statusCfg: Record = { + normal: { label: '正常', color: '#07C160', bg: '#E8F8EF' }, + alarm: { label: '报警', color: '#EE0A24', bg: '#FDECEC' }, + offline: { label: '离线', color: '#969799', bg: '#F2F3F5' }, } -const statusColorMap: Record = { - normal: 'success', - alarm: 'danger', - offline: '#999', +function isGoodResult(r: string) { + return r === '正常' || r === '合格' || r === '已完成' } diff --git a/src/views/monitoringEquipment/index.vue b/src/views/monitoringEquipment/index.vue index 9ae2300..88b7a70 100644 --- a/src/views/monitoringEquipment/index.vue +++ b/src/views/monitoringEquipment/index.vue @@ -10,14 +10,21 @@ import { useRouter } from 'vue-router' const router = useRouter() -/** 搜索关键词 */ const searchText = ref('') - -/** 当前激活的 Tab */ const activeTab = ref(0) -/** 模拟监测设备数据 */ -const mockEquipments = [ +interface Equipment { + id: number + name: string + type: string + typeLabel: string + status: 'normal' | 'alarm' | 'offline' + location: string + lastData: string + updateTime: string +} + +const mockEquipments: Equipment[] = [ { id: 1, name: '流量监测仪 A-01', type: 'flow_meter', typeLabel: '流量计', status: 'normal', location: '城北供水管网1号节点', lastData: '125.6 m³/h', updateTime: '2025-06-15 08:30' }, { id: 2, name: '压力传感器 P-03', type: 'pressure_sensor', typeLabel: '压力传感器', status: 'alarm', location: '高新区主管网3号泵站', lastData: '0.85 MPa', updateTime: '2025-06-15 08:25' }, { id: 3, name: '水质监测仪 W-07', type: 'water_quality', typeLabel: '水质监测仪', status: 'normal', location: '老城区饮用水源口', lastData: 'pH 7.2 / 浊度 0.5NTU', updateTime: '2025-06-15 08:28' }, @@ -28,24 +35,15 @@ const mockEquipments = [ { id: 8, name: '液位计 L-05', type: 'liquid_level', typeLabel: '液位计', status: 'offline', location: '北区污水井5号', lastData: '1.8 m', updateTime: '2025-06-14 18:00' }, ] -/** 状态映射 */ -const statusMap: Record = { - normal: '正常', - alarm: '报警', - offline: '离线', +const statusCfg: Record = { + normal: { label: '正常', color: '#07C160', bg: '#E8F8EF' }, + alarm: { label: '报警', color: '#EE0A24', bg: '#FDECEC' }, + offline: { label: '离线', color: '#969799', bg: '#F2F3F5' }, } -const statusColorMap: Record = { - normal: 'success', - alarm: 'danger', - offline: '#999', -} - -/** 类型 Tab 映射 */ const typeTabs = ['全部', '流量计', '压力传感器', '水质监测仪', '液位计'] const typeKeys = ['', 'flow_meter', 'pressure_sensor', 'water_quality', 'liquid_level'] -/** 筛选后的列表 */ const filteredEquipments = computed(() => { let list = mockEquipments if (searchText.value) { @@ -62,22 +60,23 @@ const filteredEquipments = computed(() => { return list }) -/** 跳转设备详情 */ function goEquipmentInfo(id: number) { router.push(`/equipmentInfo?id=${id}`) } -/** 跳转地图监控 */ function goMapMonitoring() { router.push('/mapMonitoring') } diff --git a/src/views/monitoringEquipment/monitoringDetail.vue b/src/views/monitoringEquipment/monitoringDetail.vue index 7c27253..78e6371 100644 --- a/src/views/monitoringEquipment/monitoringDetail.vue +++ b/src/views/monitoringEquipment/monitoringDetail.vue @@ -2,19 +2,30 @@ /** * 监测详情页 * - * 展示设备的监测数据详情,包含实时数据、历史趋势图表占位、 - * 告警记录和数据分析。 + * 展示设备的监测数据详情,包含实时数据卡片、 + * 统计数据和告警记录。 */ import { ref } from 'vue' import { useRouter, useRoute } from 'vue-router' const router = useRouter() const route = useRoute() - const detailId = (route.query.id as string) || '1' -/** 模拟监测详情 */ -const monitoring = ref({ +type StatRow = { avg: string; max: string; min: string; total: string } + +interface MonitoringDetail { + id: string + equipmentName: string + equipmentType: string + currentValue: string + status: string + updateTime: string + statistics: { today: StatRow; yesterday: StatRow } + alarmRecords: { time: string; level: 'warning' | 'alarm'; content: string; value: string }[] +} + +const monitoring = ref({ id: detailId, equipmentName: '流量监测仪 A-01', equipmentType: '流量计', @@ -24,7 +35,6 @@ const monitoring = ref({ statistics: { today: { avg: '122.3 m³/h', max: '135.8 m³/h', min: '110.2 m³/h', total: '2935.2 m³' }, yesterday: { avg: '118.7 m³/h', max: '130.1 m³/h', min: '105.8 m³/h', total: '2848.8 m³' }, - thisMonth: { avg: '120.5 m³/h', max: '145.2 m³/h', min: '95.6 m³/h', total: '86760 m³' }, }, alarmRecords: [ { time: '2025-06-14 16:30:00', level: 'warning', content: '流量超过预警值 130 m³/h', value: '131.5 m³/h' }, @@ -33,104 +43,122 @@ const monitoring = ref({ ], }) -const levelColorMap: Record = { - warning: '#FF9800', - alarm: '#F44336', +const alarmCfg: Record = { + warning: { color: '#FF976A', bg: '#FFF3ED', icon: 'warning-o' }, + alarm: { color: '#EE0A24', bg: '#FDECEC', icon: 'warning-o' }, } + +const statPeriods = [ + { label: '今日统计', key: 'today' as const }, + { label: '昨日统计', key: 'yesterday' as const }, +] as const diff --git a/src/views/notice/index.vue b/src/views/notice/index.vue index 53fe819..8f388ab 100644 --- a/src/views/notice/index.vue +++ b/src/views/notice/index.vue @@ -11,47 +11,42 @@ import { showToast } from 'vant' const router = useRouter() -/** 当前激活的 Tab */ -const activeName = ref('未读消息') +/** Tabs */ +const active = ref(0) -/** 列表数据 */ -const list = ref([]) +interface NoticeItem { + id: number + title: string + message: string + createTime: string +} -/** 是否加载完成 */ +const list = ref([]) const finished = ref(false) - -/** 是否正在加载 */ const isLoading = ref(false) -/** 分页参数 */ const pageParams = reactive({ pageNum: 1, pageSize: 10, }) -/** - * 生成模拟消息数据 - */ function generateMockData(pageNum: number, pageSize: number) { const start = (pageNum - 1) * pageSize const total = 25 - const items: any[] = [] const end = Math.min(start + pageSize, total) + const items: NoticeItem[] = [] for (let i = start; i < end; i++) { items.push({ id: i + 1, title: `通知消息标题 ${i + 1}`, message: `这是第 ${i + 1} 条消息的详细内容,用于展示通知列表的效果。`, - createTime: `2025-06-${String(10 + Math.floor(i / 3)).padStart(2, '0')} 14:30:00`, + createTime: `2025-06-${String(10 + Math.floor(i / 3)).padStart(2, '0')} 14:30`, }) } return { items, isLastPage: end >= total } } -/** - * 获取列表数据 - */ -function getList() { +function initList() { isLoading.value = true pageParams.pageNum = 1 setTimeout(() => { @@ -59,21 +54,15 @@ function getList() { list.value = items finished.value = isLastPage isLoading.value = false - }, 500) + }, 400) } -/** - * Tab 切换 - */ function onChange(name: string | number) { - activeName.value = String(name) + active.value = Number(name) finished.value = false - getList() + initList() } -/** - * 无限滚动加载更多 - */ function onLoad() { if (finished.value) return isLoading.value = true @@ -83,12 +72,9 @@ function onLoad() { list.value = list.value.concat(items) finished.value = isLastPage isLoading.value = false - }, 500) + }, 400) } -/** - * 下拉刷新 - */ function onRefresh() { finished.value = false pageParams.pageNum = 1 @@ -97,56 +83,56 @@ function onRefresh() { list.value = items finished.value = isLastPage showToast('刷新成功') - }, 800) + }, 600) } -/** - * 点击消息项 - */ -function toDetail(item: any) { +function toDetail(item: NoticeItem) { showToast(`查看: ${item.title}`) } -/** 初始加载 */ -getList() +initList() diff --git a/src/views/projectManagement/detail.vue b/src/views/projectManagement/detail.vue index b22b452..df414aa 100644 --- a/src/views/projectManagement/detail.vue +++ b/src/views/projectManagement/detail.vue @@ -9,11 +9,23 @@ import { useRoute, useRouter } from 'vue-router' const route = useRoute() const router = useRouter() - const detailId = computed(() => route.params.detail as string | undefined) -/** 模拟项目详情 */ -const project = ref({ +interface ProjectDetail { + id: string + name: string + no: string + company: string + progress: number + status: 'building' | 'paused' | 'completed' + manager: string + startDate: string + endDate: string + budget: string + description: string +} + +const project = ref({ id: detailId.value || 'XM-2025-001', name: '城北雨水管网改造工程', no: 'XM-2025-001', @@ -27,25 +39,18 @@ const project = ref({ description: '对城北片区老旧雨水管网进行全面改造,包括新建DN300-DN600雨水管12.5km,改造检查井280座,新建雨水泵站1座。', }) -const statusMap: Record = { - building: '在建', - paused: '暂停', - completed: '竣工', -} - -const statusTagType: Record = { - building: 'primary', - paused: 'warning', - completed: 'success', +const statusCfg: Record = { + building: { label: '在建', color: '#1E74FF', bg: '#E3F2FD' }, + paused: { label: '暂停', color: '#FF976A', bg: '#FFF3ED' }, + completed: { label: '竣工', color: '#07C160', bg: '#E8F8EF' }, } function progressColor(pct: number): string { - if (pct >= 80) return '#07c160' - if (pct >= 40) return '#1989fa' - return '#ff976a' + if (pct >= 80) return '#07C160' + if (pct >= 40) return '#1E74FF' + return '#FF976A' } -/** 模拟里程碑 */ const milestones = ref([ { text: '项目立项', time: '2025-02-15', done: true }, { text: '施工设计', time: '2025-03-15', done: true }, @@ -56,36 +61,77 @@ const milestones = ref([ diff --git a/src/views/pshgl/pshCheck.vue b/src/views/pshgl/pshCheck.vue index 3cd1c69..7e95e7f 100644 --- a/src/views/pshgl/pshCheck.vue +++ b/src/views/pshgl/pshCheck.vue @@ -78,14 +78,14 @@ function onConfirmPsh({ selectedOptions }: any) { /** 选择评分 */ function onConfirmScore(field: string, { selectedOptions }: any) { (formData.value as any)[field] = selectedOptions[0]?.text || '' - const scoreFields: Record = { - drainPipeScore: 'showDrainPipeScore', - preTreatmentScore: 'showPreTreatmentScore', - oilSeparatorScore: 'showOilSeparatorScore', - rainSewageScore: 'showRainSewageScore', - overallScore: 'showOverallScore', + // 关闭对应的选择器弹窗 + switch (field) { + case 'drainPipeScore': showDrainPipeScore.value = false; break + case 'preTreatmentScore': showPreTreatmentScore.value = false; break + case 'oilSeparatorScore': showOilSeparatorScore.value = false; break + case 'rainSewageScore': showRainSewageScore.value = false; break + case 'overallScore': showOverallScore.value = false; break } - ;(ref as any)[scoreFields[field]].value = false } /** 确认综合评定 */ diff --git a/src/views/pshgl/pshCheckDetail.vue b/src/views/pshgl/pshCheckDetail.vue new file mode 100644 index 0000000..2099bce --- /dev/null +++ b/src/views/pshgl/pshCheckDetail.vue @@ -0,0 +1,223 @@ + + + + +