feat: Batch 2 - inspection & maintenance pages (12 pages)

Agent Loop: 3 agents, all passed
- Inspection: index/detail, records index/detail
- InspectionProblem: index/detail/management
- Maintenance: index/detail, records index/detail, check index
This commit is contained in:
Ubuntu
2026-06-15 21:11:12 +08:00
parent d1612fe376
commit 7cd91082d6
14 changed files with 1925 additions and 0 deletions

View File

@@ -34,6 +34,9 @@ import {
GridItem,
Empty,
Search,
Card,
Image,
Tag,
} from 'vant'
// ── Vant 样式(组件样式按需引入) ──
@@ -84,6 +87,9 @@ const vantComponents = [
GridItem,
Empty,
Search,
Card,
Image,
Tag,
]
for (const component of vantComponents) {

View File

@@ -111,6 +111,105 @@ const routes: RouteRecordRaw[] = [
title: '分组管理',
},
},
// ── 巡检模块 ──
{
path: '/inspection',
name: 'Inspection',
component: () => import('@/views/inspection/index.vue'),
meta: {
title: '巡检任务',
},
},
{
path: '/inspection/detail',
name: 'InspectionDetail',
component: () => import('@/views/inspection/detail.vue'),
meta: {
title: '任务详情',
},
},
{
path: '/inspectionRecords',
name: 'InspectionRecords',
component: () => import('@/views/inspectionRecords/index.vue'),
meta: {
title: '巡检记录',
},
},
{
path: '/inspectionRecords/detail',
name: 'InspectionRecordsDetail',
component: () => import('@/views/inspectionRecords/detail.vue'),
meta: {
title: '记录详情',
},
},
// ── 问题工单模块 ──
{
path: '/inspectionProblem/:type?',
name: 'InspectionProblem',
component: () => import('@/views/inspectionProblem/index.vue'),
meta: {
title: '问题工单',
},
},
{
path: '/inspectionProblemDetail/:detail?/:obj?',
name: 'InspectionProblemDetail',
component: () => import('@/views/inspectionProblem/detail.vue'),
meta: {
title: '工单详情',
},
},
{
path: '/inspectionProblemManagement/:detail?/:obj?',
name: 'InspectionProblemManagement',
component: () => import('@/views/inspectionProblem/management.vue'),
meta: {
title: '工单管理',
},
},
{
path: '/maintenance/:type?',
name: 'Maintenance',
component: () => import('@/views/maintenance/index.vue'),
meta: {
title: '养护管理',
},
},
// ── 养护模块 ──
{
path: '/maintenanceDetail/:detail?',
name: 'MaintenanceDetail',
component: () => import('@/views/maintenance/detail.vue'),
meta: {
title: '养护详情',
},
},
{
path: '/maintenanceRecords',
name: 'MaintenanceRecords',
component: () => import('@/views/maintenanceRecords/index.vue'),
meta: {
title: '养护记录',
},
},
{
path: '/maintenanceRecordsDetail/:detail?',
name: 'MaintenanceRecordsDetail',
component: () => import('@/views/maintenanceRecords/detail.vue'),
meta: {
title: '养护记录详情',
},
},
{
path: '/maintenanceCheck',
name: 'MaintenanceCheck',
component: () => import('@/views/maintenanceCheck/index.vue'),
meta: {
title: '养护检查',
},
},
// 404 兜底
{
path: '/:pathMatch(.*)*',

View File

@@ -0,0 +1,133 @@
<script setup lang="ts">
/**
* 巡检任务详情页
*
* 展示任务详细信息,包含基本信息和操作按钮。
* 可通过路由 query.id 获取任务 id。
*/
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showToast } from 'vant'
const router = useRouter()
const route = useRoute()
const taskId = route.query.id as string
/** 模拟任务详情 */
const task = ref({
id: taskId || '1',
name: '管网巡检-城北片区',
facility: '供水管网',
status: 'pending',
planDate: '2025-06-15',
inspector: '张工',
address: '城北工业大道1-50号',
description: '对城北片区供水管网进行全面巡检,检查管道是否有渗漏、腐蚀等情况。重点检查老旧管道及近期施工区域。',
createTime: '2025-06-10 09:00:00',
updateTime: '2025-06-14 17:30:00',
})
const statusMap: Record<string, string> = {
pending: '待巡检',
in_progress: '巡检中',
completed: '已完成',
}
const statusColorMap: Record<string, 'primary' | 'success' | 'warning' | 'danger'> = {
pending: 'warning',
in_progress: 'primary',
completed: 'success',
}
/** 开始巡检 */
function startInspection() {
showToast('开始巡检')
}
/** 查看巡检记录 */
function viewRecords() {
router.push(`/inspectionRecords?taskId=${taskId}`)
}
</script>
<template>
<div class="inspection-detail-page">
<van-nav-bar title="任务详情" left-text="返回" left-arrow fixed placeholder @click-left="router.back()" />
<!-- 状态标签 -->
<div class="status-bar">
<van-tag :type="statusColorMap[task.status]" size="large">
{{ statusMap[task.status] }}
</van-tag>
</div>
<!-- 基本信息 -->
<van-cell-group title="基本信息" class="info-group">
<van-cell title="任务名称" :value="task.name" />
<van-cell title="设施类型" :value="task.facility" />
<van-cell title="计划日期" :value="task.planDate" />
<van-cell title="巡检人员" :value="task.inspector" />
<van-cell title="巡检地址" :value="task.address" />
</van-cell-group>
<!-- 任务描述 -->
<van-cell-group title="任务描述" class="info-group">
<van-cell>
<p class="desc-text">{{ task.description }}</p>
</van-cell>
</van-cell-group>
<!-- 时间信息 -->
<van-cell-group title="时间信息" class="info-group">
<van-cell title="创建时间" :value="task.createTime" />
<van-cell title="更新时间" :value="task.updateTime" />
</van-cell-group>
<!-- 操作按钮 -->
<div class="action-buttons">
<van-button type="primary" block round @click="startInspection">开始巡检</van-button>
<van-button plain block round class="mt-sm" @click="viewRecords">查看巡检记录</van-button>
</div>
</div>
</template>
<style lang="scss" scoped>
.inspection-detail-page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: 24px;
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.status-bar {
padding: 12px 16px;
background: var(--color-bg-card);
text-align: center;
}
.info-group {
margin-top: 8px;
}
.desc-text {
font-size: 14px;
line-height: 1.6;
color: var(--color-text-regular);
padding: 4px 0;
}
.action-buttons {
padding: 24px 16px;
.mt-sm {
margin-top: 12px;
}
}
</style>

