吉安感知网项目-前端
shuishen
2026-01-12 42da72b00e5a709478c6e82477a63360f93a7a8e
Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web
18 files modified
9 files renamed
1 files copied
4 files deleted
9 files added
3751 ■■■■ changed files
applications/drone-command/src/main.js 4 ●●● patch | view | raw | blame | history
applications/drone-command/src/utils/cesium/publicCesium.js 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/aICrudTemplate/FormDiaLog.vue 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/aICrudTemplate/index.vue 28 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/areaStatistics/index.vue patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/defenseZone/index.vue patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/index.vue patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo.vue 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue 219 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/index.vue 124 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js 37 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/index.vue patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceScrap/FormDiaLog.vue 91 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceScrap/deviceScrap.vue 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceScrap/index.vue 37 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceScrap/接口文档.txt patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue 113 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue 4 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js 28 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/index.vue 14 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue 17 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/index.vue 5 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/接口文档.txt 341 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/alarmRecords/FormDiaLog.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/alarmRecords/index.vue patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue 87 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/index.vue patch | view | raw | blame | history
uniapps/work-app/src/api/user/index.js 17 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/voiceCall/index.vue 42 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/userDetail/password/index.vue 204 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/pages.json 19 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/pages/equipmentManagement/index.vue 138 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/pages/legalPolicy/index.vue 154 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/pages/page/index.vue 246 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/static/images/add.svg 24 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/store/modules/app/index.js 1 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/deviceRegistration/add.vue 622 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/add.vue 831 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/index.vue 257 ●●●● patch | view | raw | blame | history
applications/drone-command/src/main.js
@@ -41,6 +41,7 @@
import dayjs from 'dayjs'
import 'styles/common.scss'
import * as Cesium from 'cesium'
import _ from 'lodash'
// 视频回放
import 'video.js/dist/video-js.css'
@@ -81,13 +82,14 @@
    location.reload()
} else {
    if (localStorage.getItem("isReload")) {
        localStorage.removeItem("isReload")
        localStorage.removeItem('isReload')
    }
}
window.$crudCommon = crudCommon
window.axios = axios
window._ = _
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
applications/drone-command/src/utils/cesium/publicCesium.js
@@ -159,7 +159,7 @@
                        terrainLoadCallback?.()
                    })
                } else {
                    // 正则:第一位非0,第二位任意数字,后10位都是0
                    /*// 正则:第一位非0,第二位任意数字,后10位都是0
                    const re = /^[1-9]\d0{10}$/
                    let result = null
@@ -186,7 +186,7 @@
                    }).then(terrainProvider => {
                        this.viewer.terrainProvider = terrainProvider
                        terrainLoadCallback?.()
                    })
                    })*/
                }
            } catch (error) {
                console.error('地形加载失败:', error)
applications/drone-command/src/views/aICrudTemplate/FormDiaLog.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false">
        <div v-if="readonly">
            <el-row>
                <el-col :span="12">
@@ -123,7 +123,7 @@
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = ref(false) // 弹框显隐
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const userList = ref([]) // 用户列表
@@ -179,15 +179,9 @@
    formData.value = res?.data?.data ?? {}
}
// 关闭后重置
function handleClosed() {
    formData.value = initForm()
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    formData.value = dialogMode.value === 'add' ? initForm() : row
    dialogMode.value !== 'add' && (await loadDetail())
}
applications/drone-command/src/views/aICrudTemplate/index.vue
File was renamed from applications/drone-command/src/views/aICrudTemplate/aICrudTemplate.vue
@@ -56,7 +56,7 @@
            </el-row>
        </el-form>
        <div>
            <el-button type="primary" @click="handleAdd">新增</el-button>
            <el-button type="primary" @click="openForm('add')">新增</el-button>
            <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
@@ -73,8 +73,8 @@
            <el-table-column prop="deviceSpecification" label="规格" />
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="handleView(row)" type="primary">查看</el-link>
                    <el-link @click="handleEdit(row)" type="warning">编辑</el-link>
                    <el-link @click="openForm('view', row)" type="primary">查看</el-link>
                    <el-link @click="openForm('edit', row)" type="warning">编辑</el-link>
                    <el-link @click="handleDelete(row)" type="danger">删除</el-link>
                </template>
            </el-table-column>
@@ -88,7 +88,7 @@
            />
        </div>
        <FormDiaLog ref="dialogRef" @success="getList" />
        <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" />
    </basic-container>
