From 150ba60e3436b3e6a418b0a66723938b5dc2cfb8 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Fri, 31 Jul 2026 17:17:14 +0800
Subject: [PATCH] feat:处置功能

---
 uniapps/work-app/src/pages/work/index.vue |  643 +++++++++++++++++++++++++++++++--------------------------
 1 files changed, 348 insertions(+), 295 deletions(-)

diff --git a/uniapps/work-app/src/pages/work/index.vue b/uniapps/work-app/src/pages/work/index.vue
index 31499da..205c21e 100644
--- a/uniapps/work-app/src/pages/work/index.vue
+++ b/uniapps/work-app/src/pages/work/index.vue
@@ -1,18 +1,23 @@
 <template>
-  <div class="eventTickets" :style="{ paddingTop: topMargin + 'px' }">
-
-    <div class="searchTop">
-      <up-search placeholder="请输入关键字搜索"  :animation="true"  v-model="listParams.keyword" :show-action="false" @search="handleSearch" @clear="handleClear"></up-search>
-    </div>
-    <div class="listBox">
-      <div class="tabs-container">
-        <up-tabs :list="tabList" @click="handleClick"></up-tabs>
-      </div>
-<!-- @scrolltolower="loadMore" -->
-      <scroll-view class="eventBox" scroll-y :lower-threshold="80" >
-        <div class="eventList">
-          <div class="eventItem"  v-for="(item,index) in dataList" :key="index">
-            <image
+	<div class="eventTickets" :style="{ paddingTop: topMargin + 'px' }">
+		<div class="searchTop">
+			<up-search
+				placeholder="请输入关键字搜索"
+				:animation="true"
+				v-model="listParams.keyword"
+				:show-action="false"
+				@search="handleSearch"
+				@clear="handleClear"
+			></up-search>
+		</div>
+		<div class="listBox">
+			<div class="tabs-container">
+				<up-tabs :scrollable="true" :list="tabList" @click="handleClick"></up-tabs>
+			</div>
+			<scroll-view class="eventBox" scroll-y :lower-threshold="80" @scrolltolower="loadMore">
+				<div class="eventList">
+					<div class="eventItem" v-for="(item, index) in dataList" :key="index">
+						<image
 							v-if="[1, 2].includes(item.attachmentType)"
 							:src="item?.aiImg || item?.eventImageUrl"
 							mode="aspectFill"
@@ -21,88 +26,112 @@
 						<div v-if="item.attachmentType === 3" class="videoBox" @click="detailHandle(item)">
 							<div class="playIcon"></div>
 						</div>
-            <div class="informationDisplay">
+						<div class="informationDisplay">
 							<div class="itemTitle">{{ item.eventName }}</div>
-              <div class="itemContent">{{ formatDate(item.createTime) }}</div>
-            </div>
-          </div>
-        </div>
+							<div class="itemContent">{{ formatDate(item.createTime) }}</div>
+						</div>
+					</div>
+				</div>
 
-        <!-- 暂无数据提示 -->
-        <div class="noData" v-if="!loading && dataList.length === 0">
-          <text>暂无数据</text>
-        </div>
+				<!-- 暂无数据提示 -->
+				<div class="noData" v-if="!loading && dataList.length === 0">
+					<text>暂无数据</text>
+				</div>
 
-        <!-- 加载提示 -->
-        <div class="loadingMore">
-          <text v-if="loading">加载中...</text>
-
-        </div>
-      </scroll-view>
-    </div>
+				<!-- 加载提示 -->
+				<div class="loadingMore">
+					<text v-if="loading">加载中...</text>
+					<text v-else-if="!hasMore">没有更多了</text>
+				</div>
+			</scroll-view>
+		</div>
 		<canvas
 			canvas-id="ai-image-canvas"
 			id="ai-image-canvas"
 			class="ai-image-canvas"
 			:style="{ width: canvasSize.width + 'px', height: canvasSize.height + 'px' }"
 		></canvas>
-
-  </div>
-
+	</div>
 </template>
 
 <script setup>
-
 import { getCurrentInstance, nextTick } from 'vue'
 import { useUserStore } from '@/store/index.js'
-import {getGdList,getstatusCount} from '@/api/work/index.js'
-import dayjs from 'dayjs';
-import { getStatusBarHeight } from '@/utils/common';
+import { getGdList, getstatusCount } from '@/api/work/index.js'
+import dayjs from 'dayjs'
+import { getStatusBarHeight } from '@/utils/common'
 
 const canvasOwner = getCurrentInstance()?.proxy
 const userStore = useUserStore()
 const userInfo = userStore.userInfo
 const dataList = ref([])