View File

@@ -0,0 +1,144 @@
<script setup lang="ts">
/**
* 巡检任务列表页
*
* 支持搜索、状态筛选(全部/待巡检/巡检中/已完成),
* 点击任务卡片跳转到任务详情。
*/
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
/** 搜索关键词 */
const searchText = ref('')
/** 当前激活的 Tab状态筛选 */
const activeTab = ref(0)
/** 模拟任务数据 */
const mockTasks = [
{ id: 1, name: '管网巡检-城北片区', facility: '供水管网', status: 'pending', planDate: '2025-06-15', inspector: '张工', address: '城北工业大道1-50号' },
{ id: 2, name: '阀门巡检-高新区', facility: '阀门井', status: 'in_progress', planDate: '2025-06-15', inspector: '李工', address: '高新区创新路' },
{ id: 3, name: '水表巡检-老城区', facility: '水表', status: 'completed', planDate: '2025-06-14', inspector: '王工', address: '老城区中山路' },
{ id: 4, name: '泵站巡检-东区', facility: '泵站', status: 'pending', planDate: '2025-06-16', inspector: '赵工', address: '东区滨河路88号' },
{ id: 5, name: '消防栓巡检-商业区', facility: '消防栓', status: 'in_progress', planDate: '2025-06-15', inspector: '孙工', address: '商业区解放路' },
]
/** 状态映射 */
const statusMap: Record<string, string> = {
pending: '待巡检',
in_progress: '巡检中',
completed: '已完成',
}
const statusColorMap: Record<string, 'primary' | 'success' | 'warning' | 'danger'> = {
pending: 'warning',
in_progress: 'primary',
completed: 'success',
}
/** 筛选后的任务列表 */
const filteredTasks = computed(() => {
let list = mockTasks
// 搜索筛选
if (searchText.value) {
const kw = searchText.value.toLowerCase()
list = list.filter(t =>
t.name.toLowerCase().includes(kw) ||
t.facility.toLowerCase().includes(kw) ||
t.address.toLowerCase().includes(kw)
)
}
// 状态筛选
if (activeTab.value === 1) {
list = list.filter(t => t.status === 'pending')
} else if (activeTab.value === 2) {
list = list.filter(t => t.status === 'in_progress')
} else if (activeTab.value === 3) {
list = list.filter(t => t.status === 'completed')
}
return list
})
/** 跳转到任务详情 */
function goDetail(id: number) {
router.push(`/inspection/detail?id=${id}`)
}
</script>
<template>
<div class="inspection-page">
<van-nav-bar title="巡检任务" left-arrow fixed placeholder @click-left="router.back()" />
<van-search v-model="searchText" placeholder="搜索任务名称、设施、地址" shape="round" />
<van-tabs v-model:active="activeTab" sticky>
<van-tab title="全部" />
<van-tab title="待巡检" />
<van-tab title="巡检中" />
<van-tab title="已完成" />
</van-tabs>
<div class="task-list">
<van-empty v-if="filteredTasks.length === 0" description="暂无巡检任务" />
<van-card
v-for="task in filteredTasks"
:key="task.id"
:title="task.name"
:desc="`设施: ${task.facility}`"
@click="goDetail(task.id)"
>
<template #tags>
<van-tag :type="statusColorMap[task.status]" size="medium">
{{ statusMap[task.status] }}
</van-tag>
</template>
<template #footer>
<div class="task-meta">
<span>计划日期: {{ task.planDate }}</span>
<span>巡检人: {{ task.inspector }}</span>
</div>
<div class="task-address">{{ task.address }}</div>
</template>
</van-card>
</div>
</div>
</template>
<style lang="scss" scoped>
.inspection-page {
min-height: 100vh;
background: var(--color-bg-page);
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.task-list {
padding: 0 8px;
:deep(.van-card) {
margin: 8px;
border-radius: 10px;
background: var(--color-bg-card);
}
}
.task-meta {
display: flex;
gap: 12px;
font-size: 12px;
color: var(--color-text-secondary);
}
.task-address {
margin-top: 4px;
font-size: 12px;
color: var(--color-text-placeholder);
}
</style>

View File

@@ -0,0 +1,167 @@
<script setup lang="ts">
/**
* 问题工单详情页
*
* 展示工单详细信息,支持接单/转派/解决操作。
*/
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showToast } from 'vant'
const router = useRouter()
const route = useRoute()
const orderId = (route.params.detail || route.query.id) as string
/** 模拟工单详情 */
const order = ref({
id: orderId || '1',
title: '管道渗漏-城北片区',
facility: '供水管网',
status: 'pending',
reporter: '张工',
reportTime: '2025-06-15 09:30',
address: '城北工业大道12号',
description: '巡检过程中发现城北工业大道12号附近供水管道存在渗漏现象渗漏点位于主管道与分支管道连接处初步判断为密封圈老化导致。需尽快安排维修处理避免水资源浪费及路面塌陷风险。',
severity: 'medium',
assignee: '待分配',
createTime: '2025-06-15 09:30:00',
updateTime: '2025-06-15 09:30:00',
})
const statusMap: Record<string, string> = {
pending: '待处理',
processing: '处理中',
resolved: '已解决',
}
const statusColorMap: Record<string, 'primary' | 'success' | 'warning' | 'danger'> = {
pending: 'warning',
processing: 'primary',
resolved: 'success',
}
const severityMap: Record<string, string> = {
low: '低',
medium: '中',
high: '高',
critical: '紧急',
}
const severityColorMap: Record<string, string> = {
low: '#07c160',
medium: '#ff976a',
high: '#ee0a24',
critical: '#ee0a24',
}
/** 接单 */
function acceptOrder() {
showToast('已接单')
}
/** 转派 */
function transferOrder() {
showToast('转派功能')
}
/** 解决 */
function resolveOrder() {
showToast('已标记为已解决')
}
</script>
<template>
<div class="detail-page">
<van-nav-bar title="工单详情" left-text="返回" left-arrow fixed placeholder @click-left="router.back()" />
<!-- 状态栏 -->
<div class="status-bar">
<van-tag :type="statusColorMap[order.status]" size="large">
{{ statusMap[order.status] }}
</van-tag>
<span class="severity-tag" :style="{ color: severityColorMap[order.severity] }">
严重程度: {{ severityMap[order.severity] }}
</span>
</div>
<!-- 基本信息 -->
<van-cell-group title="基本信息" class="info-group">
<van-cell title="工单编号" :value="`WO-${order.id}`" />
<van-cell title="工单标题" :value="order.title" />
<van-cell title="设施类型" :value="order.facility" />
<van-cell title="上报人员" :value="order.reporter" />
<van-cell title="上报时间" :value="order.reportTime" />
<van-cell title="问题地址" :value="order.address" />
<van-cell title="责任人" :value="order.assignee" />
</van-cell-group>
<!-- 问题描述 -->
<van-cell-group title="问题描述" class="info-group">
<van-cell>
<p class="desc-text">{{ order.description }}</p>
</van-cell>
</van-cell-group>
<!-- 时间信息 -->
<van-cell-group title="时间信息" class="info-group">
<van-cell title="创建时间" :value="order.createTime" />
<van-cell title="更新时间" :value="order.updateTime" />
</van-cell-group>
<!-- 操作按钮 -->
<div class="action-buttons">
<van-button type="primary" block round @click="acceptOrder">接单处理</van-button>
<van-button plain block round class="mt-sm" @click="transferOrder">转派他人</van-button>
<van-button type="success" block round class="mt-sm" @click="resolveOrder">标记已解决</van-button>
</div>
</div>
</template>
<style lang="scss" scoped>
.detail-page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: 24px;
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.status-bar {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
padding: 12px 16px;
background: var(--color-bg-card);
}
.severity-tag {
font-size: 13px;
font-weight: 500;
}
.info-group {
margin-top: 8px;
}
.desc-text {
font-size: 14px;
line-height: 1.6;
color: var(--color-text-regular);
padding: 4px 0;
}
.action-buttons {
padding: 24px 16px;
.mt-sm {
margin-top: 12px;
}
}
</style>

