From 4b3c357f2fa24a27eac3fe59cbf64e34255ab212 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Tue, 30 Jun 2026 11:56:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 uniapps/work-app/src/pages/work/index.vue |  193 ++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 179 insertions(+), 14 deletions(-)

diff --git a/uniapps/work-app/src/pages/work/index.vue b/uniapps/work-app/src/pages/work/index.vue
index d0a0221..31499da 100644
--- a/uniapps/work-app/src/pages/work/index.vue
+++ b/uniapps/work-app/src/pages/work/index.vue
@@ -12,10 +12,18 @@
       <scroll-view class="eventBox" scroll-y :lower-threshold="80" >
         <div class="eventList">
           <div class="eventItem"  v-for="(item,index) in dataList" :key="index">
-            <img :src="item.eventImageUrl" alt="" @click="detailHandle(item)" />
+            <image
+							v-if="[1, 2].includes(item.attachmentType)"
+							:src="item?.aiImg || item?.eventImageUrl"
+							mode="aspectFill"
+							@click="detailHandle(item)"
+						/>
+						<div v-if="item.attachmentType === 3" class="videoBox" @click="detailHandle(item)">
+							<div class="playIcon"></div>
+						</div>
             <div class="informationDisplay">
-              <!--              <div class="itemTitle">{{item.event_name}}</div>-->
-              <div class="itemContent">{{formatDate(item.createTime) }}</div>
+							<div class="itemTitle">{{ item.eventName }}</div>
+              <div class="itemContent">{{ formatDate(item.createTime) }}</div>
             </div>
           </div>
         </div>
@@ -32,6 +40,12 @@
         </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>
 
@@ -39,10 +53,13 @@
 
 <script setup>
 
+import { getCurrentInstance, nextTick } from 'vue'
 import { useUserStore } from '@/store/index.js'
-import {getGdList,getstatusCount} from '/src/api/work/index.js'
+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([])
@@ -82,6 +99,106 @@
   keyword:''
 
 })