-const currentTab=ref('all')
+const currentTab = ref('all')
 const loading = ref(false)
 const hasMore = ref(true)
 const tabList = ref([
-  {
-    name: '全部工单',
-    key: 'all',
-    badge: {
-      value: 0,
-      showZero: true
-    },
-
-  },
-  {
-    name: '我的工单',
-    key: 'myTickets',
-    badge: {
-      value: 0,
-      showZero: true
-    }
-  }
-
-
+	{
+		name: '全部工单',
+		key: 'all',
+		badge: {
+			value: 0,
+			showZero: true,
+		},
+	},
+	{
+		name: '我的工单',
+		key: 'myTickets',
+		badge: {
+			value: 0,
+			showZero: true,
+		},
+	},
+	{
+		name: '处置中',
+		key: 'disposing',
+		badge: {
+			value: 0,
+			showZero: true,
+		},
+	},
+	{
+		name: '已处置',
+		key: 'handled',
+		badge: {
+			value: 0,
+			showZero: true,
+		},
+	},
+	{
+		name: '已完成',
+		key: 'completed',
+		badge: {
+			value: 0,
+			showZero: true,
+		},
+	},
 ])
-const formatDate = (dateString) => {
-  return dayjs(dateString).format('MM/DD HH:mm');
-};
+// tab key 与事件状态码映射
+const tabStatusMap = {
+	disposing: 1,
+	handled: 3,
+	completed: 4,
+}
+const formatDate = dateString => {
+	return dayjs(dateString).format('MM/DD HH:mm')
+}
 const listParams = ref({
-
-  current: 1,
-  size: 12,
-  source: 1,
-  department:'',
-  keyword:''
-
+	current: 1,
+	size: 12,
+	source: 1,
+	department: '',
+	keyword: '',
+	eventStatus: '',
 })
