张含笑
2025-10-11 446823f71c9f7b867244035433febb3d8fd66573
feat:事件工单
2 files modified
1 files added
526 ■■■■ changed files
src/api/work/index.js 24 ●●●●● patch | view | raw | blame | history
src/pages/work/index.vue 255 ●●●● patch | view | raw | blame | history
src/subPackages/workDetail/index.vue 247 ●●●●● patch | view | raw | blame | history
src/api/work/index.js
New file
@@ -0,0 +1,24 @@
import {request} from "@/utils/index.js";
export const getList = (data) => {
  return request({
    url: '/drone-device-core/jobEvent/eventPage',
    method: 'post',
    data,
  })
}
// 获取状态统计数据
export const getstatusCount = (params) => {
  return request({
    url: '/drone-device-core/jobEvent/getstatusCount',
    method: 'get',
    params,
  })
}
export const getStepInfo = (eventNum) => {
  return request({
    url: '/drone-device-core/jobEvent/getStepInfo',
    method: 'get',
    params: { eventNum }
  })
}
src/pages/work/index.vue
@@ -1,67 +1,208 @@
<!-- 事件工单 -->
<template>
  <view class="min-h-screen flex flex-col items-center">
    <image
      class="mb-50rpx mt-200rpx h-200rpx w-200rpx"
      src="@/static/images/logo.png"
      width="200rpx"
      height="200rpx"
    />
    <view class="flex justify-center">
      <text class="font-size-36rpx">
        {{ $t("home.intro") }}
      </text>
    </view>
    <view class="mt-100rpx flex gap-30rpx">
      <lang-select />
      <view class="cursor-pointer" @click="toGithub">
        <view class="i-mdi-github text-40rpx" />
      </view>
    </view>
    <view class="flex flex-col eventTickets">
        <view class="searchTop">
            <up-search placeholder="请输入关键字搜索"  :animation="true"  v-model="listParams.keyword" :show-action="false"></up-search>
            <div>111</div>
        </view>
        <view>
            <up-tabs :list="tabList" @click="handleClick"></up-tabs>
            <view class="eventBox">
                <view class="eventItem"  v-for="(item,index) in dataList" :key="index">
                    <img :src="item.photo_url" alt="" @click="detailHandle(item)" />
                    <div class="itemTitle">{{item.event_name}}</div>
                    <div class="itemContent">
                        <div class="itemStatus">
                            <span v-if="item.status === 0" style="background-color: #ff7411;"></span>
                            <span v-else-if="item.status === 2" style="background-color: #ff472f;"></span>
                            <span v-else-if="item.status === 3" style="background-color: #ffc300;"></span>
                            <span v-else-if="item.status === 4" style="background-color: #06d957;"></span>
                            <p>{{formatDate(item.create_time) }}</p>
                        </div>
                        <div>全屏</div>
                    </div>
                </view>
            </view>
        </view>
    </view>
    <!-- #ifdef MP-WEIXIN -->
    <!-- 隐私协议组件 -->
    <agree-privacy
      v-model="showAgreePrivacy"
      :disable-check-privacy="false"
      @agree="handleAgree"
    />
    <!-- #endif -->
  </view>
</template>
<script setup>
// #ifdef MP-WEIXIN
import { useShare } from "@/hooks";
// #endif
// #ifdef MP-WEIXIN
// 分享使用示例
const { onShareAppMessage, onShareTimeline } = useShare({
  title: "首页",
  path: "pages/tab/map/index",
  imageUrl: "",
});
onShareAppMessage();
onShareTimeline();
// #endif
import {getList,getstatusCount} from '/src/api/work/index.js'
    import dayjs from 'dayjs';
    const dataList = ref([])
    const keyword = ref('')
    const tabList = ref([{
            name: '我的工单',
            key: 'myTickets',
            badge: {
                value: 1
            }
        },
        {
            name: '全部状态',
            key: 'all',
            badge: {
                value: 2
            },
            status: null
        },
        {
            name: '待审核',
            key: 'pending',
            badge: {
                value: 3
            },
            status: '2'
        },
        {
            name: '待处理',
            key: 'processing',
            badge: {
                value: 4
            },
            status: '0'
        },
        {
            name: '处理中',
            key: 'inProgress',
            badge: {
                value: 5
            },
            status: '3'
        },
        {
            name: '已完成',
            key: 'completed',
            badge: {
                value: 6
            },
            status: '4'
        }
    ])
    const formatDate = (dateString) => {
        return dayjs(dateString).format('MM/DD HH:mm');
    };
    const listParams = ref({
        status: null,
        current: 1,
        size: 9999,
        source: 1,
        department:'',
        keyword:''
const title = ref();
title.value = import.meta.env.VITE_APP_TITLE;
    })
    const getDataList = () => {
        const params = {
            current: 1,
            size: 9999,
            source: 1,
            status:listParams.value.status,
            event_name:listParams.value.keyword
        }
        getList(params).then(res => {
            const response = res.data.data.records
            dataList.value = response
        })
    }
        const getstatusCountData=()=>{
            getstatusCount().then(res=>{
                const response = res.data.data
                 const { statusCount, totalCount, userCount } = response
                tabList.value.forEach(tab=>{
                    if(tab.key === 'all'){
                        tab.badge.value = totalCount || 0
const showAgreePrivacy = ref(false);
                    }else if(tab.key === 'myTickets'){
                        tab.badge.value = userCount || 0
                    }else{
                        tab.badge.value=statusCount[String(tab.status)] || 0
                    }
                })
            })
        }