</template>
@@ -115,6 +115,7 @@
const selectedIds = ref([]) // 勾选的设备ID列表
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dialogVisible = ref(null) // 弹框实例
const dictObj = ref({
    deviceType: [], //设备类型
    deviceAtt: [], //设备属性
@@ -156,16 +157,6 @@
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 查看
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
// 编辑
function handleEdit(row) {
    dialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
// 删除
@@ -210,9 +201,12 @@
    })
}
// 新增
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
// 新增/编辑/查看 弹框
function openForm(mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
    })
}
onMounted(() => {
applications/drone-command/src/views/areaManage/areaStatistics/index.vue
applications/drone-command/src/views/areaManage/defenseZone/index.vue
applications/drone-command/src/views/areaManage/partition/index.vue
applications/drone-command/src/views/areaManage/precinctInfo.vue
File was deleted
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
New file
@@ -0,0 +1,219 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]">
        <div v-if="readonly">
            <el-row>
                <el-col :span="12">
                    <div>派出所名称: {{ formData.stationName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>联系人: {{ formData.contactPerson }}</div>
                </el-col>
                <el-col :span="12">
                    <div>位置: {{ formData.address }}</div>
                </el-col>
                <el-col :span="12">
                    <div>联系方式: {{ formData.contactPhone }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="派出所名称" prop="stationName">
                        <el-input v-model="formData.stationName" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系人" prop="contactPerson">
                        <el-input v-model="formData.contactPerson" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系方式" prop="contactPhone">
                        <el-input v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="位置" prop="longitude">
                        <el-button @click="selectLocation" style="width: 100%">
                            {{ formData.longitude }}, {{ formData.longitude }}, {{ formData.address }}
                        </el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
            <el-button v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
                确定
            </el-button>
        </template>
        <el-dialog v-model="visible1" title="选择位置" destroy-on-close @closed="positionClosed">
            <div class="ztzf-cesium" id="mapContainer"></div>
            <template #footer>
                <el-button @click="handleMapCancel">取消</el-button>
                <el-button type="primary" @click="handleMapConfirm">确定</el-button>
            </template>
        </el-dialog>
    </el-dialog>
</template>
<script setup>
import { computed, nextTick, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwPoliceStationDetailApi, fwPoliceStationSubmitApi } from './precinctInfoApi'
import { fieldRules } from '@ztzf/utils'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import * as Cesium from 'cesium'
import axios from 'axios'
const initForm = () => ({
    address: '', // 位置
    contactPerson: '', // 联系人
    contactPhone: '', // 联系方式
    stationName: '', // 派出所名称
    longitude: 0,
    latitude: 0,
})
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const visible1 = ref(false) // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
const tempLocation = ref({ longitude: null, latitude: null })
const rules = {
    address: fieldRules(true, 50),
    stationName: fieldRules(true, 50),
    contactPerson: fieldRules(true, 50),
    contactPhone: fieldRules(true, 0),
}
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        await fwPoliceStationSubmitApi(formData.value)
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
// 加载详情
async function loadDetail() {
    if (!formData.value.id) return
    const res = await fwPoliceStationDetailApi({ id: formData.value.id })
    formData.value = res?.data?.data ?? {}
}
function LeftClickEvent(click) {
    const pos = click.position // Cartesian2 屏幕坐标
    // 屏幕坐标 -> 椭球面坐标(不考虑地形/模型)
    const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
    if (!cartesian3) {
        return { longitude: 112, latitude: 23 }
    }
    // 椭球面坐标 -> 经纬度
    const carto = Cesium.Cartographic.fromCartesian(cartesian3)
    const longitude = Cesium.Math.toDegrees(carto.longitude)
    const latitude = Cesium.Math.toDegrees(carto.latitude)
    tempLocation.value = { longitude, latitude }
    // 添加红点(全局仅保留一个)并渲染经纬度标签
    if (!redPointEntity) {
        redPointEntity = viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
            point: {
                color: Cesium.Color.RED,
                pixelSize: 10,
            },
            label: {
                text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
                fillColor: Cesium.Color.WHITE,
                outlineColor: Cesium.Color.BLACK,
                outlineWidth: 2,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                pixelOffset: new Cesium.Cartesian2(0, -12),
            },
        })
    } else {
        redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
        redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`
    }
}
function positionClosed() {
    redPointEntity = null
}
let viewer
let redPointEntity
async function selectLocation() {
    visible1.value = true
    await nextTick()
    const publicCesiumInstance = new PublicCesium({
        dom: 'mapContainer',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    publicCesiumInstance.addLeftClickEvent(null, LeftClickEvent)
    viewer = publicCesiumInstance.getViewer()
}
function handleMapCancel() {
    tempLocation.value = { longitude: null, latitude: null }
    visible1.value = false
}
async function getLocationName() {
    const tk = import.meta.env.VITE_APP_TDT_TOKEN
    const { latitude, longitude } = tempLocation.value
    const http = axios.create({ withCredentials: false })
    const res = await http({
        url: `https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&tk=${tk}`,
        method: 'get',
    })
    formData.value.address = res.data.result.formatted_address
}
function handleMapConfirm() {
    if (tempLocation.value.longitude != null && tempLocation.value.latitude != null) {
        formData.value.longitude = _.round(tempLocation.value.longitude, 6)
        formData.value.latitude = _.round(tempLocation.value.latitude, 6)
        getLocationName()
    }
    visible1.value = false
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    formData.value = dialogMode.value === 'add' ? initForm() : row
    dialogMode.value !== 'add' && (await loadDetail())
}
defineExpose({ open })
</script>
<style scoped lang="scss">
#mapContainer {
    width: 100%;
    height: 500px;
}
</style>
applications/drone-command/src/views/areaManage/precinctInfo/index.vue
New file
@@ -0,0 +1,124 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams">
            <el-row :gutter="16">
                <el-col :span="4">
                    <el-form-item label="派出所名称" prop="stationName">
                        <el-input v-model="searchParams.stationName" placeholder="请输入" clearable @clear="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item>
                        <el-button @click="resetForm">重置</el-button>
                        <el-button type="primary" @click="handleSearch">查询</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div>
            <el-button type="primary" @click="openForm('add')">新增</el-button>
            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
            <el-table-column type="selection" width="55" />
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="stationName" label="派出所名称" />
            <el-table-column prop="address" label="位置" />
            <el-table-column prop="longitude" label="经度" />
            <el-table-column prop="latitude" label="纬度" />
            <el-table-column prop="contactPerson" label="联系人" />
            <el-table-column prop="contactPhone" label="联系方式" />
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="openForm('edit', row)" type="warning">编辑</el-link>
                    <el-link @click="handleDelete(row)" type="danger">删除</el-link>
                </template>
            </el-table-column>
        </el-table>
        <div>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
        </div>
        <FormDiaLog ref="dialogRef" @success="getList" v-if="dialogVisible" v-model="dialogVisible" />
    </basic-container>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fwPoliceStationPageApi, fwPoliceStationRemoveApi } from './precinctInfoApi'
import FormDiaLog from './FormDiaLog.vue'
const initSearchParams = () => ({
    stationName: '', // 名称
    current: 1, // 当前页
    size: 10, // 每页大小
})
const searchParams = ref(initSearchParams()) // 查询参数
const total = ref(0) // 总条数
const loading = ref(false) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dialogVisible = ref(false)
// 获取列表
async function getList() {
    loading.value = true
    try {
        const res = await fwPoliceStationPageApi(searchParams.value)
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
        loading.value = false
    }
}
// 查询
function handleSearch() {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 删除
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwPoliceStationRemoveApi({ ids })
    ElMessage.success('删除成功')
    selectedIds.value = []
    getList()
}
// 勾选值设置
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
// 新增
function openForm(mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
    })
}
onMounted(() => {
    getList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js
New file
@@ -0,0 +1,37 @@
import request from '@/axios'
// 查list
export const fwPoliceStationPageApi = params => {
    return request({
        url: `/drone-fw/area/fwPoliceStation/page`,
        method: 'get',
        params,
    })
}
// 增加或更新
export const fwPoliceStationSubmitApi = data => {
    return request({
        url: `/drone-fw/area/fwPoliceStation/submit`,
        method: 'post',
        data,
    })
}
//删除
export const fwPoliceStationRemoveApi = params => {
    return request({
        url: `/drone-fw/area/fwPoliceStation/remove`,
        method: 'post',
        params,
    })
}
//详情
export const fwPoliceStationDetailApi = params => {
    return request({
        url: `/drone-fw/area/fwPoliceStation/detail`,
        method: 'get',
        params,
    })
}
applications/drone-command/src/views/areaManage/sceneConfig/index.vue
applications/drone-command/src/views/basicManage/deviceScrap/FormDiaLog.vue
New file
@@ -0,0 +1,91 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close>
        <div v-if="dialogReadonly">
            <el-row>
                <el-col :span="12">
                    <div>设备名称: {{ formData.deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>设备类型: {{ getDictLabel(formData.deviceType, dictObj.deviceType) }}</div>
                </el-col>
                <el-col :span="12">
                    <div>型号 : {{ formData.deviceModel }}</div>
                </el-col>
                <el-col :span="12">
                    <div>生产厂商 : {{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div>入库时间 : {{ formData.createTime }}</div>
                </el-col>
                <el-col :span="12">
                    <div>来源 : {{ formData.source }}</div>
                </el-col>
                <el-col :span="12">
                    <div>所属部门 : {{ formData.belongDeptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>负责人 : {{ formData.charger }}</div>
                </el-col>
                <el-col :span="12">
                    <div>报废时间 : {{ formData.scrapTime }}</div>
                </el-col>
                <el-col :span="24">
                    <div>报废原因 : {{ formData.scrapReason }}</div>
                </el-col>
                <el-col :span="24">
                    <div>处置方式 : {{ formData.disposeWay }}</div>
                </el-col>
            </el-row>
        </div>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { getDictLabel } from '@ztzf/utils'
import { fwDeviceScrapDetailApi } from '@/views/basicManage/deviceStock/fwDeviceScrapApi'
const initForm = () => ({})
const emit = defineEmits(['success'])
const formData = ref(initForm()) // 表单数据
const visible = ref(false) // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const dictObj = inject('dictObj')
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '维护计划' })
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 加载详情
async function loadDetail() {
    if (!formData.value.id) return
    const res = await fwDeviceScrapDetailApi({ id: formData.value.id })
    let obj = res?.data?.data || {}
    formData.value = { ...formData.value, ...obj }
}
// 关闭后重置
function handleClosed() {
    formData.value = initForm()
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    if (dialogMode.value === 'add') {
        formData.value = initForm()
    } else {
        formData.value = row
        await loadDetail()
    }
}
defineExpose({ open })
</script>
applications/drone-command/src/views/basicManage/deviceScrap/deviceScrap.vue
File was deleted
applications/drone-command/src/views/basicManage/deviceScrap/index.vue
copy from applications/drone-command/src/views/basicManage/maintainRecord/maintainRecord.vue copy to applications/drone-command/src/views/basicManage/deviceScrap/index.vue
File was copied from applications/drone-command/src/views/basicManage/maintainRecord/maintainRecord.vue
@@ -40,10 +40,6 @@
                </el-col>
            </el-row>
        </el-form>
        <div>
            <el-button type="primary" @click="handleAdd">维护计划</el-button>
            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
            <el-table-column type="selection" width="55" />
            <el-table-column type="index" width="60" label="序号" />
@@ -55,18 +51,16 @@
            </el-table-column>
            <el-table-column prop="deviceModel" label="型号" />
            <el-table-column prop="manufacturer" label="生产厂商" />
            <el-table-column prop="createTime" label="入库时间" />
            <el-table-column prop="source" label="来源" />
            <el-table-column prop="charger" label="负责人" />
            <el-table-column prop="scrapTime" label="报废时间" />
            <el-table-column prop="scrapReason" label="报废原因" />
            <el-table-column prop="disposeWay" label="处置方式" />
            <el-table-column prop="maintainReminder" label="维护提醒" />
            <el-table-column prop="planCycleType" label="维护计划">
                <template v-slot="{ row }">
                    {{ getPlanCycleLabel(row) }}
                </template>
            </el-table-column>
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="handleView(row)" type="primary">查看</el-link>
                    <el-link @click="maintenance(row)" type="warning">维护</el-link>
                    <el-link @click="handleDelete(row)" type="danger">删除</el-link>
                </template>
            </el-table-column>
        </el-table>
@@ -84,13 +78,12 @@
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDeptTree } from '@/api/system/dept'
import { getDictLabel } from '@ztzf/utils'
import { fwDeviceMaintainPlanPageApi, fwDeviceMaintainPlanRemoveApi } from './maintainRecordApi'
import FormDiaLog from './FormDiaLog.vue'
import MaintenanceDiaLog from '@/views/basicManage/maintainRecord/MaintenanceDiaLog.vue'
import { fwDeviceScrapPageApi } from '@/views/basicManage/deviceStock/fwDeviceScrapApi'
const initSearchParams = () => ({
    deviceName: '', // 设备名称
@@ -128,7 +121,7 @@
async function getList() {
    loading.value = true
    try {
        const res = await fwDeviceMaintainPlanPageApi(searchParams.value)
        const res = await fwDeviceScrapPageApi(searchParams.value)
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
@@ -152,22 +145,6 @@
// 查看
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
// 维护
function maintenance(row) {
    maintenanceDialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
// 删除
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDeviceMaintainPlanRemoveApi({ ids })
    ElMessage.success('删除成功')
    selectedIds.value = []
    getList()
}
// 勾选值设置
applications/drone-command/src/views/basicManage/deviceScrap/接口文档.txt
applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue
New file
@@ -0,0 +1,113 @@
<template>
    <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="24">
                    <el-form-item label="报废原因" prop="scrapReason">
                        <el-input v-model="formData.scrapReason" maxlength="200" type="textarea" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="处置方式" prop="disposeWay">
                        <el-input v-model="formData.disposeWay" maxlength="200" type="textarea" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="报废时间" prop="scrapTime">
                        <el-date-picker
                            v-model="formData.scrapTime"
                            type="date"
                            placeholder="选择日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
                        />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                v-if="!dialogReadonly"
                type="primary"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
            >
                确定
            </el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fieldRules } from '@ztzf/utils'
import { fwDeviceScrapSaveApi } from '@/views/basicManage/deviceStock/fwDeviceScrapApi'
const initForm = () => ({
    deviceId: '', // 设备ID
    disposeWay: '', //处置方式
    scrapReason: '', //       报废原因
    scrapTime: '', //  报废时间
})
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = ref(false) // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false)
const dialogReadonly = computed(() => dialogMode.value === 'view')
const dialogTitle = computed(() => {
    if (dialogMode.value === 'edit') {
        return '报废'
    } else if (dialogMode.value === 'view') {
        return '查看'
    } else {
        return '新增'
    }
})
const rules = {
    scrapReason: fieldRules(true, 200),
    disposeWay: fieldRules(true, 200),
    scrapTime: fieldRules(true, 0),
}
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        await fwDeviceScrapSaveApi(formData.value)
        ElMessage.success(dialogMode.value === 'add' ? '出库成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
// 关闭后重置
function handleClosed() {
    formData.value = initForm()
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    formData.value.deviceId = row.id
}
onMounted(() => {})
defineExpose({ open })
</script>
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue
@@ -55,7 +55,6 @@
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDeviceDetailApi, fwDeviceSubmitApi } from '@/views/basicManage/deviceStock/fwDevice'
import { getUserListApi } from '@/api/system/user'
import { fieldRules } from '@ztzf/utils'
import { fwDeviceTrackSubmitApi } from '@/views/basicManage/deviceStock/fwDeviceTrackApi'
@@ -76,7 +75,7 @@
const dialogReadonly = computed(() => dialogMode.value === 'view')
const dialogTitle = computed(() => {
    if (dialogMode.value === 'edit') {
        return '编辑'
        return '出库'
    } else if (dialogMode.value === 'view') {
        return '查看'
    } else {
@@ -119,7 +118,6 @@
        submitting.value = false
    }
}
// 关闭后重置
function handleClosed() {
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue
@@ -28,7 +28,7 @@
                    <div>用途: {{ formData.purpose }}</div>
                </el-col>
                <el-col :span="12">
                    <div>所属部门: {{ formData.belongDept }}</div>
                    <div>所属部门: {{ formData.belongDeptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>负责人: {{ formData.charger }}</div>
@@ -155,7 +155,7 @@
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDeviceDetailApi, fwDevicePageApi, fwDeviceSubmitApi } from '@/views/basicManage/deviceStock/fwDevice'
import { fwDeviceDetailApi,  fwDeviceSubmitApi } from '@/views/basicManage/deviceStock/fwDevice'
import { getUserListApi } from '@/api/system/user'
import { fieldRules, getDictLabel } from '@ztzf/utils'
import { fwDeviceTrackPageApi } from '@/views/basicManage/deviceStock/fwDeviceTrackApi'
applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js
New file
@@ -0,0 +1,28 @@
import request from '@/axios'
// 新增报废记录
export const fwDeviceScrapSaveApi = data => {
    return request({
        url: `/drone-fw/device/fwDeviceScrap/save`,
        method: 'post',
        data,
    })
}
// 分页
export const fwDeviceScrapPageApi = params => {
    return request({
        url: `/drone-fw/device/fwDeviceScrap/page`,
        method: 'get',
        params,
    })
}
// 详情
export const fwDeviceScrapDetailApi = params => {
    return request({
        url: `/drone-fw/device/fwDeviceScrap/detail`,
        method: 'get',
        params,
    })
}
applications/drone-command/src/views/basicManage/deviceStock/index.vue
File was renamed from applications/drone-command/src/views/basicManage/deviceStock/deviceStock.vue
@@ -58,7 +58,7 @@
        <div>
            <el-button type="primary" @click="handleAdd">新增</el-button>
            <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
            <!--            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>-->
        </div>
        <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
            <el-table-column type="selection" width="55" />
@@ -87,7 +87,8 @@
                    <el-link @click="handleView(row)" type="primary">查看</el-link>
                    <el-link @click="handleEdit(row)" type="warning">编辑</el-link>
                    <el-link @click="outbound(row)" type="warning">出库</el-link>
                    <el-link @click="handleDelete(row)" type="danger">删除</el-link>
                    <el-link @click="scrap(row)" type="warning" v-if="row.status !== 3">报废</el-link>
                    <!--                    <el-link @click="handleDelete(row)" type="danger">删除</el-link>-->
                </template>
            </el-table-column>
        </el-table>
@@ -102,6 +103,7 @@
        <FormDiaLog ref="dialogRef" @success="getList" />
        <DeviceTrackDiaLog ref="outboundDialogRef" @success="getList" />
        <DeviceScrapDiaLog ref="scrapDialogRef" @success="getList" />
    </basic-container>
</template>
@@ -115,6 +117,7 @@
import { blobDownload, dateRangeFormat, getDictLabel } from '@ztzf/utils'
import { DEVICE_STATUS } from '@ztzf/constants'
import DeviceTrackDiaLog from '@/views/basicManage/deviceStock/DeviceTrackDiaLog.vue'
import DeviceScrapDiaLog from '@/views/basicManage/deviceStock/DeviceScrapDiaLog.vue'
const initSearchParams = () => ({
    deviceName: '', // 设备名称
@@ -131,6 +134,7 @@
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const outboundDialogRef = ref(null) // 弹框实例
const scrapDialogRef = ref(null) // 弹框实例
const dictObj = ref({
    deviceType: [], //设备类型
    deviceAtt: [], //设备属性
@@ -184,7 +188,11 @@
}
function outbound(row) {
    outboundDialogRef.value?.open({ mode: 'add', row: { ...row } })
    outboundDialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
function scrap(row) {
    scrapDialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
// 删除
applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue
@@ -16,7 +16,7 @@
                    <div>生产厂商 : {{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div>所属部门 : {{ formData.belongDept }}</div>
                    <div>所属部门 : {{ formData.belongDeptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>负责人 : {{ formData.charger }}</div>
@@ -108,9 +108,12 @@
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fieldRules, getDictLabel } from '@ztzf/utils'
import { fwDeviceMaintainPlanDetailApi, fwDeviceMaintainPlanSubmitApi, fwDevicePageApi } from './maintainRecordApi'
import { fwDeviceTrackPageApi } from '@/views/basicManage/deviceStock/fwDeviceTrackApi'
import { fwDeviceMaintainRecordPageApi } from '@/views/basicManage/maintainRecord/fwDeviceMaintainRecord'
import {
    fwDeviceMaintainPlanDetailApi,
    fwDeviceMaintainPlanSubmitApi,
    fwDevicePageApi,
} from '@/views/basicManage/maintainRecord/maintainRecordApi'
const initForm = () => ({
    deviceId: '',
applications/drone-command/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue
@@ -14,12 +14,18 @@
            <el-row>
                <el-col :span="24">
                    <el-form-item label="维护内容" prop="maintainContent">
                        <el-input v-model="formData.maintainContent" maxlength="50" placeholder="请输入" clearable />
                        <el-input
                            v-model="formData.maintainContent"
                            maxlength="200"
                            type="textarea"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="更换部件" prop="replacePart">
                        <el-input v-model="formData.replacePart" maxlength="50" placeholder="请输入" clearable />
                        <el-input v-model="formData.replacePart" maxlength="200" type="textarea" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
@@ -54,8 +60,8 @@
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fieldRules } from '@ztzf/utils'
import { fwDeviceMaintainPlanDetailApi, fwDeviceMaintainPlanSubmitApi, fwDevicePageApi } from './maintainRecordApi'
import { fwDeviceMaintainRecordSubmitApi } from '@/views/basicManage/maintainRecord/fwDeviceMaintainRecord'
import { fwDeviceMaintainPlanDetailApi } from '@/views/basicManage/maintainRecord/maintainRecordApi'
const initForm = () => ({
    deviceId: '',
@@ -92,8 +98,8 @@
})
const rules = {
    maintainContent: fieldRules(true, 50),
    replacePart: fieldRules(true, 50),
    maintainContent: fieldRules(true, 200),
    replacePart: fieldRules(true, 200),
    maintainTime: fieldRules(true, 0),
}
@@ -126,7 +132,6 @@
        planId: res?.data?.data?.id,
        deviceId: res?.data?.data.deviceId,
    }
    console.log(formData.value, 66)
}
// 关闭后重置
applications/drone-command/src/views/basicManage/maintainRecord/index.vue
File was renamed from applications/drone-command/src/views/basicManage/maintainRecord/maintainRecord.vue
@@ -88,9 +88,12 @@
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDeptTree } from '@/api/system/dept'
import { getDictLabel } from '@ztzf/utils'
import { fwDeviceMaintainPlanPageApi, fwDeviceMaintainPlanRemoveApi } from './maintainRecordApi'
import FormDiaLog from './FormDiaLog.vue'
import MaintenanceDiaLog from '@/views/basicManage/maintainRecord/MaintenanceDiaLog.vue'
import {
    fwDeviceMaintainPlanPageApi,
    fwDeviceMaintainPlanRemoveApi,
} from '@/views/basicManage/maintainRecord/maintainRecordApi'
const initSearchParams = () => ({
    deviceName: '', // 设备名称
applications/drone-command/src/views/basicManage/maintainRecord/接口文档.txt
File was deleted
applications/drone-command/src/views/recordManage/alarmRecords/FormDiaLog.vue
@@ -127,11 +127,11 @@
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    formData.value = row
    visible.value = true
    if (dialogMode.value === 'add') {
        formData.value = initForm()
    } else {
        formData.value = row
        await loadDetail()
    }
}
applications/drone-command/src/views/recordManage/alarmRecords/index.vue
applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue
@@ -1,19 +1,24 @@
<template>
    <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <div>
            <div>无人机名称:{{ formData.droneName }}</div>
            <div>序列号: {{ formData.serialNo }}</div>
            <div>告警时间: {{ formData.alarmTime }}</div>
            <div>触发原因: {{ formData.triggerType }}</div>
            <div>停留时长: {{ formData.stayDuration }}</div>
        <div class="bodyBox">
            <div class="leftMap ztzf-cesium" id="leftMapContainer"></div>
            <div class="rightInfo">
                <div>
                    <div>无人机名称:{{ formData.droneName }}</div>
                    <div>序列号: {{ formData.serialNo }}</div>
                    <div>告警时间: {{ formData.alarmTime }}</div>
                    <div>触发原因: {{ formData.triggerType }}</div>
                    <div>停留时长: {{ formData.stayDuration }}</div>
                </div>
                <el-table :data="list">
                    <el-table-column type="index" width="60" label="序号" />
                    <el-table-column label="经纬度">
                        <template #default="{ row }">{{ row.longitude }},{{ row.latitude }}</template>
                    </el-table-column>
                    <el-table-column prop="createTime" label="时间" />
                </el-table>
            </div>
        </div>
        <el-table :data="list">
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="areaCode" label="areaCode" />
            <el-table-column prop="createTime" label="createTime" />
            <el-table-column prop="latitude" label="latitude" />
            <el-table-column prop="longitude" label="longitude" />
        </el-table>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
        </template>
@@ -24,6 +29,9 @@
import { computed, ref } from 'vue'
import { fwDroneFlightRecordDetailApi } from '@/views/recordManage/historyTracks/fwDroneFlightRecord'
import { fwDroneFlightRecordDetailPageApi } from '@/views/recordManage/historyTracks/flyTrajectory'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import * as Cesium from 'cesium'
import { flyVisual } from '@/utils/cesium/mapUtil'
const initForm = () => ({
    droneName: '',
@@ -59,7 +67,28 @@
    const res = await fwDroneFlightRecordDetailApi({ id: formData.value.id })
    const res1 = await fwDroneFlightRecordDetailPageApi({ flightRecordId: formData.value.id })
    list.value = res1.data.data.records ?? []
    drawFlightPath()
    formData.value = res?.data?.data ?? {}
}
// 角度转c3
function degreesToC3({ longitude, latitude, height = 0 }) {
    return Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
}
// 渲染航线
function drawFlightPath() {
    const positions = list.value.map(p => degreesToC3(p))
    droneLineEntity && geometricSource.entities.remove(droneLineEntity)
    const color = Cesium.Color.fromCssColorString('#00D690')
    droneLineEntity = geometricSource.entities.add({
        polyline: {
            positions: positions,
            width: 3,
            material: new Cesium.PolylineOutlineMaterialProperty({ color }),
            clampToGround: false,
        },
    })
    viewer.flyTo(droneLineEntity)
}
// 关闭后重置
@@ -67,17 +96,47 @@
    formData.value = initForm()
}
let viewer, geometricSource, droneLineEntity
function initMap() {
    const publicCesiumInstance = new PublicCesium({
        dom: 'leftMapContainer',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    viewer = publicCesiumInstance.getViewer()
    geometricSource = new Cesium.CustomDataSource('geometricSource')
    viewer.dataSources.add(geometricSource)
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    formData.value = row
    visible.value = true
    await nextTick(() => {
        initMap()
    })
    if (dialogMode.value === 'add') {
        formData.value = initForm()
    } else {
        formData.value = row
        await loadDetail()
    }
}
defineExpose({ open })
</script>
<style scoped lang="scss">
.bodyBox {
    display: flex;
    .rightInfo {
        width: 50%;
        height: 500px;
    }
    .leftMap {
        width: 50%;
        height: 500px;
    }
}
</style>
applications/drone-command/src/views/recordManage/historyTracks/index.vue
uniapps/work-app/src/api/user/index.js
@@ -72,6 +72,23 @@
  });
};
/** 发送重置密码验证码 */
export const sendResetCode = (phone) => {
  return request({
    url: `/system/verificationCode/resetCode/${phone}`,
    method: 'get',
  });
};
/** 根据验证码重置密码 */
export const resetPassword = (data) => {
  return request({
    url: '/system/user/retrievePassword',
    method: 'post',
    data: data,
  });
};
// 微信登录接口
export const loginByWechat = (code) => {
  return request({
uniapps/work-app/src/pages/voiceCall/index.vue
@@ -2,7 +2,7 @@
  <div class="voiceCallContainer" :style="{ paddingTop: topMargin + 'px' }">
    <!-- 顶部搜索栏 -->
    <div class="searchBar">
      <up-search placeholder="请输入关键字搜索" v-model="defaultParam.searchKeyword"  :animation="true"   :show-action="false"></up-search>
      <up-search placeholder="请输入关键字搜索" v-model="defaultParam.friendNickName"  :animation="true"   :show-action="false" @confirm="onSearch"  @search="onSearch" @clear="handleClear"></up-search>
    </div>
    <!-- 联系人列表 -->
@@ -43,16 +43,17 @@
<script setup>
import { getStatusBarHeight } from '@/utils/common';
const topMargin = getStatusBarHeight()
import { getPhoneBookListApi } from '@/api/voiceCall/index.js'
import { onShow } from '@dcloudio/uni-app'
import defaultAvatar  from '/static/images/defaultAvatar.svg'
import defaultAvatar from '/static/images/defaultAvatar.svg'
// 联系人数据
const contacts = ref([])
const defaultParam =ref( {
const defaultParam = ref({
  current: 1,
  size: 10,
  searchKeyword:''
  friendNickName: ''
})
// 加载状态
@@ -62,19 +63,16 @@
// 获取通讯录
const getPhoneBookList = () => {
  if (loading.value || !hasMore.value) return
  loading.value = true
  // 添加分页参数
  const params = {
    current: defaultParam.value.current,
    size: defaultParam.value.size,
    searchKeyword: defaultParam.value.searchKeyword
    friendNickName: defaultParam.value.friendNickName
  }
  getPhoneBookListApi(params).then(res => {
    const response = res.data.data
    console.log('通讯录',  response)
    if (defaultParam.value.current === 1) {
      contacts.value = response.records
    } else {
@@ -108,7 +106,18 @@
  defaultParam.value.current++
  getPhoneBookList()
}
const onSearch = () => {
  defaultParam.value.current = 1
  hasMore.value = true
  getPhoneBookList()
}
const handleClear = () => {
  defaultParam.value.friendNickName = ''
  defaultParam.value.current = 1
  hasMore.value = true
  getPhoneBookList()
}
// 拨打电话方法
const makeCall = (contact) => {
  const contactName = contact.friendNickName || '未知联系人'
@@ -224,12 +233,13 @@
  height: 60rpx;
}
  .loadingMore {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 80rpx;
    font-size: 28rpx;
    color: #999;
  }
.loadingMore {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 80rpx;
  font-size: 28rpx;
  color: #999;
}
</style>
uniapps/work-app/src/subPackages/userDetail/password/index.vue
@@ -4,17 +4,26 @@
        <view class="detailBox">
            <div class="detailCon">
                <div class="orderRow">
                    <div class="rowTitle">原始密码</div>
                    <input type="password" v-model="passwordForm.oldPassword" placeholder="请输入" class="input-item" />
                    <div class="rowTitle">账号</div>
                    <input type="text" v-model="passwordForm.username" placeholder="请输入账号" class="input-item" />
                </div>
                <div class="orderRow">
                    <div class="rowTitle">新密码</div>
                    <input type="password" v-model="passwordForm.newPassword" placeholder="请输入" class="input-item" />
                    <div class="rowTitle">密码</div>
                    <input type="password" v-model="passwordForm.password" placeholder="请输入" class="input-item" />
                </div>
                <div class="orderRow">
                    <div class="rowTitle">确认密码</div>
                    <input type="password" v-model="passwordForm.newPassword1" placeholder="请输入" class="input-item" />
                    <input type="password" v-model="passwordForm.password2" placeholder="请输入" class="input-item" />
                </div>
                <div class="orderRow">
                    <div class="rowTitle">验证码</div>
                    <div class="code-container">
                        <input type="text" v-model="passwordForm.code" placeholder="请输入验证码" class="input-item code-input" />
                        <u-button :disabled="countDown > 0" @click="sendVerificationCode" class="send-code-btn">
                            {{ countDown > 0 ? `${countDown}s后重发` : '发送验证码' }}
                        </u-button>
                    </div>
                </div>
            </div>
        </view>
@@ -30,21 +39,25 @@
        useUserStore
    } from "@/store/index.js";
    import md5 from "js-md5";
    import {
        getUserInfo,
        updateInfo,
        updatePassword
    } from '@/api/user/index.js';
    import { getUserInfo,
    updateInfo,
    updatePassword,
    sendResetCode,
    resetPassword
} from '@/api/user/index.js';
    const passwordIcon = {
        show: 'fas fa-eye', // 显示密码时的图标
        hide: 'fas fa-eye-slash' // 隐藏密码时的图标
    }
    const userStore = useUserStore();
    const passwordForm = ref({
        oldPassword: '',
        newPassword: '',
        newPassword1: '',
    });
const passwordForm = ref({
    username: '',
    password: '',
    password2: '',
    code: ''
});
const countDown = ref(0);
let timer = null;
    // 校验密码不能包含中文
    const validatePasswordNoChinese = (password) => {
        const chineseRegex = /[\u4e00-\u9fa5]/;
@@ -80,30 +93,82 @@
    };
    const clearForm = () => {
        passwordForm.value = {
            oldPassword: '',
            newPassword: '',
            newPassword1: '',
            username: '',
            password: '',
            password2: '',
            code: ''
        };
        countDown.value = 0;
        if (timer) {
            clearInterval(timer);
            timer = null;
        }
    };
    const reset = () => {
        clearForm();
    };
    const submit = () => {
        // 原始密码校验
        if (!passwordForm.value.oldPassword) {
    // 发送验证码
    const sendVerificationCode = () => {
        const { username } = passwordForm.value;
        if (!username) {
            uni.showToast({
                title: '请输入原始密码',
                title: '请先输入账号',
                icon: 'none',
                duration: 2000
            });
            return;
        }
        // 中文校验
        if (!validatePasswordNoChinese(passwordForm.value.newPassword)) return;
        // 新密码校验
        if (!passwordForm.value.newPassword) {
        // 这里假设账号就是手机号,实际项目中可能需要验证格式
        sendResetCode(username).then(res => {
            if (res.data.code === 200) {
                uni.showToast({
                    title: '验证码发送成功',
                    icon: 'success',
                    duration: 2000
                });
                // 开始倒计时
                countDown.value = 60;
                timer = setInterval(() => {
                    countDown.value--;
                    if (countDown.value <= 0) {
                        clearInterval(timer);
                        timer = null;
                    }
                }, 1000);
            } else {
                uni.showToast({
                    title: res.data.msg || '验证码发送失败',
                    icon: 'none',
                    duration: 2000
                });
            }
        }).catch(err => {
            uni.showToast({
                title: '网络错误,请稍后重试',
                icon: 'none',
                duration: 2000
            });
        });
    };
    const reset = () => {
        clearForm();
    };
    const submit = () => {
        const { username, password, password2, code } = passwordForm.value;
        // 账号校验
        if (!username) {
            uni.showToast({
                title: '请输入账号',
                icon: 'none',
                duration: 2000
            });
            return;
        }
        // 新密码校验
        if (!password) {
            uni.showToast({
                title: '请输入新密码',
                icon: 'none',
@@ -111,9 +176,12 @@
            });
            return;
        }
        // 确认密码校验
        if (passwordForm.value.newPassword !== passwordForm.value.newPassword1) {
        // 中文校验
        if (!validatePasswordNoChinese(password)) return;
        // 确认密码校验
        if (password !== password2) {
            uni.showToast({
                title: '两次输入的新密码不一致',
                icon: 'none',
@@ -122,34 +190,56 @@
            return;
        }
        // 验证码校验
        if (!code) {
            uni.showToast({
                title: '请输入验证码',
                icon: 'none',
                duration: 2000
            });
            return;
        }
        // 复杂度校验
        if (!validatePasswordStrength(passwordForm.value.newPassword)) return;
        updatePassword(md5(passwordForm.value.oldPassword),
            md5(passwordForm.value.newPassword),
            md5(passwordForm.value.newPassword1)).then(res => {
        if (!validatePasswordStrength(password)) return;
        // 调用重置密码接口
        const resetData = {
            username,
            // password: md5(password),
            // confirmPassword: md5(password2),
      password: password,
      password2: password2,
            code
        };
    console.log('canshu',resetData)
        resetPassword(resetData).then(res => {
            if (res.data.code === 200) {
                uni.showToast({
                    title: '修改信息成功',
                    title: '密码修改成功',
                    icon: 'none',
                    duration: 2000
                });
                clearForm();
                userStore.setUserInfo(null)
                userStore.setUserInfo(null);
                uni.removeStorageSync('rememberedUser');
                uni.reLaunch({
                    url: '/pages/login/index'
                })
                });
            } else {
                uni.showToast({
                    title: res.msg,
                    title: res.data.msg || '密码修改失败',
                    icon: 'none',
                    duration: 2000
                });
            }
        }).catch(err => {
            uni.showToast({
                title: '网络错误,请稍后重试',
                icon: 'none',
                duration: 2000
            });
        });
    };
    onShow(async () => {
        clearForm();
@@ -215,6 +305,36 @@
            input.input-item::placeholder {
                font-size: 30rpx;
            }
            .code-container {
                display: flex;
                align-items: center;
                justify-content: flex-end;
                width: 100%;
            }
            .code-input {
                flex: 1;
                max-width: 400rpx;
                margin-right: 20rpx;
            }
            .send-code-btn {
                width: 200rpx;
                height: 70rpx;
                line-height: 70rpx;
                font-size: 26rpx;
                background: #f5f5f5;
                color: #666;
                border-radius: 8rpx;
                text-align: center;
                border: none;
            }
            .send-code-btn:disabled {
                background: #e0e0e0;
                color: #999;
            }
        }
    }
uniapps/work-wx/src/pages.json
@@ -56,6 +56,7 @@
                        "navigationStyle": "custom"
                    }
                },
                {
                    "path": "airspaceInformation/index",
                    "style": {
@@ -69,6 +70,24 @@
                    }
                },
                {
                    "path": "flightApplication/add",
                    "style": {
                        "navigationBarTitleText": "飞行申请"
                    }
                },
                {
                    "path": "deviceRegistration/add",
                    "style": {
                        "navigationBarTitleText": "飞行器注册"
                    }
                },
                {
                    "path": "regulationsDetail/details",
                    "style": {
                        "navigationBarTitleText": "法律政策详情"
                    }
                },
                {
                    "path": "workDetail/mapWork/index",
                    "style": {
                        "navigationBarTitleText": "地图展示",
uniapps/work-wx/src/pages/equipmentManagement/index.vue
@@ -1,62 +1,130 @@
<template>
  <view class="equipmentManagement">
    <u-search :show-action="true" actionText="搜索" :animation="true"></u-search>
    <view class="card" v-for="(item, index) in list" :key="index">
      <view class="left">
        <u-image :src="item.src" mode="aspectFill" :width="100" :height="100"></u-image>
    <u-search :bgColor="'white'" v-model="noticeTitle" :show-action="true" actionText="搜索" :animation="true" @custom="getEquipmentList"></u-search>
    <view class="content">
      <view class="title">飞行设备</view>
      <view class="card" v-for="(item, index) in equipmentList" :key="index">
        <view class="left">
          <u-image :src="droneSvg" mode="aspectFill" :width="70" :height="90"></u-image>
        </view>
        <view class="right">
          <view class="name">设备名称:{{ item.name }}</view>
          <view class="txt">设备类型:{{ item.type }}</view>
          <view class="txt">设备型号:{{ item.type }}</view>
        <view class="txt">设备sn:{{ item.sn }}</view>
        </view>
      </view>
      <view class="right">
        <view class="grid-text">设备名称:{{ item.name }}</view>
        <view class="grid-text">设备类型:{{ item.type }}</view>
        <view class="grid-text">设备型号:{{ item.xh }}</view>
       <view class="grid-text">设备sn:{{ item.sn }}</view>
      </view>
    </view>
    <view class="add-btn" @click="addEquipment">
      <u-image :src="addSvg" mode="aspectFill" :width="60" :height="68"></u-image>
    </view>
  </view>
</template>
<script setup>
   import { reactive, ref } from 'vue'
  import lbPng from '@/static/images/lb.png'
const list = reactive([
  {
    src: lbPng,
    name: '设备1',
    type: '类型1',
    xh: '型号1',
    sn: 'SN123456',
  },
  {
    src: lbPng,
    name: '设备2',
    type: '类型2',
    xh: '型号2',
    sn: 'SN654321',
  },
])
import { reactive, ref } from 'vue'
import lbPng from '@/static/images/lb.png'
import addSvg from '@/static/images/add.svg'
import droneSvg from '@/static/images/drone.svg'
import { myDevicePageInfoApi, aircraftInfoPageInfoOfMyApi } from '@/api/index.js'
const equipmentList = ref([])
const noticeTitle = ref('')
// 设备列表
const getEquipmentList = async () => {
  try {
    const res = await aircraftInfoPageInfoOfMyApi({
      current: 1,
      size: 10,
      model:'',
      name: '',
    })
    console.log(8888888,res.data.data.records)
    equipmentList.value = res.data.data.records || []
  } catch (error) {
    console.error('获取设备列表失败:', error)
  }
}
// 我的设备详情
const getMyDeviceDetail = async (id) => {
  try {
    const res = await myDevicePageInfoApi({ id })
    if (res.code === 200) {
      // 处理设备详情数据
    }
  } catch (error) {
    console.error('获取我的设备详情失败:', error)
  }
}
// 新增设备
const addEquipment = () => {
  uni.navigateTo({
    url: '/subPackages/deviceRegistration/add',
  })
}
onMounted(() => {
  getEquipmentList()
})
</script>
<style scoped lang="scss">
.equipmentManagement {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  .u-search {
    padding: 10px 20px;
    height: 68rpx;
    flex: 0;
  }
  .content {
    font-family: Source Han Sans CN, Source Han Sans CN;
    height: 0;
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    background: #FFFFFF;
    padding: 0 40rpx 40rpx 40rpx;
    overflow-y: auto;
    box-sizing: border-box;
    border-radius: 12px 12px 6px 6px;
  }
  .title {
    font-weight: bold;
    font-size: 32rpx;
    color: #222324;
    padding: 40rpx 0;
  }
  .card {
    display: flex;
    margin-top: 20rpx;
    background-color: #f5f5f5;
    padding: 20rpx;
    border-radius: 10rpx;
    border-bottom: 1px solid #EBEEF3;
    .left {
      width: 100px;
      height: 100px;
      width: 70px;
      height: 90px;
      margin-right: 16rpx;
    }
    .right {
      flex: 1;
      height: 100px;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      .name {
        font-weight: 500;
        font-size: 28rpx;
        color: #1D2129;
      }
      .txt {
        font-weight: 400;
        font-size: 24rpx;
        color: #86909C;
      }
    }
  }
  .add-btn {
    position: absolute;
    bottom: 40rpx;
    right: 40rpx;
  }
}
</style>
uniapps/work-wx/src/pages/legalPolicy/index.vue
@@ -1,20 +1,33 @@
<template>
  <view class="legalPolicy">
    <u-search :show-action="true" actionText="搜索" :animation="true"></u-search>
    <u-tabs :list="list1" @click="click"></u-tabs>
    <!-- <u-navbar title="法律政策" @rightClick="rightClick" :autoBack="true"></u-navbar> -->
    <u-search :bgColor="'white'" v-model="noticeTitle" :show-action="true" actionText="搜索" :animation="true" @custom="getSysNoticeList"></u-search>
    <view class="tabs-container">
      <u-tabs :list="tabs" @click="tabClick"></u-tabs>
    </view>
    <view class="content">
      <view v-for="(item, index) in list2" :key="index">
        <view>{{ item.name }}</view>
        <view>{{ item.item }}</view>
      <view class="card" v-for="(item, index) in announcementList" :key="index" @click="toRegulationsDetail(item)">
        <view class="left">
          <u-image :src="docSvg" mode="aspectFill" :width="30" :height="36"></u-image>
        </view>
        <view class="right">
          <view class="r-top">{{ item.title }}</view>
          <view class="r-middle">{{ item.description || '暂无描述' }}</view>
          <view class="r-bottom">{{ item.time || '暂无' }}</view>
        </view>
      </view>
    </view>
  </view>
</template>
<script setup>
import { ref } from 'vue'
  import { sysNoticePageInfoApi } from '@/api/index'
  import docSvg from '@/static/images/doc.png'
import { ref, onMounted } from 'vue'
const list1 = ref([
const noticeTitle = ref('')
const tabs = ref([
  {
    name: '法律法规',
  },
@@ -23,25 +36,128 @@
  },
])
const click = (index) => {
  console.log(index)
const currentTab = ref(2)
const tabClick = (val) => {
  if (val.index === 0) {
     currentTab.value = 2
    getSysNoticeList()
  } else {
    currentTab.value = 1
    getSysNoticeList()
  }
}
const list2 = ref([
  {
    name: '法律法规1',
    item: '法律法规1的内容',
  },
  {
    name: '法律法规2',
    item: '法律法规2的内容',
  },
])
const announcementList = ref()
// 获取通知公告列表
async function getSysNoticeList() {
  try {
    const res = await sysNoticePageInfoApi({
      current: 1,
      size: 10,
      noticeType: currentTab.value,
      noticeTitle: noticeTitle.value,
    })
    announcementList.value = res.data.data.records.map(item => ({
      ...item,
      title: item.noticeTitle,
      description: item.remark,
      time: item.pubDate,
      num: item.num,
      like: item.like,
    }))
  } catch (error) {
    uni.showToast({
      title: error.message,
      icon: 'none',
      duration: 2000,
    })
  }
}
// 跳转到法律政策详情页
function toRegulationsDetail(item) {
  uni.navigateTo({
    url: '/subPackages/regulationsDetail/details?id=' + item.noticeId+'&type=2',
  })
}
onMounted(() => {
  getSysNoticeList()
})
</script>
<style scoped lang="scss">
.legalPolicy {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
   .u-search {
    padding: 10px 20px;
    height: 68rpx;
    flex: 0;
  }
  .tabs-container {
    display: flex;
    justify-content: center;
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 14px;
    color: #4E5969;
    background: #FFFFFF;
    border-radius: 12px 12px 6px 6px;
    margin-top: 10px;
  }
  .tabs-container >>> .u-tabs__nav {
    width: auto !important;
  }
  .content {
    width: 100%;
    height: 0;
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    background: #FFFFFF;
    padding: 0 40rpx 40rpx 40rpx;
    overflow-y: auto;
    box-sizing: border-box;
    .card {
      font-family: Source Han Sans CN, Source Han Sans CN;
      display: flex;
      justify-content: space-between;
      border-bottom: 1px solid #EBEEF3;
      padding: 40rpx 0 30rpx 0;
      width: 100%;
      box-sizing: border-box;
      .left {
        .u-image {
          width: 60rpx;
          height: 72rpx;
        }
      }
      .right {
        width: calc(100% - 80rpx);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .r-middle {
        font-weight: 400;
        font-size: 13px;
        color: #86909C;
      }
      .r-bottom {
        font-weight: 400;
        font-size: 13px;
        color: #86909C;
        margin-top: 10rpx;
      }
    }
  }
}
</style>
uniapps/work-wx/src/pages/page/index.vue
@@ -5,7 +5,6 @@
            :keyName="'image'"
            :height="200"
            :indicator="true"
            @change="change"
    ></u-swiper>
    <u-grid :col="4" :border="false" class="grid" @click="gridClick">
        <u-grid-item v-for="(baseListItem,baseListIndex) in baseList" :key="baseListIndex">
@@ -13,27 +12,56 @@
            <text class="grid-text">{{baseListItem.title}}</text>
        </u-grid-item>
    </u-grid>
    <u-tabs :list="tabs" class="tabs">
      <template #right>
            <view
                style="padding-left: 4px;"
                @tap="$u.toast('插槽被点击')"
            >
                更多
    <view class="content">
      <view>
        <view class="title">空域信息</view>
        <view class="card" v-for="(item,index) in announcementList" :key="index">
          <view class="left">
            <u-image :src="docSvg" :width="30" :height="36"></u-image>
          </view>
          <view class="right" @click.stop="airspaceClick(item)">
            <view class="r-top">{{ item.title }}</view>
            <view class="r-middle">{{ item.description || '暂无' }}</view>
            <view class="r-bottom">
              <view>{{ item.time }}</view>
              <!-- <view>{{ item.num || 0 }}</view>
              <view>{{ item.like || 0 }}</view> -->
            </view>
        </template>
    </u-tabs>
    <view class="card" v-for="(item,index) in announcementList" :key="index">
      <view class="left">
        <u-image :src="lbPng" :width="20" :height="20"></u-image>
          </view>
        </view>
      </view>
      <view class="right">
        <view class="c-top">{{ item.airspaceName }}</view>
        <view class="c-middle">{{ item.remarks || '暂无' }}</view>
        <view class="c-bottom">
          <view>{{ item.createTime }}</view>
          <view>{{ item.num || 0 }}</view>
          <view>{{ item.like || 0 }}</view>
      <view>
        <view class="title">法律法规</view>
        <view class="card" v-for="(item,index) in regulationsList" :key="index">
          <view class="left">
            <u-image :src="docSvg" :width="30" :height="36"></u-image>
          </view>
          <view class="right" @click.stop="toRegulationsDetail(item)">
            <view class="r-top">{{ item.title }}</view>
            <view class="r-middle">{{ item.description || '暂无' }}</view>
            <view class="r-bottom">
              <view>{{ item.time }}</view>
              <!-- <view>{{ item.num || 0 }}</view>
              <view>{{ item.like || 0 }}</view> -->
            </view>
          </view>
        </view>
      </view>
      <view>
        <view class="title">政策制度</view>
        <view class="card" v-for="(item,index) in policyList" :key="index">
          <view class="left">
            <u-image :src="docSvg" :width="30" :height="36"></u-image>
          </view>
          <view class="right" @click.stop="policiesClick(item)">
            <view class="r-top">{{ item.title }}</view>
            <view class="r-middle">{{ item.description || '暂无' }}</view>
            <view class="r-bottom">
              <view>{{ item.time }}</view>
              <!-- <view>{{ item.num || 0 }}</view>
              <view>{{ item.like || 0 }}</view> -->
            </view>
          </view>
        </view>
      </view>
    </view>
@@ -47,6 +75,7 @@
  import fxsqPng from '@/static/images/fxsq.svg'
  import flfgPng from '@/static/images/flfg.svg'
  import zczdPng from '@/static/images/zczd.svg'
  import docSvg from '@/static/images/doc.png'
  import { sysNoticePageInfoApi, flightAirspacePageInfoApi } from '@/api/index'
const lbList = reactive([]);
// lbPng 轮播图 5次
@@ -58,22 +87,9 @@
  })
}
const change = (index) => {
  console.log('轮播图切换到第', index + 1, '张');
}
const click = (...args) => {
  // 处理轮播图点击事件
  if (args.length >= 2) {
    const [item, index] = args;
    console.log('点击了轮播图第', index + 1, '张', item);
  }
  // 处理网格组件点击事件
  else if (args.length === 1 && typeof args[0] === 'object') {
    const { index, item } = args[0];
    console.log('点击了网格第', index + 1, '项', item);
  }
}
// const change = (index) => {
//   console.log('轮播图切换到第', index + 1, '张');
// }
const baseList = ref([
    {
@@ -94,11 +110,6 @@
    },
]);
const tabs = ref([
    { name: '空域信息' },
    { name: '法律法规' },
    { name: '政策制度' },
]);
const announcementList = ref([]);
function gridClick(index) {
@@ -127,7 +138,14 @@
      current: 1,
      size: 10,
    })
    announcementList.value = res.data.data.records
    announcementList.value = res.data.data.records.slice(0, 3).map(item => ({
      ...item,
      title: item.airspaceName,
      description: item.remark,
      time: item.createTime,
      num: item.num,
      like: item.like,
    }))
  } catch (error) {
    uni.showToast({
      title: error.message,
@@ -137,28 +155,67 @@
  }
}
// 获取通知公告列表
async function getSysNoticeList() {
  try {
    const res = await sysNoticePageInfoApi({
      current: 1,
      size: 10,
      noticeType: 2,
    })
    if (res.code === 200) {
      announcementList.value = res.data.records
  // 获取通知公告列表
  const policyList = ref([]); // 政策制度
  const regulationsList = ref([]); // 法律法规
  async function getSysNoticeList(val) {
    try {
      const res = await sysNoticePageInfoApi({
        current: 1,
        size: 10,
        noticeType: val,
      })
      // 只需要取前3条 公告
      if (val === 1) {
        regulationsList.value = res.data.data.records.slice(0, 3).map(item => ({
          ...item,
          title: item.noticeTitle,
          description: item.remark,
          time: item.pubDate,
          num: item.num,
          like: item.like,
        }))
      } else if (val === 2) {
        policyList.value = res.data.data.records.slice(0, 3).map(item => ({
          ...item,
          title: item.noticeTitle,
          description: item.remark,
          time: item.pubDate,
          num: item.num,
          like: item.like,
        }))
      }
    } catch (error) {
      uni.showToast({
        title: error.message,
        icon: 'none',
        duration: 2000,
      })
    }
  } catch (error) {
    uni.showToast({
      title: error.message,
      icon: 'none',
      duration: 2000,
  }
  // 跳转到空域信息详情页
  function airspaceClick(item) {
    uni.navigateTo({
      url: '/subPackages/regulationsDetail/details?id=' + item.id,
    })
  }
}
  // 跳转到法律政策详情页
  function toRegulationsDetail(item) {
    uni.navigateTo({
      url: '/subPackages/regulationsDetail/details?id=' + item.noticeId,
    })
  }
  // 跳转到政策制度详情页
  function policiesClick(item) {
    uni.navigateTo({
      url: '/subPackages/regulationsDetail/details?id=' + item.noticeId,
    })
  }
onMounted(() => {
  getFlightAirspaceList()
  getSysNoticeList(2)
  getSysNoticeList(1)
})
</script>
@@ -166,6 +223,8 @@
.page {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  .u-grid {
    margin-top: 20px;
    .grid-text {
@@ -173,30 +232,75 @@
      font-weight: 500;
      font-size: 14px;
      color: #222324;
      margin-top: 10px;
    }
  }
  .tabs {
    background-color: white;
    border-radius: 10px;
    padding: 0 10px;
    margin-top: 20px;
  .tabs-container {
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 14px;
    color: #4E5969;
    background: #FFFFFF;
    border-radius: 12px 12px 6px 6px;
    padding: 0 20rpx;
    margin-top: 10px;
  }
  .content {
    margin-top: 10px;
    width: 100%;
    height: 0;
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    background: #FFFFFF;
    padding: 0 40rpx 40rpx 40rpx;
    border-radius: 12px 12px 6px 6px;
    overflow-y: auto;
    box-sizing: border-box;
    .title {
      padding: 40rpx 40rpx 0rpx 20rpx;
      font-family: Source Han Sans CN, Source Han Sans CN;
      font-weight: 500;
      font-size: 14px;
      color: #222324;
    }
  }
  .card {
    background-color: white;
    padding: 0 10px;
    padding-top: 10px;
    font-family: Source Han Sans CN, Source Han Sans CN;
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid #EBEEF3;
    padding: 40rpx 0 30rpx 0;
    width: 100%;
    box-sizing: border-box;
    .left {
      width: 20px;
      height: 20px;
      .u-image {
          width: 60rpx;
          height: 72rpx;
        }
    }
    .right {
      flex: 1;
      margin-left: 10px;
      .c-bottom {
      width: calc(100% - 80rpx);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      .r-top {
        font-weight: 500;
        font-size: 14px;
        color: #1D2129;
      }
      .r-middle {
        font-weight: 400;
        font-size: 13px;
        color: #86909C;
      }
      .r-bottom {
        display: flex;
        justify-content: space-between;
        font-weight: 400;
        font-size: 13px;
        color: #86909C;
        margin-top: 10rpx;
      }
    }
  }
uniapps/work-wx/src/static/images/add.svg
New file
@@ -0,0 +1,24 @@
<svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 1321316305" filter="url(#filter0_d_88_1711)">
<g id="&#229;&#155;&#190;&#230;&#160;&#135;">
<circle id="bg" cx="34" cy="30" r="30" fill="#1D6FE9"/>
</g>
<g id="Group 1321316267">
<g id="Frame">
<path id="Vector" d="M35.002 15.8025C35.1865 15.8025 35.3635 15.8758 35.494 16.0063C35.6245 16.1368 35.6978 16.3138 35.6978 16.4983V27.6314H46.8309C47.0154 27.6314 47.1924 27.7047 47.3229 27.8352C47.4534 27.9656 47.5267 28.1426 47.5267 28.3272V29.7188C47.5267 29.9033 47.4534 30.0803 47.3229 30.2108C47.1924 30.3413 47.0154 30.4146 46.8309 30.4146H35.6978V41.5477C35.6978 41.7322 35.6245 41.9092 35.494 42.0397C35.3635 42.1702 35.1865 42.2435 35.002 42.2435H33.6104C33.4258 42.2435 33.2489 42.1702 33.1184 42.0397C32.9879 41.9092 32.9146 41.7322 32.9146 41.5477V30.4146H21.7815C21.597 30.4146 21.42 30.3413 21.2895 30.2108C21.159 30.0803 21.0857 29.9033 21.0857 29.7188V28.3272C21.0857 28.1426 21.159 27.9656 21.2895 27.8352C21.42 27.7047 21.597 27.6314 21.7815 27.6314H32.9146V16.4983C32.9146 16.3138 32.9879 16.1368 33.1184 16.0063C33.2489 15.8758 33.4258 15.8025 33.6104 15.8025H35.002Z" fill="white" stroke="white" stroke-width="0.4"/>
</g>
</g>
</g>
<defs>
<filter id="filter0_d_88_1711" x="0" y="0" width="68" height="68" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_88_1711"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_88_1711" result="shape"/>
</filter>
</defs>
</svg>
uniapps/work-wx/src/store/modules/app/index.js
@@ -10,6 +10,7 @@
    theme: storage.get(THEME_KEY) || 'light',
    deviceUpdateKey: 0, //设备刷新key
    jobUpdateKey: 0, //任务刷新key
  }),
  getters: {
    getSystemInfo (state) {
uniapps/work-wx/src/subPackages/deviceRegistration/add.vue
New file
@@ -0,0 +1,622 @@
<template>
<view class="deviceRegistration">
    <u-form labelPosition="top" :model="formParams" :rules="rules" ref="formRef">
        <u-form-item
                label="飞行器名称"
                labelWidth="200rpx"
                prop="name"
                :borderBottom="true"
                ref="item1"
                required
        >
            <u-input v-model="formParams.name" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="SN(出厂序列号)"
                labelWidth="230rpx"
                prop="sn"
                :borderBottom="true"
                ref="item2"
                required
        >
            <u-input v-model="formParams.sn" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="飞行器厂商"
                labelWidth="230rpx"
                prop="manufacturer"
                :borderBottom="true"
                ref="item2"
                required
                @click="showManufacturer = true"
        >
            <u-input
                v-model="formParams.manufacturerText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行器厂商"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="飞行器型号"
                labelWidth="200rpx"
                prop="model"
                :borderBottom="true"
                ref="item1"
                required
        >
            <u-input v-model="formParams.model" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="是否改装"
                labelWidth="200rpx"
                prop="isModifiedText"
                :borderBottom="true"
                ref="item1"
                @click="showIsModified = true"
                required
        >
            <u-input
                v-model="formParams.isModifiedText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择是否改装"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="飞行器类型"
                labelWidth="200rpx"
                prop="type"
                :borderBottom="true"
                ref="item1"
                required
                @click="showType = true"
        >
            <u-input
                v-model="formParams.typeText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行器类型"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
         <u-form-item
                label="持有人"
                labelWidth="200rpx"
                prop="aircraftName"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.aircraftName"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
         <u-form-item
                label="购买日期"
                labelWidth="200rpx"
                prop="purchaseDate"
                :borderBottom="true"
                ref="item1"
                @click="showPurchaseDate = true"
        >
            <u-input v-model="formParams.purchaseDate" border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
        </u-form-item>
        <u-form-item
                label="有无保险"
                labelWidth="230rpx"
                prop="hasInsurance"
                :borderBottom="true"
                ref="item2"
                @click="showHasInsurance = true"
        >
            <u-input
                v-model="formParams.hasInsuranceText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择有无保险"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="所属区域"
                labelWidth="230rpx"
                prop="serialNumber"
                :borderBottom="true"
                ref="item2"
        >
            <view class="select-wrapper">
                <u-input
                        v-model="formParams.serialNumber"
                        border="none"
                        placeholder="请选择"
                        readonly
                ></u-input>
                <view class="select-icon">▼</view>
            </view>
        </u-form-item>
        <u-form-item
                label="民用航空器登记证书"
                labelWidth="300rpx"
                prop="caacRegistrationCodeText"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.caacRegistrationCodeText"
                    border="none"
                    placeholder="请输入"
            ></u-input>
            <template #right>
                <u-upload
                    @afterRead="afterReadFile"
                    @delete="deletePic"
                    name="6"
                    multiple
                    :maxCount="1"
                >
                    <view class="upload">上传文件</view>
                </u-upload>
            </template>
        </u-form-item>
        <u-form-item
                label="飞行器照片"
                labelWidth="200rpx"
                prop="deviceImageText"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.deviceImageText"
                    border="none"
                    placeholder="请输入"
            ></u-input>
            <template #right>
                <u-upload
                    @afterRead="afterReadImage"
                    @delete="deletePic"
                    name="6"
                    multiple
                    :maxCount="1"
                    accept="image"
                >
                    <view class="upload">上传图片</view>
                </u-upload>
            </template>
        </u-form-item>
        <u-form-item
                label="备注"
                labelWidth="200rpx"
                prop="remarks"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.remarks"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
        <u-button
        class="submit-button-wrapper"
                type="primary"
                size="small"
                @click="submitForm"
            >
                提交
            </u-button>
            <!-- 飞行器型号 -->
    <u-picker
        :show="showType"
        :columns="[typeList]"
        keyName="label"
        @confirm="onPickerConfirmType"
        @cancel="showType = false"
    />
    <!-- 飞行器厂商 -->
    <u-picker
        :show="showManufacturer"
        :columns="[manufacturerList]"
        keyName="label"
        @confirm="onPickerConfirmManufacturer"
        @cancel="showManufacturer = false"
    />
    <u-picker
        :show="showIsModified"
        :columns="[actionsModified]"
        keyName="label"
        @confirm="onPickerConfirm"
        @cancel="showIsModified = false"
    />
    <u-picker
        :show="showHasInsurance"
        :columns="[actionsHasInsurance]"
        keyName="label"
        @confirm="onPickerConfirmHasInsurance"
        @cancel="showHasInsurance = false"
    />
    <u-datetime-picker
      :show="showPurchaseDate"
      v-model="birthdayValue"
      mode="date"
      @confirm="onPurchaseDate"
      @cancel="showPurchaseDate = false"
    />
    </u-form>
</view>
</template>
<script setup>
import { aircraftInfoSaveApi,uploadFileApi } from '@/api/index'
import { ref, computed } from 'vue'
import dayjs from 'dayjs'
const formRef = ref(null);
const formParams = ref({
    name: '',
    manufacturer: '', // 制造商
    manufacturerText: '', // 制造商文本
    model: '', // 型号
    sn: '', // sn编号
    type: '', // 航空器类型
    typeText: '', // 航空器类型文本
    holder: '', // 持有人
    pilotId: '', // 飞行员id
    status: '', // 状态 飞行器状态:0-未激活,1-正常,2-维修中,3-报废:
    purchaseDate: '', // 购买日期
    isModified: '', // 是否改装 0否 1是:
    isModifiedText: '', // 是否改装文本
    hasInsurance: '', // 是否有保险
    hasInsuranceText: '', // 是否有保险文本
    region: '', // 所属区域
    caacRegistrationCode: '', // 民用航空器登记证书
    caacRegistrationCodeText: '', // 民用航空器登记证书文本
    deviceImage: '', // 飞行器照片
    deviceImageText: '', // 飞行器照片文本
    remarks: '', // 备注
})
const rules = ref({
    name: [
        { required: true, message: '请输入飞行器名称', trigger: 'blur' }
    ],
    manufacturer: [
        { required: true, message: '请选择飞行器厂商', trigger: 'change' }
    ],
    model: [
        { required: true, message: '请输入飞行器型号', trigger: 'blur' }
    ],
    sn: [
        { required: true, message: '请选择SN(出厂序列号)', trigger: 'change' }
    ],
    isModifiedText: [
        { required: true, message: '请选择是否改装', trigger: 'change' }
    ],
})
// 是否改装
const actionsModified = ref([
    {
        label: '否',
        value: 0
    },
    {
        label: '是',
        value: 1
    }
])
const showIsModified = ref(false);
const onPickerConfirm = (e) => {
  const selected = e.value[0]
  formParams.value.isModified = selected.value
  formParams.value.isModifiedText = selected.label
  showIsModified.value = false
}
const showPurchaseDate = ref(false);
const birthdayValue = ref(Date.now()); // 设置默认值为当前时间戳
// 购买日期
const onPurchaseDate = (e) => {
    formParams.value.purchaseDate = dayjs(e.value).format('YYYY-MM-DD')
    showPurchaseDate.value = false
}
// 是否有保险
const actionsHasInsurance = ref([
    {
        label: '无',
        value: '0'
    },
    {
        label: '有',
        value: '1'
    }
])
const showHasInsurance = ref(false);
const onPickerConfirmHasInsurance = (e) => {
  const selected = e.value[0]
  formParams.value.hasInsurance = selected.value
  formParams.value.hasInsuranceText = selected.label
  showHasInsurance.value = false
}
// 飞行器厂商
const showManufacturer = ref(false);
const manufacturerList = ref([
    {
        label: 'A厂商',
        value: 'A厂商'
    },
    {
        label: 'B厂商',
        value: 'B厂商'
    },
])
// 选择飞行器厂商
const onPickerConfirmManufacturer = (e) => {
  const selected = e.value[0]
  formParams.value.manufacturer = selected.value
  formParams.value.manufacturerText = selected.label
  showManufacturer.value = false
}
// 飞行器型号
const showType = ref(false);
const typeList = ref([
    {
        label: 'A型号',
        value: 'A型号'
    },
    {
        label: 'B型号',
        value: 'B型号'
    },
])
// 选择飞行器型号
const onPickerConfirmType = (e) => {
  const selected = e.value[0]
  formParams.value.type = selected.value
  formParams.value.typeText = selected.label
  showType.value = false
}
// 提交文件
function afterReadFile(event) {
    // 获取上传的文件对象
    const file = event.file;
    console.log('上传的文件:', file);
    // 显示加载提示
    uni.showLoading({
        title: '上传中...'
    });
    // 准备上传参数
    const uploadParams = {
        name: 'file', // 服务器端接收文件的字段名
        file: file, // 传入文件对象
        filePath: file.path || file.url, // 文件路径
    };
    // 调用上传文件接口
    uploadFileApi(uploadParams).then(res => {
        uni.hideLoading();
        console.log('上传成功结果:', res);
        // 根据接口返回的数据结构处理结果
        if (res.data && res.data.code === 200) {
            // 假设接口返回的数据结构是 { url, fileName } 等
            const uploadResult = res.data.data;
            // 更新表单数据,将上传结果展示在input内
            formParams.value.caacRegistrationCodeText = uploadResult.fileName || uploadResult.url || '文件上传成功';
            // 可以将完整的上传信息保存到表单中,以便提交时使用
            formParams.value.caacRegistrationCode= uploadResult.fileUrl || '文件上传成功';
            uni.showToast({
                title: '上传成功',
                icon: 'success'
            });
        } else {
            uni.showToast({
                title: res.msg || '上传失败',
                icon: 'none'
            });
        }
    }).catch(err => {
        uni.hideLoading();
        console.error('上传失败:', err);
        uni.showToast({
            title: '上传失败',
            icon: 'none'
        });
    });
}
// 提交图片
function afterReadImage(event) {
    // 获取上传的文件对象
    const file = event.file;
    console.log('上传的文件:', file);
    // 显示加载提示
    uni.showLoading({
        title: '上传中...'
    });
    // 准备上传参数
    const uploadParams = {
        name: 'file', // 服务器端接收文件的字段名
        file: file, // 传入文件对象
        filePath: file.path || file.url, // 文件路径
    };
    // 调用上传文件接口
    uploadFileApi(uploadParams).then(res => {
        uni.hideLoading();
        console.log('上传成功结果:', res);
        // 根据接口返回的数据结构处理结果
        if (res.data && res.data.code === 200) {
            // 假设接口返回的数据结构是 { url, fileName } 等
            const uploadResult = res.data.data;
            // 更新表单数据,将上传结果展示在input内
            formParams.value.deviceImageText = uploadResult.fileName || uploadResult.url || '文件上传成功';
            // 可以将完整的上传信息保存到表单中,以便提交时使用
            formParams.value.deviceImage= uploadResult.fileUrl || '文件上传成功';
            uni.showToast({
                title: '上传成功',
                icon: 'success'
            });
        } else {
            uni.showToast({
                title: res.msg || '上传失败',
                icon: 'none'
            });
        }
    }).catch(err => {
        uni.hideLoading();
        console.error('上传失败:', err);
        uni.showToast({
            title: '上传失败',
            icon: 'none'
        });
    });
}
// 提交表单
const submitForm = async () => {
    try {
        if (await formRef.value.validate()) {
            const res = await aircraftInfoSaveApi(formParams.value)
            if (res.data.code === 200) {
                uni.showToast({
                    title: '航空器注册成功',
                    icon: 'success'
                })
                // 跳转设备列表页
                uni.switchTab({
                    url: '/pages/equipmentManagement/index',
                })
            } else {
                uni.showToast({
                    title: res.msg || '航空器注册失败',
                    icon: 'error'
                })
            }
        }
    } catch (error) {
        console.log('表单验证失败:', error)
        // 验证失败时,u-form组件会自动显示错误提示
        // 这里可以添加额外的失败处理逻辑
    }
}
</script>
<style scoped lang="scss">
.deviceRegistration {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    // padding: 24rpx;
    .select-wrapper {
        position: relative;
        width: 100%;
        .select-icon {
            position: absolute;
            right: 20rpx;
            top: 50%;
            transform: translateY(-50%);
            color: #999;
            font-size: 24rpx;
            pointer-events: none;
        }
    }
    .upload {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 15px;
        color: #1D6FE9;
    }
    :deep(.u-form) {
        flex: 1;
        display: flex;
        flex-direction: column;
        overflow-y: auto;
        box-sizing: border-box;
        padding: 24rpx;
    }
    .submit-button-wrapper {
        padding: 28rpx;
    }
    :deep(.u-form-item) {
        margin-bottom: 40rpx;
        padding: 16rpx 24rpx 18rpx 24rpx;
        border-radius: 6px 6px 6px 6px;
        background: white;
        :deep(.u-line) {
            border-bottom: 1px solid #EFEFEF;
        }
        :deep(.u-input) {
            width: 100%;
            height: 80rpx;
            border-radius: 8rpx;
            padding: 0 20rpx;
            background-color: #f5f5f5;
            &::placeholder {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
                font-size: 30rpx;
                color: #D2D2D2;
            }
        }
        .clickable-input {
            width: 100%;
            cursor: pointer;
        }
        :deep(.u-form-item__label) {
            margin-bottom: 20rpx;
            // font-weight: bold;
            // color: #333;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 400;
            font-size: 30rpx;
            color: #222324;
        }
        :deep(.u-form-item__required) {
            color: #FF2600;
            margin-right: 8rpx;
        }
    }
}
</style>
uniapps/work-wx/src/subPackages/flightApplication/add.vue
New file
@@ -0,0 +1,831 @@
<template>
<view class="flightApplication-add">
    <view class="title">
        基本信息
    </view>
    <u-form labelPosition="top" :model="formParams" :rules="rules" ref="formRef">
        <u-form-item
                label="飞行计划"
                labelWidth="200rpx"
                prop="flightPlanName"
                :borderBottom="true"
                ref="item1"
                required
        >
            <u-input v-model="formParams.flightPlanName" border="none" placeholder="请输入飞行计划名称"></u-input>
        </u-form-item>
        <u-form-item
                label="任务类型"
                labelWidth="230rpx"
                prop="flightTaskType"
                :borderBottom="true"
                ref="item2"
                required
                @click="isShowFlightTaskType = true"
        >
            <u-input
                v-model="formParams.flightTaskTypeText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择任务类型"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="飞行规则"
                labelWidth="230rpx"
                prop="flightRule"
                :borderBottom="true"
                ref="item2"
                required
                @click="isShowFlightRule = true"
        >
            <u-input
                v-model="formParams.flightRuleText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行规则"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="飞行方式"
                labelWidth="200rpx"
                prop="flightMode"
                :borderBottom="true"
                ref="item1"
                @click="isShowFlightMode = true"
                required
        >
            <u-input
                v-model="formParams.flightModeText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行方式"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="飞行活动开始日期"
                labelWidth="260rpx"
                prop="flightStartTime"
                :borderBottom="true"
                ref="item1"
                @click="isShowStartTime = true"
                required
        >
            <u-input v-model="formParams.flightStartTime" border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
        </u-form-item>
         <u-form-item
                label="飞行活动结束日期"
                labelWidth="260rpx"
                prop="flightEndTime"
                :borderBottom="true"
                ref="item1"
                @click="isShowEndTime = true"
                required
        >
            <u-input v-model="formParams.flightEndTime" border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
        </u-form-item>
        <u-form-item
                label="计划类型"
                labelWidth="200rpx"
                prop="planType"
                :borderBottom="true"
                ref="item1"
                required
                @click="isShowPlanType = true"
        >
            <u-input
                v-model="formParams.planTypeText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择计划类型"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
         <u-form-item
                label="联系人"
                labelWidth="200rpx"
                prop="contactPerson"
                :borderBottom="true"
                required
                ref="item1"
        >
            <u-input v-model="formParams.contactPerson" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="联系电话"
                labelWidth="200rpx"
                prop="contactPhone"
                :borderBottom="true"
                required
                ref="item1"
        >
            <u-input v-model="formParams.contactPhone" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="承办人"
                labelWidth="200rpx"
                prop="undertaker"
                :borderBottom="true"
                required
                ref="item1"
        >
            <u-input v-model="formParams.undertaker" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="承办人联系电话"
                labelWidth="300rpx"
                prop="undertakerPhone"
                :borderBottom="true"
                required
                ref="item1"
        >
            <u-input v-model="formParams.undertakerPhone" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="临时空域批件"
                labelWidth="200rpx"
                prop="aircraftName"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.tempAirspaceApprovalText" border="none" readonly placeholder="(建议文件大小100M内,仅支持pdf、jpg、jpeg、png等格式)"></u-input>
            <template #right>
                <u-upload
                    @afterRead="afterRead"
                    @delete="deletePic"
                    name="6"
                    multiple
                    :maxCount="1"
                    width="250"
                    height="150"
                >
                    <view class="upload">上传文件</view>
                </u-upload>
            </template>
        </u-form-item>
        <u-form-item
                label="气象条件"
                labelWidth="200rpx"
                prop="weatherCondition"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.weatherCondition"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
         <view class="title form-title">飞行器/飞手</view>
        <u-form-item
                label="飞行器"
                labelWidth="230rpx"
                prop="aircraftId"
                :borderBottom="true"
                required
                ref="item2"
                @click="isShowAircraftInfo = true"
        >
            <u-input
                v-model="formParams.aircraftText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行器"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="飞手"
                labelWidth="230rpx"
                prop="pilotId"
                :borderBottom="true"
                ref="item2"
                required
                @click="isShowPilotInfo = true"
        >
            <u-input
                v-model="formParams.pilotText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞手"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <view class="title form-title">空域/航线/场点</view>
        <u-form-item
                label="空域"
                labelWidth="230rpx"
                prop="airspaceId"
                :borderBottom="true"
                ref="item2"
                required
                @click="isShowAirspaceInfo = true"
        >
            <u-input
                v-model="formParams.airspaceText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择空域"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-form-item
                label="航线"
                labelWidth="230rpx"
                prop="routeId"
                :borderBottom="true"
                ref="item2"
                required
                @click="isShowFlightRouteInfo = true"
        >
            <u-input
                v-model="formParams.routeText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择航线"
                border="none"
            ></u-input>
            <template #right>
                <up-icon
                    name="arrow-down"
                ></up-icon>
            </template>
        </u-form-item>
        <u-button class="submit-button-wrapper"
                type="primary"
                size="small"
                @click="submitForm"
            >
                提交
            </u-button>
            <!-- 飞任务类型 -->
    <u-picker
        :show="isShowFlightTaskType"
        :columns="[actionsFlightTaskType]"
        keyName="label"
        @confirm="onPickerFlightTaskType"
        @cancel="isShowFlightTaskType = false"
    />
    <!-- 飞行规则 -->
    <u-picker
        :show="isShowFlightRule"
        :columns="[actionsFlightRule]"
        keyName="label"
        @confirm="onPickerFlightRule"
        @cancel="isShowFlightRule = false"
    />
    <!-- 飞行方式 -->
    <u-picker
        :show="isShowFlightMode"
        :columns="[actionsFlightMode]"
        keyName="label"
        @confirm="onPickerFlightMode"
        @cancel="isShowFlightMode = false"
    />
    <!-- 计划类型 -->
    <u-picker
        :show="isShowPlanType"
        :columns="[actionsPlanType]"
        keyName="label"
        @confirm="onPickerPlanType"
        @cancel="isShowPlanType = false"
    />
    <up-datetime-picker
            :show="isShowStartTime"
            v-model="startTime"
            mode="datetime"
            @confirm="onConfirmStartTime"
             @cancel="isShowStartTime = false"
        ></up-datetime-picker>
        <up-datetime-picker
            :show="isShowEndTime"
            v-model="endTime"
            mode="datetime"
            @confirm="onConfirmEndTime"
            @cancel="isShowEndTime = false"
        ></up-datetime-picker>
    <!-- <u-calendar :show="isShowTimeRange" mode="date" @confirm="onConfirmTimeRange" @close="isShowTimeRange = false" :close-on-click-overlay="true"></u-calendar> -->
    <!-- <u-calendar :show="isShowTimeRange" mode="range" @confirm="onConfirmTimeRange" @close="isShowTimeRange = false" :close-on-click-overlay="true" :allow-same-day="true" :show-time="true" time-format="HH:mm:ss"></u-calendar> -->
    <!-- 飞行器 -->
     <u-picker
        :show="isShowAircraftInfo"
        :columns="[aircraftInfoList]"
        keyName="label"
        @confirm="onPickerAircraftInfo"
        @cancel="isShowAircraftInfo = false"
    />
    <!-- 飞手 -->
    <u-picker
        :show="isShowPilotInfo"
        :columns="[pilotInfoList]"
        keyName="label"
        @confirm="onPickerPilotInfo"
        @cancel="isShowPilotInfo = false"
    />
    <!-- 空域 -->
    <u-picker
        :show="isShowAirspaceInfo"
        :columns="[airspaceInfoList]"
        keyName="label"
        @confirm="onPickerAirspaceInfo"
        @cancel="isShowAirspaceInfo = false"
    />
    <!-- 航线 -->
    <u-picker
        :show="isShowFlightRouteInfo"
        :columns="[flightRouteInfoList]"
        keyName="label"
        @confirm="onPickerFlightRouteInfo"
        @cancel="isShowFlightRouteInfo = false"
    />
    </u-form>
</view>
</template>
<script setup>
import { flightPlanSaveApi, flightRoutePageInfoApi, aircraftInfoPageInfoOfMyApi, pilotInfoPageInfoOfMyApi, flightAirspacePageInfoApi, uploadFileApi } from '@/api/index'
import { ref, computed, onMounted } from 'vue'
import dayjs from 'dayjs'
const formRef = ref(null);
const formParams = ref({
    flightPlanNo: '', //飞行计划编号
    flightPlanName: '', // 飞行计划名称
    flightTaskType: '', // 飞行任务类型
    flightTaskTypeText: '', // 飞行任务类型文本
    flightRule: '', // 飞行规则
    flightRuleText: '', // 飞行规则文本
    flightMode: '', // 飞行方式
    flightModeText: '', // 飞行方式文本
    tempAirspaceApproval: '', // 临时空域批件
    tempAirspaceApprovalText: '', // 临时空域批件文本
    planType: '', // 计划类型
    planTypeText: '', // 计划类型文本
    contactPerson: '', // 联系人
    contactPhon: '', // 联系人电话
    undertaker: '', // 承办人
    undertakerPhone: '', // 承办人电话
    timeRange: '', // 时间范围
    flightStartTime: '', // 飞行活动开始时间
    flightEndTime: '', // 飞行活动结束时间
    weatherCondition: '', // 气象条件
    spacecraftId: '', // 航天器id
    spacecraftText: '', // 航天器文本
    pilotId: '', // 飞手id
    pilotText: '', // 飞手文本
    airspaceId: '', // 空域id
    airspaceText: '', // 空域文本
    routeId: '', // 航线id
    routeText: '', // 航线文本
})
const rules = ref({
    flightPlanName: [
        { required: true, message: '请输入飞行计划名称', trigger: 'blur' }
    ],
    flightTaskType: [
        { required: true, message: '请选择飞行任务类型', trigger: 'change' }
    ],
    flightRule: [
        { required: true, message: '请选择飞行规则', trigger: 'change' }
    ],
    flightMode: [
        { required: true, message: '请选择飞行方式', trigger: 'change' }
    ],
    contactPerson: [
        { required: true, message: '请输入联系人', trigger: 'blur' }
    ],
    contactPhon: [
        { required: true, message: '请输入联系人电话', trigger: 'blur' }
    ],
    undertaker: [
        { required: true, message: '请输入承办人', trigger: 'blur' }
    ],
    undertakerPhone: [
        { required: true, message: '请输入承办人联系电话', trigger: 'blur' }
    ],
    startTime: [
        { required: true, message: '请选择飞行活动开始时间', trigger: 'change' }
    ],
    endTime: [
        { required: true, message: '请选择飞行活动结束时间', trigger: 'change' }
    ],
    spacecraftId: [
        { required: true, message: '请选择飞行器', trigger: 'change' }
    ],
    pilotId: [
        { required: true, message: '请选择飞手', trigger: 'change' }
    ],
    airspaceId: [
        { required: true, message: '请选择空域', trigger: 'change' }
    ],
    routeId: [
        { required: true, message: '请选择航线', trigger: 'change' }
    ],
})
// 任务类型
const isShowFlightTaskType = ref(false)
const actionsFlightTaskType = [
    {
        label: '警务',
        value: '警务',
    },
    {
        label: '文旅',
        value: '文旅',
    },
    {
        label: '航拍',
        value: '航拍',
    },
    {
        label: '物流',
        value: '物流',
    },
    {
        label: '巡检',
        value: '巡检',
    },
    {
        label: '植保',
        value: '植保',
    },
]
const onPickerFlightTaskType = (e) => {
  const selected = e.value[0]
  formParams.value.flightTaskType = selected.value
  formParams.value.flightTaskTypeText = selected.label
  isShowFlightTaskType.value = false
}
// 飞行规则
const isShowFlightRule = ref(false)
const actionsFlightRule = [
    {
        label: '目视餡相飞行规则(VFR)',
        value: '目视餡相飞行规则(VFR)',
    },
    {
        label: '仪表飞行规则(IFR)',
        value: '仪表飞行规则(IFR)',
    },
]
const onPickerFlightRule = (e) => {
  const selected = e.value[0]
  formParams.value.flightRule = selected.value
  formParams.value.flightRuleText = selected.label
  isShowFlightRule.value = false
}
// 飞行方式
const isShowFlightMode = ref(false)
const actionsFlightMode = [
    {
        label: '视距内飞行 (VLOS)',
        value: '视距内飞行 (VLOS)',
    },
    {
        label: '超视距飞行(BVLOS)',
        value: '超视距飞行(BVLOS)',
    },
]
const onPickerFlightMode = (e) => {
  const selected = e.value[0]
  formParams.value.flightMode = selected.value
  formParams.value.flightModeText = selected.label
  isShowFlightMode.value = false
}
// 计划类型
const isShowPlanType = ref(false)
const actionsPlanType = [
    {
        label: '一般',
        value: '0',
    },
    {
        label: '紧急',
        value: '1',
    }
]
const onPickerPlanType = (e) => {
  const selected = e.value[0]
  formParams.value.planType = selected.value
  formParams.value.planTypeText = selected.label
  isShowPlanType.value = false
}
// 是否显示开始时间选择器
const isShowStartTime = ref(false)
// 是否显示结束时间选择器
const isShowEndTime = ref(false)
const startTime = ref(Date.now()); // 设置默认值为当前时间戳
const endTime = ref(Date.now()); // 设置默认值为当前时间戳
const onConfirmStartTime = (e) => {
    formParams.value.flightStartTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
    isShowStartTime.value = false
}
const onConfirmEndTime = (e) => {
    formParams.value.flightEndTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
    isShowEndTime.value = false
}
// 获取飞行器信息
const isShowAircraftInfo = ref(false)
const aircraftInfoList = ref([])
function getAircraftInfo() {
    aircraftInfoPageInfoOfMyApi({
        current: 1,
        size: 1000,
    }).then(res => {
        aircraftInfoList.value = res.data.data.records.map(item => ({
            label: item.name || '暂无',
            value: item.id,
        }))
    })
}
const onPickerAircraftInfo = (e) => {
  const selected = e.value[0]
  formParams.value.aircraftId = selected.value
  formParams.value.aircraftText = selected.label
  isShowAircraftInfo.value = false
}
// 获取飞手信息
const isShowPilotInfo = ref(false)
const pilotInfoList = ref([])
function getPilotInfo() {
    pilotInfoPageInfoOfMyApi({
        current: 1,
        size: 1000,
    }).then(res => {
        pilotInfoList.value = res.data.data.records.map(item => ({
            label: item.name || '暂无',
            value: item.id,
        }))
    })
}
const onPickerPilotInfo = (e) => {
  const selected = e.value[0]
  formParams.value.pilotId = selected.value
  formParams.value.pilotText = selected.label
  isShowPilotInfo.value = false
}
// 获取空域信息
const isShowAirspaceInfo = ref(false)
const airspaceInfoList = ref([])
function getAirspaceInfo() {
    flightAirspacePageInfoApi({
        current: 1,
        size: 1000,
    }).then(res => {
        airspaceInfoList.value = res.data.data.records.map(item => ({
            label: item.airspaceName || '暂无',
            value: item.id,
        }))
    })
}
const onPickerAirspaceInfo = (e) => {
  const selected = e.value[0]
  formParams.value.airspaceId = selected.value
  formParams.value.airspaceText = selected.label
  isShowAirspaceInfo.value = false
}
// 获取航线信息
const isShowFlightRouteInfo = ref(false)
const flightRouteInfoList = ref([])
function getFlightRouteInfo() {
    flightRoutePageInfoApi({
        current: 1,
        size: 1000,
    }).then(res => {
        console.log(res.data.data, '0000')
        flightRouteInfoList.value = res.data.data.records.map(item => ({
            label: item.name,
            value: item.id,
        }))
    })
}
const onPickerFlightRouteInfo = (e) => {
  const selected = e.value[0]
  formParams.value.routeId = selected.value
  formParams.value.routeText = selected.label
  isShowFlightRouteInfo.value = false
}
// 文件上传处理
const afterRead = (event) => {
    // 获取上传的文件对象
    const file = event.file;
    console.log('上传的文件:', file);
    // 显示加载提示
    uni.showLoading({
        title: '上传中...'
    });
    // 准备上传参数
    const uploadParams = {
        name: 'file', // 服务器端接收文件的字段名
        file: file, // 传入文件对象
        filePath: file.path || file.url, // 文件路径
    };
    // 调用上传文件接口
    uploadFileApi(uploadParams).then(res => {
        uni.hideLoading();
        console.log('上传成功结果:', res);
        // 根据接口返回的数据结构处理结果
        if (res.data && res.data.code === 200) {
            // 假设接口返回的数据结构是 { url, fileName } 等
            const uploadResult = res.data.data;
            // 更新表单数据,将上传结果展示在input内
            formParams.value.tempAirspaceApprovalText = uploadResult.fileName || uploadResult.url || '文件上传成功';
            // 可以将完整的上传信息保存到表单中,以便提交时使用
            formParams.value.tempAirspaceApproval = uploadResult.fileUrl || '文件上传成功';
            uni.showToast({
                title: '上传成功',
                icon: 'success'
            });
        } else {
            uni.showToast({
                title: res.msg || '上传失败',
                icon: 'none'
            });
        }
    }).catch(err => {
        uni.hideLoading();
        console.error('上传失败:', err);
        uni.showToast({
            title: '上传失败',
            icon: 'none'
        });
    });
};
// 提交表单
const submitForm = async () => {
  await formRef.value.validate()
  flightPlanSaveApi(formParams.value).then(res => {
    if (res.data.code === 200) {
      uni.showToast({
        title: '提交成功',
        icon: 'success',
        duration: 2000
      })
      // 跳转申请列表页
        uni.navigateTo({
            url: '/subPackages/flightApplication/index',
        })
    } else {
      uni.showToast({
        title: res.msg || '提交失败',
        icon: 'none',
        duration: 2000
      })
    }
  })
}
onMounted(() => {
    getAircraftInfo()
    getPilotInfo()
    getAirspaceInfo()
    getFlightRouteInfo()
})
</script>
<style scoped lang="scss">
.flightApplication-add {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    .title {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 17px;
        color: #222324;
        padding-left: 24rpx;
    }
    .form-title {
        margin-bottom: 24rpx;
        padding-left: 0rpx !important;
    }
    .select-wrapper {
        position: relative;
        width: 100%;
        .select-icon {
            position: absolute;
            right: 20rpx;
            top: 50%;
            transform: translateY(-50%);
            color: #999;
            font-size: 24rpx;
            pointer-events: none;
        }
    }
    .upload {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 15px;
        color: #1D6FE9;
    }
     :deep(.u-form) {
        flex: 1;
        display: flex;
        flex-direction: column;
        overflow-y: auto;
        box-sizing: border-box;
        padding: 24rpx;
    }
     .submit-button-wrapper {
        padding: 28rpx;
    }
    :deep(.u-form-item) {
        margin-bottom: 40rpx;
        padding: 16rpx 24rpx 18rpx 24rpx;
        border-radius: 6px 6px 6px 6px;
        background: white;
        :deep(.u-line) {
            border-bottom: 1px solid #EFEFEF;
        }
        :deep(.u-input) {
            width: 100%;
            height: 80rpx;
            border-radius: 8rpx;
            padding: 0 20rpx;
            background-color: #f5f5f5;
            &::placeholder {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
                font-size: 30rpx;
                color: #D2D2D2;
            }
        }
        .clickable-input {
            width: 100%;
            cursor: pointer;
        }
        :deep(.u-form-item__label) {
            margin-bottom: 20rpx;
            // font-weight: bold;
            // color: #333;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 400;
            font-size: 30rpx;
            color: #222324;
        }
        :deep(.u-form-item__required) {
            color: #FF2600;
            margin-right: 8rpx;
        }
    }
}
</style>
uniapps/work-wx/src/subPackages/flightApplication/index.vue
@@ -1,40 +1,44 @@
<template>
<view class="flightApplication">
    <u-grid :col="2" :border="true" class="grid" @click="gridClick">
        <u-grid-item v-for="(baseListItem,baseListIndex) in baseList" :key="baseListIndex">
            <u-image :src="baseListItem.src" :width="26" :height="26"></u-image>
            <text class="grid-text">{{baseListItem.title}}</text>
        </u-grid-item>
    </u-grid>
    <u-search :show-action="true" actionText="搜索" :animation="true"></u-search>
    <view class="title">
        审批记录
    </view>
    <view class="approvalRecord" v-for="(approvalRecord,approvalRecordIndex) in approvalRecords" :key="approvalRecordIndex">
        <view class="name-status">
            <view>{{approvalRecord.name}}</view>
            <view>{{approvalRecord.status}}</view>
    <view class="header-top">
        <view class="ht-content">
            <view class="item" @click="gridClick(0)">
                <image :src="fxsqSvg" class="icon"></image>
                <view class="txt">飞行申请</view>
            </view>
            <view class="item" @click="gridClick(1)">
                <image :src="mysbSvg" class="icon"></image>
                <view class="txt">我的设备</view>
            </view>
        </view>
        <view>飞行设备:{{approvalRecord.device}}</view>
        <view>行政区划:{{approvalRecord.area}}</view>
        <view>飞行时间:{{approvalRecord.time}}</view>
    </view>
    <u-search :bgColor="'white'" :show-action="true" actionText="搜索" :animation="true"></u-search>
    <view class="content">
        <view class="sp-title">
            审批记录
        </view>
        <view class="approvalRecord" v-for="(approvalRecord,approvalRecordIndex) in approvalRecords" :key="approvalRecordIndex">
            <view class="name-status">
                <view class="name-image">
                    <u-image :src="spSvg" class="icon" width="16" height="16"></u-image>
                    <view class="name">{{approvalRecord.flightPlanName}}</view>
                </view>
                <view class="status">{{approvalRecord.planStatus || '无状态'}}</view>
            </view>
            <view class="txt">飞行设备:{{approvalRecord.device || '无设备'}}</view>
            <view class="txt">行政区划:{{approvalRecord.area || '无行政区划'}}</view>
            <view class="txt">飞行时间:{{approvalRecord.time || '无飞行时间'}}</view>
        </view>
    </view>
</view>
</template>
<script setup>
    import lbPng from '@/static/images/lb.png'
    import spSvg from '@/static/images/sp.svg'
    import { flightPlanPageInfoApi } from '@/api/index.js'
    import fxsqSvg from '@/static/images/fly-apply.svg'
    import mysbSvg from '@/static/images/my-sb.svg'
import { ref } from 'vue'
const baseList = ref([
    {
        src: lbPng,
        title: '飞行申请',
    },
    {
        src: lbPng,
        title: '我的设备',
    },
])
function gridClick(index) {
    // 跳转设备管理 /pages/equipmentManagement/index
@@ -42,56 +46,205 @@
        uni.switchTab({
            url: '/pages/equipmentManagement/index',
        })
        return
    } else {
        uni.navigateTo({
            url: '/subPackages/flightApplication/add',
        })
    }
}
// 审批记录是很多条
const approvalRecords = ref([
    {
        name: '飞行需求名称',
        status: '申请中',
        flightPlanName: '飞行需求名称',
        planStatus: '申请中',
        device: '大疆无人机',
        area: '吉州区',
        time: '2026/01/07至2026/01/08',
    },
    {
        name: '飞行需求名称',
        status: '申请中',
        flightPlanName: '飞行需求名称',
        planStatus: '申请中',
        device: '大疆无人机',
        area: '吉州区',
        time: '2026/01/07至2026/01/08',
    },
    {
        name: '飞行需求名称',
        status: '申请中',
        flightPlanName: '飞行需求名称',
        planStatus: '申请中',
        device: '大疆无人机',
        area: '吉州区',
        time: '2026/01/07至2026/01/08',
    },
    {
        name: '飞行需求名称',
        status: '申请中',
        flightPlanName: '飞行需求名称',
        planStatus: '申请中',
        device: '大疆无人机',
        area: '吉州区',
        time: '2026/01/07至2026/01/08',
    },
    {
        flightPlanName: '飞行需求名称',
        planStatus: '申请中',
        device: '大疆无人机',
        area: '吉州区',
        time: '2026/01/07至2026/01/08',
    },
    {
        flightPlanName: '飞行需求名称',
        planStatus: '申请中',
        device: '大疆无人机',
        area: '吉州区',
        time: '2026/01/07至2026/01/08',
    },
    {
        flightPlanName: '飞行需求名称',
        planStatus: '申请中',
        device: '大疆无人机',
        area: '吉州区',
        time: '2026/01/07至2026/01/08',
    },
])
// 申请审批列表
const getApprovalRecords = async () => {
    try {
        const res = await flightPlanPageInfoApi({
            current: 1,
            size: 10,
        })
        approvalRecords.value = res.data.data.records || []
    } catch (error) {
        uni.showToast({
            title: error.message,
            icon: 'none',
            duration: 2000,
        })
    }
}
onMounted(() => {
    getApprovalRecords()
})
</script>
<style scoped>
.title {
    font-size: 32rpx;
    font-weight: bold;
    color: #1D6FE9;
    margin: 32rpx 0 24rpx 0;
}
.approvalRecord {
    font-size: 28rpx;
    color: #333333;
    margin-bottom: 24rpx;
}
.name-status {
    display: flex;
    justify-content: space-between;
<style scoped lang="scss">
.flightApplication {
    .header-top {
        width: 100%;
        height: 350rpx;
        background: url('@/static/images/fxsq-bg.png') no-repeat center center;
        background-size: 100% 100%;
        position: fixed;
        top: 0;
        .ht-content {
            position: absolute;
            bottom: -40rpx;
            width: 100%;
            height: 166rpx;
            padding: 0 40rpx;
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 60rpx;
            box-sizing: border-box;
            .item {
                flex: 1;
                max-width: 350rpx;
                height: 166rpx;
                background: rgba(251,253,255,0.39);
                box-shadow: inset 0px 0px 7px 0px rgba(64,156,230,0.14), 0px 1px 3px 0px rgba(35,49,96,0.11);
                border-radius: 8px 8px 8px 8px;
                border: 1px solid #fff;
                display: flex;
                justify-content: center;
                align-items: center;
                box-sizing: border-box;
                .icon {
                    width: 128rpx;
                    height: 128rpx;
                }
                .txt {
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: bold;
                    font-size: 16px;
                    color: #222324;
                }
            }
        }
    }
     .u-search {
        position: absolute;
        width: 90%;
        left: 50%;
        transform: translateX(-50%);
        top: 340rpx !important;
        height: 68rpx;
        flex: 0;
    }
    .content {
        position: absolute;
        top: 450rpx !important;
        bottom: 0;
        background: white;
        width: 100%;
        display: flex;
        flex-direction: column;
        background: #FFFFFF;
        padding: 0 40rpx 40rpx 40rpx;
        overflow-y: auto;
        box-sizing: border-box;
        border-radius: 12px 12px 6px 6px;
        .sp-title {
            font-weight: bold;
            font-size: 32rpx;
            color: #222324;
            padding: 40rpx 0 0rpx 0;
        }
    }
    .approvalRecord {
        font-family: Source Han Sans CN, Source Han Sans CN;
        // display: flex;
        // justify-content: space-between;
        border-bottom: 1px solid #EBEEF3;
        padding: 40rpx 0 20rpx 0;
        // margin-bottom: 20rpx;
        width: 100%;
        box-sizing: border-box;
    }
    .name-status {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 20rpx;
        .name-image {
            display: flex;
            justify-content: center;
            align-items: center;
            .u-image {
                width: 32rpx;
                height: 32rpx;
            }
            .name {
                font-weight: bold;
                font-size: 32rpx;
                color: #1D2129;
                margin-left: 6rpx;
            }
        }
        .status {
            width: 100rpx;
            text-align: center;
            line-height: 40rpx;
            background: #FF9604;
            border-radius: 2px 2px 2px 2px;
            font-weight: 500;
            font-size: 24rpx;
            color: #FFFFFF;
        }
    }
    .txt {
        font-weight: 400;
        font-size: 12px;
        color: #86909C;
        margin-bottom: 14rpx;
    }
}
</style>