任务需求板块调整

This commit is contained in:
zhangqi 2025-10-31 15:35:06 +08:00
parent cfc0787476
commit e58518faa7
16 changed files with 896 additions and 581 deletions

View File

@ -5,7 +5,8 @@
VITE_APP_ENV='development' VITE_APP_ENV='development'
# api接口请求地址 # api接口请求地址
VITE_APP_BASE_API='http://crm.hzzxq.com/ds' VITE_APP_BASE_API='http://test.gopmas.com/rs'
VITE_APP_DS_API='http://test.gopmas.com/ds'

View File

@ -3,3 +3,4 @@
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup). Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).

View File

@ -16,8 +16,8 @@
<!-- 登录后才显示的菜单项 --> <!-- 登录后才显示的菜单项 -->
<template v-if="authStore.isLoggedIn"> <template v-if="authStore.isLoggedIn">
<el-menu-item index="/">首页</el-menu-item> <el-menu-item index="/">首页</el-menu-item>
<el-menu-item index="/demand">任务需求</el-menu-item> <el-menu-item index="/search">任务需求</el-menu-item>
<el-menu-item index="/search">任务检索</el-menu-item> <el-menu-item index="/demand">任务检索</el-menu-item>
<el-menu-item index="/statistics">统计分析</el-menu-item> <el-menu-item index="/statistics">统计分析</el-menu-item>
<el-menu-item index="/contact">联系我们</el-menu-item> <el-menu-item index="/contact">联系我们</el-menu-item>
</template> </template>

View File