// 同意隐私协议
function handleAgree() {
  console.log("同意隐私政策");
}
// 打开github
function toGithub() {
  if (window?.open) {
    window.open("https://github.com/oyjt/uniapp-vue3-template");
  } else {
    uni.$u.toast("请使用浏览器打开");
  }
}
    const handleClick = (item) => {
    listParams.value.status = item.status
        getDataList()
    }
    const detailHandle = (val) => {
        uni.navigateTo({
            url: `/subPackages/workDetail/index?eventNum=${val.event_num}`,
        })
    }
    onShow(() => {
        getDataList()
        getstatusCountData()
    })
</script>
<style scoped lang="scss">
    .eventTickets {
        padding: 0 10px;
        .searchTop {
            display: flex;
            align-items: center;
            margin-top: 10px;
        }
        :deep(.u-badge) {
            background-color: #1d6fe9 !important;
        }
        .eventBox {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            padding: 10px 0;
            background-color: #ebeff2;
            .eventItem {
                width: calc(50% - 5px);
                background-color: #fff;
                border-radius: 5px;
                overflow: hidden;
                img {
                    width: 100%;
                    height: 100px;
                    border-radius: 5px;
                    overflow: hidden;
                }
                .itemTitle {
                    padding: 0 5px;
                     white-space: nowrap;
                    overflow: hidden;
                    text-overflow: ellipsis;
                }
                .itemContent {
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    padding: 5px;
                    .itemStatus {
                        display: flex;
                        align-items: center;
                        span {
                            display: inline-block;
                            width: 10px;
                            height: 10px;
                            border-radius: 50%;
                            margin-right: 5px;
                        }
                    }
                }
            }
        }
    }
</style>
src/subPackages/workDetail/index.vue
@@ -1,18 +1,239 @@
<!--
 * @Author       : yuan
 * @Date         : 2025-09-29 13:52:16
 * @LastEditors  : yuan
 * @LastEditTime : 2025-09-29 13:53:32
 * @FilePath     : \src\subPackages\workDetail\index.vue
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-09-29 13:52:16
-->
<!-- 工单详情 - 包含待审核、待处理、处理中、已完成 -->
<template>
  <view> 基础 </view>
    <view class="workDetailContainer">
        <div class="detailTop">
            <div class="image-container">
                <u-image class="detailImage" :src="workDetailData.photo_url" mode="cover" width="100%" height="200px"
                    @click="previewImage(workDetailData.photo_url)"></u-image>
                <div class="detailTitle">
                    <div class="titleText">
                        <p>{{workDetailData.event_name}}</p>
                        <p>{{workDetailData.create_time}}</p>
                    </div>
                </div>
            </div>
        </div>
        <!-- 步骤条 -->
        <div class="stepContainer">
          <up-steps :current="currentStep" direction="column">
              <up-steps-item
                v-for="(step, index) in stepTitles"
                :key="index"
              >
                <template #title>
                  <div class="horizontal-step">
                    <span class="step-title">{{ step.title }}</span>
                    <div class="step-desc" v-if="stepResponse[index]">
                      <span>{{ stepResponse[index].name || '' }}</span>
                      <span>{{ stepResponse[index].create_time || '' }}</span>
                    </div>
                  </div>
                </template>
              </up-steps-item>
            </up-steps>
        </div>
        <!-- 工单内容 -->
        <div class="workOrderContent">
            <div class="workOrderTitle">工单内容</div>
            <div class="workOrderContainer">
                <div>工单名称:{{workDetailData.event_name}}</div>
                <div>工单类型:{{workDetailData.$type}}</div>
                <div>关联任务:{{workDetailData.job_name}}</div>
                <div>工单创建人:{{workDetailData.creator}}</div>
                <div>事件地址:{{workDetailData.address}}</div>
                <div>关联算法:{{workDetailData.ai_types}}</div>
                <div>发起部门:{{workDetailData.dept_name}}</div>
                <div>发起任务时间:{{workDetailData.create_time}}</div>
                <div>工单内容:{{workDetailData.remark}}</div>
            </div>
        </div>
        <!-- 操作按钮 -->
        <div class="actionButton">
            <div class="leftBtn" @click="leftClick">
                上一页
            </div>
            <div class="btngroups">
                <up-button type="primary" text="通过"></up-button>
                <up-button type="error" text="不通过"></up-button>
            </div>
            <div class="leftBtn" @click="rightClick">
                下一页
            </div>
        </div>
    </view>