View File

@@ -0,0 +1,138 @@
<script setup lang="ts">
/**
* 问题工单列表页
*
* 支持搜索、状态筛选(全部/待处理/处理中/已解决),
* 点击工单卡片跳转详情。
*/
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
/** 搜索关键词 */
const searchText = ref('')
/** 当前激活的 Tab */
const activeTab = ref(0)
/** 模拟工单数据 */
const mockOrders = [
{ id: 1, title: '管道渗漏-城北片区', facility: '供水管网', status: 'pending', reporter: '张工', reportTime: '2025-06-15 09:30', address: '城北工业大道12号' },
{ id: 2, title: '阀门损坏-高新区', facility: '阀门井', status: 'processing', reporter: '李工', reportTime: '2025-06-15 11:00', address: '高新区创新路55号' },
{ id: 3, title: '水表故障-老城区', facility: '水表', status: 'resolved', reporter: '王工', reportTime: '2025-06-14 08:00', address: '老城区中山路88号' },
{ id: 4, title: '消防栓漏水-商业区', facility: '消防栓', status: 'pending', reporter: '孙工', reportTime: '2025-06-16 10:00', address: '商业区解放路101号' },
{ id: 5, title: '泵站异常-东区', facility: '泵站', status: 'processing', reporter: '赵工', reportTime: '2025-06-15 14:30', address: '东区滨河路88号' },
]
/** 状态映射 */
const statusMap: Record<string, string> = {
pending: '待处理',
processing: '处理中',
resolved: '已解决',
}
const statusColorMap: Record<string, 'primary' | 'success' | 'warning' | 'danger'> = {
pending: 'warning',
processing: 'primary',
resolved: 'success',
}
/** 筛选后的工单列表 */
const filteredOrders = computed(() => {
let list = mockOrders
if (searchText.value) {
const kw = searchText.value.toLowerCase()
list = list.filter(o =>
o.title.toLowerCase().includes(kw) ||
o.facility.toLowerCase().includes(kw) ||
o.address.toLowerCase().includes(kw)
)
}
if (activeTab.value === 1) list = list.filter(o => o.status === 'pending')
else if (activeTab.value === 2) list = list.filter(o => o.status === 'processing')
else if (activeTab.value === 3) list = list.filter(o => o.status === 'resolved')
return list
})
/** 跳转详情 */
function goDetail(id: number) {
router.push(`/inspectionProblemDetail/${id}`)
}
</script>
<template>
<div class="problem-page">
<van-nav-bar title="问题工单" left-arrow fixed placeholder @click-left="router.back()" />
<van-search v-model="searchText" placeholder="搜索工单标题、设施、地址" shape="round" />
<van-tabs v-model:active="activeTab" sticky>
<van-tab title="全部" />
<van-tab title="待处理" />
<van-tab title="处理中" />
<van-tab title="已解决" />
</van-tabs>
<div class="order-list">
<van-empty v-if="filteredOrders.length === 0" description="暂无问题工单" />
<van-card
v-for="order in filteredOrders"
:key="order.id"
:title="order.title"
:desc="`设施: ${order.facility}`"
@click="goDetail(order.id)"
>
<template #tags>
<van-tag :type="statusColorMap[order.status]" size="medium">
{{ statusMap[order.status] }}
</van-tag>
</template>
<template #footer>
<div class="order-meta">
<span>上报人: {{ order.reporter }}</span>
<span>{{ order.reportTime }}</span>
</div>
<div class="order-address">{{ order.address }}</div>
</template>
</van-card>
</div>
</div>
</template>
<style lang="scss" scoped>
.problem-page {
min-height: 100vh;
background: var(--color-bg-page);
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.order-list {
padding: 0 8px;
:deep(.van-card) {
margin: 8px;
border-radius: 10px;
background: var(--color-bg-card);
}
}
.order-meta {
display: flex;
gap: 12px;
font-size: 12px;
color: var(--color-text-secondary);
}
.order-address {
margin-top: 4px;
font-size: 12px;
color: var(--color-text-placeholder);
}
</style>

View File

@@ -0,0 +1,221 @@
<script setup lang="ts">
/**
* 问题工单管理页
*
* 支持工单表单编辑和流程分派。
*/
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showToast } from 'vant'
const router = useRouter()
const route = useRoute()
const orderId = (route.params.detail || route.query.id) as string
/** 表单数据 */
const formData = ref({
title: '',
facility: '',
severity: 'medium',
address: '',
description: '',
assignee: '',
})
/** 设施选项 */
const facilityOptions = [
{ text: '供水管网', value: '供水管网' },
{ text: '阀门井', value: '阀门井' },
{ text: '水表', value: '水表' },
{ text: '消防栓', value: '消防栓' },
{ text: '泵站', value: '泵站' },
]
/** 严重程度选项 */
const severityOptions = [
{ text: '低', value: 'low' },
{ text: '中', value: 'medium' },
{ text: '高', value: 'high' },
{ text: '紧急', value: 'critical' },
]
/** 设施选择器状态 */
const showFacilityPicker = ref(false)
/** 严重程度选择器状态 */
const showSeverityPicker = ref(false)
/** 责任人选择器状态 */
const showAssigneePicker = ref(false)
/** 可选责任人 */
const assigneeOptions = [
{ text: '李工', value: '李工' },
{ text: '王工', value: '王工' },
{ text: '赵工', value: '赵工' },
{ text: '孙工', value: '孙工' },
]
/** 确认设施选择 */
function onConfirmFacility({ selectedOptions }: any) {
formData.value.facility = selectedOptions[0]?.text || ''
showFacilityPicker.value = false
}
/** 确认严重程度 */
function onConfirmSeverity({ selectedOptions }: any) {
formData.value.severity = selectedOptions[0]?.value || 'medium'
showSeverityPicker.value = false
}
/** 确认责任人 */
function onConfirmAssignee({ selectedOptions }: any) {
formData.value.assignee = selectedOptions[0]?.text || ''
showAssigneePicker.value = false
}
/** 提交表单 */
function onSubmit() {
if (!formData.value.title) {
showToast('请输入工单标题')
return
}
if (!formData.value.address) {
showToast('请输入问题地址')
return
}
showToast('工单提交成功')
router.back()
}
</script>
<template>
<div class="management-page">
<van-nav-bar title="工单管理" left-text="返回" left-arrow fixed placeholder @click-left="router.back()" />
<van-form @submit="onSubmit" class="form-wrapper">
<van-cell-group title="工单信息" class="form-group">
<!-- 工单标题 -->
<van-field
v-model="formData.title"
name="title"
label="工单标题"
placeholder="请输入工单标题"
:rules="[{ required: true, message: '请输入工单标题' }]"
/>
<!-- 设施类型 -->
<van-field
v-model="formData.facility"
is-link
readonly
name="facility"
label="设施类型"
placeholder="请选择设施类型"
@click="showFacilityPicker = true"
/>
<van-popup v-model:show="showFacilityPicker" round position="bottom">
<van-picker
:columns="facilityOptions"
@confirm="onConfirmFacility"
@cancel="showFacilityPicker = false"
/>
</van-popup>
<!-- 严重程度 -->
<van-field
:model-value="severityOptions.find(o => o.value === formData.severity)?.text || '请选择'"
is-link
readonly
name="severity"
label="严重程度"
@click="showSeverityPicker = true"
/>
<van-popup v-model:show="showSeverityPicker" round position="bottom">
<van-picker
:columns="severityOptions"
@confirm="onConfirmSeverity"
@cancel="showSeverityPicker = false"
/>
</van-popup>
<!-- 问题地址 -->
<van-field
v-model="formData.address"
name="address"
label="问题地址"
placeholder="请输入问题地址"
:rules="[{ required: true, message: '请输入问题地址' }]"
/>
<!-- 问题描述 -->
<van-field
v-model="formData.description"
name="description"
label="问题描述"
type="textarea"
rows="3"
autosize
placeholder="请输入问题描述"
maxlength="500"
show-word-limit
/>
</van-cell-group>
<van-cell-group title="流程分派" class="form-group">
<!-- 责任人 -->
<van-field
v-model="formData.assignee"
is-link
readonly
name="assignee"
label="责任人"
placeholder="请选择责任人"
@click="showAssigneePicker = true"
/>
<van-popup v-model:show="showAssigneePicker" round position="bottom">
<van-picker
:columns="assigneeOptions"
@confirm="onConfirmAssignee"
@cancel="showAssigneePicker = false"
/>
</van-popup>
</van-cell-group>
<!-- 提交按钮 -->
<div class="submit-area">
<van-button round block type="primary" native-type="submit">
提交工单
</van-button>
</div>
</van-form>
</div>
</template>
<style lang="scss" scoped>
.management-page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: 24px;
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.form-wrapper {
margin-top: 0;
}
.form-group {
margin-top: 8px;
}
.submit-area {
padding: 24px 16px;
}
</style>

