| | |
| | | <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> |
| | |
| | | </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> |
| | | |
| | |
| | | |
| | | <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([]) |
| | |
| | | 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 |
| | | |
| | |
| | | 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 |
| | |
| | | |
| | | const detailHandle = (val) => { |
| | | uni.navigateTo({ |
| | | url: `/subPackages/workDetail/index?id=${val.id}`, |
| | | url: `/subPackages/workDetail/index?id=${val.id}` + (val.aiImg ? `&aiImg=${val.aiImg}` : ''), |
| | | }) |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | font-weight: 400; |
| | | font-size: 24rpx; |
| | | color: #FFFFFF; |
| | | padding-right: 14rpx; |
| | | margin-left: 12rpx; |
| | | white-space: nowrap; |
| | | |
| | | } |
| | | } |
| | |
| | | color: #999; |
| | | width: 100%; |
| | | } |
| | | |
| | | .ai-image-canvas { |
| | | position: fixed; |
| | | left: -9999px; |
| | | top: -9999px; |
| | | pointer-events: none; |
| | | } |
| | | } |
| | | </style> |