-const mockGeojson = "[{\"score\":0.89990234375,\"bbox\":{\"x_cen\":195.5,\"y_cen\":326.5,\"width\":117.0,\"height\":265.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"},{\"score\":0.89306640625,\"bbox\":{\"x_cen\":1194.5,\"y_cen\":559.5,\"width\":115.0,\"height\":261.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"},{\"score\":0.88720703125,\"bbox\":{\"x_cen\":179.0,\"y_cen\":955.5,\"width\":124.0,\"height\":249.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"},{\"score\":0.88330078125,\"bbox\":{\"x_cen\":1198.5,\"y_cen\":260.5,\"width\":115.0,\"height\":285.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"},{\"score\":0.84716796875,\"bbox\":{\"x_cen\":204.5,\"y_cen\":71.5,\"width\":115.0,\"height\":143.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"},{\"score\":0.83203125,\"bbox\":{\"x_cen\":186.0,\"y_cen\":657.5,\"width\":114.0,\"height\":269.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"},{\"score\":0.78662109375,\"bbox\":{\"x_cen\":1205.5,\"y_cen\":49.5,\"width\":117.0,\"height\":99.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"}]"
+const mockGeojson =
+	'[{"score":0.89990234375,"bbox":{"x_cen":195.5,"y_cen":326.5,"width":117.0,"height":265.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"},{"score":0.89306640625,"bbox":{"x_cen":1194.5,"y_cen":559.5,"width":115.0,"height":261.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"},{"score":0.88720703125,"bbox":{"x_cen":179.0,"y_cen":955.5,"width":124.0,"height":249.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"},{"score":0.88330078125,"bbox":{"x_cen":1198.5,"y_cen":260.5,"width":115.0,"height":285.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"},{"score":0.84716796875,"bbox":{"x_cen":204.5,"y_cen":71.5,"width":115.0,"height":143.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"},{"score":0.83203125,"bbox":{"x_cen":186.0,"y_cen":657.5,"width":114.0,"height":269.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"},{"score":0.78662109375,"bbox":{"x_cen":1205.5,"y_cen":49.5,"width":117.0,"height":99.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}]'
 const canvasSize = ref({
 	width: 1,
-	height: 1
+	height: 1,
 })
 
 const parseAiFrame = aiFrameSource => {
@@ -125,26 +154,29 @@
 			fail: error => {
 				console.log(error)
 				resolve(null)
-			}
+			},
 		})
 	})
 
 const canvasToTempFilePath = (width, height) =>
 	new Promise(resolve => {
-		uni.canvasToTempFilePath({
-			canvasId: 'ai-image-canvas',
-			width,
-			height,
-			destWidth: width,
-			destHeight: height,
-			fileType: 'jpg',
-			quality: 0.92,
-			success: res => resolve(res.tempFilePath),
-			fail: error => {
-				console.log(error)
-				resolve('')
-			}
-		}, canvasOwner)
+		uni.canvasToTempFilePath(
+			{
+				canvasId: 'ai-image-canvas',
+				width,
+				height,
+				destWidth: width,
+				destHeight: height,
+				fileType: 'jpg',
+				quality: 0.92,
+				success: res => resolve(res.tempFilePath),
+				fail: error => {
+					console.log(error)
+					resolve('')
+				},
+			},
+			canvasOwner
+		)
 	})
 
 const drawAiImage = async (url, aiFrameSource) => {
@@ -200,202 +232,224 @@
 }
 
 const getDataList = () => {
-  if (loading.value || !hasMore.value) return
+	if (loading.value || !hasMore.value) return
 
-  loading.value = true
-  const params = {
-    current: listParams.value.current,
-    size: listParams.value.size,
-    keyword:listParams.value.keyword,
-    onlyMine:currentTab.value=== 'myTickets' ? 1 : 0
-  }
-  getGdList(params).then(async res => {
-    const response = res.data.data
-		const list = []
-		for (const item of response) {
-			// const aiImg = await drawAiImage(item.eventImageUrl, mockGeojson)
-			if (item.attachmentType !== 2) {
-				list.push(item)
-			}else{
-				const aiImg = await drawAiImage(item.eventImageUrl, item.geojson)
-				list.push({ ...item, aiImg })
+	loading.value = true
+	const params = {
+		current: listParams.value.current,
+		size: listParams.value.size,
+		keyword: listParams.value.keyword,
+		onlyMine: currentTab.value === 'myTickets' ? 1 : 0,
+		eventStatus: listParams.value.eventStatus,
+	}
+	getGdList(params)
+		.then(async res => {
+			const resData = res.data.data
+			const response = resData.records
+			const list = []
+			for (const item of response) {
+				// const aiImg = await drawAiImage(item.eventImageUrl, mockGeojson)
+				if (item.attachmentType !== 2) {
+					list.push(item)
+				} else {
+					const aiImg = await drawAiImage(item.eventImageUrl, item.geojson)
+					list.push({ ...item, aiImg })
+				}
 			}
-		}
-		dataList.value = list
-    // 根据当前页码决定是替换还是追加数据
-    // if (listParams.value.current === 1) {
-    //   dataList.value = response
-    // } else {
-    //   dataList.value = [...dataList.value, ...response]
-    // }
-
-    // // 判断是否还有更多数据
-    // if (response.length < listParams.value.size || response.current >= response.pages) {
-    //   hasMore.value = false
-    // } else {
-    //   hasMore.value = true
-    // }
-  }).finally(() => {
-    loading.value = false
-  })
-}
-const getstatusCountData=()=>{
-  const params={
-    keyword:listParams.value.keyword,
-  }
-	getstatusCount(params).then(res=>{
-		const response = res.data.data
-		 const {  totalCount,myCount } = response
-		tabList.value.forEach(tab=>{
-
-			if(tab.key === 'all'){
-        tab.badge.value = (totalCount || 0).toString()
-        tab.badge.showZero = true
-
-			}else if(tab.key === 'myTickets'){
-				tab.badge.value = (myCount || 0).toString()
-        tab.badge.showZero = true
+			// 根据当前页码决定是替换还是追加数据
+			if (listParams.value.current === 1) {
+				dataList.value = list
+			} else {
+				dataList.value = [...dataList.value, ...list]
+			}
+			// 判断是否还有更多数据
+			if (list.length < listParams.value.size || resData.current >= resData.pages) {
+				hasMore.value = false
+			} else {
+				hasMore.value = true
 			}
 		})
-
+		.finally(() => {
+			loading.value = false
+		})
+}
+const getstatusCountData = () => {
+	const params = {
+		keyword: listParams.value.keyword,
+	}
+	getstatusCount(params).then(res => {
+		const response = res.data.data
+		const { totalCount, myCount, disposingCount, disposedCount, completedCount } = response
+		tabList.value.forEach(tab => {
+			if (tab.key === 'all') {
+				tab.badge.value = (totalCount || 0).toString()
+				tab.badge.showZero = true
+			} else if (tab.key === 'myTickets') {
+				tab.badge.value = (myCount || 0).toString()
+				tab.badge.showZero = true
+			} else if (tab.key === 'disposing') {
+				tab.badge.value = (disposingCount || 0).toString()
+				tab.badge.showZero = true
+			} else if (tab.key === 'handled') {
+				tab.badge.value = (disposedCount || 0).toString()
+				tab.badge.showZero = true
+			} else if (tab.key === 'completed') {
+				tab.badge.value = (completedCount || 0).toString()
+				tab.badge.showZero = true
+			}
+		})
 	})
 }
 
-const handleClick = (item) => {
-  currentTab.value = item.key
+const handleClick = item => {
+	currentTab.value = item.key
+	listParams.value.eventStatus = tabStatusMap[item.key] ?? ''
 
-  listParams.value.current = 1
-  hasMore.value = true
-  dataList.value = []
-  getDataList()
+	listParams.value.current = 1
+	hasMore.value = true
+	dataList.value = []
+	getDataList()
 }
 const loadMore = () => {
-  if (loading.value || !hasMore.value) return
-  listParams.value.current++
-  getDataList()
+	if (loading.value || !hasMore.value) return
+	listParams.value.current++
+	getDataList()
 }
 
-const detailHandle = (val) => {
-  uni.navigateTo({
-    url: `/subPackages/workDetail/index?id=${val.id}` + (val.aiImg ? `&aiImg=${val.aiImg}` : ''),
-  })
+const detailHandle = val => {
+	uni.navigateTo({
+		url: `/subPackages/workDetail/index?id=${val.id}` + (val.aiImg ? `&aiImg=${val.aiImg}` : ''),
+	})
 }
 
 // 搜索功能
 const handleSearch = () => {
-  listParams.value.current = 1
-  hasMore.value = true
-  dataList.value = []
-  getDataList()
-  getstatusCountData()
+	listParams.value.current = 1
+	hasMore.value = true
+	dataList.value = []
+	getDataList()
+	getstatusCountData()
 }
 // 清除搜索
 const handleClear = () => {
-  listParams.value.keyword = ''
-  listParams.value.current = 1
-  hasMore.value = true
-  dataList.value = []
-  getDataList()
-  getstatusCountData()
+	listParams.value.keyword = ''
+	listParams.value.current = 1
+	hasMore.value = true
+	dataList.value = []
+	getDataList()
+	getstatusCountData()
 }
 const topMargin = getStatusBarHeight()
 onShow(() => {
-  listParams.value.current = 1
-  hasMore.value = true
-  dataList.value = []
-  getDataList()
-  getstatusCountData()
+	listParams.value.current = 1
+	hasMore.value = true
+	dataList.value = []
+	getDataList()
+	getstatusCountData()
 })
 </script>
 
 <style scoped lang="scss">
 .eventTickets {
-  padding: 0 20rpx;
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  box-sizing: border-box;
-  width: 100%;
-  overflow-x: hidden;
-  .searchTop {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding-right: 24rpx;
-    width: 100%;
-    height: 108rpx;
-    :deep(){
-      .u-search__content{
-        background: #fff !important;
-      }
-      .u-search__content__input{
-        background: #fff !important;
-      }
-    }
-  }
-  :deep(){
+	padding: 0 20rpx;
+	display: flex;
+	flex-direction: column;
+	height: 100%;
+	box-sizing: border-box;
+	width: 100%;
+	overflow-x: hidden;
+	.searchTop {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding-right: 24rpx;
+		width: 100%;
+		height: 108rpx;
+		:deep() {
+			.u-search__content {
+				background: #fff !important;
+			}
+			.u-search__content__input {
+				background: #fff !important;
+			}
+		}
+	}
+	:deep() {
+		.u-tabs__wrapper__nav__line {
+			// width: 80rpx !important;
+			background: #1d6fe9 !important;
+		}
+		.u-badge {
+			background-color: #1d6fe9 !important;
+			margin-top: -14px !important;
+			margin-left: -5px !important;
+		}
+	}
+	tabs-container {
+		display: flex;
+		align-items: center;
+		height: 98rpx;
+		width: 100%;
+		overflow-x: auto; // 横向滚动
+		overflow-y: hidden;
+		white-space: nowrap; // 防止换行
+		-webkit-overflow-scrolling: touch; // 移动端平滑滚动
 
-    .u-tabs__wrapper__nav__line{
-      // width: 80rpx !important;
-      background: #1D6FE9 !important;
-    }
-    .u-badge {
-      background-color: #1d6fe9 !important;
-      margin-top: -14px !important;
-      margin-left: -5px !important;
-    }
-  }
+		// 隐藏滚动条(可选)
+		&::-webkit-scrollbar {
+			height: 0;
+			display: none;
+		}
+	}
 
+	// 子元素不压缩
+	.tabs-container > * {
+		flex-shrink: 0;
+	}
+	.listBox {
+		position: relative;
+		display: flex;
+		flex-direction: column;
+		flex: 1;
+		width: 100%;
+		box-sizing: border-box;
+	}
+	.eventBox {
+		padding: 20rpx 0;
+		overflow-y: auto;
+		overflow-x: hidden;
+		height: 0;
+		flex-grow: 1;
+		align-content: flex-start;
+		width: 100%;
+		box-sizing: border-box;
+	}
 
-  .tabs-container{
-    display: flex;
-    justify-content: center;
-    height: 98rpx;
-  }
-  .listBox {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    flex: 1;
-    width: 100%;
-    box-sizing: border-box;
-  }
-  .eventBox {
-    padding: 20rpx 0;
-    overflow-y: auto;
-    overflow-x: hidden;
-    height: 0;
-    flex-grow: 1;
-    align-content: flex-start;
-    width: 100%;
-    box-sizing: border-box;
-  }
+	.eventList {
+		display: flex;
+		flex-wrap: wrap;
+		gap: 20rpx;
+		width: 100%;
+		box-sizing: border-box;
+		justify-content: space-between;
 
-  .eventList {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 20rpx;
-    width: 100%;
-    box-sizing: border-box;
-    justify-content: space-between;
+		.eventItem {
+			width: calc(50% - 10rpx);
+			background-color: #fff;
+			border-radius: 12rpx;
+			overflow: hidden;
+			position: relative;
+			box-sizing: border-box;
+			max-width: 100%;
 
-    .eventItem {
-      width: calc(50% - 10rpx);
-      background-color: #fff;
-      border-radius: 12rpx;
-      overflow: hidden;
-      position: relative;
-      box-sizing: border-box;
-      max-width: 100%;
-
-      image,
+			image,
 			.videoBox {
-        width: 100%;
-        height: 208rpx;
-        border-radius: 12rpx;
-        overflow: hidden;
+				width: 100%;
+				height: 208rpx;
+				border-radius: 12rpx;
+				overflow: hidden;
 				display: block;
-      }
+			}
 			.videoBox {
 				background: linear-gradient(135deg, #eef4ff 0%, #dfe8f8 100%);
 				display: flex;
@@ -422,61 +476,60 @@
 					border-left: 22rpx solid #fff;
 				}
 			}
-      .informationDisplay{
-        width: 100%;
-        position: absolute;
-        bottom: 3px;
-        background: rgba(11,11,11,0.35);
-        border-radius: 0rpx 0rpx 12rpx 12rpx;
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-        padding: 10rpx 14rpx;
+			.informationDisplay {
+				width: 100%;
+				position: absolute;
+				bottom: 3px;
+				background: rgba(11, 11, 11, 0.35);
+				border-radius: 0rpx 0rpx 12rpx 12rpx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding: 10rpx 14rpx;
 				box-sizing: border-box;
-        .itemTitle {
-          flex: 1;
+				.itemTitle {
+					flex: 1;
 					min-width: 0;
-          font-family: Source Han Sans CN, Source Han Sans CN;
-          font-weight: 500;
-          font-size: 24rpx;
-          color: #FFFFFF;
-          white-space: nowrap;
-          overflow: hidden;
-          text-overflow: ellipsis;
-        }
-
-        .itemContent {
-          font-family: Source Han Sans CN, Source Han Sans CN;
-          font-weight: 400;
-          font-size: 24rpx;
-          color: #FFFFFF;
-          margin-left: 12rpx;
+					font-family: Source Han Sans CN, Source Han Sans CN;
+					font-weight: 500;
+					font-size: 24rpx;
+					color: #ffffff;
 					white-space: nowrap;
+					overflow: hidden;
+					text-overflow: ellipsis;
+				}
 
-        }
-      }
-    }
-  }
+				.itemContent {
+					font-family: Source Han Sans CN, Source Han Sans CN;
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #ffffff;
+					margin-left: 12rpx;
+					white-space: nowrap;
+				}
+			}
+		}
+	}
 
-  .loadingMore {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    height: 80rpx;
-    font-size: 28rpx;
-    color: #999;
-    width: 100%;
-  }
+	.loadingMore {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		height: 80rpx;
+		font-size: 28rpx;
+		color: #999;
+		width: 100%;
+	}
 
-  .noData {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    height: 300rpx;
-    font-size: 28rpx;
-    color: #999;
-    width: 100%;
-  }
+	.noData {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		height: 300rpx;
+		font-size: 28rpx;
+		color: #999;
+		width: 100%;
+	}
 
 	.ai-image-canvas {
 		position: fixed;

--
Gitblit v1.9.3