View File

@@ -0,0 +1,172 @@
<script setup lang="ts">
/**
* 巡检记录详情页
*
* 展示巡检记录的基本信息、发现的问题列表及现场照片。
* 可通过路由 query.id 获取记录 id。
*/
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const recordId = route.query.id as string
/** 模拟记录详情 */
const record = ref({
id: recordId || '2',
taskName: '阀门巡检-高新区',
inspector: '李工',
startTime: '2025-06-15 14:00',
endTime: '2025-06-15 16:00',
duration: '2小时',
result: 'abnormal',
remark: '发现3处阀门漏水问题已记录并上报。部分阀门存在锈蚀情况建议尽快安排维修。',
})
/** 模拟问题列表 */
const problemList = ref([
{ id: 1, description: 'DN200闸阀密封圈老化漏水', level: '严重', location: '高新区创新路12号阀井', photos: [] },
{ id: 2, description: 'DN150蝶阀操作手柄锈蚀', level: '一般', location: '高新区创新路28号', photos: [] },
{ id: 3, description: 'DN100排气阀堵塞', level: '轻微', location: '高新区创新路45号', photos: [] },
])
const levelColorMap: Record<string, 'danger' | 'warning' | 'primary'> = {
'严重': 'danger',
'一般': 'warning',
'轻微': 'primary',
}
</script>
<template>
<div class="record-detail-page">
<van-nav-bar title="记录详情" left-text="返回" left-arrow fixed placeholder @click-left="router.back()" />
<!-- 基本信息 -->
<van-cell-group title="基本信息" class="info-group">
<van-cell title="任务名称" :value="record.taskName" />
<van-cell title="巡检人员" :value="record.inspector" />
<van-cell title="开始时间" :value="record.startTime" />
<van-cell title="结束时间" :value="record.endTime" />
<van-cell title="巡检时长" :value="record.duration" />
<van-cell title="巡检结果">
<template #value>
<van-tag :type="record.result === 'normal' ? 'success' : 'danger'" size="medium">
{{ record.result === 'normal' ? '正常' : '异常' }}
</van-tag>
</template>
</van-cell>
</van-cell-group>
<!-- 备注 -->
<van-cell-group v-if="record.remark" title="备注" class="info-group">
<van-cell>
<p class="desc-text">{{ record.remark }}</p>
</van-cell>
</van-cell-group>
<!-- 问题列表 -->
<van-cell-group title="发现问题" class="info-group">
<van-empty v-if="problemList.length === 0" description="未发现问题" />
<div v-for="(problem, idx) in problemList" :key="problem.id" class="problem-item">
<div class="problem-header">
<span class="problem-index">#{{ idx + 1 }}</span>
<van-tag :type="levelColorMap[problem.level]" size="medium">
{{ problem.level }}
</van-tag>
</div>
<p class="problem-desc">{{ problem.description }}</p>
<p class="problem-location">📍 {{ problem.location }}</p>
</div>
</van-cell-group>
<!-- 现场照片 -->
<van-cell-group title="现场照片" class="info-group">
<div class="photo-grid">
<div class="photo-placeholder" v-for="i in 3" :key="i">
<span>照片 {{ i }}</span>
</div>
</div>
</van-cell-group>
</div>
</template>
<style lang="scss" scoped>
.record-detail-page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: 24px;
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.info-group {
margin-top: 8px;
}
.desc-text {
font-size: 14px;
line-height: 1.6;
color: var(--color-text-regular);
padding: 4px 0;
}
.problem-item {
padding: 12px 16px;
border-bottom: 1px solid var(--color-border-light);
&:last-child {
border-bottom: none;
}
}
.problem-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.problem-index {
font-weight: 600;
font-size: 14px;
color: var(--color-text-primary);
}
.problem-desc {
font-size: 14px;
color: var(--color-text-regular);
margin: 4px 0;
line-height: 1.5;
}
.problem-location {
font-size: 12px;
color: var(--color-text-placeholder);
}
.photo-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
padding: 12px 16px;
}
.photo-placeholder {
aspect-ratio: 1;
background: var(--color-bg-page);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: var(--color-text-placeholder);
border: 1px dashed var(--color-border);
}
</style>