+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
+})
+
+const parseAiFrame = aiFrameSource => {
+	if (!aiFrameSource) return []
+	if (Array.isArray(aiFrameSource)) return aiFrameSource
+
+	try {
+		return JSON.parse(aiFrameSource)
+	} catch (error) {
+		console.log(error)
+		return []
+	}
+}
+
+const getImageInfo = src =>
+	new Promise(resolve => {
+		uni.getImageInfo({
+			src,
+			success: resolve,
+			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)
+	})
+
+const drawAiImage = async (url, aiFrameSource) => {
+	if (!url) return url
+
+	const aiFrame = parseAiFrame(aiFrameSource)
+	if (!aiFrame.length) return url
+
+	const imageInfo = await getImageInfo(url)
+	if (!imageInfo?.path || !imageInfo.width || !imageInfo.height) return url
+
+	const width = Math.round(imageInfo.width)
+	const height = Math.round(imageInfo.height)
+	canvasSize.value = { width, height }
+	await nextTick()
+
+	const ctx = uni.createCanvasContext('ai-image-canvas', canvasOwner)
+	ctx.drawImage(imageInfo.path, 0, 0, width, height)
+
+	aiFrame.forEach(item => {
+		const { x_cen, y_cen, width: boxWidth, height: boxHeight } = item.bbox || {}
+		if ([x_cen, y_cen, boxWidth, boxHeight].some(value => typeof value !== 'number')) return
+
+		const x = x_cen - boxWidth / 2
+		const y = y_cen - boxHeight / 2
+		const label = item.class_name || ''
+		const fontSize = Math.max(18, Math.round(width / 80))
+		const labelHeight = fontSize + 10
+		const labelY = y - labelHeight >= 0 ? y - labelHeight : y
+		const lineWidth = Math.max(3, Math.round(width / 640))
+
+		ctx.setStrokeStyle('#FF3B30')
+		ctx.setLineWidth(lineWidth)
+		ctx.strokeRect(x, y, boxWidth, boxHeight)
+
+		if (label) {
+			ctx.setFontSize(fontSize)
+			const labelWidth = label.length * fontSize + 16
+			ctx.setFillStyle('#FF3B30')
+			ctx.fillRect(x, labelY, labelWidth, labelHeight)
+			ctx.setFillStyle('#FFFFFF')
+			ctx.setTextBaseline('middle')
+			ctx.fillText(label, x + 8, labelY + labelHeight / 2)
+		}
+	})
+
+	return new Promise(resolve => {
+		ctx.draw(false, async () => {
+			const tempFilePath = await canvasToTempFilePath(width, height)
+			resolve(tempFilePath || url)
+		})
+	})
+}
+
 const getDataList = () => {
   if (loading.value || !hasMore.value) return
 
@@ -92,9 +209,19 @@
     keyword:listParams.value.keyword,
     onlyMine:currentTab.value=== 'myTickets' ? 1 : 0
   }
-  getGdList(params).then(res => {
+  getGdList(params).then(async res => {
     const response = res.data.data
-    dataList.value = response
+		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
@@ -150,7 +277,7 @@
 
 const detailHandle = (val) => {
   uni.navigateTo({
-    url: `/subPackages/workDetail/index?id=${val.id}`,
+    url: `/subPackages/workDetail/index?id=${val.id}` + (val.aiImg ? `&aiImg=${val.aiImg}` : ''),
   })
 }
 
@@ -261,12 +388,40 @@
       box-sizing: border-box;
       max-width: 100%;
 
-      img {
+      image,
+			.videoBox {
         width: 100%;
         height: 208rpx;
         border-radius: 12rpx;
         overflow: hidden;
+				display: block;
       }
+			.videoBox {
+				background: linear-gradient(135deg, #eef4ff 0%, #dfe8f8 100%);
+				display: flex;
+				align-items: center;
+				justify-content: center;
+			}
+			.playIcon {
+				width: 72rpx;
+				height: 72rpx;
+				border-radius: 50%;
+				background: rgba(29, 111, 233, 0.9);
+				position: relative;
+				box-shadow: 0 8rpx 18rpx rgba(29, 111, 233, 0.24);
+
+				&::after {
+					content: '';
+					position: absolute;
+					left: 29rpx;
+					top: 20rpx;
+					width: 0;
+					height: 0;
+					border-top: 16rpx solid transparent;
+					border-bottom: 16rpx solid transparent;
+					border-left: 22rpx solid #fff;
+				}
+			}
       .informationDisplay{
         width: 100%;
         position: absolute;
@@ -275,14 +430,16 @@
         border-radius: 0rpx 0rpx 12rpx 12rpx;
         display: flex;
         align-items: center;
-        justify-content: flex-end;
-        padding: 10rpx 0rpx 10rpx 0rpx;
+        justify-content: space-between;
+        padding: 10rpx 14rpx;
+				box-sizing: border-box;
         .itemTitle {
-          width: 144rpx;
+          flex: 1;
+					min-width: 0;
           font-family: Source Han Sans CN, Source Han Sans CN;
           font-weight: 500;
-          font-size: 28rpx;
-          color: #000000;
+          font-size: 24rpx;
+          color: #FFFFFF;
           white-space: nowrap;
           overflow: hidden;
           text-overflow: ellipsis;
@@ -293,7 +450,8 @@
           font-weight: 400;
           font-size: 24rpx;
           color: #FFFFFF;
-          padding-right: 14rpx;
+          margin-left: 12rpx;
+					white-space: nowrap;
 
         }
       }
@@ -319,5 +477,12 @@
     color: #999;
     width: 100%;
   }
+
+	.ai-image-canvas {
+		position: fixed;
+		left: -9999px;
+		top: -9999px;
+		pointer-events: none;
+	}
 }
 </style>

--
Gitblit v1.9.3