吉安感知网项目-前端
罗广辉
2026-01-08 31d0fc3caacc159faddf0c4f3bb4f84bf7a00c69
Merge remote-tracking branch 'origin/master'
12 files modified
11 files added
2172 ■■■■■ changed files
applications/drone-command/env/.env.development 12 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/fzsb.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/title-bg.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/zcsb.png patch | view | raw | blame | history
applications/drone-command/src/styles/element-ui.scss 292 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue 12 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/EquipmentWarning.vue 61 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/HistoryWarning.vue 136 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue 93 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/MapContainer.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue 76 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue 202 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue 171 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue 110 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue 199 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/templateComponents/RealTemplate.vue 204 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue 43 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/index.vue 4 ●●● patch | view | raw | blame | history
uniapps/work-app/src/pages.json 13 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/login/index.vue 20 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/work/index.vue 199 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/index.vue 310 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/mapWork/index.vue 13 ●●●● patch | view | raw | blame | history
applications/drone-command/env/.env.development
@@ -1,3 +1,13 @@
###
 # @Author       : yuan
 # @Date         : 2026-01-07 14:58:30
 # @LastEditors  : yuan
 # @LastEditTime : 2026-01-08 15:14:52
 # @FilePath     : \applications\drone-command\env\.env.development
 # @Description  :
 # Copyright 2026 OBKoro1, All Rights Reserved.
 # 2026-01-07 14:58:30