View File

@@ -0,0 +1,165 @@
<script setup lang="ts">
/**
* 巡检记录列表页
*
* 支持搜索、日期筛选(全部/今天/本周/本月),
* 点击记录卡片跳转到记录详情。
*/
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
/** 搜索关键词 */
const searchText = ref('')
/** 当前激活的 Tab日期筛选 */
const activeTab = ref(0)
/** 模拟记录数据 */
const mockRecords = [
{ id: 1, taskName: '管网巡检-城北片区', inspector: '张工', startTime: '2025-06-15 09:00', endTime: '2025-06-15 11:30', result: 'normal', problemCount: 0 },
{ id: 2, taskName: '阀门巡检-高新区', inspector: '李工', startTime: '2025-06-15 14:00', endTime: '2025-06-15 16:00', result: 'abnormal', problemCount: 3 },
{ id: 3, taskName: '水表巡检-老城区', inspector: '王工', startTime: '2025-06-14 08:30', endTime: '2025-06-14 10:00', result: 'normal', problemCount: 0 },
{ id: 4, taskName: '消防栓巡检-商业区', inspector: '孙工', startTime: '2025-06-13 15:00', endTime: '2025-06-13 17:00', result: 'abnormal', problemCount: 1 },
{ id: 5, taskName: '泵站巡检-东区', inspector: '赵工', startTime: '2025-06-10 09:00', endTime: '2025-06-10 11:00', result: 'normal', problemCount: 0 },
]
/** 结果映射 */
const resultMap: Record<string, string> = {
normal: '正常',
abnormal: '异常',
}
const resultColorMap: Record<string, 'success' | 'danger'> = {
normal: 'success',
abnormal: 'danger',
}
/** 日期筛选 */
function getToday() {
const d = new Date()
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
const today = getToday()
function isThisWeek(dateStr: string) {
const d = new Date(dateStr)
const now = new Date()
const startOfWeek = new Date(now)
startOfWeek.setDate(now.getDate() - now.getDay())
startOfWeek.setHours(0, 0, 0, 0)
return d >= startOfWeek
}
function isThisMonth(dateStr: string) {
const d = new Date(dateStr)
const now = new Date()
return d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth()
}
/** 筛选后的记录列表 */
const filteredRecords = computed(() => {
let list = mockRecords
// 搜索筛选
if (searchText.value) {
const kw = searchText.value.toLowerCase()
list = list.filter(r =>
r.taskName.toLowerCase().includes(kw) ||
r.inspector.toLowerCase().includes(kw)
)
}
// 日期筛选
if (activeTab.value === 1) {
list = list.filter(r => r.startTime.startsWith(today))
} else if (activeTab.value === 2) {
list = list.filter(r => isThisWeek(r.startTime))
} else if (activeTab.value === 3) {
list = list.filter(r => isThisMonth(r.startTime))
}
return list
})
/** 跳转到记录详情 */
function goDetail(id: number) {
router.push(`/inspectionRecords/detail?id=${id}`)
}
</script>
<template>
<div class="records-page">
<van-nav-bar title="巡检记录" left-arrow fixed placeholder @click-left="router.back()" />
<van-search v-model="searchText" placeholder="搜索任务名称、巡检人" shape="round" />
<van-tabs v-model:active="activeTab" sticky>
<van-tab title="全部" />
<van-tab title="今天" />
<van-tab title="本周" />
<van-tab title="本月" />
</van-tabs>
<div class="record-list">
<van-empty v-if="filteredRecords.length === 0" description="暂无巡检记录" />
<van-card
v-for="record in filteredRecords"
:key="record.id"
:title="record.taskName"
:desc="`巡检人: ${record.inspector}`"
@click="goDetail(record.id)"
>
<template #tags>
<van-tag :type="resultColorMap[record.result]" size="medium">
{{ resultMap[record.result] }}
</van-tag>
<van-tag v-if="record.problemCount > 0" type="danger" size="medium" plain>
问题 {{ record.problemCount }}
</van-tag>
</template>
<template #footer>
<div class="record-meta">
<span>开始: {{ record.startTime }}</span>
<span>结束: {{ record.endTime }}</span>
</div>
</template>
</van-card>
</div>
</div>
</template>
<style lang="scss" scoped>
.records-page {
min-height: 100vh;
background: var(--color-bg-page);
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.record-list {
padding: 0 8px;
:deep(.van-card) {
margin: 8px;
border-radius: 10px;
background: var(--color-bg-card);
}
:deep(.van-tag) {
margin-right: 4px;
}
}
.record-meta {
display: flex;
flex-direction: column;
gap: 2px;
font-size: 12px;
color: var(--color-text-secondary);
}
</style>

View File

@@ -0,0 +1,135 @@
<script setup lang="ts">
/**
* 养护任务详情页
*
* 展示养护任务详细信息,包含基本信息、任务描述和操作按钮。
* 可通过路由 params.detail 获取任务 id。
*/
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showToast } from 'vant'
const router = useRouter()
const route = useRoute()
const detailId = route.params.detail as string
/** 模拟养护任务详情 */
const task = ref({
id: detailId || '1',
name: '管网养护-城北片区',
facility: '供水管网',
type: '定期养护',
status: 'pending',
planDate: '2025-06-15',
maintainer: '刘工',
address: '城北工业大道1-50号',
description: '对城北片区供水管网进行定期养护,包括管道冲洗、阀门润滑、井盖检查等常规养护工作。重点检查老旧管道腐蚀情况及阀门操作灵活性。',
createTime: '2025-06-10 09:00:00',
updateTime: '2025-06-14 17:30:00',
})
const statusMap: Record<string, string> = {
pending: '待养护',
in_progress: '养护中',
completed: '已完成',
}
const statusColorMap: Record<string, 'primary' | 'success' | 'warning' | 'danger'> = {
pending: 'warning',
in_progress: 'primary',
completed: 'success',
}
/** 开始养护 */
function startMaintenance() {
showToast('开始养护')
}
/** 查看养护记录 */
function viewRecords() {
router.push(`/maintenanceRecords?taskId=${detailId}`)
}
</script>
<template>
<div class="maintenance-detail-page">
<van-nav-bar title="养护详情" left-text="返回" left-arrow fixed placeholder @click-left="router.back()" />
<!-- 状态标签 -->
<div class="status-bar">
<van-tag :type="statusColorMap[task.status]" size="large">
{{ statusMap[task.status] }}
</van-tag>
</div>
<!-- 基本信息 -->
<van-cell-group title="基本信息" class="info-group">
<van-cell title="任务名称" :value="task.name" />
<van-cell title="设施类型" :value="task.facility" />
<van-cell title="养护类型" :value="task.type" />
<van-cell title="计划日期" :value="task.planDate" />
<van-cell title="养护人员" :value="task.maintainer" />
<van-cell title="养护地址" :value="task.address" />
</van-cell-group>
<!-- 任务描述 -->
<van-cell-group title="任务描述" class="info-group">
<van-cell>
<p class="desc-text">{{ task.description }}</p>
</van-cell>
</van-cell-group>
<!-- 时间信息 -->
<van-cell-group title="时间信息" class="info-group">
<van-cell title="创建时间" :value="task.createTime" />
<van-cell title="更新时间" :value="task.updateTime" />
</van-cell-group>
<!-- 操作按钮 -->
<div class="action-buttons">
<van-button type="primary" block round @click="startMaintenance">开始养护</van-button>
<van-button plain block round class="mt-sm" @click="viewRecords">查看养护记录</van-button>
</div>
</div>
</template>
<style lang="scss" scoped>
.maintenance-detail-page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: 24px;
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.status-bar {
padding: 12px 16px;
background: var(--color-bg-card);
text-align: center;
}
.info-group {
margin-top: 8px;
}
.desc-text {
font-size: 14px;
line-height: 1.6;
color: var(--color-text-regular);
padding: 4px 0;
}
.action-buttons {
padding: 24px 16px;
.mt-sm {
margin-top: 12px;
}
}
</style>

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
/**
* 养护管理模块(占位页)
*
* 预留养护管理功能入口,后续迭代完善。
*/
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<template>
<div class="maintenance-page">
<van-nav-bar title="养护管理" left-arrow fixed placeholder @click-left="router.back()" />
<div class="maintenance-content">
<van-empty image="search" description="养护管理功能开发中,敬请期待" />
</div>
</div>
</template>
<style lang="scss" scoped>
.maintenance-page {
min-height: 100vh;
background: var(--color-bg-page);
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.maintenance-content {
display: flex;
align-items: center;
justify-content: center;
min-height: 60vh;
}
</style>

View File

@@ -0,0 +1,151 @@
<script setup lang="ts">
/**
* 养护检查列表页
*
* 支持搜索、状态筛选(全部/待检查/已检查),
* 点击卡片查看检查详情并评分。
*/
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
/** 搜索关键词 */
const searchText = ref('')
/** 当前激活的 Tab状态筛选 */
const activeTab = ref(0)
/** 模拟检查数据 */
const mockChecks = [
{ id: 1, name: '管网养护检查-城北片区', maintainer: '刘工', checkDate: '2025-06-15', status: 'pending', address: '城北工业大道1-50号', facility: '供水管网', lastScore: 95 },
{ id: 2, name: '阀门养护检查-高新区', maintainer: '陈工', checkDate: '2025-06-15', status: 'checked', address: '高新区创新路', facility: '阀门井', score: 88, checkedBy: '主管张', checkTime: '2025-06-15 17:00' },
{ id: 3, name: '水表养护检查-老城区', maintainer: '杨工', checkDate: '2025-06-14', status: 'pending', address: '老城区中山路', facility: '水表', lastScore: 91 },
{ id: 4, name: '消防栓养护检查-商业区', maintainer: '周工', checkDate: '2025-06-13', status: 'checked', address: '商业区解放路', facility: '消防栓', score: 76, checkedBy: '主管李', checkTime: '2025-06-13 18:00' },
{ id: 5, name: '泵站养护检查-东区', maintainer: '吴工', checkDate: '2025-06-10', status: 'checked', address: '东区滨河路88号', facility: '泵站', score: 93, checkedBy: '主管王', checkTime: '2025-06-10 16:30' },
]
/** 状态映射 */
const statusMap: Record<string, string> = {
pending: '待检查',
checked: '已检查',
}
const statusColorMap: Record<string, 'primary' | 'success' | 'warning' | 'danger'> = {
pending: 'warning',
checked: 'success',
}
/** 筛选后的检查列表 */
const filteredChecks = computed(() => {
let list = mockChecks
// 搜索筛选
if (searchText.value) {
const kw = searchText.value.toLowerCase()
list = list.filter(c =>
c.name.toLowerCase().includes(kw) ||
c.maintainer.toLowerCase().includes(kw) ||
c.facility.toLowerCase().includes(kw) ||
c.address.toLowerCase().includes(kw)
)
}
// 状态筛选
if (activeTab.value === 1) {
list = list.filter(c => c.status === 'pending')
} else if (activeTab.value === 2) {
list = list.filter(c => c.status === 'checked')
}
return list
})
/** 跳转到检查详情 */
function goDetail(id: number) {
router.push(`/maintenance/detail?id=${id}`)
}
</script>
<template>
<div class="check-page">
<van-nav-bar title="养护检查" left-arrow fixed placeholder @click-left="router.back()" />
<van-search v-model="searchText" placeholder="搜索任务、养护人、设施、地址" shape="round" />
<van-tabs v-model:active="activeTab" sticky>
<van-tab title="全部" />
<van-tab title="待检查" />
<van-tab title="已检查" />
</van-tabs>
<div class="check-list">
<van-empty v-if="filteredChecks.length === 0" description="暂无检查任务" />
<van-card
v-for="check in filteredChecks"
:key="check.id"
:title="check.name"
:desc="`设施: ${check.facility} | 养护人: ${check.maintainer}`"
@click="goDetail(check.id)"
>
<template #tags>
<van-tag :type="statusColorMap[check.status]" size="medium">
{{ statusMap[check.status] }}
</van-tag>
<van-tag v-if="check.status === 'checked' && check.score" type="primary" size="medium" plain>
评分 {{ check.score }}
</van-tag>
<van-tag v-if="check.status === 'pending' && check.lastScore" type="primary" size="medium" plain>
上次 {{ check.lastScore }}
</van-tag>
</template>
<template #footer>
<div class="check-meta">
<span>检查日期: {{ check.checkDate }}</span>
<span v-if="check.checkedBy">检查人: {{ check.checkedBy }}</span>
</div>
<div class="check-address">{{ check.address }}</div>
</template>
</van-card>
</div>
</div>
</template>
<style lang="scss" scoped>
.check-page {
min-height: 100vh;
background: var(--color-bg-page);
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.check-list {
padding: 0 8px;
:deep(.van-card) {
margin: 8px;
border-radius: 10px;
background: var(--color-bg-card);
}
:deep(.van-tag) {
margin-right: 4px;
}
}
.check-meta {
display: flex;
gap: 12px;
font-size: 12px;
color: var(--color-text-secondary);
flex-wrap: wrap;
}
.check-address {
margin-top: 4px;
font-size: 12px;
color: var(--color-text-placeholder);
}
</style>

View File

@@ -0,0 +1,188 @@
<script setup lang="ts">
/**
* 养护记录详情页
*
* 展示养护记录的基本信息、养护前后对比照片及评分详情。
* 可通过路由 query.id 获取记录 id。
*/
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const recordId = route.query.id as string
/** 模拟养护记录详情 */
const record = ref({
id: recordId || '2',
taskName: '阀门养护-高新区',
maintainer: '陈工',
startTime: '2025-06-15 14:00',
endTime: '2025-06-15 16:00',
duration: '2小时',
result: 'abnormal',
score: 72,
remark: '完成高新区阀门养护工作发现2处阀门存在轻微锈蚀已进行防锈处理并记录。整体养护效果良好。',
})
/** 养护项目明细 */
const checkItems = ref([
{ name: '阀门操作灵活性', score: 4, maxScore: 5 },
{ name: '密封性检查', score: 3, maxScore: 5 },
{ name: '外观清洁度', score: 4, maxScore: 5 },
{ name: '螺栓紧固', score: 5, maxScore: 5 },
{ name: '润滑保养', score: 2, maxScore: 5 },
])
</script>
<template>
<div class="record-detail-page">
<van-nav-bar title="养护记录详情" left-text="返回" left-arrow fixed placeholder @click-left="router.back()" />
<!-- 基本信息 -->
<van-cell-group title="基本信息" class="info-group">
<van-cell title="任务名称" :value="record.taskName" />
<van-cell title="养护人员" :value="record.maintainer" />
<van-cell title="开始时间" :value="record.startTime" />
<van-cell title="结束时间" :value="record.endTime" />
<van-cell title="养护时长" :value="record.duration" />
<van-cell title="养护结果">
<template #value>
<van-tag :type="record.result === 'normal' ? 'success' : 'danger'" size="medium">
{{ record.result === 'normal' ? '正常' : '异常' }}
</van-tag>
</template>
</van-cell>
<van-cell title="综合评分">
<template #value>
<span class="score-value">{{ record.score }} </span>
</template>
</van-cell>
</van-cell-group>
<!-- 评分明细 -->
<van-cell-group title="评分明细" class="info-group">
<van-cell v-for="item in checkItems" :key="item.name" :title="item.name">
<template #value>
<span class="item-score">{{ item.score }} / {{ item.maxScore }}</span>
</template>
</van-cell>
</van-cell-group>
<!-- 养护前照片 -->
<van-cell-group title="养护前照片" class="info-group">
<div class="photo-grid">
<van-image
v-for="i in 2"
:key="'before-' + i"
width="100%"
height="100%"
fit="cover"
radius="8"
src="https://via.placeholder.com/300?text=养护前照片{{ i }}"
class="photo-item"
>
<template #loading>
<van-loading type="spinner" size="20" />
</template>
<template #error>
<div class="photo-placeholder">暂无照片</div>
</template>
</van-image>
</div>
</van-cell-group>
<!-- 养护后照片 -->
<van-cell-group title="养护后照片" class="info-group">
<div class="photo-grid">
<van-image
v-for="i in 2"
:key="'after-' + i"
width="100%"
height="100%"
fit="cover"
radius="8"
src="https://via.placeholder.com/300?text=养护后照片{{ i }}"
class="photo-item"
>
<template #loading>
<van-loading type="spinner" size="20" />
</template>
<template #error>
<div class="photo-placeholder">暂无照片</div>
</template>
</van-image>
</div>
</van-cell-group>
<!-- 备注 -->
<van-cell-group v-if="record.remark" title="备注" class="info-group">
<van-cell>
<p class="desc-text">{{ record.remark }}</p>
</van-cell>
</van-cell-group>
</div>
</template>
<style lang="scss" scoped>
.record-detail-page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: 24px;
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.info-group {
margin-top: 8px;
}
.score-value {
font-weight: 600;
color: var(--color-primary);
font-size: 15px;
}
.item-score {
font-size: 13px;
color: var(--color-text-secondary);
}
.desc-text {
font-size: 14px;
line-height: 1.6;
color: var(--color-text-regular);
padding: 4px 0;
}
.photo-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
padding: 12px 16px;
}
.photo-item {
aspect-ratio: 4 / 3;
}
.photo-placeholder {
width: 100%;
height: 100%;
aspect-ratio: 4 / 3;
background: var(--color-bg-page);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: var(--color-text-placeholder);
border: 1px dashed var(--color-border);
}
</style>

View File

@@ -0,0 +1,165 @@
<script setup lang="ts">
/**
* 养护记录列表页
*
* 支持搜索、日期筛选(全部/今天/本周/本月),
* 点击记录卡片跳转到记录详情。
*/
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
/** 搜索关键词 */
const searchText = ref('')
/** 当前激活的 Tab日期筛选 */
const activeTab = ref(0)
/** 模拟养护记录数据 */
const mockRecords = [
{ id: 1, taskName: '管网养护-城北片区', maintainer: '刘工', startTime: '2025-06-15 09:00', endTime: '2025-06-15 11:30', result: 'normal', score: 95 },
{ id: 2, taskName: '阀门养护-高新区', maintainer: '陈工', startTime: '2025-06-15 14:00', endTime: '2025-06-15 16:00', result: 'abnormal', score: 72 },
{ id: 3, taskName: '水表养护-老城区', maintainer: '杨工', startTime: '2025-06-14 08:30', endTime: '2025-06-14 10:00', result: 'normal', score: 88 },
{ id: 4, taskName: '消防栓养护-商业区', maintainer: '周工', startTime: '2025-06-13 15:00', endTime: '2025-06-13 17:00', result: 'abnormal', score: 65 },
{ id: 5, taskName: '泵站养护-东区', maintainer: '吴工', startTime: '2025-06-10 09:00', endTime: '2025-06-10 11:00', result: 'normal', score: 91 },
]
/** 结果映射 */
const resultMap: Record<string, string> = {
normal: '正常',
abnormal: '异常',
}
const resultColorMap: Record<string, 'success' | 'danger'> = {
normal: 'success',
abnormal: 'danger',
}
/** 日期筛选工具函数 */
function getToday() {
const d = new Date()
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
const today = getToday()
function isThisWeek(dateStr: string) {
const d = new Date(dateStr)
const now = new Date()
const startOfWeek = new Date(now)
startOfWeek.setDate(now.getDate() - now.getDay())
startOfWeek.setHours(0, 0, 0, 0)
return d >= startOfWeek
}
function isThisMonth(dateStr: string) {
const d = new Date(dateStr)
const now = new Date()
return d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth()
}
/** 筛选后的记录列表 */
const filteredRecords = computed(() => {
let list = mockRecords
// 搜索筛选
if (searchText.value) {
const kw = searchText.value.toLowerCase()
list = list.filter(r =>
r.taskName.toLowerCase().includes(kw) ||
r.maintainer.toLowerCase().includes(kw)
)
}
// 日期筛选
if (activeTab.value === 1) {
list = list.filter(r => r.startTime.startsWith(today))
} else if (activeTab.value === 2) {
list = list.filter(r => isThisWeek(r.startTime))
} else if (activeTab.value === 3) {
list = list.filter(r => isThisMonth(r.startTime))
}
return list
})
/** 跳转到记录详情 */
function goDetail(id: number) {
router.push(`/maintenanceRecords/detail?id=${id}`)
}
</script>
<template>
<div class="records-page">
<van-nav-bar title="养护记录" left-arrow fixed placeholder @click-left="router.back()" />
<van-search v-model="searchText" placeholder="搜索任务名称、养护人" shape="round" />
<van-tabs v-model:active="activeTab" sticky>
<van-tab title="全部" />
<van-tab title="今天" />
<van-tab title="本周" />
<van-tab title="本月" />
</van-tabs>
<div class="record-list">
<van-empty v-if="filteredRecords.length === 0" description="暂无养护记录" />
<van-card
v-for="record in filteredRecords"
:key="record.id"
:title="record.taskName"
:desc="`养护人: ${record.maintainer}`"
@click="goDetail(record.id)"
>
<template #tags>
<van-tag :type="resultColorMap[record.result]" size="medium">
{{ resultMap[record.result] }}
</van-tag>
<van-tag type="primary" size="medium" plain>
评分 {{ record.score }}
</van-tag>
</template>
<template #footer>
<div class="record-meta">
<span>开始: {{ record.startTime }}</span>
<span>结束: {{ record.endTime }}</span>
</div>
</template>
</van-card>
</div>
</div>
</template>
<style lang="scss" scoped>
.records-page {
min-height: 100vh;
background: var(--color-bg-page);
:deep(.van-nav-bar) {
background: var(--color-primary);
--van-nav-bar-title-text-color: #fff;
--van-nav-bar-text-color: #fff;
--van-nav-bar-icon-color: #fff;
}
}
.record-list {
padding: 0 8px;
:deep(.van-card) {
margin: 8px;
border-radius: 10px;
background: var(--color-bg-card);
}
:deep(.van-tag) {
margin-right: 4px;
}
}
.record-meta {
display: flex;
flex-direction: column;
gap: 2px;
font-size: 12px;
color: var(--color-text-secondary);
}
</style>