瀏覽代碼

新增超时工单列表,查看全部

HD_wangm 4 月之前
父節點
當前提交
5225c46b70
共有 5 個文件被更改,包括 1146 次插入25 次删除
  1. 10 3
      api/order/list.uts
  2. 177 16
      pages/index/index.uvue
  3. 476 0
      pages/order/almostOverdue.uvue
  4. 0 6
      pages/order/index.uvue
  5. 483 0
      pages/order/overdue.uvue

+ 10 - 3
api/order/list.uts

@@ -15,7 +15,6 @@ export const getOrderList = (page: number, rows: number, keyword: string | null,
     if (keyword != null && keyword.length > 0) {
         // 支持工单编号和风机编号查询
         url += `&keyword=${encodeURIComponent(keyword)}`
-        // url += `&pcsDeviceName=${encodeURIComponent(keyword)}`
     }
     // 添加状态筛选参数
     if (status != null && status.length > 0) {
@@ -31,8 +30,12 @@ export const getOrderList = (page: number, rows: number, keyword: string | null,
  * @param page 页码
  * @param rows 每页数量
  */
-export const almostOverdueList = (page: number, rows: number): Promise<any> => {
+export const almostOverdueList = (page: number, rows: number, keyword: string | null): Promise<any> => {
     let url = `/mobile/order/almostOverdue?pageNum=${page}&pageSize=${rows}`
+	if (keyword != null && keyword.length > 0) {
+	    // 支持工单编号和风机编号查询
+	    url += `&keyword=${encodeURIComponent(keyword)}`
+	}
     return request({
         url: url,
         method: 'GET'
@@ -44,8 +47,12 @@ export const almostOverdueList = (page: number, rows: number): Promise<any> => {
  * @param page 页码
  * @param rows 每页数量
  */
-export const overdueList = (page: number, rows: number): Promise<any> => {
+export const overdueList = (page: number, rows: number, keyword: string | null): Promise<any> => {
     let url = `/mobile/order/overdue?pageNum=${page}&pageSize=${rows}`
+	if (keyword != null && keyword.length > 0) {
+	    // 支持工单编号和风机编号查询
+	    url += `&keyword=${encodeURIComponent(keyword)}`
+	}
     return request({
         url: url,
         method: 'GET'

+ 177 - 16
pages/index/index.uvue

@@ -26,7 +26,7 @@
 					</view>
 					<text class="db-text">评分工单</text>
 				</view>
-				<view class="db-box">
+				<view class="db-box" @click="navigateToOverdueOrders">
 					<view v-if="overdueCount > 0" class="badge"><text class="count">{{ overdueCount }}</text></view>
 					<view class="db-block">
 						<view class="db-center-3">
@@ -49,11 +49,14 @@
 
 			<!-- 即将超时工单列表 -->
 			<view class="section-title-container" v-if="almostOverdueListData.length > 0">
-				<text class="section-title">即将超时工单</text>
+				<view class="section-header">
+					<text class="section-title">即将超时工单</text>
+					<text class="view-all" @click="navigateToAlmostOverdueOrders">查看全部</text>
+				</view>
 			</view>
 			<common-list
 				v-if="almostOverdueListData.length > 0"
-				:dataList="almostOverdueListData"
+				:dataList="almostOverdueListData.slice(0, 2)"
 				:loading="almostOverdueLoading"
 				:hasMore="false"
 				:refresherEnabled="false"
@@ -64,24 +67,31 @@
 					<view class="list-item">
 						<view class="item-container">
 							<view class="item-header">
-								<text class="item-title">{{ getWorkOrderProjectNo(item) }}-{{ getPcsDeviceName(item) }}</text>
+								<text class="item-title">{{ getPcsDeviceName(item) }}-{{ getStationName(item) }}</text>
 								<!-- <text class="status-tag status-assigned">{{ getWorkOrderStatus(item) }}</text> -->
 							</view>
 							<view class="info-row">
-								<text class="text-gray">{{ getCreateTime(item) }}</text>
-								<text class="text-gray">{{ getRemark(item) }}</text>
+								<text class="text-gray">工单编号:{{ getWorkOrderProjectNo(item) }}</text>
+								<text class="text-gray" :class="getRemarkClass(item)">{{ getRemark(item) }}</text>
 							</view>
 						</view>
 					</view>
 				</template>
+				<template #loadMore>
+					<view></view>
+				</template>
 			</common-list>
 
 			<!-- 超时工单列表 -->
-			<view class="section-title-container">
-				<text class="section-title">超时工单</text>
+			<view class="section-title-container" v-if="overdueListData.length > 0">
+				<view class="section-header">
+					<text class="section-title">超时工单</text>
+					<text class="view-all" @click="navigateToOverdueOrders">查看全部</text>
+				</view>
 			</view>
 			<common-list
-				:dataList="overdueListData"
+				v-if="overdueListData.length > 0"
+				:dataList="overdueListData.slice(0, 2)"
 				:loading="overdueLoading"
 				:hasMore="false"
 				:refresherEnabled="false"
@@ -92,16 +102,19 @@
 					<view class="list-item">
 						<view class="item-container">
 							<view class="item-header">
-								<text class="item-title">{{ getWorkOrderProjectNo(item) }}-{{ getPcsDeviceName(item) }}</text>
+								<text class="item-title">{{ getPcsDeviceName(item) }}-{{ getStationName(item) }}</text>
 								<!-- <text class="status-tag status-suspended">{{ getWorkOrderStatus(item) }}</text> -->
 							</view>
 							<view class="info-row">
-								<text class="text-gray">{{ getCreateTime(item) }}</text>
-								<text class="text-gray">{{ getRemark(item) }}</text>
+								<text class="text-gray">工单编号:{{ getWorkOrderProjectNo(item) }}</text>
+								<text class="text-gray overdue-title">{{ getRemark(item) }}</text>
 							</view>
 						</view>
 					</view>
 				</template>
+				<template #loadMore>
+					<view></view>
+				</template>
 			</common-list>
 
         </scroll-view>
@@ -117,6 +130,8 @@
     import FlowChart from '../../components/index/flow-chart/flow-chart.uvue'
     import { getOrderList, almostOverdueList, overdueList } from '../../api/order/list'
     import { listMobileOrderScores } from '../../api/score/index'
+	import type { SysDictData } from '../../types/dict'
+	import { getDictDataByType } from '../../api/dict/index'
 
     // 用户名
     const userName = ref<string>('用户')
@@ -137,6 +152,52 @@
 	// 超时工单列表
 	const overdueListData = ref<any[]>([])
 	const overdueLoading = ref<boolean>(false)
+	
+	// 维保类型字典列表
+	const inspectionTypeList = ref<SysDictData[]>([]) 
+	// 添加字典加载状态
+	const dictLoaded = ref<boolean>(false)
+	
+	// 获取工单状态字典列表
+	const loadStatusDictList = async (): Promise<void> => {
+	    try {
+	        const result = await getDictDataByType('gxt_inspection_type')
+	        const resultObj = result as UTSJSONObject
+	
+	        if (resultObj['code'] == 200) {
+	            const data = resultObj['data'] as any[]
+	            const dictData: SysDictData[] = []
+	
+	            if (data.length > 0) {
+	                for (let i = 0; i < data.length; i++) {
+	                    const item = data[i] as UTSJSONObject
+	                    // 只提取需要的字段
+	                    const dictItem: SysDictData = {
+							dictValue: item['dictValue'] as string | null,
+							dictLabel: item['dictLabel'] as string | null,
+							dictCode: null,
+							dictSort: null,
+							dictType: null,
+							cssClass: null,
+							listClass: null,
+							isDefault: null,
+							status: null,
+							default: null,
+							createTime: null,
+							remark: null
+						}
+	                    dictData.push(dictItem)
+	                }
+	            }
+	
+	            inspectionTypeList.value = dictData
+	            dictLoaded.value = true
+	        }
+	    } catch (e: any) {
+	        console.error('获取工单状态字典失败:', e.message)
+	        dictLoaded.value = true
+	    }
+	}
 
 	// 获取待接单数量
 	const loadAssignedCount = async (): Promise<void> => {
@@ -177,7 +238,7 @@
 	// 获取超时工单数量
 	const loadOverdueCount = async (): Promise<void> => {
 	    try {
-	        const result = await overdueList(1, 1) // 只需要获取总数,不需要具体数据
+	        const result = await overdueList(1, 1, '') // 只需要获取总数,不需要具体数据
 	        const resultObj = result as UTSJSONObject
 
 	        if (resultObj['code'] == 200) {
@@ -193,7 +254,7 @@
 	const loadAlmostOverdueList = async (): Promise<void> => {
 		try {
 			almostOverdueLoading.value = true
-			const result = await almostOverdueList(1, 5) // 获取前5
+			const result = await almostOverdueList(1, 2, '') // 获取前2
 			const resultObj = result as UTSJSONObject
 
 			if (resultObj['code'] == 200) {
@@ -211,7 +272,7 @@
 	const loadOverdueList = async (): Promise<void> => {
 		try {
 			overdueLoading.value = true
-			const result = await overdueList(1, 5) // 获取前5
+			const result = await overdueList(1, 2, '') // 获取前2
 			const resultObj = result as UTSJSONObject
 
 			if (resultObj['code'] == 200) {
@@ -250,6 +311,22 @@
 		// }, 500)
 	}
 
+	// 跳转到超时工单列表
+	const navigateToOverdueOrders = (): void => {
+		// 跳转到超时工单页面
+		uni.navigateTo({
+		    url: '/pages/order/overdue'
+		})
+	}
+
+	// 跳转到即将超时工单列表
+	const navigateToAlmostOverdueOrders = (): void => {
+		// 跳转到即将超时工单页面
+		uni.navigateTo({
+		    url: '/pages/order/almostOverdue'
+		})
+	}
+
 	const handleCreateChannel = (showToast : boolean) => {
 	  // #ifdef APP-ANDROID
 	  const manager = uni.getPushChannelManager()
@@ -345,6 +422,12 @@
 		return orderInfoItem['pcsDeviceName'] as string != null ? orderInfoItem['pcsDeviceName'] as string : ''
 	}
 
+	const getStationName = (item: any | null): string | null=> {
+		if (item == null) return ''
+		const orderInfoItem = item as UTSJSONObject
+		return orderInfoItem['pcsStationName'] as string != null ? orderInfoItem['pcsStationName'] as string : ''
+	}
+
 	const getCreateTime = (item: any | null): string|null => {
 		if (item == null) return null
 		const orderInfoItem = item as UTSJSONObject
@@ -355,6 +438,55 @@
 		const orderInfoItem = item as UTSJSONObject
 		return orderInfoItem['remark'] as string != null ? orderInfoItem['remark'] as string : ''
 	}
+	
+	// 根据剩余时间获取颜色类名
+	const getRemarkClass = (item: any | null): string => {
+		if (item == null) return ''
+		const orderInfoItem = item as UTSJSONObject
+		const remark = orderInfoItem['remark'] as string
+		
+		if (remark == null || remark.length === 0) return ''
+		
+		// 解析备注中的剩余时间(假设格式为包含数字和小时的字符串)
+		// 例如:"剩余时间:5小时" 或 "还剩10小时"
+		const timeMatch = remark.match(/(\d+(\.\d+)?)\s*(小时|时)/)
+		
+		if (timeMatch != null && timeMatch.length > 1 && timeMatch[1] != null) {
+			const hours = parseFloat(timeMatch[1] as string)
+			
+			if (hours < 6) {
+				return 'time-critical' // 红色
+			} else if (hours < 12) {
+				return 'time-warning' // 橙色
+			} else if (hours < 24) {
+				return 'time-notice' // 黄色
+			}
+		}
+		
+		return ''
+	}
+	
+	// 辅助函数:从 any 类型提取属性
+	const getOrderType = (item: any | null): string => {
+	    if (item == null) return ''
+	    const orderInfoItem = item as UTSJSONObject
+	    return orderInfoItem['orderType'] == 1?"维修工单":"维保工单";
+	}
+	
+	const getInspectionType = (item: any | null): string | null => {
+	    if (item == null) return ''
+	    const orderInfoItem = item as UTSJSONObject
+	    const rawStatus = orderInfoItem['inspectionType']
+	
+	    if (rawStatus==null) return ''
+		// 如果字典尚未加载,返回原始值
+		    if (!dictLoaded.value) {
+		        return rawStatus
+		    }
+	    // 查找字典中对应的标签
+	    const dictItem = inspectionTypeList.value.find(dict => dict.dictValue == rawStatus)
+	    return dictItem!=null ? dictItem.dictLabel : rawStatus
+	}
 
 	// 即将超时工单点击事件
 	const handleAlmostOverdueItemClick = (item: any, index: number): void => {
@@ -480,12 +612,23 @@
         font-size: 32rpx;
         color: #333333;
         font-weight: bold;
-        margin-bottom: 20rpx;
+        // margin-bottom: 20rpx;
     }
 
 	.section-title-container {
 		padding: 20rpx 30rpx 10rpx 30rpx;
 	}
+	
+	.section-header {
+		flex-direction: row;
+		justify-content: space-between;
+		align-items: center;
+	}
+	
+	.view-all {
+		font-size: 28rpx;
+		color: #165dff;
+	}
 
     /* 图表区域 */
     .chart-section {
@@ -618,6 +761,19 @@
 		color: #666;
 	}
 
+	/* 剩余时间颜色样式 */
+	.time-critical {
+		color: #ff4d4f; /* 红色 */
+	}
+
+	.time-warning {
+		color: #fa8c16; /* 橙色 */
+	}
+
+	.time-notice {
+		color: #faad14; /* 黄色 */
+	}
+
 	.status-tag {
 		padding: 8rpx 20rpx;
 		border-radius: 20rpx;
@@ -627,6 +783,11 @@
 		border: 1rpx solid;
 	}
 
+	/* 已超时工单标题红色样式 */
+	.overdue-title {
+		color: #ff4d4f;
+	}
+
 	/* 待接单 */
 	.status-assigned {
 		background-color: #ebf5ff;

+ 476 - 0
pages/order/almostOverdue.uvue

@@ -0,0 +1,476 @@
+<template>
+	<uni-navbar-lite :showLeft=true title="即将超时工单"></uni-navbar-lite>
+    <view class="list-page">
+        <!-- 搜索栏 -->
+        <view class="search-bar">
+            <view class="search-box">
+                <image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
+                <input class="search-input" type="text" placeholder="搜索工单编号、风机编号" v-model="keyword" @confirm="handleSearch" @blur="handleSearch" />
+                <text v-if="keyword.length > 0" class="clear-icon" @click="clearSearch">✕</text>
+            </view>
+        </view>
+
+        <!-- 列表内容 -->
+        <common-list
+            :dataList="dataList"
+            :loading="loading"
+            :refreshing="refreshing"
+            :hasMore="hasMore"
+            @refresh="handleRefresh"
+            @loadMore="loadMore"
+            @itemClick="handleItemClick"
+        >
+            <template #default="{ item, index }">
+                <view class="list-item">
+                    <view class="item-container">
+                        <view class="item-header">
+							<text class="item-title">{{ getWorkOrderProjectNo(item) }}-{{ getPcsDeviceName(item) }}{{ getOrderType(item) }}</text>
+							<text class="status-tag" :class="getStatusClass(item)">{{ getWorkOrderStatus(item) }}</text>
+                        </view>
+						<view class="info-row">
+							<view class="info-label">
+								<text class="text-gray">{{ getCreateTime(item) }}</text>
+							</view>
+						</view>
+                    </view>
+                </view>
+            </template>
+        </common-list>
+    </view>
+</template>
+
+<script setup lang="uts">
+import { ref, onBeforeUnmount, onMounted } from 'vue'
+import type { acceptOrderInfo } from '../../types/order'
+import type { SysDictData } from '../../types/dict'
+import { almostOverdueList } from '../../api/order/list'
+import { getDictDataByType } from '../../api/dict/index'
+
+    // 列表数据
+    const dataList = ref<acceptOrderInfo[]>([])
+    let keyword = ref<string>("")
+    const page = ref<number>(1)
+    const pageSize: number = 10
+    const hasMore = ref<boolean>(true)
+    const loading = ref<boolean>(false)
+    const refreshing = ref<boolean>(false)
+    const total = ref<number>(0)
+    const statusDictList = ref<SysDictData[]>([]) // 工单状态字典列表
+
+	// 添加字典加载状态
+	const dictLoaded = ref<boolean>(false)
+
+	// 获取工单状态字典列表
+	const loadStatusDictList = async (): Promise<void> => {
+	    try {
+	        const result = await getDictDataByType('gxt_work_order_status')
+	        const resultObj = result as UTSJSONObject
+
+	        if (resultObj['code'] == 200) {
+	            const data = resultObj['data'] as any[]
+	            const dictData: SysDictData[] = []
+
+	            if (data.length > 0) {
+	                for (let i = 0; i < data.length; i++) {
+	                    const item = data[i] as UTSJSONObject
+	                    // 只提取需要的字段
+	                    const dictItem: SysDictData = {
+							dictValue: item['dictValue'] as string | null,
+							dictLabel: item['dictLabel'] as string | null,
+							dictCode: null,
+							dictSort: null,
+							dictType: null,
+							cssClass: null,
+							listClass: null,
+							isDefault: null,
+							status: null,
+							default: null,
+							createTime: null,
+							remark: null
+						}
+	                    dictData.push(dictItem)
+	                }
+	            }
+
+	            statusDictList.value = dictData
+	            dictLoaded.value = true
+	        }
+	    } catch (e: any) {
+	        console.error('获取工单状态字典失败:', e.message)
+	        dictLoaded.value = true
+	    }
+	}
+
+    // 加载列表数据
+    const loadData = async (isRefresh: boolean | null): Promise<void> => {
+        if (loading.value) {
+            // 如果正在加载,直接重置刷新状态
+            refreshing.value = false
+            return
+        }
+
+        try {
+            loading.value = true
+
+            // 处理默认值
+            const shouldRefresh = isRefresh != null ? isRefresh : false
+
+            if (shouldRefresh) {
+                page.value = 1
+            }
+			console.log("searchKeyword===" + keyword.value)
+            // 调用 API,传递关键字参数
+            const searchKeyword = keyword.value.length > 0 ? keyword.value : null
+
+            const result = await almostOverdueList(page.value, pageSize, searchKeyword)
+
+            // 提取响应数据
+            const resultObj = result as UTSJSONObject
+            const code = resultObj['code'] as number
+            const responseData = resultObj['rows'] as any[]
+            const responseTotal = resultObj['total'] as number
+
+            if (code == 200) {
+                // 将 any[] 转换为 acceptOrderInfo[]
+                const newData: acceptOrderInfo[] = []
+                for (let i = 0; i < responseData.length; i++) {
+                    const item = responseData[i] as UTSJSONObject
+                    const orderItem: acceptOrderInfo = {
+						orderType: item['orderType'] as Number,
+                        id: item['id'] as Number,
+						teamLeaderId: item['teamLeaderId'] != null ? (item['teamLeaderId'] as Number) : 0,
+						acceptUserId: item['acceptUserId'] != null ? (item['acceptUserId'] as Number) : 0,
+                        teamLeaderName: item['teamLeaderName'] as string | null,
+                        acceptUserName: item['acceptUserName'] as string | null,
+                        acceptTime: item['acceptTime'] as string | null,
+                        assignTime: item['assignTime'] as string | null,
+                        assignUserName: item['assignUserName'] as string | null,
+                        status: (item['status']==null)?0:item['status'] as Number,
+                        workOrderProjectNo: item['workOrderProjectNo'] as string | null,
+                        workOrderStatus: item['workOrderStatus'] as string | null,
+                        gxtCenterId: item['gxtCenterId'] as Number | 0,
+                        gxtCenter: item['gxtCenter'] as string | null,
+                        pcsStationId: item['pcsStationId'] as Number | 0,
+                        pcsStationName: item['pcsStationName'] as string | null,
+                        pcsDeviceId: item['pcsDeviceId'] as Number | 0,
+                        pcsDeviceName: item['pcsDeviceName'] as string | null,
+                        brand: item['brand'] as string | null,
+                        model: item['model'] as string | null,
+                        createTime: item['createTime'] as string | null,
+						suspendReason: item['suspendReason'] as string | null,
+						rejectionReason: item['rejectionReason'] as string | null
+                    }
+                    newData.push(orderItem)
+                }
+
+                if (shouldRefresh) {
+                    dataList.value = newData
+                } else {
+                    dataList.value = [...dataList.value, ...newData]
+                }
+
+                total.value = responseTotal
+                hasMore.value = dataList.value.length < responseTotal
+            } else {
+                const msg = resultObj['msg'] as string | null
+                uni.showToast({
+                    title: msg ?? '加载失败',
+                    icon: 'none'
+                })
+            }
+
+        } catch (e: any) {
+            uni.showToast({
+                title: e.message ?? '加载失败',
+                icon: 'none'
+            })
+        } finally {
+            loading.value = false
+            // #ifdef WEB
+            // Web 平台立即重置
+            refreshing.value = false
+            // #endif
+            // #ifndef WEB
+            // App 平台延迟重置刷新状态,确保 UI 更新
+            setTimeout(() => {
+                refreshing.value = false
+            }, 100)
+            // #endif
+        }
+
+        // #ifdef WEB
+        // Web 平台额外确保重置
+        refreshing.value = false
+        // #endif
+    }
+
+    // 辅助函数:从 any 类型提取属性
+    const getOrderType = (item: any | null): string => {
+        if (item == null) return ''
+        const orderInfoItem = item as acceptOrderInfo
+        return orderInfoItem.orderType == 1?"维修工单":"维保工单";
+    }
+
+    const getWorkOrderProjectNo = (item: any | null): string | null => {
+        if (item == null) return ''
+        const orderInfoItem = item as acceptOrderInfo
+        return orderInfoItem.workOrderProjectNo
+    }
+
+    const getPcsDeviceName = (item: any | null): string | null=> {
+        if (item == null) return ''
+        const orderInfoItem = item as acceptOrderInfo
+        return orderInfoItem.pcsDeviceName
+    }
+
+	const getCreateTime = (item: any | null): string|null => {
+	    if (item == null) return null
+	    const orderInfoItem = item as acceptOrderInfo
+	    return orderInfoItem.createTime
+	}
+
+	const getWorkOrderStatus = (item: any | null): string | null => {
+	    if (item == null) return ''
+	    const orderInfoItem = item as acceptOrderInfo
+	    const rawStatus = orderInfoItem.workOrderStatus
+
+	    if (rawStatus==null) return ''
+
+		// 如果字典尚未加载,返回原始值
+		if (!dictLoaded.value) {
+		    return rawStatus
+		}
+
+	    // 查找字典中对应的标签
+	    const dictItem = statusDictList.value.find(dict => dict.dictValue == rawStatus)
+	    return dictItem!=null ? dictItem.dictLabel : rawStatus
+	}
+	
+	const getStatusClass = (item: any | null): string => {
+		if (item == null) return ''
+		const orderInfoItem = item as acceptOrderInfo
+		const rawStatus = orderInfoItem.workOrderStatus
+		if (rawStatus==null) return ''
+		// const status = rawStatus
+		// 返回对应的状态类名
+		return `status-${rawStatus}`
+	}
+
+    // 下拉刷新
+    const handleRefresh = async (): Promise<void> => {
+        refreshing.value = true
+        await loadData(true as boolean | null)
+    }
+
+    // 加载更多
+    const loadMore = (): void => {
+        if (!hasMore.value || loading.value) {
+            return
+        }
+        page.value++
+        loadData(false as boolean | null)
+    }
+
+    // 搜索
+    const handleSearch = (): void => {
+        page.value = 1
+		console.log("======搜索=====" + keyword.value)
+        loadData(true as boolean | null)
+    }
+
+    // 点击列表项
+    const handleItemClick = (item: any | null, index: number): void => {
+        if (item == null) return
+        const orderItem = item as acceptOrderInfo
+		// 跳转到工单详情页
+		uni.navigateTo({
+		    url: `/pages/order/detail/index?id=${orderItem.id}&orderType=${orderItem.orderType}`
+		})
+    }
+
+    // 清空搜索
+    const clearSearch = (): void => {
+        keyword.value = ""
+        page.value = 1
+        loadData(true)
+    }
+
+	// 初始化
+	onMounted(() => {
+	    loadStatusDictList()
+	    loadData(true as boolean | null)
+	})
+
+    // 组件卸载前清理事件监听
+    onBeforeUnmount(() => {
+        refreshing.value = false
+        loading.value = false
+    })
+</script>
+
+<style lang="scss">
+    .list-page {
+        flex: 1;
+        background-color: #e8f0f9;
+    }
+
+    .search-bar {
+        padding: 20rpx 30rpx;
+        background-color: #d7eafe;
+    }
+
+    .search-box {
+        flex-direction: row;
+        align-items: center;
+        height: 72rpx;
+        padding: 0 24rpx;
+        background-color: #f5f5f5;
+        border-radius: 36rpx;
+
+        .search-icon {
+            width: 32rpx;
+            height: 32rpx;
+            margin-right: 12rpx;
+        }
+
+        .search-input {
+            flex: 1;
+            font-size: 28rpx;
+            color: #333333;
+        }
+
+        .clear-icon {
+            margin-left: 12rpx;
+            font-size: 28rpx;
+            color: #999999;
+        }
+    }
+
+    .list-item {
+        margin: 24rpx 30rpx;
+        background-color: #ffffff;
+        border-radius: 16rpx;
+    }
+
+    .item-container {
+        padding: 30rpx;
+    }
+
+    .item-header {
+        flex-direction: row;
+        align-items: center;
+        margin-bottom: 16rpx;
+
+        .item-title {
+            flex: 1;
+            font-size: 32rpx;
+            color: #333333;
+            font-weight: bold;
+        }
+
+        .detail-link {
+            font-size: 28rpx;
+            color: #999999;
+        }
+    }
+
+.item-header {
+  flex-direction: row;
+  align-items: flex-start;
+  margin-bottom: 16rpx;
+
+  .item-title {
+    font-size: 30rpx;
+    color: #333333;
+    font-weight: bold;
+    flex-wrap: wrap;
+    flex: 0 1 80%;
+    min-width: 0;
+  }
+
+  .info-value {
+    font-size: 28rpx;
+    color: #999999;
+    margin-left: auto;
+    flex: 0 0 auto;
+    white-space: nowrap;
+  }
+}
+.info-row {
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+
+  .info-label {
+    font-size: 26rpx;
+    color: #666;
+  }
+
+  .info-value {
+    font-size: 26rpx;
+  }
+}
+.text-gray{
+  font-size: 26rpx;
+  color: #666;
+}
+
+.status-tag {
+	padding: 8rpx 20rpx;
+	border-radius: 20rpx;
+	font-size: 24rpx;
+	white-space: nowrap;
+	margin-left: 20rpx;
+	border: 1rpx solid;
+}
+
+/* 待接单 */
+.status-assigned {
+	background-color: #ebf5ff;
+	color: #409eff;
+	border-color: #d8ebff;
+}
+
+
+/* 待结单 */
+.status-to_finish {
+	background-color: #fff7e6;
+	color: #fa8c16;
+	border-color: #ffd591;
+}
+
+/* 待审批 */
+.status-to_approve {
+	background-color: #fff7e6;
+	color: #fa8c16;
+	border-color: #ffd591;
+}
+
+/* 已挂起 */
+.status-suspended {
+	background-color: #fff2f0;
+	color: #ff4d4f;
+	border-color: #ffccc7;
+}
+
+/* 已完成 */
+.status-completed {
+	background-color: #f0f9eb;
+	color: #5cb87a;
+	border-color: #c2e7b0;
+}
+
+/* 待下发 */
+.status-to_issue {
+	background-color: #f4f4f5;
+	color: #909399;
+	border-color: #e9e9eb;
+}
+
+/* 已归档 */
+.status-archived {
+	background-color: #f0f9eb;
+	color: #5cb87a;
+	border-color: #c2e7b0;
+}
+</style>

+ 0 - 6
pages/order/index.uvue

@@ -648,9 +648,6 @@ import { getDictDataByType } from '../../api/dict/index'
 	background-color: #fff7e6;
 	color: #fa8c16;
 	border-color: #ffd591;
-	// background-color: #fff2f0;
-	// color: #ff4d4f;
-	// border-color: #ffccc7;
 }
 
 /* 待审批 */
@@ -658,9 +655,6 @@ import { getDictDataByType } from '../../api/dict/index'
 	background-color: #fff7e6;
 	color: #fa8c16;
 	border-color: #ffd591;
-	// background-color: #f6ffed;
-	// color: #52c41a;
-	// border-color: #b7eb8f;
 }
 
 /* 已挂起 */

+ 483 - 0
pages/order/overdue.uvue

@@ -0,0 +1,483 @@
+<template>
+	<uni-navbar-lite :showLeft=true title="超时工单"></uni-navbar-lite>
+    <view class="list-page">
+        <!-- 搜索栏 -->
+        <view class="search-bar">
+            <view class="search-box">
+                <image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
+                <input class="search-input" type="text" placeholder="搜索工单编号、风机编号" v-model="keyword" @confirm="handleSearch" @blur="handleSearch" />
+                <text v-if="keyword.length > 0" class="clear-icon" @click="clearSearch">✕</text>
+            </view>
+        </view>
+
+        <!-- 列表内容 -->
+        <common-list
+            :dataList="dataList"
+            :loading="loading"
+            :refreshing="refreshing"
+            :hasMore="hasMore"
+            @refresh="handleRefresh"
+            @loadMore="loadMore"
+            @itemClick="handleItemClick"
+        >
+            <template #default="{ item, index }">
+                <view class="list-item">
+                    <view class="item-container">
+                        <view class="item-header">
+							<text class="item-title">{{ getWorkOrderProjectNo(item) }}-{{ getPcsDeviceName(item) }}{{ getOrderType(item) }}</text>
+							<text class="status-tag" :class="getStatusClass(item)">{{ getWorkOrderStatus(item) }}</text>
+                        </view>
+						<view class="info-row">
+							<view class="info-label">
+								<text class="text-gray">{{ getCreateTime(item) }}</text>
+							</view>
+						</view>
+                    </view>
+                </view>
+            </template>
+        </common-list>
+    </view>
+</template>
+
+<script setup lang="uts">
+import { ref, onBeforeUnmount, onMounted } from 'vue'
+import type { acceptOrderInfo } from '../../types/order'
+import type { SysDictData } from '../../types/dict'
+import { overdueList } from '../../api/order/list'
+import { getDictDataByType } from '../../api/dict/index'
+
+    // 列表数据
+    const dataList = ref<acceptOrderInfo[]>([])
+    let keyword = ref<string>("")
+    const page = ref<number>(1)
+    const pageSize: number = 10
+    const hasMore = ref<boolean>(true)
+    const loading = ref<boolean>(false)
+    const refreshing = ref<boolean>(false)
+    const total = ref<number>(0)
+    const statusDictList = ref<SysDictData[]>([]) // 工单状态字典列表
+
+	// 添加字典加载状态
+	const dictLoaded = ref<boolean>(false)
+
+	// 获取工单状态字典列表
+	const loadStatusDictList = async (): Promise<void> => {
+	    try {
+	        const result = await getDictDataByType('gxt_work_order_status')
+	        const resultObj = result as UTSJSONObject
+
+	        if (resultObj['code'] == 200) {
+	            const data = resultObj['data'] as any[]
+	            const dictData: SysDictData[] = []
+
+	            if (data.length > 0) {
+	                for (let i = 0; i < data.length; i++) {
+	                    const item = data[i] as UTSJSONObject
+	                    // 只提取需要的字段
+	                    const dictItem: SysDictData = {
+							dictValue: item['dictValue'] as string | null,
+							dictLabel: item['dictLabel'] as string | null,
+							dictCode: null,
+							dictSort: null,
+							dictType: null,
+							cssClass: null,
+							listClass: null,
+							isDefault: null,
+							status: null,
+							default: null,
+							createTime: null,
+							remark: null
+						}
+	                    dictData.push(dictItem)
+	                }
+	            }
+
+	            statusDictList.value = dictData
+	            dictLoaded.value = true
+	        }
+	    } catch (e: any) {
+	        console.error('获取工单状态字典失败:', e.message)
+	        dictLoaded.value = true
+	    }
+	}
+
+    // 加载列表数据
+    const loadData = async (isRefresh: boolean | null): Promise<void> => {
+        if (loading.value) {
+            // 如果正在加载,直接重置刷新状态
+            refreshing.value = false
+            return
+        }
+
+        try {
+            loading.value = true
+
+            // 处理默认值
+            const shouldRefresh = isRefresh != null ? isRefresh : false
+
+            if (shouldRefresh) {
+                page.value = 1
+            }
+			console.log("searchKeyword===" + keyword.value)
+            // 调用 API,传递关键字参数
+            const searchKeyword = keyword.value.length > 0 ? keyword.value : null
+
+            const result = await overdueList(page.value, pageSize, searchKeyword)
+
+            // 提取响应数据
+            const resultObj = result as UTSJSONObject
+            const code = resultObj['code'] as number
+            const responseData = resultObj['rows'] as any[]
+            const responseTotal = resultObj['total'] as number
+
+            if (code == 200) {
+                // 将 any[] 转换为 acceptOrderInfo[]
+                const newData: acceptOrderInfo[] = []
+                for (let i = 0; i < responseData.length; i++) {
+                    const item = responseData[i] as UTSJSONObject
+                    const orderItem: acceptOrderInfo = {
+						orderType: item['orderType'] as Number,
+                        id: item['id'] as Number,
+						teamLeaderId: item['teamLeaderId'] != null ? (item['teamLeaderId'] as Number) : 0,
+						acceptUserId: item['acceptUserId'] != null ? (item['acceptUserId'] as Number) : 0,
+                        teamLeaderName: item['teamLeaderName'] as string | null,
+                        acceptUserName: item['acceptUserName'] as string | null,
+                        acceptTime: item['acceptTime'] as string | null,
+                        assignTime: item['assignTime'] as string | null,
+                        assignUserName: item['assignUserName'] as string | null,
+                        status: (item['status']==null)?0:item['status'] as Number,
+                        workOrderProjectNo: item['workOrderProjectNo'] as string | null,
+                        workOrderStatus: item['workOrderStatus'] as string | null,
+                        gxtCenterId: item['gxtCenterId'] as Number | 0,
+                        gxtCenter: item['gxtCenter'] as string | null,
+                        pcsStationId: item['pcsStationId'] as Number | 0,
+                        pcsStationName: item['pcsStationName'] as string | null,
+                        pcsDeviceId: item['pcsDeviceId'] as Number | 0,
+                        pcsDeviceName: item['pcsDeviceName'] as string | null,
+                        brand: item['brand'] as string | null,
+                        model: item['model'] as string | null,
+                        createTime: item['createTime'] as string | null,
+						suspendReason: item['suspendReason'] as string | null,
+						rejectionReason: item['rejectionReason'] as string | null
+                    }
+                    newData.push(orderItem)
+                }
+
+                if (shouldRefresh) {
+                    dataList.value = newData
+                } else {
+                    dataList.value = [...dataList.value, ...newData]
+                }
+
+                total.value = responseTotal
+                hasMore.value = dataList.value.length < responseTotal
+            } else {
+                const msg = resultObj['msg'] as string | null
+                uni.showToast({
+                    title: msg ?? '加载失败',
+                    icon: 'none'
+                })
+            }
+
+        } catch (e: any) {
+            uni.showToast({
+                title: e.message ?? '加载失败',
+                icon: 'none'
+            })
+        } finally {
+            loading.value = false
+            // #ifdef WEB
+            // Web 平台立即重置
+            refreshing.value = false
+            // #endif
+            // #ifndef WEB
+            // App 平台延迟重置刷新状态,确保 UI 更新
+            setTimeout(() => {
+                refreshing.value = false
+            }, 100)
+            // #endif
+        }
+
+        // #ifdef WEB
+        // Web 平台额外确保重置
+        refreshing.value = false
+        // #endif
+    }
+
+    // 辅助函数:从 any 类型提取属性
+    const getOrderType = (item: any | null): string => {
+        if (item == null) return ''
+        const orderInfoItem = item as acceptOrderInfo
+        return orderInfoItem.orderType == 1?"维修工单":"维保工单";
+    }
+
+    const getWorkOrderProjectNo = (item: any | null): string | null => {
+        if (item == null) return ''
+        const orderInfoItem = item as acceptOrderInfo
+        return orderInfoItem.workOrderProjectNo
+    }
+
+    const getPcsDeviceName = (item: any | null): string | null=> {
+        if (item == null) return ''
+        const orderInfoItem = item as acceptOrderInfo
+        return orderInfoItem.pcsDeviceName
+    }
+
+	const getCreateTime = (item: any | null): string|null => {
+	    if (item == null) return null
+	    const orderInfoItem = item as acceptOrderInfo
+	    return orderInfoItem.createTime
+	}
+
+	const getWorkOrderStatus = (item: any | null): string | null => {
+	    if (item == null) return ''
+	    const orderInfoItem = item as acceptOrderInfo
+	    const rawStatus = orderInfoItem.workOrderStatus
+
+	    if (rawStatus==null) return ''
+
+		// 如果字典尚未加载,返回原始值
+		if (!dictLoaded.value) {
+		    return rawStatus
+		}
+
+	    // 查找字典中对应的标签
+	    const dictItem = statusDictList.value.find(dict => dict.dictValue == rawStatus)
+	    return dictItem!=null ? dictItem.dictLabel : rawStatus
+	}
+	
+	const getStatusClass = (item: any | null): string => {
+		if (item == null) return ''
+		const orderInfoItem = item as acceptOrderInfo
+		const rawStatus = orderInfoItem.workOrderStatus
+		if (rawStatus==null) return ''
+		// const status = rawStatus
+		// 返回对应的状态类名
+		return `status-${rawStatus}`
+	}
+
+    // 下拉刷新
+    const handleRefresh = async (): Promise<void> => {
+        refreshing.value = true
+        await loadData(true as boolean | null)
+    }
+
+    // 加载更多
+    const loadMore = (): void => {
+        if (!hasMore.value || loading.value) {
+            return
+        }
+        page.value++
+        loadData(false as boolean | null)
+    }
+
+    // 搜索
+    const handleSearch = (): void => {
+        page.value = 1
+		console.log("======搜索=====" + keyword.value)
+        loadData(true as boolean | null)
+    }
+
+    // 点击列表项
+    const handleItemClick = (item: any | null, index: number): void => {
+        if (item == null) return
+        const orderItem = item as acceptOrderInfo
+		// 跳转到工单详情页
+		uni.navigateTo({
+		    url: `/pages/order/detail/index?id=${orderItem.id}&orderType=${orderItem.orderType}`
+		})
+    }
+
+    // 清空搜索
+    const clearSearch = (): void => {
+        keyword.value = ""
+        page.value = 1
+        loadData(true)
+    }
+
+	// 初始化
+	onMounted(() => {
+	    loadStatusDictList()
+	    loadData(true as boolean | null)
+	})
+
+    // 组件卸载前清理事件监听
+    onBeforeUnmount(() => {
+        refreshing.value = false
+        loading.value = false
+    })
+</script>
+
+<style lang="scss">
+    .list-page {
+        flex: 1;
+        background-color: #e8f0f9;
+    }
+
+    .search-bar {
+        padding: 20rpx 30rpx;
+        background-color: #d7eafe;
+    }
+
+    .search-box {
+        flex-direction: row;
+        align-items: center;
+        height: 72rpx;
+        padding: 0 24rpx;
+        background-color: #f5f5f5;
+        border-radius: 36rpx;
+
+        .search-icon {
+            width: 32rpx;
+            height: 32rpx;
+            margin-right: 12rpx;
+        }
+
+        .search-input {
+            flex: 1;
+            font-size: 28rpx;
+            color: #333333;
+        }
+
+        .clear-icon {
+            margin-left: 12rpx;
+            font-size: 28rpx;
+            color: #999999;
+        }
+    }
+
+    .list-item {
+        margin: 24rpx 30rpx;
+        background-color: #ffffff;
+        border-radius: 16rpx;
+    }
+
+    .item-container {
+        padding: 30rpx;
+    }
+
+    .item-header {
+        flex-direction: row;
+        align-items: center;
+        margin-bottom: 16rpx;
+
+        .item-title {
+            flex: 1;
+            font-size: 32rpx;
+            color: #333333;
+            font-weight: bold;
+        }
+
+        .detail-link {
+            font-size: 28rpx;
+            color: #999999;
+        }
+    }
+
+.item-header {
+  flex-direction: row;
+  align-items: flex-start;
+  margin-bottom: 16rpx;
+
+  .item-title {
+    font-size: 30rpx;
+    color: #333333;
+    font-weight: bold;
+    flex-wrap: wrap;
+    flex: 0 1 80%;
+    min-width: 0;
+  }
+
+  .info-value {
+    font-size: 28rpx;
+    color: #999999;
+    margin-left: auto;
+    flex: 0 0 auto;
+    white-space: nowrap;
+  }
+}
+.info-row {
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+
+  .info-label {
+    font-size: 26rpx;
+    color: #666;
+  }
+
+  .info-value {
+    font-size: 26rpx;
+  }
+}
+.text-gray{
+  font-size: 26rpx;
+  color: #666;
+}
+
+.status-tag {
+	padding: 8rpx 20rpx;
+	border-radius: 20rpx;
+	font-size: 24rpx;
+	white-space: nowrap;
+	margin-left: 20rpx;
+	border: 1rpx solid;
+}
+
+/* 待接单 */
+.status-assigned {
+	background-color: #ebf5ff;
+	color: #409eff;
+	border-color: #d8ebff;
+}
+
+
+/* 待结单 */
+.status-to_finish {
+	background-color: #fff7e6;
+	color: #fa8c16;
+	border-color: #ffd591;
+}
+
+/* 待审批 */
+.status-to_approve {
+	background-color: #fff7e6;
+	color: #fa8c16;
+	border-color: #ffd591;
+}
+
+/* 已挂起 */
+.status-suspended {
+	background-color: #fff2f0;
+	color: #ff4d4f;
+	border-color: #ffccc7;
+}
+
+/* 已完成 */
+.status-completed {
+	background-color: #f0f9eb;
+	color: #5cb87a;
+	border-color: #c2e7b0;
+}
+
+/* 待下发 */
+.status-to_issue {
+	background-color: #f4f4f5;
+	color: #909399;
+	border-color: #e9e9eb;
+}
+
+/* 已归档 */
+.status-archived {
+	background-color: #f0f9eb;
+	color: #5cb87a;
+	border-color: #c2e7b0;
+}
+
+// /* 超时 */
+// .status-overdue {
+// 	background-color: #fff2f0;
+// 	color: #ff4d4f;
+// 	border-color: #ffccc7;
+// }
+</style>