@ -21,11 +21,38 @@ export const getParam = () => {
export const voyageReg = (data) => { export const voyageReg = (data) => {
return request({ return request({
baseURL: data.baseURL, baseURL: data.baseURL,
url: '/voyage/reg/list.htm', url: '/api/voyage/list.htm',
method: 'post' method: 'post'
}) })
} }
// 关联航次
export const sampleAdd = (data) => {
return request({
url: '/rescue/reg/sample/add.htm',
method: 'post',
data
})
}
// 获取已关联航次列表
export const sampleList = (data) => {
return request({
url: '/rescue/reg/sample/list.htm',
method: 'post',
data
})
}
// 删除已关联航次
export const sampleDelete = (data) => {
return request({
url: '/rescue/reg/sample/delete.htm',
method: 'post',
data
})
}
// 获取任务列表 // 获取任务列表
export const regPage = (data) => { export const regPage = (data) => {
return request({ return request({
@ -79,4 +106,22 @@ export const regFileDelete = (data) => {
}) })
} }
// 统计接口
export const reportMain = (data) => {
return request({
url: '/report/main.htm',
method: 'post',
data
})
}
// 完成任务
export const regComplete = (data) => {
return request({
url: '/rescue/reg/complete.htm',
method: 'post',
data
})
}

View File

@ -25,7 +25,7 @@ const router = createRouter({
{ {
path: '/input/:id?', path: '/input/:id?',
name: 'TaskInput', name: 'TaskInput',
component: () => import('@/views/TaskInput.vue'), component: () => import('@/views/TaskData.vue'),
meta: { title: '任务需求信息录入', requiresAuth: true } meta: { title: '任务需求信息录入', requiresAuth: true }
}, },
{ {

View File

@ -13,7 +13,6 @@ const service = axios.create({
// request拦截器 // request拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(config => {
console.log(config)
// 过滤掉参数值为空的参数项 // 过滤掉参数值为空的参数项
if (config.params) { if (config.params) {
config.params = filterEmptyParams(config.params); config.params = filterEmptyParams(config.params);

View File

@ -10,35 +10,35 @@
<div class="item"> <div class="item">
<img src="@/assets/icons/任务.png" alt="任务" /> <img src="@/assets/icons/任务.png" alt="任务" />
<div class="right"> <div class="right">
<div><span>560</span></div> <div><span>0</span></div>
<div>应急任务累计完成</div> <div>应急任务累计完成</div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/区域.png" alt="区域" /> <img src="@/assets/icons/区域.png" alt="区域" />
<div class="right"> <div class="right">
<div><span>2650</span>km²</div> <div><span>{{ areaTotal }}</span>km²</div>
<div>专项调查区域覆盖</div> <div>专项调查区域覆盖</div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/航次.png" alt="航次" /> <img src="@/assets/icons/航次.png" alt="航次" />
<div class="right"> <div class="right">
<div><span>387</span></div> <div><span>{{ rescueTotal }}</span></div>
<div>执行航次</div> <div>执行航次</div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/潜水艇.png" alt="潜水艇" /> <img src="@/assets/icons/潜水艇.png" alt="潜水艇" />
<div class="right"> <div class="right">
<div><span>260</span></div> <div><span>0</span></div>
<div>载人深潜</div> <div>载人深潜</div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/潜艇.png" alt="潜艇" /> <img src="@/assets/icons/潜艇.png" alt="潜艇" />
<div class="right"> <div class="right">
<div><span>893</span></div> <div><span>0</span></div>
<div>无人潜水器作业</div> <div>无人潜水器作业</div>
</div> </div>
</div> </div>
@ -76,7 +76,7 @@
<script setup> <script setup>
import * as Cesium from "cesium"; import * as Cesium from "cesium";
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { getParam } from '@/api/task.js' import {getParam, reportMain} from '@/api/task.js'
import { modifyCesiumMouseWheel, setImageryViewModels } from '@/utils/common' import { modifyCesiumMouseWheel, setImageryViewModels } from '@/utils/common'
// icon // icon
@ -109,6 +109,24 @@ const formatIcon = (type, level) => {
return img return img
} }
const areaTotal = ref('')
const divingTotal = ref('')
const hourTotal = ref('')
const mileageTotal = ref('')
const priceTotal = ref('')
const rescueTotal = ref('')
const getData = () => {
reportMain().then(res => {
const obj = res.array[0]
areaTotal.value = obj.area_total
divingTotal.value = obj.diving_total
hourTotal.value = obj.hour_total
mileageTotal.value = obj.mileage_total
priceTotal.value = obj.price_total
rescueTotal.value = obj.rescue_total
})
}
// //
const dataList = ref([]) const dataList = ref([])
const markers = ref([]) const markers = ref([])
@ -160,17 +178,25 @@ const getDataList = () => {
const CesiumHostAddr = ref('') const CesiumHostAddr = ref('')
// //
const getParamsList = () => { const getParamsList = () => {
//
if (import.meta.env.VITE_APP_ENV === 'development') {
// 使
CesiumHostAddr.value = import.meta.env.VITE_APP_DS_API
initMap();
} else {
//
getParam().then(res => { getParam().then(res => {
const data = res.array.find(item => item.param_name === 'CesiumHostAddr') const data = res.array.find(item => item.param_name === 'CesiumHostAddr')
if (data?.param_value) { if (data?.param_value) {
CesiumHostAddr.value = data.param_value CesiumHostAddr.value = data.param_value
} else { } else {
// URL[1](@ref)
CesiumHostAddr.value = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/tiles/` CesiumHostAddr.value = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/tiles/`
} }
initMap(); initMap();
}) })
}
} }
let viewer; let viewer;
@ -259,6 +285,7 @@ const initMap = () => {
onMounted(() => { onMounted(() => {
getDataList() getDataList()
getParamsList() getParamsList()
getData()
}) })
</script> </script>

View File

@ -6,46 +6,66 @@
<img src="@/assets/icons/任务.png" alt="任务" /> <img src="@/assets/icons/任务.png" alt="任务" />
<div class="right"> <div class="right">
<div>完成航次</div> <div>完成航次</div>
<div><span>2650</span></div> <div><span>{{ rescueTotal }}</span></div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/区域.png" alt="区域" /> <img src="@/assets/icons/区域.png" alt="区域" />
<div class="right"> <div class="right">
<div>潜水次数</div> <div>潜水次数</div>
<div><span>2650</span></div> <div><span>{{ divingTotal }}</span></div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/航次.png" alt="航次" /> <img src="@/assets/icons/航次.png" alt="航次" />
<div class="right"> <div class="right">
<div>挽回经济损失</div> <div>挽回经济损失</div>
<div><span>2650</span></div> <div><span>{{ priceTotal }}</span></div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/潜水艇.png" alt="潜水艇" /> <img src="@/assets/icons/潜水艇.png" alt="潜水艇" />
<div class="right"> <div class="right">
<div>专项调查区域覆盖</div> <div>专项调查区域覆盖</div>
<div><span>2650</span>平方公里</div> <div><span>{{ areaTotal }}</span>平方公里</div>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<img src="@/assets/icons/潜艇.png" alt="潜艇" /> <img src="@/assets/icons/潜艇.png" alt="潜艇" />
<div class="right"> <div class="right">
<div>航行时间</div> <div>航行时间</div>
<div><span>2650</span>小时</div> <div><span>{{ hourTotal }}</span>小时</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup>
import { reportMain } from '@/api/task.js'
import {ref, onMounted} from "vue";
const areaTotal = ref('')
const divingTotal = ref('')
const hourTotal = ref('')
const mileageTotal = ref('')
const priceTotal = ref('')
const rescueTotal = ref('')
const getData = () => {
reportMain().then(res => {
const obj = res.array[0]
areaTotal.value = obj.area_total
divingTotal.value = obj.diving_total
hourTotal.value = obj.hour_total
mileageTotal.value = obj.mileage_total
priceTotal.value = obj.price_total
rescueTotal.value = obj.rescue_total
})
}
onMounted(() => {
getData()
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

43
src/views/TaskData.vue Normal file
View File

@ -0,0 +1,43 @@
<template>
<div class="container">
<div class="title">任务需求信息录入</div>
<el-divider />
<el-tabs v-model="activeName" type="card">
<el-tab-pane label="任务信息" name="任务信息">
<FormData :rescueId="rescueId" />
</el-tab-pane>
<el-tab-pane label="关联航次" name="关联航次" v-if="rescueId">
<RelatedVoyage :rescueId="rescueId" />
</el-tab-pane>
<el-tab-pane label="附件信息" name="附件信息" v-if="rescueId">
<FileList :rescueId="rescueId" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { ref } from 'vue'
import FormData from "@/views/common/FormData.vue"
import RelatedVoyage from "@/views/common/RelatedVoyage.vue"
import FileList from "@/views/common/FileList.vue"
import {useRoute} from 'vue-router'
const route = useRoute();
const rescueId = route.params.id; //
const activeName = ref('任务信息')
</script>
<style lang="scss" scoped>
.container {
padding: 20px;
color: #242424;
.title {
font-weight: bold;
color: #333;
}
}
</style>

View File

@ -3,6 +3,7 @@
<div class="left" v-show="isShow"> <div class="left" v-show="isShow">
<img class="fold" v-show="isShow" src="@/assets/icons/折叠.png" @click="handleFold"> <img class="fold" v-show="isShow" src="@/assets/icons/折叠.png" @click="handleFold">
<div class="search"> <div class="search">
<p class="rule-text">编号规则:任务类型_目标类型_4位编号</p>
<div class="input-bar"> <div class="input-bar">
<el-input <el-input
v-model="rescueCode" v-model="rescueCode"
@ -198,17 +199,25 @@ const onSearch = () => {
const CesiumHostAddr = ref('') const CesiumHostAddr = ref('')
// //
const getParamsList = () => { const getParamsList = () => {
//
if (import.meta.env.VITE_APP_ENV === 'development') {
// 使
CesiumHostAddr.value = import.meta.env.VITE_APP_DS_API
initMap();
} else {
//
getParam().then(res => { getParam().then(res => {
const data = res.array.find(item => item.param_name === 'CesiumHostAddr') const data = res.array.find(item => item.param_name === 'CesiumHostAddr')
if (data?.param_value) { if (data?.param_value) {
CesiumHostAddr.value = data.param_value CesiumHostAddr.value = data.param_value
} else { } else {
// URL[1](@ref)
CesiumHostAddr.value = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/tiles/` CesiumHostAddr.value = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/tiles/`
} }
initMap(); initMap();
}) })
}
} }
let viewer; let viewer;
@ -321,7 +330,7 @@ onMounted(() => {
.left { .left {
width: 450px; width: 450px;
position: relative; position: relative;
padding: 30px; padding: 0 30px 30px;
box-sizing: border-box; box-sizing: border-box;
.fold { .fold {
@ -330,6 +339,10 @@ onMounted(() => {
right: 0; right: 0;
} }
.rule-text {
color: #000;
}
.search { .search {
.input-bar { .input-bar {
@ -359,7 +372,7 @@ onMounted(() => {
.list { .list {
color: #333; color: #333;
height: 640px; height: 620px;
overflow-y: scroll; overflow-y: scroll;
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */ -ms-overflow-style: none; /* IE/Edge */

View File

@ -1,537 +0,0 @@
<template>
<div class="container">
<div class="title">任务需求信息录入</div>
<el-divider />
<div class="info">任务信息</div>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="auto"
class="ruleForm"
:size="formSize"
status-icon
>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="关联航次" prop="voyage_name">
<el-select v-model="ruleForm.voyage_name" >
<el-option v-for="(item, index) in voyageOptions" :key="index" :label="item.voyage_name" :value="item.voyage_name" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="状态信息" prop="rescue_status">
<el-select v-model="ruleForm.rescue_status" >
<el-option label="任务开始" value="任务开始" />
<el-option label="任务处理中" value="任务处理中" />
<el-option label="任务已完成" value="任务已完成" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="状态变更时间" prop="rescue_status_time">
<el-date-picker
style="width: 100%"
v-model="ruleForm.rescue_status_time"
type="datetime"
placeholder="请选择"
format="YYYY/MM/DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="状态变更说明" prop="rescue_status_note">
<el-input placeholder="请输入" v-model="ruleForm.rescue_status_note" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="任务类型" prop="rescue_type">
<el-select v-model="ruleForm.rescue_type" >
<el-option label="应急搜捞" value="SAR" />
<el-option label="专项调查" value="SI" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标类别" prop="rescue_target_type">
<el-select v-model="ruleForm.rescue_target_type" >
<el-option label="大型沉物" value="Wreck" />
<el-option label="锚系类目标" value="Mooring" />
<el-option label="非合作方目标" value="NCT" />
<el-option label="其他" value="Other" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="目标名称" prop="rescue_target_name">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_name" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标经度" prop="rescue_target_lon">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_lon" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标纬度" prop="rescue_target_lat">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_lat" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="搜寻范围半径" prop="rescue_target_radius">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_radius">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标宽度" prop="rescue_target_width">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_width">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标长度" prop="rescue_target_length">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_length">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="目标高度" prop="rescue_target_height">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_height">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜寻处置目标重量(空气中)" prop="rescue_air_weight">
<el-input placeholder="请输入" v-model="ruleForm.rescue_air_weight">
<template #append>千克</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜寻处置目标重量(水中)" prop="rescue_water_weight">
<el-input placeholder="请输入" v-model="ruleForm.rescue_water_weight">
<template #append>千克</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="搜寻处置目标水深" prop="rescue_water_depth">
<el-input placeholder="请输入" v-model="ruleForm.rescue_water_depth">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜寻处置目标预估价值" prop="rescue_target_price">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_price" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="快速响应等级" prop="rescue_response_level">
<el-select v-model="ruleForm.rescue_response_level" >
<el-option label="一级" value="一级" />
<el-option label="二级" value="二级" />
<el-option label="三级" value="三级" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="是否涉密" prop="rescue_secrecy">
<el-select v-model="ruleForm.rescue_secrecy" >
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜捞目标的所属单位" prop="rescue_unit">
<el-input placeholder="请输入" v-model="ruleForm.rescue_unit" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="快速响应时间要求" prop="rescue_response_day">
<el-input placeholder="请输入" v-model="ruleForm.rescue_response_day">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="填报时间" prop="fill_time">
<el-date-picker
style="width: 100%"
v-model="ruleForm.fill_time"
type="datetime"
placeholder="请选择"
format="YYYY/MM/DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系人姓名" prop="fill_person">
<el-input placeholder="请输入" v-model="ruleForm.fill_person" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系人电话" prop="fill_tel">
<el-input placeholder="请输入" v-model="ruleForm.fill_tel" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="其他信息" prop="rescue_note">
<el-input placeholder="请输入" v-model="ruleForm.rescue_note" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="填报来源描述" prop="fill_note">
<el-input placeholder="请输入" v-model="ruleForm.fill_note" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" v-if="rescueId">
<el-col :span="8">
<el-form-item label="其他附件">
<el-button type="primary" @click="onFileList">查看附件</el-button>
</el-form-item>
</el-col>
</el-row>
<div class="btn-bar">
<el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
<el-button @click="resetForm">取消</el-button>
</div>
</el-form>
<el-dialog v-model="dialogTableVisible" title="附件列表" width="800">
<div class="upload-btn">
<el-upload
v-model:file-list="fileList"
class="upload-demo"
:action="uploadUrl"
:show-file-list="false"
:data="{
rescue_id: rescueId
}"
:on-success="onSuccess"
>
<el-button type="primary">上传附件</el-button>
</el-upload>
</div>
<el-table :data="fileData">
<el-table-column property="file_org_name" label="文件名称" />
<el-table-column property="file_length" label="文件大小" width="150">
<template #default="scope">
<span class="file-size">{{ formatFileSize(scope.row.file_length) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleDownload(scope.row.tsy_id)">下载</el-button>
<el-popconfirm
confirm-button-text="确定"
cancel-button-text="取消"
:icon="InfoFilled"
icon-color="#626AEF"
title="是否确认删除?"
@confirm="handleDelete(scope.row.tsy_id)"
>
<template #reference>
<el-button link type="primary" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue'
import {useRoute, useRouter} from 'vue-router'
import type { ComponentSize, FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { InfoFilled } from '@element-plus/icons-vue'
import { getParam, voyageReg, regEdit, regFind, regFileList, regFileDelete } from '@/api/task.js'
const router = useRouter();
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/rescue/reg/file/upload.htm'
const dialogTableVisible = ref(false)
const fileList = ref([])
const fileData = ref([])
const route = useRoute();
const rescueId = route.params.id; //
const formSize = ref<ComponentSize>('default')
const ruleFormRef = ref<FormInstance>()
const ruleForm = ref({
voyage_name: '',
rescue_status: '',
rescue_status_time: '',
rescue_status_note: '',
rescue_type: '',
rescue_target_type: '',
rescue_target_name: '',
rescue_target_lon: '',
rescue_target_lat: '',
rescue_target_radius: '',
rescue_target_width: '',
rescue_target_length: '',
rescue_target_height: '',
rescue_air_weight: '',
rescue_water_weight: '',
rescue_water_depth: '',
rescue_target_price: '',
rescue_response_level: '',
rescue_secrecy: '',
rescue_unit: '',
rescue_response_day: '',
fill_time: '',
fill_person: '',
fill_tel: '',
rescue_note: '',
fill_note: '',
})
const rules = reactive({
voyage_name: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_status: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_status_time: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_status_note: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_type: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_target_type: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_name: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_lon: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_lat: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_radius: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_width: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_length: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_height: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_air_weight: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_water_weight: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_water_depth: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_price: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_response_level: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_secrecy: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_unit: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_response_day: [
{ required: true, message: '请输入', trigger: 'blur' },
],
fill_time: [
{ required: true, message: '请选择', trigger: 'change' },
],
fill_person: [
{ required: true, message: '请输入', trigger: 'blur' },
],
fill_tel: [
{ required: true, message: '请输入', trigger: 'blur' },
],
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
regEdit(ruleForm.value).then(res => {
ruleForm.value = res.entity
ElMessage({
message: '提交成功',
type: 'success'
})
})
} else {
console.log('error submit!', fields)
}
})
}
const resetForm = () => {
router.push('/search')
}
//
const getDetail = () => {
regFind({rescue_id: rescueId}).then(res => {
ruleForm.value = res.entity
})
}
//
const onFileList = () => {
dialogTableVisible.value = true
regFileList({rescue_id: rescueId}).then(res => {
fileData.value = res.array
})
}
//
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
//
const onSuccess = () => {
regFileList({rescue_id: rescueId}).then(res => {
fileData.value = res.array
})
}
//
const handleDownload = async (fileId) => {
window.open(`${import.meta.env.VITE_APP_BASE_API}/rescue/reg/file/download.htm?file_id=${fileId}`)
}
//
const handleDelete = (fileId) => {
regFileDelete({
file_id: fileId
}).then(res => {
regFileList({rescue_id: rescueId}).then(res => {
fileData.value = res.array
})
})
}
const VoyageSocketUrl = ref('')
//
const getParamsList = () => {
getParam().then(res => {
const data = res.array.find(item => item.param_name === 'VoyageSocketUrl')
if (data?.param_value) {
VoyageSocketUrl.value = data.param_value
} else {
VoyageSocketUrl.value = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/ds/`
}
getVoyageReg()
})
}
const voyageOptions = ref([])
//
const getVoyageReg = () => {
voyageReg({ baseURL: VoyageSocketUrl.value }).then(res => {
voyageOptions.value = res.array
console.log(res.array)
})
}
onMounted(() => {
getParamsList()
if (rescueId) {
getDetail()
}
})
</script>
<style lang="scss" scoped>
.container {
padding: 20px;
color: #242424;
.title {
font-weight: bold;
color: #333;
}
.info {
display: inline-block;
padding: 0 20px 10px;
border-bottom: 2px solid #409eff;
}
.ruleForm {
margin-top: 20px;
.btn-bar {
display: flex;
justify-content: center;
}
}
}
.upload-btn {
text-align: right;
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<div>
<div class="upload-btn">
<el-upload
v-model:file-list="fileList"
class="upload-demo"
:action="uploadUrl"
:show-file-list="false"
:data="{
rescue_id: props.rescueId
}"
:on-success="onSuccess"
>
<el-button type="primary">上传附件</el-button>
</el-upload>
</div>
<el-table :data="fileData" border style="width: 100%">
<el-table-column property="file_org_name" label="文件名称" />
<el-table-column property="file_length" label="文件大小" width="150">
<template #default="scope">
<span class="file-size">{{ formatFileSize(scope.row.file_length) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleDownload(scope.row.tsy_id)">下载</el-button>
<el-popconfirm
confirm-button-text="确定"
cancel-button-text="取消"
:icon="InfoFilled"
icon-color="#626AEF"
title="是否确认删除?"
@confirm="handleDelete(scope.row.tsy_id)"
>
<template #reference>
<el-button link type="primary" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {regFileDelete, regFileList} from "@/api/task.js"
import {InfoFilled} from '@element-plus/icons-vue'
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/rs/rescue/reg/file/upload.htm'
const fileList = ref([])
const fileData = ref([])
const props = defineProps({
rescueId: {
type: String, //
default: ''
}
})
//
const getFileList = () => {
regFileList({rescue_id: props.rescueId}).then(res => {
fileData.value = res.array
})
}
//
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
//
const onSuccess = () => {
regFileList({rescue_id: props.rescueId}).then(res => {
fileData.value = res.array
})
}
//
const handleDownload = async (fileId) => {
window.open(`${import.meta.env.VITE_APP_BASE_API}/rs/rescue/reg/file/download.htm?file_id=${fileId}`)
}
//
const handleDelete = (fileId) => {
regFileDelete({
file_id: fileId
}).then(res => {
regFileList({rescue_id: props.rescueId}).then(res => {
fileData.value = res.array
})
})
}
onMounted(() => {
if (props.rescueId) {
getFileList()
}
})
</script>
<style scoped lang="scss">
.upload-btn {
text-align: right;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,381 @@
<template>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="auto"
class="ruleForm"
:size="formSize"
status-icon
>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="任务类型" prop="rescue_type">
<el-select v-model="ruleForm.rescue_type" >
<el-option label="应急搜捞" value="SAR" />
<el-option label="专项调查" value="SI" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标类别" prop="rescue_target_type">
<el-select v-model="ruleForm.rescue_target_type" >
<el-option label="大型沉物" value="Wreck" />
<el-option label="锚系类目标" value="Mooring" />
<el-option label="非合作方目标" value="NCT" />
<el-option label="其他" value="Other" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="目标名称" prop="rescue_target_name">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_name" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标经度" prop="rescue_target_lon">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_lon" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标纬度" prop="rescue_target_lat">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_lat" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="搜寻范围半径" prop="rescue_target_radius">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_radius">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标宽度" prop="rescue_target_width">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_width">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标长度" prop="rescue_target_length">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_length">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="目标高度" prop="rescue_target_height">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_height">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜寻处置目标重量(空气中)" prop="rescue_air_weight">
<el-input placeholder="请输入" v-model="ruleForm.rescue_air_weight">
<template #append>千克</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜寻处置目标重量(水中)" prop="rescue_water_weight">
<el-input placeholder="请输入" v-model="ruleForm.rescue_water_weight">
<template #append>千克</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="搜寻处置目标水深" prop="rescue_water_depth">
<el-input placeholder="请输入" v-model="ruleForm.rescue_water_depth">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜寻处置目标预估价值" prop="rescue_target_price">
<el-input placeholder="请输入" v-model="ruleForm.rescue_target_price" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="快速响应等级" prop="rescue_response_level">
<el-select v-model="ruleForm.rescue_response_level" >
<el-option label="一级" value="一级" />
<el-option label="二级" value="二级" />
<el-option label="三级" value="三级" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="是否涉密" prop="rescue_secrecy">
<el-select v-model="ruleForm.rescue_secrecy" >
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="搜捞目标的所属单位" prop="rescue_unit">
<el-input placeholder="请输入" v-model="ruleForm.rescue_unit" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="快速响应时间要求" prop="rescue_response_day">
<el-input placeholder="请输入" v-model="ruleForm.rescue_response_day">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="填报时间" prop="fill_time">
<el-date-picker
style="width: 100%"
v-model="ruleForm.fill_time"
type="datetime"
placeholder="请选择"
format="YYYY/MM/DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系人姓名" prop="fill_person">
<el-input placeholder="请输入" v-model="ruleForm.fill_person" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系人电话" prop="fill_tel">
<el-input placeholder="请输入" v-model="ruleForm.fill_tel" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="其他信息" prop="rescue_note">
<el-input placeholder="请输入" v-model="ruleForm.rescue_note" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="填报来源描述" prop="fill_note">
<el-input placeholder="请输入" v-model="ruleForm.fill_note" />
</el-form-item>
</el-col>
</el-row>
<div class="btn-bar">
<el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
<el-button @click="resetForm">取消</el-button>
<el-popconfirm
v-if="props.rescueId && ruleForm.rescue_status === '任务进行中'"
confirm-button-text="确定"
cancel-button-text="取消"
:icon="InfoFilled"
icon-color="#626AEF"
title="是否确认完成任务?"
@confirm="completeTask"
>
<template #reference>
<el-button type="success">完成任务</el-button>
</template>
</el-popconfirm>
</div>
</el-form>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue'
import type { ComponentSize, FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { regEdit, regFind, regComplete } from '@/api/task.js'
import {InfoFilled} from "@element-plus/icons-vue";
import {useRouter} from "vue-router";
const router = useRouter();
const formSize = ref<ComponentSize>('default')
const ruleFormRef = ref<FormInstance>()
const ruleForm = ref({
voyage_name: '',
rescue_status: '',
rescue_status_time: '',
rescue_status_note: '',
rescue_type: '',
rescue_target_type: '',
rescue_target_name: '',
rescue_target_lon: '',
rescue_target_lat: '',
rescue_target_radius: '',
rescue_target_width: '',
rescue_target_length: '',
rescue_target_height: '',
rescue_air_weight: '',
rescue_water_weight: '',
rescue_water_depth: '',
rescue_target_price: '',
rescue_response_level: '',
rescue_secrecy: '',
rescue_unit: '',
rescue_response_day: '',
fill_time: '',
fill_person: '',
fill_tel: '',
rescue_note: '',
fill_note: '',
})
const rules = reactive({
voyage_name: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_status: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_status_time: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_status_note: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_type: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_target_type: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_name: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_lon: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_lat: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_radius: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_width: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_length: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_height: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_air_weight: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_water_weight: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_water_depth: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_target_price: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_response_level: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_secrecy: [
{ required: true, message: '请选择', trigger: 'change' },
],
rescue_unit: [
{ required: true, message: '请输入', trigger: 'blur' },
],
rescue_response_day: [
{ required: true, message: '请输入', trigger: 'blur' },
],
fill_time: [
{ required: true, message: '请选择', trigger: 'change' },
],
fill_person: [
{ required: true, message: '请输入', trigger: 'blur' },
],
fill_tel: [
{ required: true, message: '请输入', trigger: 'blur' },
],
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
regEdit(ruleForm.value).then(res => {
ruleForm.value = res.entity
ElMessage({
message: '提交成功',
type: 'success'
})
})
} else {
console.log('error submit!', fields)
}
})
}
const resetForm = () => {
router.push('/search')
}
const props = defineProps({
rescueId: {
type: String, //
default: ''
}
})
//
const getDetail = () => {
regFind({rescue_id: props.rescueId}).then(res => {
ruleForm.value = res.entity
})
}
//
const completeTask = () => {
regComplete({ rescue_id: props.rescueId }).then(() => {
ElMessage({
message: '操作成功',
type: 'success'
})
getDetail()
})
}
onMounted(() => {
if (props.rescueId) {
getDetail()
}
})
</script>
<style lang="scss" scoped>
.ruleForm {
margin-top: 20px;
.btn-bar {
display: flex;
justify-content: center;
}
}
</style>

View File

@ -0,0 +1,209 @@
<template>
<div>
<div class="upload-btn">
<el-button type="primary" @click="handleVoyage">关联航次</el-button>
</div>
<el-table :data="tableData" border style="width: 100%">
<el-table-column prop="voyage_name" label="航次名称" />
<el-table-column prop="diving_sn" label="潜次号" />
<el-table-column prop="dataset_name" label="样品集名称" />
<el-table-column prop="sample_name" label="样品名称" />
<el-table-column prop="conver_area" label="覆盖面积" />
<el-table-column label="操作" width="150" align="center">
<template #default="scope">
<el-popconfirm
confirm-button-text="确定"
cancel-button-text="取消"
:icon="InfoFilled"
icon-color="#626AEF"
title="是否确认删除?"
@confirm="handleDelete(scope.row.tsy_id)"
>
<template #reference>
<el-button link type="primary" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="dialogFormVisible" title="关联航次" width="600">
<el-cascader
v-model="selectedValues"
:options="cascadeData"
:props="{ expandTrigger: 'hover' }"
@change="handleChange"
style="width: 100%;"
/>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="onConfirm">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {getParam, sampleAdd, sampleDelete, sampleList, voyageReg} from "@/api/task.js"
import {InfoFilled} from "@element-plus/icons-vue";
import {ElMessage} from "element-plus";
const dialogFormVisible = ref(false)
const props = defineProps({
rescueId: {
type: String, //
default: ''
}
})
const VoyageSocketUrl = ref('')
//
const getParamsList = () => {
//
if (import.meta.env.VITE_APP_ENV === 'development') {
// 使
VoyageSocketUrl.value = import.meta.env.VITE_APP_DS_API
voyageList()
} else {
//
getParam().then(res => {
const data = res.array.find(item => item.param_name === 'VoyageSocketUrl')
if (data?.param_value) {
VoyageSocketUrl.value = data.param_value
} else {
// URL[1](@ref)
VoyageSocketUrl.value = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/ds/`
}
voyageList()
})
}
}
const entity = ref()
const allVoyage = ref()
const tableData = ref([])
//
const voyageList = () => {
voyageReg({ baseURL: VoyageSocketUrl.value }).then(res => {
entity.value = res.entity
allVoyage.value = res.entity.VoyageArray
cascadeData.value = convertToCascadeData(res.entity)
})
}
//
const dataList = () => {
sampleList({ rescue_id: props.rescueId }).then(res => {
tableData.value = res.array
})
}
//
const handleVoyage = () => {
dialogFormVisible.value = true
}
//
const handleDelete = (id) => {
sampleDelete({ tsy_id: id }).then(() => {
ElMessage({
message: '删除成功',
type: 'success'
})
dataList()
})
}
const selectedValues = ref('')
const cascadeData = ref([])
//
const convertToCascadeData = (entity) => {
const cascadeData = [];
// VoyageArray
if (entity.VoyageArray && Array.isArray(entity.VoyageArray)) {
entity.VoyageArray.forEach(voyage => {
const voyageName = voyage.voyage_name;
//
const firstLevelItem = {
value: voyageName,
label: `${voyageName} (${voyage.ship_name})`,
children: []
};
// voyage_name
if (entity[voyageName] && Array.isArray(entity[voyageName])) {
entity[voyageName].forEach(dataset => {
const secondLevelItem = {
value: dataset.dataset_id.toString(),
label: dataset.dataset_name
};
firstLevelItem.children.push(secondLevelItem);
});
}
cascadeData.push(firstLevelItem);
});
}
return cascadeData;
}
const selectedDataset = ref('')
//
const handleChange = (value) => {
if (value && value.length === 2) {
const [voyageName, datasetId] = value;
selectedDataset.value = findDatasetById(entity.value, voyageName, datasetId);
}
}
//
const findDatasetById = (entity, voyageName, datasetId) => {
if (!entity[voyageName]) return null;
return entity[voyageName].find(dataset =>
dataset.dataset_id.toString() === datasetId.toString()
)
}
//
const onConfirm = () => {
if (selectedDataset.value) {
selectedDataset.value.rescue_id = props.rescueId
sampleAdd(selectedDataset.value).then(() => {
ElMessage({
message: '操作成功',
type: 'success'
})
dataList()
dialogFormVisible.value = false
selectedValues.value = ''
selectedDataset.value = null
})
}
}
onMounted(() => {
getParamsList()
voyageList()
if (props.rescueId) {
dataList()
}
})
</script>
<style scoped lang="scss">
.upload-btn {
text-align: right;
margin-bottom: 20px;
}
</style>

View File

@ -1 +1 @@
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/router/index.ts","./src/app.vue","./src/components/helloworld.vue","./src/views/contact.vue","./src/views/home.vue","./src/views/statistics.vue","./src/views/taskdemand.vue","./src/views/taskinput.vue","./src/views/tasksearch.vue"],"errors":true,"version":"5.8.3"} {"root":["./src/main.ts","./src/vite-env.d.ts","./src/router/index.ts","./src/app.vue","./src/components/helloworld.vue","./src/views/contact.vue","./src/views/home.vue","./src/views/login.vue","./src/views/statistics.vue","./src/views/taskdata.vue","./src/views/taskdemand.vue","./src/views/tasksearch.vue","./src/views/common/filelist.vue","./src/views/common/formdata.vue","./src/views/common/relatedvoyage.vue"],"errors":true,"version":"5.8.3"}

View File

@ -1 +1 @@
{"root":["./vite.config.ts"],"errors":true,"version":"5.8.3"} {"root":["./vite.config.ts"],"version":"5.8.3"}