| | |
| | | <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 |
| | | v-if="[1, 2].includes(item.attachmentType)" |
| | | :src="item?.aiImg || item?.eventImageUrl" |
| | | mode="aspectFill" |
| | | <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="item.isImage" :src="item.thumbnail" mode="aspectFill" @click="detailHandle(item)" /> |
| | | <div |
| | | v-else-if="item.isVideo" |
| | | class="videoBox" |
| | | :style="item.thumbnailUrl ? { backgroundImage: `url(${item.thumbnailUrl})`, backgroundSize: 'cover', backgroundPosition: 'center' } : {}" |
| | | @click="detailHandle(item)" |
| | | /> |
| | | <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" |
| | | :width="canvasSize.width" |
| | | :height="canvasSize.height" |
| | | :style="{ width: canvasSize.width + 'px', height: canvasSize.height + 'px' }" |
| | | ></canvas> |
| | | |
| | | </div> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | |
| | | import { getCurrentInstance, nextTick } from 'vue' |
| | | import { getCurrentInstance } 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 PREVIEW_IMG_CACHE_KEY_PREFIX = 'work-detail-ai-img-' |
| | | const imageTaskMap = new Map() |
| | | let imageRenderQueue = Promise.resolve() |
| | | 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\"}]" |
| | | let requestSeq = 0 // 请求序列号,用于丢弃快速切换tab时的过期响应 |
| | | let imageGeneration = 0 // 图片任务批次,用于取消已离开列表的绘制任务 |
| | | const MAX_CANVAS_EDGE = 2048 |
| | | const canvasSize = ref({ |
| | | width: 1, |
| | | height: 1 |
| | | width: MAX_CANVAS_EDGE, |
| | | height: MAX_CANVAS_EDGE, |
| | | }) |
| | | |
| | | // 解析算法框数据 |
| | | const parseAiFrame = aiFrameSource => { |
| | | if (!aiFrameSource) return [] |
| | | if (Array.isArray(aiFrameSource)) return aiFrameSource |
| | |
| | | } |
| | | } |
| | | |
| | | // 获取图片信息 |
| | | const getImageInfo = src => |
| | | new Promise(resolve => { |
| | | uni.getImageInfo({ |
| | |
| | | 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) => { |
| | | 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 drawAiImage = async (imageInfo, aiFrame, scaleX = 1, scaleY = 1) => { |
| | | if (!imageInfo?.path || !imageInfo.width || !imageInfo.height || !aiFrame.length) return '' |
| | | const canvasScale = Math.min(1, MAX_CANVAS_EDGE / imageInfo.width, MAX_CANVAS_EDGE / imageInfo.height) |
| | | const width = Math.round(imageInfo.width * canvasScale) |
| | | const height = Math.round(imageInfo.height * canvasScale) |
| | | const boxScaleX = scaleX * canvasScale |
| | | const boxScaleY = scaleY * canvasScale |
| | | |
| | | const ctx = uni.createCanvasContext('ai-image-canvas', canvasOwner) |
| | | ctx.drawImage(imageInfo.path, 0, 0, width, height) |
| | |
| | | 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 scaledWidth = boxWidth * boxScaleX |
| | | const scaledHeight = boxHeight * boxScaleY |
| | | const x = x_cen * boxScaleX - scaledWidth / 2 |
| | | const y = y_cen * boxScaleY - scaledHeight / 2 |
| | | const label = item.class_name || '' |
| | | const fontSize = Math.max(18, Math.round(width / 80)) |
| | | const labelHeight = fontSize + 10 |
| | |
| | | |
| | | ctx.setStrokeStyle('#FF3B30') |
| | | ctx.setLineWidth(lineWidth) |
| | | ctx.strokeRect(x, y, boxWidth, boxHeight) |
| | | ctx.strokeRect(x, y, scaledWidth, scaledHeight) |
| | | |
| | | if (label) { |
| | | ctx.setFontSize(fontSize) |
| | |
| | | return new Promise(resolve => { |
| | | ctx.draw(false, async () => { |
| | | const tempFilePath = await canvasToTempFilePath(width, height) |
| | | resolve(tempFilePath || url) |
| | | resolve(tempFilePath) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | const getDataList = () => { |
| | | if (loading.value || !hasMore.value) return |
| | | // 生成缩略图地址 |
| | | const getThumbnailUrl = url => { |
| | | if (!url) return '' |
| | | return url.replace(/(\.[^./?]+)(\?.*)?$/, '_thumbnail$1$2') |
| | | } |
| | | |
| | | 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 }) |
| | | } |
| | | // 前置整理列表媒体字段 |
| | | const normalizeWorkItem = item => { |
| | | const isImage = [1, 2].includes(item.attachmentType) |
| | | const aiFrame = item.attachmentType === 2 ? parseAiFrame(item.geojson) : [] |
| | | return { |
| | | ...item, |
| | | isImage, |
| | | isVideo: item.attachmentType === 3, |
| | | thumbnail: item.eventImageUrl, |
| | | previewUrl: item.eventImageUrl, |
| | | thumbnailSource: isImage ? getThumbnailUrl(item.eventImageUrl) : '', |
| | | aiFrame, |
| | | imageReady: !aiFrame.length, |
| | | } |
| | | } |
| | | |
| | | // 串行绘制单条 AI 原图和缩略图 |
| | | const enqueueAiImages = (item, originalImageInfo, thumbnailImageInfo, generation) => { |
| | | const task = imageRenderQueue.then(async () => { |
| | | if (generation !== imageGeneration) return |
| | | const previewUrl = (await drawAiImage(originalImageInfo, item.aiFrame)) || item.eventImageUrl |
| | | if (generation !== imageGeneration) return |
| | | item.previewUrl = previewUrl |
| | | if (!thumbnailImageInfo) { |
| | | item.thumbnail = previewUrl |
| | | return |
| | | } |
| | | 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 scaleX = thumbnailImageInfo.width / originalImageInfo.width |
| | | const scaleY = thumbnailImageInfo.height / originalImageInfo.height |
| | | item.thumbnail = (await drawAiImage(thumbnailImageInfo, item.aiFrame, scaleX, scaleY)) || previewUrl |
| | | }) |
| | | imageRenderQueue = task.catch(() => {}) |
| | | return task |
| | | } |
| | | 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 |
| | | // 异步准备单条记录的缩略图和 AI 图片 |
| | | const prepareItemImages = async (item, generation) => { |
| | | if (!item.isImage || !item.eventImageUrl) return |
| | | const thumbnailImagePromise = getImageInfo(item.thumbnailSource) |
| | | if (!item.aiFrame.length) { |
| | | const thumbnailImageInfo = await thumbnailImagePromise |
| | | if (generation === imageGeneration && thumbnailImageInfo) item.thumbnail = item.thumbnailSource |
| | | return |
| | | } |
| | | |
| | | }else if(tab.key === 'myTickets'){ |
| | | tab.badge.value = (myCount || 0).toString() |
| | | tab.badge.showZero = true |
| | | } |
| | | }) |
| | | const [originalImageInfo, thumbnailImageInfo] = await Promise.all([ |
| | | getImageInfo(item.eventImageUrl), |
| | | thumbnailImagePromise, |
| | | ]) |
| | | if (generation !== imageGeneration) return |
| | | if (thumbnailImageInfo) item.thumbnail = item.thumbnailSource |
| | | if (!originalImageInfo) return |
| | | return enqueueAiImages(item, originalImageInfo, thumbnailImageInfo, generation) |
| | | } |
| | | |
| | | // 启动当前页图片后台处理任务 |
| | | const startImageTasks = (list, generation) => { |
| | | list.forEach(item => { |
| | | const task = prepareItemImages(item, generation) |
| | | .catch(() => {}) |
| | | .finally(() => { |
| | | item.imageReady = true |
| | | }) |
| | | if (item.id) imageTaskMap.set(item.id, task) |
| | | }) |
| | | } |
| | | |
| | | const handleClick = (item) => { |
| | | currentTab.value = item.key |
| | | // 获取工单列表 |
| | | const getDataList = () => { |
| | | if (!hasMore.value) return |
| | | |
| | | listParams.value.current = 1 |
| | | hasMore.value = true |
| | | dataList.value = [] |
| | | getDataList() |
| | | // 每次请求自增序列号,响应回来时校验是否过期 |
| | | const seq = ++requestSeq |
| | | const generation = imageGeneration |
| | | 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(res => { |
| | | // 已有新请求发出,丢弃过期响应 |
| | | if (seq !== requestSeq) return |
| | | const resData = res.data.data |
| | | const response = resData.records |
| | | const list = response.map(normalizeWorkItem) |
| | | let currentPageList = [] |
| | | // 根据当前页码决定是替换还是追加数据 |
| | | if (listParams.value.current === 1) { |
| | | dataList.value = list |
| | | currentPageList = dataList.value |
| | | } else { |
| | | const startIndex = dataList.value.length |
| | | dataList.value = [...dataList.value, ...list] |
| | | currentPageList = dataList.value.slice(startIndex) |
| | | } |
| | | // 判断是否还有更多数据 |
| | | if (list.length < listParams.value.size || resData.current >= resData.pages) { |
| | | hasMore.value = false |
| | | } else { |
| | | hasMore.value = true |
| | | } |
| | | startImageTasks(currentPageList, generation) |
| | | }) |
| | | .finally(() => { |
| | | // 仅在最新一次请求结束时关闭loading,避免过早关闭 |
| | | if (seq === requestSeq) 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 => { |
| | | imageGeneration++ |
| | | imageTaskMap.clear() |
| | | currentTab.value = item.key |
| | | listParams.value.eventStatus = tabStatusMap[item.key] ?? '' |
| | | |
| | | 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 = async val => { |
| | | const imageTask = imageTaskMap.get(val.id) |
| | | if (val.aiFrame.length && !val.imageReady && imageTask) { |
| | | uni.showLoading({ title: '图片处理中', mask: true }) |
| | | try { |
| | | await imageTask |
| | | } finally { |
| | | uni.hideLoading() |
| | | } |
| | | } |
| | | if (val.aiFrame.length) { |
| | | uni.setStorageSync(`${PREVIEW_IMG_CACHE_KEY_PREFIX}${val.id}`, val.previewUrl) |
| | | } |
| | | uni.navigateTo({ url: `/subPackages/workDetail/index?id=${val.id}` }) |
| | | } |
| | | |
| | | // 搜索功能 |
| | | const handleSearch = () => { |
| | | listParams.value.current = 1 |
| | | hasMore.value = true |
| | | dataList.value = [] |
| | | getDataList() |
| | | getstatusCountData() |
| | | imageGeneration++ |
| | | imageTaskMap.clear() |
| | | 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() |
| | | imageGeneration++ |
| | | imageTaskMap.clear() |
| | | 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() |
| | | imageGeneration++ |
| | | imageTaskMap.clear() |
| | | 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; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding-bottom: 48rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | .playIcon { |
| | | width: 72rpx; |
| | |
| | | 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; |