</template>
<script setup></script>
<script setup>
    import {getStepInfo,getList} from '/src/api/work/index.js'
    const eventNum = ref('');
    const currentStatus = ref('')
    const stepResponse = ref([]);
    const currentStep = ref(0);
    // 工单内容
    const workDetailData = ref({})
    const stepTitles  =ref([
        {
            title: '待审核',
            status: '2'
        },
        {
            title: '待处理',
            status: '0'
        },
        {
            title: '处理中',
            status: '3'
        },{
            title: '已完成',
            status: '4'
        }
    ])
    onLoad(async (options) => {
      eventNum.value = options.eventNum;
      await getDataList(eventNum.value);
      await getStepInfoData(eventNum.value);
    });
    const getDataList = async (val) => {
      const params = {
        current: 1,
        size: 9999,
        source: 1,
        event_name: val
      }
      const res = await getList(params);
      const response = res.data.data.records;
      workDetailData.value = response[0];
      currentStatus.value = workDetailData.value.status;
    }
    // 步骤条
    const calculateCurrentStep = (status) => {
      const statusToStep = {
        '2': 0,  // 待审核 -> 第0步
        '0': 1,  // 待处理 -> 第1步
        '3': 2,  // 处理中 -> 第2步
        '4': 3   // 已完成 -> 第3步
      }
      return statusToStep[String(status)] || 0
    }
    const getStepInfoData = async (val) => {
      const res = await getStepInfo(val);
      stepResponse.value = res.data.data;
      currentStep.value = calculateCurrentStep(currentStatus.value);
    }
    // 图片预览
    const previewImage = (url) => {
        uni.previewImage({
            urls: [url],
            current: 0
        });
    };
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
    .workDetailContainer {
        padding: 0 10px;
        .detailTop {
            .image-container {
                position: relative;
                width: 100%;
                height: 200px;
                .detailImage {
                    width: 100%;
                    height: 100%;
                    display: block;
                    object-fit: cover
                }
                .detailTitle {
                    position: absolute;
                    left: 0;
                    bottom: 0;
                        width: 100%;
                }
                .titleText {
                    display: flex;
                    width: 100%;
                    justify-content: space-between;
                    align-items: center;
                }
            }
        }
        .stepContainer {
            margin-top: 10px;
            display: flex;
            background-color: aquamarine;
            border-radius: 5px;
            padding: 10px;
             .horizontal-step {
                display: flex;
                align-items: center;
                gap: 12px;
                .step-title {
                  min-width: 60px;
                }
                .step-desc {
                  display: flex;
                  justify-content: space-around;
                  gap: 8px;
                  // color: #666;
                  font-size: 14px;
                }
              }
        }
        .actionButton{
            display: flex;
            justify-content: space-between;
        margin-bottom: 10px;
            .btngroups {
                display: flex;
                justify-content: space-between;
                .u-button {
                   padding: 9px 20px;
                     height: 32px;
                     &:last-child {
                         margin-left: 12px;
                         margin-right: 12px;
                       }
                }
            }
            .leftBtn {
              width: 70px;
              height: 32px;
              background-color: #999;
              border-radius: 5px;
              text-align: center;
              line-height: 32px;
              color: #fff;
              cursor: pointer;
              opacity: 0.8;
            }
            .disableds {
              background: #999 !important;
              cursor: not-allowed !important;
              pointer-events: none;
              opacity: 0.3 !important;
            }
        }
        .workOrderContent {
            margin-top: 15px;
            .workOrderContainer {
                div {
                    margin-bottom: 10px;
                }
            }
        }
    }
</style>