###
NODE_ENV = 'development'
#开发环境配置
@@ -6,7 +16,7 @@
#开发环境代理地址(推荐本地新建文件 .env.development.local 来进行覆盖)
# VITE_APP_URL = https://wrj.shuixiongit.com/api
VITE_APP_URL= http://192.168.1.33:81
VITE_APP_URL= http://192.168.1.33
#新大屏地址
VITE_APP_DASHBOARD_URL = 'https://wrj.shuixiongit.com/command-center-dashboard/'
applications/drone-command/src/assets/images/dataCockpit/fzsb.png
applications/drone-command/src/assets/images/dataCockpit/title-bg.png
applications/drone-command/src/assets/images/dataCockpit/zcsb.png
applications/drone-command/src/styles/element-ui.scss
@@ -114,6 +114,7 @@
    .el-drawer__header {
      margin-bottom: 0;
    }
    .el-drawer__body {
      display: flex;
      flex-direction: column;
@@ -121,12 +122,16 @@
    }
  
  }
  .ztzf-el-card__body {
  
     .el-card__body {
      .avue-form{
      padding: 0;}}
            padding: 0;
  }
    }
}
  .ztzf-public-general-avue-crud {
    .el-table {
      tr {
@@ -149,6 +154,7 @@
      }
    }
  }
  .ztzf-layer-input {
      .el-input__wrapper {
          padding: 0 8px !important;
@@ -172,15 +178,18 @@
          }
      }
  }
  .ztzf-button {
    background-color: transparent !important;
    color: #fff !important;
  }
  .ztzf-el-tree {
    background-color: transparent !important;
    --el-tree-node-hover-bg-color:transparent !important;
    color: #fff !important;
  }
  .ztzf-layer-search {
    color: #fff !important;
    background-color: transparent !important;
@@ -216,6 +225,7 @@
      }
    }
  }
  .ztzf-select-popper.el-select__popper {
      border:#2D2D2D !important;
  
@@ -265,22 +275,27 @@
          border: 1px solid #fff !important;
      }
  }
  //时间日期选择器
  .ztzf-layer-date-picker {
      box-shadow: none !important;
      background: transparent;
      border: 1px solid #fff;
      border-radius: 5px;
      .el-range-editor.is-disabled input {
          background-color: transparent !important;
      }
      .el-range-editor.is-disabled {
          background-color: transparent !important;
      }
      .el-input__wrapper {
          box-shadow: none !important;
          background-color: transparent !important;
      width: 300px;
          &.is-focus {
              box-shadow: none !important;
          }
@@ -305,14 +320,19 @@
      border: 1px solid !important;
      border-radius: 0px 0px 8px 8px;
      border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
      .el-picker-panel .el-time-panel{
          background: #012350 !important;
      }
      .el-time-spinner__item {
      color: #fff !important;}
        color: #fff !important;
    }
      .el-time-spinner__item:hover:not(.is-disabled):not(.is-active){
          background: transparent !important;
      }
      .el-picker-panel {
          .el-picker-panel__body-wrapper {
              .el-picker-panel__body {
@@ -552,6 +572,7 @@
          }
      }
  }
.ztzf-custom-qrcode-popover {
    //background: url("@/assets/images/home/eventOverviewDetail/qrCodeBg.png") no-repeat center center !important;
    //background-size: cover !important;
@@ -647,6 +668,7 @@
        background: #13c6ff !important;
    }
}
  //======以下针对表格功能 增删查改样式和弹窗=========
  .ztzf-dialog-mange {
    .el-dialog__body {
@@ -655,6 +677,7 @@
      overflow: scroll;
      border-top: 0.1rem solid #f0f0f0;
    }
    .el-dialog__footer {
      z-index: 2;
      display: block;
@@ -669,18 +692,24 @@
      text-align: right;
    }
  }
  .ztzf-search-mange {
    height: 40px;
    .search-contain {
      display: flex;justify-content: space-between;
        display: flex;
        justify-content: space-between;
    }
  }
  .ztzf-form-mange {
    .el-form-item {
      width: 300px;
      .el-form-item__content {
        width: 100%;
      }
      .el-form-item__label {
        width: 120px;
      }
@@ -690,9 +719,11 @@
  .ztzf-form-search {
    .el-form-item {
      width: 100%;
      .el-form-item__label {
        width: 80px;
      }
      .el-form-item__content {
        display: flex;
        flex: 1;
@@ -713,6 +744,7 @@
    border-left: 0.1rem solid #ebeef5;
    margin-left: 0;
    margin-right: 0;
    .el-col {
      padding-left: 0;
      padding-right: 0;
@@ -722,11 +754,13 @@
      border-right: 0.1rem solid #ebeef5;
      text-align: center;
      align-items: center;
      .el-input__wrapper {
        box-shadow: none !important;
        border-bottom: 0.1rem solid #ebeef5;
      }
    }
    .label {
      color: #909399;
      box-sizing: border-box;
@@ -734,3 +768,255 @@
    }
  }
  
.ztzf-data-cockpit-search-input {
    .el-input__wrapper {
        padding: 0 8px !important;
        background: #161B2C;
        box-shadow: none !important;
        border: none;
        .el-input__inner {
            color: #ffffff;
            &::placeholder {
                color: #86909C;
            }
        }
    }
    &.is-disabled {
        .el-input__wrapper {
            background-color: #161B2C;
        }
    }
}
.ztzf-data-cockpit-date-picker {
    box-shadow: none !important;
    background: #161B2C;
    border: none;
    border-radius: 5px;
    box-sizing: border-box !important;
    .el-input__wrapper {
        box-shadow: none !important;
        background-color: transparent !important;
        &.is-focus {
            box-shadow: none !important;
        }
    }
    .el-range-input {
        color: #fff;
        &::placeholder {
            color: #86909C;
        }
    }
    .el-range-separator {
        color: #ffffff;
    }
}
.ztzf-data-cockpit-date-picker-popper {
    background: #161B2C !important;
    border: 1px solid !important;
    border-radius: 0px 0px 8px 8px;
    border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
    .el-date-picker__prev-btn,
    .el-date-picker__next-btn {
        .el-icon {
            color: #fff !important;
        }
    }
    .el-date-picker__header-label {
        color: #fff !important;
    }
    .el-picker-panel {
        .el-picker-panel__body-wrapper {
            .el-picker-panel__body {
                background: #161B2C !important;
                color: #fff !important;
                .el-date-picker__time-header {
                    border: none;
                    .el-date-picker__editor-wrap {
                        .el-time-panel {
                            background: #161B2C !important;
                            border-radius: 0px 0px 8px 8px;
                            border: 1px solid !important;
                            border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
                            // 将 time-picker-popper 的样式移到这里
                            .el-time-panel__header {
                                background: #161B2C !important;
                                color: #ffffff !important;
                                border-bottom: 1px solid rgba(71, 157, 255, 0.3) !important;
                            }
                            .el-time-panel__content {
                                .el-time-spinner__item {
                                    color: #e6e6e6 !important;
                                    &:hover {
                                        background: rgba(71, 157, 255, 0.3) !important;
                                    }
                                    &.active {
                                        color: #479dff !important;
                                        font-weight: bold;
                                    }
                                }
                            }
                            .el-time-panel__footer {
                                background: #012350 !important;
                                border-top: 1px solid rgba(71, 157, 255, 0.3) !important;
                                .el-button {
                                    color: #fff !important;
                                    &:hover {
                                        color: #479dff !important;
                                    }
                                }
                                .el-time-panel__btn.cancel {
                                    color: #fff !important;
                                    &:hover {
                                        color: #fff !important;
                                    }
                                }
                            }
                        }
                    }
                }
                .el-picker-panel__content {
                    .el-date-range-picker__header {
                        .el-picker-panel__icon-btn {
                            color: #fff !important;
                            &:hover {
                                color: var(--el-color-primary) !important;
                            }
                            .el-icon {
                                &::before {
                                    color: inherit !important;
                                }
                            }
                        }
                        &>div {
                            span {
                                color: #fff !important;
                            }
                        }
                    }
                    .el-date-table {
                        th {
                            color: #fff !important;
                        }
                        td.disabled div {
                            background-color: #00193b !important;
                            color: #505050 !important;
                            cursor: not-allowed;
                        }
                        // td:hover,
                        // td.current:not(.disabled) {
                        //     background: rgba(60, 121, 202) !important;
                        //     color: white !important;
                        // }
                        td.start-date span,
                        td.start-date span,
                        td.end-date span {
                            background-color: #012350 !important;
                        }
                        td.in-range div,
                        td.in-range div:hover,
                        &.is-week-mode .el-date-table__row.current div,
                        &.is-week-mode .el-date-table__row:hover div {
                            background-color: rgba(60, 121, 202) !important;
                        }
                    }
                }
            }
        }
    }
    .el-popper__arrow {
        &::before {
            background: #161B2C !important;
            border: 1px solid #161B2C !important;
            box-sizing: border-box;
        }
    }
    .el-picker-panel__sidebar {
        background: #012350;
        color: #fff;
        .el-picker-panel__shortcut {
            color: #ffffff;
        }
    }
    .el-picker-panel__body {
        .el-input.is-disabled .el-input__wrapper {
            background: #012350;
            box-shadow: 0 0 0 0.1rem#409eff;
            color: #fff;
            .el-input__inner {
                color: #fff !important;
            }
        }
        .el-button.is-disabled,
        .el-button.is-disabled:hover {
            background: #012350;
        }
        .el-input__wrapper {
            background: #012350;
            box-shadow: 0 0 0 0.1rem#409eff;
            color: #fff;
            .el-input__inner {
                color: #fff !important;
            }
        }
    }
    .el-picker-panel__footer {
        background: #012350;
        color: #fff;
        border-top: none;
        .el-button--small {
            box-shadow: 0 0 0 0.1rem#409eff;
            background-color: #012350;
            color: #fff;
            border: none;
            &:hover {
                background-color: #012350;
                color: #fff;
                border-color: #409eff;
            }
        }
    }
}
applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue
@@ -1,4 +1,4 @@
<template>
<template>
    <div class="center-container">
        <div class="left-box">
            <div class="logo">
@@ -6,7 +6,7 @@
            </div>
            <div class="list">
                <div class="item" v-for="item, ind in warningList" :key="ind">
                <div class="item" v-for="(item, ind) in warningList" :key="ind">
                    <div class="val" @click="warningTypeClick(item.type)">
                        {{ item.value }}
                    </div>
@@ -24,7 +24,7 @@
            </div>
            <div class="list">
                <div class="item" v-for="item, ind in equipmentList" :key="ind">
                <div class="item" v-for="(item, ind) in equipmentList" :key="ind">
                    <div class="val" :class="{highlight: item.isHighlight}" @click="equipmentClick(item.isHighlight)">
                        {{ item.value }}
                    </div>
@@ -39,9 +39,9 @@
</template>
<script setup>
import { ref } from 'vue'
import warningLogo from '@/assets/images/dataCockpit/warning.png'
import equipmentLogo from '@/assets/images/dataCockpit/equipment.png'
let warningList = ref([
    {
@@ -82,12 +82,12 @@
])
const warningTypeClick = (type) => {
    console.log('点击了告警类型:', type);
    console.log('点击了告警类型:', type)
}
const equipmentClick = (isHighlight) => {
    if (isHighlight) {
        console.log('点击了设备统计');
        console.log('点击了设备统计')
    }
}
</script>
applications/drone-command/src/views/dataCockpit/components/EquipmentWarning.vue
New file
@@ -0,0 +1,61 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-08 09:29:07
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-08 14:49:25
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\EquipmentWarning.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-08 09:29:07
-->
<template>
    <div class="equipment-warning">
        <EquipmentTemplate v-for="(item, ind) in equipmentWarningData" :key="ind" :data="item" @click="onCardClick" />
    </div>
</template>
<script setup>
import EquipmentTemplate from './templateComponents/EquipmentTemplate.vue'
const equipmentWarningData = ref([
    {
        id: 'dev_001',
        name: '反制设备名称',
        status: '离线',
        statusType: 'offline', // offline | fault | online
        type: '察打一体',
        vendor: '厂商名称',
        azimuth: 14,          // 方位角(°)
        pitch: 14,            // 俯仰角(°)
        range: '1.2KM',       // 有效范围
        model: 'TDOAX1A',     // 设备型号
        longitude: 113.877571,
        latitude: 22.547155
    },
    {
        id: 'dev_002',
        name: '反制设备名称',
        status: '故障',
        statusType: 'fault',
        type: '察打一体',
        vendor: '厂商名称',
        azimuth: 14,
        pitch: 14,
        range: '1.2KM',
        model: 'TDOAX1A',
        longitude: 113.877571,
        latitude: 22.547155
    }
])
const onCardClick = (item) => {
    console.log('点击设备卡片:', item.id)
}
</script>
<style lang="scss" scoped>
.equipment-warning {
    width: 100%;
    height: 100%;
}
</style>
applications/drone-command/src/views/dataCockpit/components/HistoryWarning.vue
New file
@@ -0,0 +1,136 @@
<template>
    <div class="history-warning">
        <!-- 搜索区(设计稿红框) -->
        <div class="search-box">
            <!-- 关键字 -->
            <el-input v-model="query.keyword" class="ztzf-data-cockpit-search-input" placeholder="请输入数据名称" clearable
                @keyup.enter="onSearch">
            </el-input>
            <!-- 日期范围 -->
            <el-date-picker v-model="query.dateRange"
                  class="ztzf-data-cockpit-date-picker"
                 popper-class="ztzf-data-cockpit-date-picker-popper"
                type="daterange"
                range-separator="~"
                start-placeholder="创建开始日期" end-placeholder="结束日期" format="YYYY年MM月DD日" value-format="YYYY-MM-DD"
                :clearable="true" @change="onSearch">
            </el-date-picker>
        </div>
        <!-- 列表 -->
        <div class="list-content">
            <HistoryTemplate v-for="(item, ind) in filteredList" :key="item.id || ind" :data="item"
                @click="onCardClick" />
        </div>
    </div>
</template>
<script setup>
import { ref, computed } from 'vue'
import HistoryTemplate from './templateComponents/HistoryTemplate.vue'
// 原始数据(别动它,过滤用 computed)
const historyWarningData = ref([
    {
        id: 'drone_001',
        name: '无人机名称',
        serialNumber: 'XLH78945645456',
        dataSource: '侦测反制设备名称',
        frequency: '5800MHZ',
        discoveredAt: { date: '2025年12月30日', time: '14:55:06' },
        stayTime: '00:10:50',
        counterMethod: '信号干扰'
    },
    {
        id: 'drone_002',
        name: '无人机名称',
        serialNumber: 'XLH78945645456',
        dataSource: '侦测反制设备名称',
        frequency: '2400MHZ',
        discoveredAt: { date: '2025年12月30日', time: '15:08:12' },
        stayTime: '00:03:21',
        counterMethod: '诱导驱离'
    }
])
// 查询条件:keyword + dateRange(YYYY-MM-DD数组)
const query = ref({
    keyword: '',
    dateRange: [] // [start, end]
})
const onSearch = () => {
    // 目前是本地过滤:computed 会自动更新
    // 后续如果你要走接口,这里换成 fetchList(query.value) 即可
}
const onCardClick = (item) => {
    console.log('点击无人机卡片:', item.id)
}
// "2025年12月30日" -> "2025-12-30"
const cnDateToISO = (cn) => {
    if (!cn) return ''
    const m = cn.match(/(\d{4})年(\d{1,2})月(\d{1,2})日/)
    if (!m) return ''
    const y = m[1]
    const mm = String(m[2]).padStart(2, '0')
    const dd = String(m[3]).padStart(2, '0')
    return `${y}-${mm}-${dd}`
}
const inRange = (targetISO, startISO, endISO) => {
    if (!targetISO) return false
    const t = new Date(`${targetISO}T00:00:00`).getTime()
    const s = startISO ? new Date(`${startISO}T00:00:00`).getTime() : null
    const e = endISO ? new Date(`${endISO}T23:59:59`).getTime() : null
    if (s !== null && t < s) return false
    if (e !== null && t > e) return false
    return true
}
const filteredList = computed(() => {
    const kw = (query.value.keyword || '').trim().toLowerCase()
    const [start, end] = query.value.dateRange || []
    return historyWarningData.value.filter((item) => {
        // 1) 关键字:name / serialNumber / dataSource
        const hitKeyword = !kw
            ? true
            : [item.name, item.serialNumber, item.dataSource]
                .filter(Boolean)
                .some((v) => String(v).toLowerCase().includes(kw))
        if (!hitKeyword) return false
        // 2) 日期范围:按 discoveredAt.date
        if (!start && !end) return true
        const itemISO = cnDateToISO(item?.discoveredAt?.date)
        return inRange(itemISO, start, end)
    })
})
</script>
<style scoped lang="scss">
.history-warning {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    .search-box {
        display: flex;
        flex-direction: column;
        gap: 10px;
        ::v-deep(.ztzf-data-cockpit-date-picker) {
            width: 100% !important;
        }
    }
    .list-content {
        height: 0;
        flex: 1;
    }
}
</style>
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
@@ -1,15 +1,108 @@
<template>
    <div class="left-container">
        <div class="wrapper">
            <TitleTemplate>异常情况告警</TitleTemplate>
            
            <div class="category-container">
                <div class="category-item" v-for="(item, ind) in categoryList" :key="ind" @click="activeId = item.id"
                    :class="{ active: activeId === item.id }">
                    {{ item.name }}
                </div>
            </div>
            <div class="content">
                <component :is="currentComponent" />
            </div>
        </div>
    </div>
</template>
<script setup>
import TitleTemplate from './templateComponents/TitleTemplate.vue'
import RealWarning from './RealWarning.vue'
import EquipmentWarning from './EquipmentWarning.vue'
import HistoryWarning from './HistoryWarning.vue'
const activeId = ref(1)
const categoryList = ref([
    {
        id: 1,
        name: '实时告警',
        component: RealWarning
    },
    {
        id: 2,
        name: '设备告警',
        component: EquipmentWarning
    },
    {
        id: 3,
        name: '历史告警',
        component: HistoryWarning
    }
])
const currentComponent = computed(() => {
    return categoryList.value.find(i => i.id === activeId.value)?.component
})
</script>
<style lang="scss" scoped>
.category-container {
    display: flex;
    justify-content: space-between;
    .category-item {
        position: relative;
        width: 0;
        flex: 1;
        margin-left: 26px;
        height: 68px;
        line-height: 68px;
        font-family: Source Code Pro, Source Code Pro;
        font-weight: 400;
        font-size: 14px;
        color: #86909C;
        text-align: center;
        font-style: normal;
        text-transform: none;
        cursor: pointer;
        &:first-child {
            margin-left: 0;
        }
        &.active {
            font-weight: normal;
            color: #FFFFFF;
        }
        &::after {
            content: '';
            position: absolute;
            left: 0;
            bottom: 0;
            width: 100%;
            height: 1px;
            background: #333355;
        }
        &.active::after {
            height: 3px;
            background: #284FE3;
            box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
            border-radius: 5px 5px 0px 0px;
        }
    }
}
.content {
    height: 0;
    flex: 1;
    margin-top: 20px;
    overflow-x: hidden;
    overflow-y: auto;
}
</style>
applications/drone-command/src/views/dataCockpit/components/MapContainer.vue
@@ -22,7 +22,7 @@
onBeforeUnmount(() => {
    if (viewInstance) {
        viewInstance.destroy()
        viewInstance?.destroy()
        viewInstance = null
    }
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue
New file
@@ -0,0 +1,76 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-08 09:29:07
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-08 14:49:09
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RealWarning.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-08 09:29:07
-->
<template>
    <div class="real-warning">
        <RealTemplate v-for="(item, ind) in realWarningData" :key="ind" :data="item"
            @signal="() => console.log('信号干扰', item.id)" @counter="() => console.log('诱导驱离', item.id)"
            @favorite="() => console.log('收藏', item.id)" />
    </div>
</template>
<script setup>
import RealTemplate from './templateComponents/RealTemplate.vue'
const realWarningData = ref([
    {
        "id": "drone_001",
        "name": "无人机A-01",
        "status": "侦测中",
        "statusType": "detecting",
        "serialNumber": "XLH789456456",
        "dataSource": "侦测反制设备名称",
        "longitude": 113.99238,
        "latitude": 22.541819,
        "frequency": "5800MHz",
        "discoverTime": "14:56:04",
        "altitude": "150m",
        "speed": "5.01m/s",
        "isFavorite": false
    },
    {
        "id": "drone_002",
        "name": "无人机B-07",
        "status": "反制中",
        "statusType": "countering",
        "serialNumber": "XLH789456789",
        "dataSource": "反制设备A区",
        "longitude": 113.99312,
        "latitude": 22.542031,
        "frequency": "2400MHz",
        "discoverTime": "15:02:41",
        "altitude": "120m",
        "speed": "3.86m/s",
        "isFavorite": true
    },
    {
        "id": "drone_002",
        "name": "无人机B-07",
        "status": "反制中",
        "statusType": "countering",
        "serialNumber": "XLH789456789",
        "dataSource": "反制设备A区",
        "longitude": 113.99312,
        "latitude": 22.542031,
        "frequency": "2400MHz",
        "discoverTime": "15:02:41",
        "altitude": "120m",
        "speed": "3.86m/s",
        "isFavorite": true
    }
])
</script>
<style lang="scss" scoped>
.real-warning {
    width: 100%;
    height: 100%;
}
</style>
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -1,15 +1,217 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-07 15:17:54
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-08 15:58:15
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-07 15:17:54
-->
<template>
    <div class="right-container">
        <div class="wrapper">
            <TitleTemplate>侦测反制设备</TitleTemplate>
            
            <div class="category-container">
                <div class="category-item" v-for="(item, ind) in categoryList" :key="ind">
                    <div class="category-item-logo">
                        <img :src="item.logo" alt="">
                    </div>
                    <div class="category-item-content">
                        <span class="name">{{ item.name }}</span>
                        <span class="val">{{ item.val }}</span>
                    </div>
                </div>
            </div>
            <div class="content">
                <RealEquipmentTemplate v-for="item in counterDeviceStatusList" :key="item.id" :data="item"
                    @history="onHistory" @click="onCardClick" />
            </div>
        </div>
    </div>
</template>
<script setup>
import TitleTemplate from './templateComponents/TitleTemplate.vue'
import RealEquipmentTemplate from './templateComponents/RealEquipmentTemplate.vue'
import zcsbLogo from '@/assets/images/dataCockpit/zcsb.png'
import fzsbLogo from '@/assets/images/dataCockpit/fzsb.png'
const categoryList = ref([
    {
        id: 1,
        name: '侦测设备',
        val: 169,
        logo: zcsbLogo
    },
    {
        id: 2,
        name: '反制设备',
        val: 169,
        logo: fzsbLogo
    }
])
const counterDeviceStatusList = ref([
    {
        id: 'cd_001',
        name: '反制设备名称',
        actionText: '侦测中',          // 胶囊文案
        actionType: 'detecting',       // detecting | jamming | idle
        type: '察打一体',
        battery: 100,                  // 电量 %
        azimuth: 14,                   // 方位角
        pitch: 14,                     // 俯仰角
        statusText: '在线',            // 状态
        vendor: '厂商名称',            // 厂商
        targetCount: 10,               // 侦测目标
        range: '1.2KM'                 // 有效范围
    },
    {
        id: 'cd_002',
        name: '反制设备名称',
        actionText: '信号干扰中',
        actionType: 'jamming',
        type: '察打一体',
        battery: 100,
        azimuth: 14,
        pitch: 14,
        statusText: '在线',
        vendor: '厂商名称',
        targetCount: 10,
        range: '1.2KM'
    }
])
const onHistory = (item) => {
    console.log('历史数据:', item.id)
}
const onCardClick = (item) => {
    console.log('点击卡片:', item.id)
}
</script>
<style lang="scss" scoped>
.category-container {
    margin-top: 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 28px;
    box-sizing: border-box;
    .category-item {
        position: relative;
        width: 0;
        flex: 1;
        display: flex;
        height: 64px;
        &-logo {
            display: flex;
            align-items: center;
            img {
                width: 40px;
                height: 40px;
            }
        }
        &-content {
            width: 0;
            flex: 1;
            display: flex;
            flex-direction: column;
            border-bottom: 1px solid #6C6C89;
            span {
                margin-left: 8px;
            }
            .name {
                font-family: Open Sans, Open Sans;
                font-weight: 400;
                font-size: 16px;
                color: #C3C3DD;
                line-height: 20px;
                text-align: left;
                font-style: normal;
                text-transform: none;
            }
            .val {
                margin-top: 10px;
                font-family: DIN, DIN;
                font-weight: bold;
                font-size: 22px;
                color: #FFFFFF;
                line-height: 20px;
                text-align: left;
                font-style: normal;
                text-transform: none;
            }
        }
    }
}
.content {
    height: 0;
    flex: 1;
    margin-top: 20px;
    overflow-x: hidden;
    overflow-y: auto;
}
.right-container {
    transition: transform 0.3s ease-in-out;
    /* position: relative; Removed to avoid conflict with parent absolute positioning */
    &.collapsed {
        transform: translateX(100%);
    }
}
.collapse-btn {
    position: absolute;
    top: 50%;
    left: -24px;
    width: 24px;
    height: 60px;
    background: rgba(17, 23, 34, 0.8);
    border: 1px solid #333355;
    border-right: none;
    border-radius: 4px 0 0 4px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    transform: translateY(-50%);
    z-index: 10;
    backdrop-filter: blur(5px);
    .arrow {
        border: solid #fff;
        border-width: 0 2px 2px 0;
        display: inline-block;
        padding: 3px;
        transition: transform 0.3s;
        &.left {
            transform: rotate(135deg);
        }
        &.right {
            transform: rotate(-45deg);
        }
    }
    &:hover {
        background: rgba(40, 79, 227, 0.6);
    }
}
</style>
applications/drone-command/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue
New file
@@ -0,0 +1,171 @@
<template>
    <div class="device-card" @click="emit('click', data)">
        <div class="header">
            <div class="title">
                <span class="name">{{ data.name }}</span>
                <span class="status">
                    <span :class="data.statusType">
                    </span>
                    <span>
                        {{ data.status }}
                    </span>
                </span>
            </div>
        </div>
        <div class="content">
            <div class="row">
                <div class="col">
                    <span class="label">类型</span>
                    {{ data.type }}
                </div>
                <div class="col">
                    <span class="label">厂商</span>
                    {{ data.vendor }}
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <span class="label">方位角</span>
                    {{ data.azimuth }}°
                </div>
                <div class="col">
                    <span class="label">俯仰角</span>
                    {{ data.pitch }}°
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <span class="label">有效范围</span>
                    {{ data.range }}
                </div>
                <div class="col">
                    <span class="label">设备型号</span>
                    {{ data.model }}
                </div>
            </div>
            <div>
                <span class="label">部署位置</span>
                <span class="value">{{ data.longitude }},{{ data.latitude }}</span>
            </div>
        </div>
    </div>
</template>
<script setup>
const props = defineProps({
    data: {
        type: Object,
        required: true,
        default: () => ({})
    }
})
const emit = defineEmits(['click'])
</script>
<style scoped lang="scss">
.device-card {
    margin-top: 10px;
    padding: 10px;
    color: #fff;
    background: #191933;
    border-radius: 6px 6px 6px 6px;
    &:first-child {
        margin-top: 0;
    }
    .header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        .title {
            display: flex;
            align-items: center;
            gap: 6px;
            .name {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: bold;
                font-size: 14px;
                color: #FFFFFF;
                text-align: left;
                font-style: normal;
                text-transform: none;
            }
            .status {
                padding: 0 10px;
                display: flex;
                align-items: center;
                justify-content: space-between;
                height: 21px;
                background: #303041;
                border-radius: 10px;
                gap: 6px;
                span {
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: 400;
                    font-size: 10px;
                    color: #FFFFFF;
                    text-align: center;
                    font-style: normal;
                    text-transform: none;
                }
                span.offline {
                    width: 4px;
                    height: 4px;
                    background: #939393;
                    border-radius: 50%;
                }
                span.fault {
                    width: 4px;
                    height: 4px;
                    background: #FF4444;
                    border-radius: 50%;
                }
            }
        }
    }
    .content {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 12px;
        color: #9E9EBA;
        text-align: left;
        font-style: normal;
        text-transform: none;
        &>div {
            margin-top: 10px;
            line-height: 22px;
            span.label {
                margin-right: 10px;
                color: #D4D5D7;
            }
        }
        .row {
            display: flex;
            &>div {
                width: 0;
                flex: 1;
            }
        }
    }
}
</style>
applications/drone-command/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue
New file
@@ -0,0 +1,110 @@
<template>
    <div class="drone-info-card" @click="emit('click', data)">
        <div class="title">
            <span class="name">
                {{ data.name }}
            </span>
        </div>
        <div class="rows">
            <div class="row">
                <span class="label">序列号</span>
                <span class="value">{{ data.serialNumber }}</span>
            </div>
            <div class="row">
                <span class="label">数据源</span>
                <span class="value">{{ data.dataSource }}</span>
            </div>
            <div class="row">
                <span class="label">信号频段</span>
                <span class="value">{{ data.frequency }}</span>
            </div>
            <div class="row">
                <span class="label">发现时间</span>
                <span class="value">
                    <span class="date">{{ data.discoveredAt?.date }}</span>
                    <span class="time">{{ data.discoveredAt?.time }}</span>
                </span>
            </div>
            <div class="row">
                <span class="label">停留时间</span>
                <span class="value">{{ data.stayTime }}</span>
            </div>
            <div class="row">
                <span class="label">反制方式</span>
                <span class="value">{{ data.counterMethod }}</span>
            </div>
        </div>
    </div>
</template>
<script setup>
defineProps({
    data: {
        type: Object,
        required: true,
        default: () => ({})
    }
})
const emit = defineEmits(['click'])
</script>
<style scoped lang="scss">
.drone-info-card {
    margin-top: 10px;
    padding: 10px;
    color: #fff;
    background: #191933;
    border-radius: 6px 6px 6px 6px;
    &:hover {
        filter: brightness(1.05);
    }
    .title {
        display: flex;
        align-items: center;
        gap: 6px;
        .name {
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: bold;
            font-size: 14px;
            color: #FFFFFF;
            text-align: left;
            font-style: normal;
            text-transform: none;
        }
    }
    .rows {
        margin-top: 10px;
        display: flex;
        flex-direction: column;
        gap: 10px;
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 12px;
        color: #9E9EBA;
        text-align: left;
        font-style: normal;
        text-transform: none;
        .row {
            line-height: 22px;
            .label {
                margin-right: 10px;
                color: #D4D5D7;
            }
        }
    }
}
</style>
applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
New file
@@ -0,0 +1,199 @@
<script setup>
defineProps({
    data: {
        type: Object,
        required: true,
        default: () => ({})
    }
})
const emit = defineEmits(['history', 'click'])
</script>
<template>
    <div class="counter-card" @click="emit('click', data)">
        <!-- 头部 -->
        <div class="header">
            <div class="title">
                <span class="name">{{ data.name }}</span>
                <span class="status">
                    <span :class="data.actionType">
                    </span>
                    <span>
                        {{ data.actionText }}
                    </span>
                </span>
            </div>
        </div>
        <!-- 字段区:两列 -->
        <div class="content">
            <div class="row">
                <div class="col">
                    <span class="label">类型</span>
                    {{ data.type }}
                </div>
                <div class="col">
                    <span class="label">电量</span>
                    {{ data.battery }}%
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <span class="label">方位角</span>
                    {{ data.azimuth }}°
                </div>
                <div class="col">
                    <span class="label">俯仰角</span>
                    {{ data.pitch }}°
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <span class="label">状态</span>
                    {{ data.statusText }}
                </div>
                <div class="col">
                    <span class="label">厂商</span>
                    {{ data.vendor }}
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <span class="label">侦测目标</span>
                    {{ data.targetCount }}台
                </div>
                <div class="col">
                    <span class="label">有效范围</span>
                    {{ data.range }}KM
                </div>
            </div>
        </div>
        <!-- 底部按钮 -->
        <div class="footer">
            <el-button color="#284FE3" type="primary" @click.stop="emit('history', data)">历史数据</el-button>
        </div>
    </div>
</template>
<style scoped lang="scss">
.counter-card {
    margin-top: 10px;
    padding: 10px;
    color: #fff;
    background: #191933;
    border-radius: 6px 6px 6px 6px;
    &:first-child {
        margin-top: 0;
    }
    .header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        .title {
            display: flex;
            align-items: center;
            gap: 6px;
            .name {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: bold;
                font-size: 14px;
                color: #FFFFFF;
                text-align: left;
                font-style: normal;
                text-transform: none;
            }
            .status {
                padding: 0 10px;
                display: flex;
                align-items: center;
                justify-content: space-between;
                height: 21px;
                background: #303041;
                border-radius: 10px;
                gap: 6px;
                span {
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: 400;
                    font-size: 10px;
                    color: #FFFFFF;
                    text-align: center;
                    font-style: normal;
                    text-transform: none;
                }
                span.detecting {
                    width: 4px;
                    height: 4px;
                    background: #18FF4A;
                    border-radius: 50%;
                }
                span.jamming {
                    width: 4px;
                    height: 4px;
                    background: #FF4444;
                    border-radius: 50%;
                }
            }
        }
    }
    .content {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 12px;
        color: #9E9EBA;
        text-align: left;
        font-style: normal;
        text-transform: none;
        &>div {
            margin-top: 10px;
            line-height: 22px;
            span.label {
                margin-right: 10px;
                color: #D4D5D7;
            }
        }
        .row {
            display: flex;
            &>div {
                width: 0;
                flex: 1;
            }
        }
    }
    .footer {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
        margin-top: 10px;
        ::v-deep(.el-button) {
            width: 96px;
            height: 36px;
        }
    }
}
</style>
applications/drone-command/src/views/dataCockpit/components/templateComponents/RealTemplate.vue
New file
@@ -0,0 +1,204 @@
<template>
    <div class="drone-card">
        <div class="header">
            <div class="title">
                <span class="name">{{ data.name }}</span>
                <span class="status">
                    <span :class="data.statusType">
                    </span>
                    <span>
                        {{ data.status }}
                    </span>
                </span>
            </div>
            <div class="actions">
                <span class="star" @click="emit('favorite')">
                    {{ data.isFavorite ? '★' : '☆' }}
                </span>
            </div>
        </div>
        <div class="content">
            <div>
                <span class="label">序列号</span>
                {{ data.serialNumber }}
            </div>
            <div>
                <span class="label">数据源</span>
                {{ data.dataSource }}
            </div>
            <div>
                <span class="label">经纬度</span>
                {{ data.longitude }}, {{ data.latitude }}
            </div>
            <div class="row">
                <div class="col">
                    <span class="label">信号频段</span>
                    {{ data.frequency }}
                </div>
                <div class="col">
                    <span class="label">发现时间</span>
                    {{ data.discoverTime }}
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <span class="label">飞行高度</span>
                    {{ data.altitude }}
                </div>
                <div class="col">
                    <span class="label">飞行速度</span>
                    {{ data.speed }}
                </div>
            </div>
        </div>
        <div class="footer">
            <el-button class="general" color="#2B2B4C" @click="emit('signal')">信号干扰</el-button>
            <el-button color="#284FE3" type="primary" @click="emit('counter')">诱导驱离</el-button>
        </div>
    </div>
</template>
<script setup>
const props = defineProps({
    data: {
        type: Object,
        required: true,
        default: () => ({})
    }
})
const emit = defineEmits(['signal', 'counter', 'favorite'])
</script>
<style scoped lang="scss">
.drone-card {
    margin-top: 10px;
    padding: 10px;
    color: #fff;
    background: #191933;
    border-radius: 6px 6px 6px 6px;
    &:hover {
        background: #29294D;
        ;
    }
    &:first-child {
        margin-top: 0;
    }
    .header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        .title {
            display: flex;
            align-items: center;
            gap: 6px;
            .name {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: bold;
                font-size: 14px;
                color: #FFFFFF;
                text-align: left;
                font-style: normal;
                text-transform: none;
            }
            .status {
                padding: 0 10px;
                display: flex;
                align-items: center;
                justify-content: space-between;
                height: 21px;
                background: #303041;
                border-radius: 10px;
                gap: 6px;
                span {
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: 400;
                    font-size: 10px;
                    color: #FFFFFF;
                    text-align: center;
                    font-style: normal;
                    text-transform: none;
                }
                span.detecting {
                    width: 4px;
                    height: 4px;
                    background: #18FF4A;
                    border-radius: 50%;
                }
                span.countering {
                    width: 4px;
                    height: 4px;
                    background: #FF4444;
                    border-radius: 50%;
                }
            }
        }
        .actions {
            display: flex;
            gap: 8px;
            font-size: 16px;
            cursor: pointer;
        }
    }
    .content {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 12px;
        color: #9E9EBA;
        text-align: left;
        font-style: normal;
        text-transform: none;
        &>div {
            margin-top: 10px;
            line-height: 22px;
            span.label {
                margin-right: 10px;
                color: #D4D5D7;
            }
        }
        .row {
            display: flex;
            justify-content: space-between;
        }
    }
    .footer {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
        margin-top: 10px;
        ::v-deep(.el-button) {
            width: 96px;
            height: 36px;
            &.general.el-button {
                border-radius: 4px 4px 4px 4px;
                border: 1px solid rgba(255, 255, 255, 0.5);
            }
        }
    }
}
</style>
applications/drone-command/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue
New file
@@ -0,0 +1,43 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-08 09:32:52
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-08 09:43:00
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\templateComponents\TitleTemplate.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-08 09:32:52
-->
<template>
    <div class="title-box">
        <div class="title">
            <slot></slot>
        </div>
    </div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
.title-box {
    padding-left: 26px;
    background: url('@/assets/images/dataCockpit/title-bg.png') center / 100% 100% no-repeat;
    height: 34px;
    .title {
        height: 25px;
        line-height: 25px;
        font-family: PangMen;
        font-weight: 400;
        font-size: 18px;
        color: #FFFFFF;
        letter-spacing: 1px;
        text-align: left;
        font-style: normal;
        text-transform: none;
    }
}
</style>
applications/drone-command/src/views/dataCockpit/index.vue
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2026-01-06 16:35:50
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-07 17:33:02
 * @LastEditTime : 2026-01-08 09:45:25
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\index.vue
 * @Description  : 
 * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -41,6 +41,8 @@
    backdrop-filter: blur(5px);
    ::v-deep(.wrapper) {
      display: flex;
      flex-direction: column;
      position: absolute;
      top: 17px;
      bottom: 20px;
uniapps/work-app/src/pages.json
@@ -63,16 +63,9 @@
                    }
                },
                {
                    "path": "workDetail/addWork/index",
                    "style": {
                        "navigationBarTitleText": "新建工单"
                    }
                },
                {
                    "path": "workDetail/mapWork/index",
                    "style": {
                        "navigationBarTitleText": "地图展示",
                        "navigationStyle": "custom"
            "navigationBarTitleText": "工单位置"
                    }
                },
                {
@@ -106,13 +99,13 @@
                "iconPath": "static/images/tabbar/icon_order.png",
                "selectedIconPath": "static/images/tabbar/icon_order_selected.png",
                "pagePath": "pages/work/index",
                "text": "工单"
        "text": "工单管理"
            },
            {
                "iconPath": "static/images/tabbar/icon_order.png",
                "selectedIconPath": "static/images/tabbar/icon_order_selected.png",
                "pagePath": "pages/voiceCall/index",
                "text": "通话"
        "text": "语音通话"
            },
            {
                "iconPath": "static/images/tabbar/icon_home1.png",
uniapps/work-app/src/pages/login/index.vue
@@ -201,7 +201,9 @@
    text-align: center;
    margin-bottom: 168rpx;
  }
.user-name{
  margin-bottom: 40rpx;
}
  .user-name,
  .pass-word {
    display: flex;
@@ -209,8 +211,10 @@
    align-items: center;
    margin-left: 62rpx;
    margin-right: 58rpx;
    height: 118rpx;
    border-bottom: 2rpx solid #e3e3e3;
    height: 100rpx;
    background: #F1F4F9;
    border-radius: 8rpx 8rpx 8rpx 8rpx;
    padding: 0 22rpx;
    image {
      width: 40rpx;
@@ -321,4 +325,14 @@
    color: $u-warning;
  }
}
.agreement {
  position: absolute;
  bottom: 40rpx;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
</style>
uniapps/work-app/src/pages/work/index.vue
@@ -1,16 +1,207 @@
<template>
  <view class="eventTickets">
    工单
  </view>
  <div class="eventTickets" :style="{ paddingTop: topMargin + 'px' }">
    <div class="searchTop">
      <up-search placeholder="请输入关键字搜索"  :animation="true"  v-model="listParams.keyword" :show-action="false"></up-search>
    </div>
    <div class="listBox">
      <div class="tabs-container">
        <up-tabs :list="tabList" @click="handleClick"></up-tabs>
      </div>
      <div class="eventBox">
        <div class="eventItem"  v-for="(item,index) in dataList" :key="index">
          <img :src="item.photo_url" alt="" @click="detailHandle(item)" />
          <div class="informationDisplay">
            <div class="itemTitle">{{item.event_name}}</div>
            <div class="itemContent">{{formatDate(item.create_time) }}</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { useUserStore } from '@/store/index.js'
import {getList,getstatusCount} from '/src/api/work/index.js'
import dayjs from 'dayjs';
import { getStatusBarHeight } from '@/utils/common';
const userStore = useUserStore()
const userInfo = userStore.userInfo
const dataList = ref([])
const currentTab=ref('all')
const tabList = ref([
  {
    name: '全部工单',
    key: 'all',
    badge: {
      value: 2
    },
    status: null
  },
  {
    name: '我的工单',
    key: 'myTickets',
    badge: {
      value: 1
    }
  },
])
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 getDataList = () => {
  const params = {
    current: 1,
    size: 9999,
    source: 1,
    status:listParams.value.status,
    event_name:listParams.value.keyword,
    user_id:currentTab.value=== 'myTickets' ?userInfo.user_id : undefined
  }
  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
//
//             }else if(tab.key === 'myTickets'){
//                 tab.badge.value = userCount || 0
//             }else{
//                 tab.badge.value=statusCount[String(tab.status)] || 0
//             }
//         })
//
//     })
// }
const handleClick = (item) => {
  currentTab.value = item.key
  listParams.value.status = item.status
  getDataList()
}
const detailHandle = (val) => {
  uni.navigateTo({
    url: `/subPackages/workDetail/index?eventNum=${val.event_num}`,
  })
}
const topMargin = getStatusBarHeight()
onShow(() => {
  getDataList()
})
</script>
<style scoped lang="scss">
.eventTickets {
  width: 100%;
  padding: 0 20rpx;
  display: flex;
  flex-direction: column;
  height: 100%;
  box-sizing: border-box;
  .searchTop {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-right: 24rpx;
    width: 100%;
    height: 108rpx;
  }
  :deep(){
    .u-tabs__wrapper__nav__line{
      width: 80rpx !important;
      background: #1D6FE9 !important;
    }
    .u-badge {
      background-color: #1d6fe9 !important;
    }
  }
  .tabs-container{
    display: flex;
    justify-content: center;
    height: 98rpx;
  }
  .listBox {
    position: relative;
    display: flex;
    flex-direction: column;
    flex: 1;
  }
  .eventBox {
    display: flex;
    flex-wrap: wrap;
    gap: 20rpx;
    padding: 20rpx 0;
    overflow-y: auto;
    height: 0;
    flex-grow: 1;
    align-content: flex-start;
    .eventItem {
      width: calc(50% - 10rpx);
      background-color: #fff;
      border-radius: 12rpx;
      overflow: hidden;
      img {
        width: 341rpx;
        height: 196rpx;
        border-radius: 12rpx;
        overflow: hidden;
      }
      .informationDisplay{
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 10rpx 20rpx 10rpx 12rpx;
        .itemTitle {
          width: 144rpx;
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 500;
          font-size: 28rpx;
          color: #000000;
          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: rgba(0,0,0,0.5);
        }
      }
    }
  }
}
</style>
uniapps/work-app/src/subPackages/workDetail/index.vue
@@ -1,68 +1,282 @@
<!-- 工单详情 - 包含待审核、待处理、处理中、已完成 -->
<!-- 工单详情 -->
<template>
  <div class="workDetailContainer">
    <WebViewPlus
      ref="sWebViewRef"
      :src="`${viewUrl}`"
      @webMessage="onPostMessage"
    />
    <div class="detailTop">
      <div class="image-container">
        <u-swiper
          class="detailImage"
          :list="getImageList"
          mode="round"
          indicator
          indicatorMode="dot"
          indicatorActiveColor="#4C85FF"
          indicatorInactiveColor="#fff"
          indicatorPosition="center"
          height="410rpx"
          @click="previewImage"
        ></u-swiper>
        <!--                <div class="detailTitle">-->
        <!--                    <div class="titleText">-->
        <!--                        <div class="itemName">{{workDetailData.event_name}}</div>-->
        <!--                        <div class="itemTime">{{formatDate(workDetailData.create_time)}}</div>-->
        <!--                    </div>-->
        <!--                </div>-->
  </div>
    </div>
    <!-- 工单内容 -->
    <div class="worderContainer">
      <div class="workOrderContent">
        <div class="workOrderTitle">工单内容</div>
        <div class="workOrderContainer">
          <div class="orderRow">
            <div class="rowTitle">工单编号</div>
            <div>{{workDetailData.event_num}}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">工单处置人</div>
            <div>{{workDetailData.event_num?.slice(0, 2) === 'AI' ? 'AI 小飞':workDetailData.creator}}
            </div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">处置部门</div>
            <div>{{ workDetailData.dept_name }}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">拍摄时间</div>
            <div>{{ workDetailData.create_time }}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">分发人员</div>
            <div>{{ workDetailData.remark }}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">分发部门</div>
            <div>{{ workDetailData.remark }}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">分发时间</div>
            <div>{{ workDetailData.create_time }}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">工单位置</div>
            <div class="rowAddress" @click="jumpMap(workDetailData)">{{ workDetailData.address }}</div>
          </div>
        </div>
      </div>
    </div>
    <!-- 操作按钮 -->
    <div class="actionButton">
      <div class="btngroups" >
        <up-button type="primary" color="#AEAEAE" text="退回"></up-button>
        <up-button type="primary" color="#1D6FE9" text="确认"></up-button>
      </div>
    </div>
  </div>
</template>
<script setup>
import { getWebViewUrl } from "@/utils/index.js";
import { onLoad } from "@dcloudio/uni-app";
import { useUserStore } from "@/store/index.js";
const userStore = useUserStore();
const userInfo = userStore.userInfo;
const sWebViewRef = ref(null);
const viewUrl = ref("");
onLoad((options) => {
  const eventNum = options.eventNum;
  viewUrl.value = getWebViewUrl("/workDetail", {
    eventNum: eventNum,
    totalNum: options.totalNum,
    keyword: options.keyword,
    aiType: options.aiType,
    wLJobInfoId: options.wLJobInfoId,
    status: options.status,
    current: options.current,
  });
});
// import { getShowImg, getSmallImg } from '@/utils/util'
import {getList,flowEvent} from '/src/api/work/index.js'
import dayjs from 'dayjs'
function onPostMessage(data) {
  if (data.type === "workback") {
    // #ifdef MP-WEIXIN
    if ("fun" in data && data.fun === "add") {
      uni.setStorageSync("joinParams", {
        type: "add",
      });
const formatDate = dateString => {
  return dayjs(dateString).format('MM/DD HH:mm')
    }
    // #endif
const eventNum = ref('');
// 工单内容
const workDetailData = ref({})
onLoad(async (options) => {
  eventNum.value = options.eventNum;
  await getDataList(eventNum.value);
    // #ifndef MP-WEIXIN
    uni.setStorageSync("joinParams", {
      type: "add",
    });
    uni.switchTab({
      url: `/pages/work/index?addLog=111`,
      // url: '/pages/work/index'
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];
  console.log('详情',response);
}
// 图片预览
const previewImage = (index) => {
  if (getImageList.value.length === 0) return;
  const currentIndex = typeof index === 'number' ? index : 0;
  uni.previewImage({
    urls: getImageList.value,
    current: currentIndex
    });
    // #endif
  } else if (data.type === "jumpMapNav") {
    // #ifndef MP-WEIXIN
};
const getImageList =computed(()=>{
  const imageArr = []
  const detail = workDetailData.value
  if (detail.photo_url) {
    const smallUrl = detail.photo_url
    imageArr.push(smallUrl)
  }
  if (detail.update_photo_url) {
    const smallUrl =detail.update_photo_url
    imageArr.push(smallUrl)
  }
  return imageArr
})
// 跳转地图
const jumpMap = item => {
    uni.navigateTo({
      url: `/subPackages/workDetail/mapWork/index?currentItem=${data.eventNum}`,
    });
    // #endif
  }
    url: `/subPackages/workDetail/mapWork/index?eventNum=${item.event_num}`,
  })
}
</script>
<style lang="scss" scoped>
.workDetailContainer {
  .detailTop {
    .image-container {
      position: relative;
      width: 100%;
      height: 410rpx;
      .detailImage {
  width: 100%;
  height: 100%;
  background-size: cover;
        display: block;
        object-fit: cover;
      }
      // 轮播图容器
      :deep(.u-swiper) {
        // 指示器容器 - 修改为居中对齐
        .u-swiper__indicator {
          position: absolute;
          left: 50%;
          transform: translateX(-50%);
          bottom: 20rpx;
          display: flex;
          justify-content: center;
          align-items: center;
          z-index: 10;
        }
        .u-swiper-indicator__wrapper__dot--active{
          width: 5px !important;
        }
      }
      .detailTitle {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 60rpx;
        line-height: 60rpx;
        background: rgba(7, 7, 7, 0.4);
      }
      .titleText {
        display: flex;
        width: 100%;
        justify-content: space-between;
        align-items: center;
        color: #fff;
        .itemName, .itemTime {
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 400;
          font-size: 28rpx;
          color: #FFFFFF;
        }
        .itemName {
          padding-left: 20rpx;
        }
        .itemTime{
          padding-right: 20rpx;
        }
      }
    }
  }
  .worderContainer {
    padding: 0 24rpx;
    padding-bottom: 4rpx;
    background: #f6f6f6;
    margin-top: 20rpx;
  }
  .workOrderContent {
    margin-top: 30epx;
    background-color: #fff;
    border-radius: 12rpx;
    padding: 20rpx;
    margin-bottom: 34rpx;
    .workOrderTitle {
      font-family: Source Han Sans CN, Source Han Sans CN;
      font-weight: bold;
      font-size: 32rpx;
      color: #222324;
    }
    .workOrderContainer {
      .orderRow {
        display: flex;
        justify-content: space-between;
        align-items: center;
        min-height: 96rpx;
        border-bottom: 1px solid #f5f5f5;
        color: #7b7b7b;
        .rowTitle {
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 400;
          font-size: 30rpx;
          color: #222324;
          white-space: nowrap;
        }
        .rowAddress {
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 400;
          font-size: 28rpx;
          color: #1D6FE9;
          //white-space: nowrap;
          ///* 禁止换行 */
          //overflow: hidden;
          //text-overflow: ellipsis;
          // max-width: 74%;
          padding-top: 1px;
          padding-left: 5px;
          padding-right: 2px;
        }
      }
    }
  }
  .actionButton {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 61px;
    border-radius: 6px 6px 6px 6px;
    .btngroups {
      display: flex;
      justify-content: space-between;
      align-items: center;
      :deep(.u-button){
        width: 276rpx !important;
        height: 100rpx !important;
        &:last-child {
          margin-left: 30rpx;
        }
      }
    }
  }
}
</style>
uniapps/work-app/src/subPackages/workDetail/mapWork/index.vue
@@ -1,13 +1,4 @@
<!--
 * @Author       : yuan
 * @Date         : 2025-10-22 14:59:10
 * @LastEditors  : yuan
 * @LastEditTime : 2025-12-19 14:52:54
 * @FilePath     : \src\subPackages\workDetail\mapWork\index.vue
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-10-22 14:59:10
-->
<!-- 地图展示 -->
<template>
  <WebViewPlus
@@ -25,7 +16,7 @@
const viewUrl = ref("");
onLoad((options) => {
  const currentItem = options.currentItem;
  const currentItem = options.eventNum;
  viewUrl.value = getWebViewUrl("/mapWork", { currentItem: currentItem });
});
function onPostMessage(data) {