完成统计分析板块

This commit is contained in:
zhangqi 2025-10-31 16:15:28 +08:00
parent 80a377b599
commit db0448b468
23 changed files with 2162 additions and 1341 deletions

View File

@ -18,7 +18,7 @@ module.exports = merge(prodEnv, {
VUE_APP_BASE_API: '"/api"', VUE_APP_BASE_API: '"/api"',
// VUE_APP_BASE_APIURL: '"http://ds2.hzzxq.com/api2"', // VUE_APP_BASE_APIURL: '"http://ds2.hzzxq.com/api2"',
VUE_APP_BASE_APIURL: '"http://crm.hzzxq.com/ds"', VUE_APP_BASE_APIURL: '"http://test.gopmas.com/ds"',
VUE_APP_FULL_API: '"http://10.10.151.5:5555"', VUE_APP_FULL_API: '"http://10.10.151.5:5555"',
VUE_APP_BASE_API_FRONT: '"http://10.10.150.128:8080/satellitePub/pub/index.html"', VUE_APP_BASE_API_FRONT: '"http://10.10.150.128:8080/satellitePub/pub/index.html"',
VUE_APP_FILE_API: '"http://10.10.151.5:5555/archives"', VUE_APP_FILE_API: '"http://10.10.151.5:5555/archives"',

View File

@ -17,7 +17,7 @@ module.exports = {
// target: 'http://10.0.90.70', // 测试环境 // target: 'http://10.0.90.70', // 测试环境
// target: 'http://1.95.42.191:8080', // 测试环境 // target: 'http://1.95.42.191:8080', // 测试环境
// target: 'http://ds2.hzzxq.com/api2', // 测试环境 // target: 'http://ds2.hzzxq.com/api2', // 测试环境
target: 'http://crm.hzzxq.com/ds', // 测试环境 target: 'http://test.gopmas.com/ds', // 测试环境
changeOrigin: true, // 是否跨域 changeOrigin: true, // 是否跨域
secure: false, // 是否使用https secure: false, // 是否使用https
pathRewrite: { pathRewrite: {

111
src/api/statistics.js Normal file
View File

@ -0,0 +1,111 @@
import request from '@/utils/request'
// 报表配置查询接口
export function reportList(data) {
return request({
url: '/report/type/list.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 科考船汇总统计属性
export function shipTotal(data) {
return request({
url: '/report/ship/total.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 科考船航次统计
export function shipVoyage(data) {
return request({
url: '/report/ship/voyage.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 科考船参航单位统计
export function shipUnit(data) {
return request({
url: '/report/ship/unit.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 科考船参航单位统计(年度)
export function shipUnitYear(data) {
return request({
url: '/report/ship/unit/year.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 载人潜水器汇总统计属性
export function platformHov(data) {
return request({
url: '/report/platform/hov.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 载人潜水器潜次统计
export function hovDiving(data) {
return request({
url: '/report/platform/hov/diving.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 载人潜水器参航单位统计
export function hovUnit(data) {
return request({
url: '/report/platform/hov/unit.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 载人潜水器参航单位统计(年度)
export function hovUnitYear(data) {
return request({
url: '/report/platform/hov/unit/year.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 无人航行潜水器统计
export function platformAuv(data) {
return request({
url: '/report/platform/auv.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 遥控潜水器潜水器统计
export function platformRov(data) {
return request({
url: '/report/platform/rov.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}

87
src/api/sysManage.js Normal file
View File

@ -0,0 +1,87 @@
import request from '@/utils/request'
/* ********************************* 角色列表 **************************************/
// 角色列表
export function roleList(data) {
return request({
url: '/frame/role/page.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
/* ********************************* 用户列表 **************************************/
// 用户列表
export function optrList(data) {
return request({
url: '/frame/optr/page.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 用户新增
export function optrAdd(data) {
return request({
url: '/frame/optr/add.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 用户编辑
export function optrEdit(data) {
return request({
url: '/frame/optr/edit.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 用户删除
export function optrDelete(data) {
return request({
url: '/frame/optr/delete.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
/* ********************************* 航次管理 **************************************/
// 航次列表
export function regList(data) {
return request({
url: '/voyage/reg/list.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 分配航次
export function regGrant(data) {
return request({
url: '/voyage/reg/optr/grant.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}
// 清除航次
export function regClear(data) {
return request({
url: '/voyage/reg/optr/clear.htm',
method: 'post',
type: 'application/x-www-form-urlencoded',
data
})
}

View File

@ -170,11 +170,11 @@ export default {
path:"visitorUnit", path:"visitorUnit",
children:[] children:[]
}, },
{ // {
name:"共享单位", // name:"",
path:"sharedUnit", // path:"sharedUnit",
children:[] // children:[]
}, // },
{ {
name:"资助机构", name:"资助机构",
path:"fundingAgencies", path:"fundingAgencies",
@ -195,11 +195,11 @@ export default {
path:"roleManage", path:"roleManage",
children:[] children:[]
}, },
{ // {
name:"单位管理", // name:"",
path:"unitManage", // path:"unitManage",
children:[] // children:[]
} // }
] ]
}, },
], ],

View File

@ -358,6 +358,16 @@ export const constantRoutes = [
loginAuthority: true loginAuthority: true
} }
}, },
{
path: 'hov',
name: 'hov',
component: () => import('@/views/statistics/HovStatistics.vue'),
meta: {
title: '载人潜水器汇总统计',
icon: 'peoples',
loginAuthority: true
}
},
{ {
path: 'AboutUs', path: 'AboutUs',
name: 'AboutUs', name: 'AboutUs',

27
src/utils/index.js Normal file
View File

@ -0,0 +1,27 @@
export function getYearRange(date) {
// 获取传入日期的年份
const year = date.getFullYear()
// 创建年份开始日期 (当年1月1日)
const startOfYear = new Date(year, 0, 1)
// 创建年份结束日期 (当年12月31日)
const endOfYear = new Date(year, 11, 31)
// 格式化日期为 YYYY-MM-DD
function formatDate(dateObj) {
const y = dateObj.getFullYear()
const m = String(dateObj.getMonth() + 1).padStart(2, '0') // 月份从0开始需要+1
const d = String(dateObj.getDate()).padStart(2, '0')
return `${y}-${m}-${d}`
}
return {
start: formatDate(startOfYear),
end: formatDate(endOfYear)
}
}
export function filterDictItems(data, dictName) {
return data.filter(item => item.report_type === dictName)
}

View File

@ -2,7 +2,7 @@
<div class="aboutUs"> <div class="aboutUs">
<div class="banner"> <div class="banner">
<p class="bold">关于我们</p> <p class="bold">关于我们</p>
<p>团队成员主要由海洋地球物理学家软件工程师和数据管理员构成隶属于中国科学院深海科学与工程研究所开展船潜载地球物理数据处理和深海数据系统信息化工具开发的应用基础研究服务支撑fundingOptions深海深渊科考深海考古等国家重大需求</p> <p>团队成员主要由海洋地球物理学家软件工程师和数据管理员构成隶属于中国科学院深海科学与工程研究所开展船潜载地球物理数据处理和深海数据系统信息化工具开发的应用基础研究服务支撑深海深渊科考深海考古等国家重大需求</p>
<p class="bold">团队组成</p> <p class="bold">团队组成</p>
<p><span class="fw-700">陈传绪</span>团队负责人船潜载地球物理数据处理算法开发DSDS架构总设计</p> <p><span class="fw-700">陈传绪</span>团队负责人船潜载地球物理数据处理算法开发DSDS架构总设计</p>
<p class="fw-700">* DSDSDeep-Sea Data System</p> <p class="fw-700">* DSDSDeep-Sea Data System</p>

View File

@ -27,11 +27,19 @@
</div> </div>
<div class="left4" v-if="radio == '数据样品集'"> <div class="left4" v-if="radio == '数据样品集'">
<div v-for="(item, index) in dataList" :key="index" :id="item.tsy_id" class="left4_1" > <div v-for="(item, index) in dataList" :key="index" :id="item.tsy_id" class="left4_1" >
<div class="left4_top">
<p @click="openList(item, 'list')" style="font-weight: 400;font-size: 14px;cursor: pointer;color: #409eff" >{{ item.dataset_name }} <span class="mileage">{{ item.file_total }}</span></p>
<span class="org">{{ item.create_time }}</span>
</div>
<ul v-show="currenId == item.tsy_id && fold">
<li :class="sample.tsy_id == currenSample ? 'bg-color' : ''" class="sample" :id="sample.tsy_id"
v-for="(sample, i) in item.sampleArray" :key="i" @click.stop="onSample(sample)">{{ sample.sample_name }}
</li>
</ul>
<div class="left4_list"> <div class="left4_list">
<div class="list1"> <div class="list1">
<p>航次</p> <p>航次</p>
<span class="hover-color" @click="turnDeatilPage(item)">{{ item.voyage_name }}</span> <span class="hover-color" style="color: #409eff" @click="turnDeatilPage(item)">{{ item.voyage_name }}</span>
</div> </div>
<div class="list1"> <div class="list1">
<p>平台类型</p> <p>平台类型</p>
@ -65,24 +73,15 @@
</div> </div>
<div class="left4_top">
<p @click="openList(item, 'list')" style="font-weight: 400;font-size: 14px;cursor: pointer" :style="currentSample === item.tsy_id ? 'color: #409eff' : ''">{{ item.dataset_name }} <span class="mileage">{{ item.file_total }}</span></p>
<span class="org">{{ item.create_time }}</span>
</div>
<ul v-show="currenId == item.tsy_id && fold">
<li :class="sample.tsy_id == currenSample ? 'bg-color' : ''" class="sample" :id="sample.tsy_id"
v-for="(sample, i) in item.sampleArray" :key="i" @click.stop="onSample(sample)">{{ sample.sample_name }}
</li>
</ul>
</div> </div>
</div> </div>
<div class="left4" v-if="radio == '航次'"> <div class="left4" v-if="radio == '航次'">
<div v-for="(item, index) in dataList" :key="index" class="left4_1" style="cursor: pointer" :id="item.tsy_id" @click="turnDeatilPage(item)"> <div v-for="(item, index) in dataList" :key="index" class="left4_1" style="cursor: pointer" :id="item.tsy_id">
<div class="left4_top"> <div class="left4_top">
<p :style="currentVoyage === item.tsy_id ? 'color: #409eff' : ''">{{ item.voyage_name }} <span <p :style="currentVoyage === item.tsy_id ? 'color: #409eff' : ''">
class="mileage">{{ item.voyage_mileage }}海里</span></p> <span style="color: #409eff" @click="turnDeatilPage(item)">{{ item.voyage_name }}</span>
<span class="mileage">{{ item.voyage_mileage }}海里</span></p>
<span class="org">{{ item.funding_org }}</span> <span class="org">{{ item.funding_org }}</span>
</div> </div>
<div class="left4_list"> <div class="left4_list">
@ -271,9 +270,6 @@ export default {
rendered: [] rendered: []
} }
}, },
created() {
this.getParams()
},
watch: { watch: {
radio(val) { radio(val) {
this.dataList = [] this.dataList = []
@ -314,23 +310,11 @@ export default {
} }
}, },
}, },
mounted() {
this.imageryViewModels = setImageryViewModels(this.CesiumHostAddr)
this.initCesium()
},
methods: { methods: {
//
getParams() {
getParamsList().then(res => {
const arr = res.array
if (arr.length) {
arr.forEach(item => {
if (item.param_name === 'CesiumHostAddr') {
// this.CesiumHostAddr = item.param_value
this.CesiumHostAddr = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/tiles/`
this.imageryViewModels = setImageryViewModels(this.CesiumHostAddr)
this.initCesium()
}
})
}
})
},
// //
handleMenu() { handleMenu() {
this.dataList = [] this.dataList = []
@ -642,12 +626,13 @@ export default {
this.viewer.imageryLayers.addImageryProvider(contour6000) this.viewer.imageryLayers.addImageryProvider(contour6000)
}, },
turnDeatilPage(item) { turnDeatilPage(item) {
this.$router.push({ const routeData = this.$router.resolve({
name: 'dataDetail', name: 'dataDetail',
query: { query: {
voyage_name: item.voyage_name voyage_name: item.voyage_name
} }
}) })
window.open(routeData.href, '_blank')
}, },
shousuo() { shousuo() {
this.show = true this.show = true
@ -807,12 +792,13 @@ export default {
font-size: 16px; font-size: 16px;
color: #212121; color: #212121;
font-weight: bold; font-weight: bold;
display: flex; //display: flex;
justify-content: space-between; //justify-content: space-between;
.mileage { .mileage {
font-size: 12px; font-size: 12px;
color: #909fa7; color: #909fa7;
float: right;
} }
} }

View File

@ -172,6 +172,13 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8" v-if="form.sample_tag_name === '应急搜捞'">
<el-form-item label="覆盖宽度" prop="conver_width">
<el-input v-model="form.conver_width">
<template slot="append"></template>
</el-input>
</el-form-item>
</el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
@ -264,7 +271,8 @@ export default {
start_lat: '', start_lat: '',
end_lon: '', end_lon: '',
end_lat: '', end_lat: '',
line_deep: '' line_deep: '',
conver_width: ''
}, },
rules: { rules: {
publish_rule: [ publish_rule: [
@ -308,6 +316,9 @@ export default {
], ],
line_deep: [ line_deep: [
{ required: true, message: '此处为必填项', trigger: 'blur' } { required: true, message: '此处为必填项', trigger: 'blur' }
],
conver_width: [
{ required: true, message: '此处为必填项', trigger: 'blur' }
] ]
}, },
// //
@ -445,7 +456,8 @@ export default {
start_lat: '', start_lat: '',
end_lon: '', end_lon: '',
end_lat: '', end_lat: '',
line_deep: '' line_deep: '',
conver_width: ''
} }
this.dynamicTags = [] this.dynamicTags = []
this.$refs['form'].resetFields() this.$refs['form'].resetFields()

View File

@ -365,8 +365,11 @@ import {
voyageRegNav, voyageRegNav,
uploadNavigation, uploadNavigation,
voyageRegPeople, voyageRegPeople,
voyageRegPeopleDelete, voyageRegPeopleEdit, voyageRegPeopleUpload, voyageRegImport voyageRegPeopleDelete,
} from '@/api/dataTransfer' voyageRegPeopleEdit,
voyageRegPeopleUpload,
voyageRegImport
} from '../../api/dataTransfer'
import { time } from '@/utils/validate' import { time } from '@/utils/validate'
import { fundingAll } from '@/api/dicManage' import { fundingAll } from '@/api/dicManage'

View File

@ -21,6 +21,7 @@
<el-table ref="tableData" v-loading="loading" stripe border :data="tableData" :header-cell-style="{ background: '#dddfe6',color: '#555960',textAlign: 'center' }" :cell-style="{ textAlign: 'center' }"> <el-table ref="tableData" v-loading="loading" stripe border :data="tableData" :header-cell-style="{ background: '#dddfe6',color: '#555960',textAlign: 'center' }" :cell-style="{ textAlign: 'center' }">
<el-table-column type="index" label="#" width="50"/> <el-table-column type="index" label="#" width="50"/>
<el-table-column prop="unit_name" label="参航单位名称" /> <el-table-column prop="unit_name" label="参航单位名称" />
<el-table-column prop="unit_type" label="参航单位类型" width="200" />
<el-table-column prop="unit_lon" label="经度" width="200" /> <el-table-column prop="unit_lon" label="经度" width="200" />
<el-table-column prop="unit_lat" label="纬度" width="200" /> <el-table-column prop="unit_lat" label="纬度" width="200" />
<el-table-column label="操作" width="120"> <el-table-column label="操作" width="120">
@ -61,6 +62,9 @@
<el-form-item label="所在纬度" prop="unit_lat"> <el-form-item label="所在纬度" prop="unit_lat">
<el-input v-model="form.unit_lat" /> <el-input v-model="form.unit_lat" />
</el-form-item> </el-form-item>
<el-form-item label="单位类型" prop="unit_type">
<el-input v-model="form.unit_type" />
</el-form-item>
<el-form-item label="备注信息" prop="unit_remark"> <el-form-item label="备注信息" prop="unit_remark">
<el-input v-model="form.unit_remark" type="textarea" /> <el-input v-model="form.unit_remark" type="textarea" />
</el-form-item> </el-form-item>
@ -99,6 +103,7 @@ export default {
unit_addr: '', unit_addr: '',
unit_lon: '', unit_lon: '',
unit_lat: '', unit_lat: '',
unit_type: '',
unit_remark: '' unit_remark: ''
}, },
rules: { rules: {
@ -110,6 +115,9 @@ export default {
], ],
unit_lat: [ unit_lat: [
{ required: true, message: '此处为必填项', trigger: 'blur' } { required: true, message: '此处为必填项', trigger: 'blur' }
],
unit_type: [
{ required: true, message: '此处为必填项', trigger: 'blur' }
] ]
} }
} }

View File

@ -284,7 +284,7 @@ export default {
}, },
{ {
pic: require('../../static/images/shuju2_bg3.png'), pic: require('../../static/images/shuju2_bg3.png'),
title: '支撑大科学计划', title: '构建应急搜寻处置快速响应能力',
des: '应急搜捞应用', des: '应急搜捞应用',
routerName: 'SalvageHome' routerName: 'SalvageHome'
} }

View File

@ -281,7 +281,7 @@ export default {
}, },
updateInfoVisible: false, updateInfoVisible: false,
updateInfoForm: { updateInfoForm: {
tsy_id: '', optr_id: '',
optr_name: '', optr_name: '',
optr_sex: '', optr_sex: '',
optr_status: '', optr_status: '',
@ -310,7 +310,7 @@ export default {
this.info = data this.info = data
this.updateInfoForm.tsy_id = data.tsy_id this.updateInfoForm.optr_id = data.optr_id
this.updateInfoForm.optr_name = data.optr_name this.updateInfoForm.optr_name = data.optr_name
this.updateInfoForm.optr_sex = data.optr_sex this.updateInfoForm.optr_sex = data.optr_sex
this.updateInfoForm.optr_status = data.optr_status this.updateInfoForm.optr_status = data.optr_status
@ -337,7 +337,7 @@ export default {
this.$refs[formName].validate((valid) => { this.$refs[formName].validate((valid) => {
if (valid) { if (valid) {
updatePassword({ updatePassword({
tsy_id: this.info.tsy_id, optr_id: this.info.optr_id,
old_password: this.updatePwdForm.old_password, old_password: this.updatePwdForm.old_password,
new_pass: this.updatePwdForm.new_pass new_pass: this.updatePwdForm.new_pass
}).then(res => { }).then(res => {

View File

@ -0,0 +1,489 @@
<template>
<div class="statistics">
<div class="banner">
<el-dropdown @command="handleCommand">
<span class="titles">{{ currentTitle }}<i class="el-icon-arrow-down el-icon--right" /></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="全部载人潜水器">全部载人潜水器</el-dropdown-item>
<el-dropdown-item
v-for="item in titles"
:key="item.tsy_id"
:command="item.report_name"
>
{{ item.report_name }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="numbers">
<div v-for="(item, index) in numbers" :key="index" class="item">
<div class="value">{{ item.value }}<span class="unit">{{ item.unit }}</span></div>
<div class="label">{{ item.label }}</div>
</div>
</div>
</div>
<!-- 地图板块-->
<div class="map-module">
<div class="bezel">
<div class="map-title">下潜单位</div>
<baidu-map
:center="baiduCenter"
:zoom="baiduZoom"
:scroll-wheel-zoom="true"
map-type="BMAP_SATELLITE_MAP"
style="width: 100%; height: 100%"
>
<bm-marker
v-for="(item, index) in baiduMarker"
:key="index"
:position="item.position"
:dragging="true"
:icon="item.icon"
@click="tapBaidu(item, index)"
>
<bm-label
:content="item.content"
:offset="item.offset"
/>
</bm-marker>
</baidu-map>
</div>
<div class="bezel">
<div class="map-title">下潜地图</div>
<div id="cesiumContainer" ref="cesiumContainer" style="height: 100%;width:100%;" />
</div>
</div>
<!-- echarts板块-->
<div class="echarts-module">
<div class="bezel">
<BarChart
:title="barData1.title"
:chart-data="barData1.salesData"
:categories="barData1.productCategories"
chart-type="single"
/>
</div>
<div class="bezel">
<BarChart
:title="barData2.title"
:chart-data="barData2.salesData"
:categories="barData2.productCategories"
chart-type="stacked"
stack-name="新增单位数"
/>
</div>
<div class="bezel">
<BarChart
:title="barData3.title"
:chart-data="barData3.salesData"
:categories="barData3.productCategories"
/>
</div>
<div class="bezel">
<BarChart
:title="barData4.title"
:chart-data="barData4.salesData"
:categories="barData4.productCategories"
/>
</div>
<div class="bezel">
<BarChart
:title="barData5.title"
:chart-data="barData5.salesData"
:categories="barData5.productCategories"
/>
</div>
<div class="bezel">
<PieChart
:chart-data="pieData"
title="下潜单位类型"
chart-type="ring"
/>
</div>
</div>
</div>
</template>
<script>
import BarChart from './components/BarChart.vue'
import PieChart from './components/PieChart.vue'
import { setImageryViewModels } from '../../utils/common'
import { reportList, platformHov, hovUnit, hovUnitYear, hovDiving } from '../../api/statistics'
import { filterDictItems } from '../../utils/index'
const Cesium = window.Cesium
export default {
components: {
BarChart,
PieChart
},
data() {
return {
currentTitle: '全部载人潜水器',
titles: [],
numbers: [
{
label: '潜次数',
value: '',
unit: '次'
},
{
label: '下潜人数',
value: '',
unit: '人'
},
{
label: '下潜单位',
value: '',
unit: '家'
},
{
label: '数据样品个数',
value: '',
unit: '个'
}
],
baiduCenter: { lng: 114.4040, lat: 30.5196 },
baiduZoom: 5,
baiduMarker: [],
activeMarkerIndex: -1, //
viewer: null,
//
CesiumHostAddr: `${window.location.protocol}//${window.location.hostname}:${window.location.port}/`,
trackLineLayers: [],
barData1: {},
barData2: {},
barData3: {},
barData4: {},
barData5: {},
pieData: []
}
},
created() {
this.getTypeList()
this.getHovList()
this.getHovUnit()
this.gitHovDiving()
this.gitHovUnitYear()
this.getChartBar({ year: '2025' })
},
mounted() {
this.imageryViewModels = setImageryViewModels(this.CesiumHostAddr)
this.initCesium()
},
methods: {
//
getTypeList() {
reportList().then(res => {
this.titles = filterDictItems(res.array, '载人潜水器')
})
},
//
handleCommand(command) {
this.currentTitle = command
this.removeSingleLineImg()
if (command === '全部载人潜水器') {
this.getHovList()
this.gitHovDiving()
this.gitHovUnitYear()
this.getChartBar({ year: '2025' })
} else {
this.getHovList({ platform_name: command })
this.gitHovDiving({ platform_name: command })
this.gitHovUnitYear({ platform_name: command })
this.getChartBar({ year: '2025', platform_name: command })
}
},
//
getHovList(params = {}) {
platformHov(params).then(res => {
const obj = res.array[0]
this.numbers[0].value = obj.diving_total
this.numbers[1].value = obj.driver_member_total
this.numbers[2].value = obj.unit_total
this.numbers[3].value = obj.dataset_total
})
},
//
getHovUnit() {
hovUnit().then(res => {
this.baiduMarker = res.array.map(item => {
return {
position: {
lng: item.unit_lon,
lat: item.unit_lat
},
content: item.unit_name,
offset: { width: 16, height: 32 },
icon: {
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAE9klEQVR4AZxXXWgcVRT+zkwaBS0mu1FpSyHFbKoIQjFoRSh5EX8QbYVKq4I+hLa7qRWCf32QRoh/gfqSzcYQiyDUlgZFffHnoRGhQsFQwWLYbKx9kFokuwkRSrHZuf3OzOzuTGaS3c3lfnPO/c65Z76Zu3Nn1kIT7Y6Jy9sSuUJfe64wRZsnlnzkfa5Pc5ooiYYEdOTmHkzkZsftGzcusfiEAL0AuomNPrp9bkJzNFfnMFa31xWQzBWOOjC/AnIAXrtK870IPjTi7FeorxyhMRo5oHOSnMvBmn1NAYnR/KAB3q9VkKFSJrWJeLKYTh1dSG8/rVBfOWITIEPwm87VGv4w1qwqIJEr7IVYx/xZ05a9YXMp0/WOP17VaI7mMmGagNZwayG+xQpIjv/1MNPPEOBVfF7KpHrmD3b+o+NGoLk6R+f6+Wf8mv6wZmIFGKf8mpsi+Hshk3rZ9ddxcOeyhk6t1tRBABEB7dn8UzBmv5/T59uQSY7lH+JtPZ7IzX3poXBcuVBSbeDVYE23do13vYgAse3dGjEG35TSqR/UDyKZze8xRr4jNwCY5zxgQDmNkQ91rWFYS8lKbfUriAjg1e9xg4I/XBs4tI3NdBrL/hiQBID/mHvKhfrkNKY5jIV7pZYxXu1ANCRgY3YmyVgHwQuzfndt4GCZFv42TCepi9zCdpb6u19QqO9yMJ1eDkfBbqq1OvxzVKMhAS0GmysR28B7jCqEZ3UHpDg5VzqUqt4h1zdyzkuBl4NaC9YKnkMzQgJ4C20lFSLXr6gNgmu5RcfGKv+kNogKV8kJxhzH8XdIwLY3tAZjIQGL/d2/MbhIoIxbHlAbhAi+1bEYK/JoVrhKjuZVsQGPVPxWe3Gm4qsNCVDCAD+7ViQiAIJJjRFPtPON2DZS6FWorxyBQA6qzXFcAQJcuHKw51qVpxMRIMa5QJ7dPM5DqOsjReIEwfOg17IxpRCgsu4n/BwEm4j1jI65PGfVBhERANtyt2CB2a1XF0xWn1tsH094mL7+SBdoFdPKaYzjUE9kZx/jXd3hkctfebZ2jAhwf9HwbrXVIpG11qnFTGqUJ+shEj56lNNYBJY873NnS/33/eL7VRMR4Ee8tTbmlbi74OfUNW3Z2V1M8rZi4BT9SI8VwKuahHg/RrFxLDKrQcKyRJcKWos1P0VMixXg5jl4Ty3XtrfeR4XmrUQyW3iW3F4CMMi6NuawqoBSf+pH5nPf55EfJk0txeBUi7HwNmfyVcGXWiY1qX4cVhWgycvX/9e7cFH9ZpYiedcWPflOzrtmO+XqJxrHkb6mgKWB+0tGREVwGdHQUrSNFXbweVcBOmdo/tV7+UEbOW+VWFOAZi2ku07TfkYAXIqOXMHdVBDT7hz993bLYAQit1HEFB/ND2LSQlRdAZq93Hrr67TnCfBzezgxOrdV/ZUoy+IEuUcBuWrb1ptooDUkYKlva0kco98CJUC2Q5xhrGiJ3Bw52ac0n5z0/KF71rz1mqdoSIAmFg93n+fv4Yj6gOxrH52tXiFPzj8t5g2wcQs/Usx0fU23od6wAK22kE6dhGAQbCLyUfvYn0+3fXJpFx/0cbCRGy5mukfoNtybEqBV+bZ7VwxOqs8357hVLvsvL3xRTHe9pXwzaFqAFi/2p14C5DLATzjB3aBfzKRexDraugToeUqZrm20+kqe9n0Oo70ecxMAAP//0tEICQAAAAZJREFUAwDQYMDuVKkB5gAAAABJRU5ErkJggg==',
size: { width: 32, height: 32 }
}
}
})
const typeCount = res.array.reduce((acc, unit) => {
const type = unit.unit_type
acc[type] = (acc[type] || 0) + 1 // +11
return acc
}, {})
this.pieData = Object.entries(typeCount).map(([type, count]) => ({
name: type,
value: count
}))
})
},
//
gitHovDiving(params = {}) {
hovDiving(params).then(res => {
//
res.array.forEach(item => {
this.addSingleLineImg(item)
})
})
},
tapBaidu(item, index) {
//
this.baiduMarker.forEach(marker => {
marker.icon.url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAE9klEQVR4AZxXXWgcVRT+zkwaBS0mu1FpSyHFbKoIQjFoRSh5EX8QbYVKq4I+hLa7qRWCf32QRoh/gfqSzcYQiyDUlgZFffHnoRGhQsFQwWLYbKx9kFokuwkRSrHZuf3OzOzuTGaS3c3lfnPO/c65Z76Zu3Nn1kIT7Y6Jy9sSuUJfe64wRZsnlnzkfa5Pc5ooiYYEdOTmHkzkZsftGzcusfiEAL0AuomNPrp9bkJzNFfnMFa31xWQzBWOOjC/AnIAXrtK870IPjTi7FeorxyhMRo5oHOSnMvBmn1NAYnR/KAB3q9VkKFSJrWJeLKYTh1dSG8/rVBfOWITIEPwm87VGv4w1qwqIJEr7IVYx/xZ05a9YXMp0/WOP17VaI7mMmGagNZwayG+xQpIjv/1MNPPEOBVfF7KpHrmD3b+o+NGoLk6R+f6+Wf8mv6wZmIFGKf8mpsi+Hshk3rZ9ddxcOeyhk6t1tRBABEB7dn8UzBmv5/T59uQSY7lH+JtPZ7IzX3poXBcuVBSbeDVYE23do13vYgAse3dGjEG35TSqR/UDyKZze8xRr4jNwCY5zxgQDmNkQ91rWFYS8lKbfUriAjg1e9xg4I/XBs4tI3NdBrL/hiQBID/mHvKhfrkNKY5jIV7pZYxXu1ANCRgY3YmyVgHwQuzfndt4GCZFv42TCepi9zCdpb6u19QqO9yMJ1eDkfBbqq1OvxzVKMhAS0GmysR28B7jCqEZ3UHpDg5VzqUqt4h1zdyzkuBl4NaC9YKnkMzQgJ4C20lFSLXr6gNgmu5RcfGKv+kNogKV8kJxhzH8XdIwLY3tAZjIQGL/d2/MbhIoIxbHlAbhAi+1bEYK/JoVrhKjuZVsQGPVPxWe3Gm4qsNCVDCAD+7ViQiAIJJjRFPtPON2DZS6FWorxyBQA6qzXFcAQJcuHKw51qVpxMRIMa5QJ7dPM5DqOsjReIEwfOg17IxpRCgsu4n/BwEm4j1jI65PGfVBhERANtyt2CB2a1XF0xWn1tsH094mL7+SBdoFdPKaYzjUE9kZx/jXd3hkctfebZ2jAhwf9HwbrXVIpG11qnFTGqUJ+shEj56lNNYBJY873NnS/33/eL7VRMR4Ee8tTbmlbi74OfUNW3Z2V1M8rZi4BT9SI8VwKuahHg/RrFxLDKrQcKyRJcKWos1P0VMixXg5jl4Ty3XtrfeR4XmrUQyW3iW3F4CMMi6NuawqoBSf+pH5nPf55EfJk0txeBUi7HwNmfyVcGXWiY1qX4cVhWgycvX/9e7cFH9ZpYiedcWPflOzrtmO+XqJxrHkb6mgKWB+0tGREVwGdHQUrSNFXbweVcBOmdo/tV7+UEbOW+VWFOAZi2ku07TfkYAXIqOXMHdVBDT7hz993bLYAQit1HEFB/ND2LSQlRdAZq93Hrr67TnCfBzezgxOrdV/ZUoy+IEuUcBuWrb1ptooDUkYKlva0kco98CJUC2Q5xhrGiJ3Bw52ac0n5z0/KF71rz1mqdoSIAmFg93n+fv4Yj6gOxrH52tXiFPzj8t5g2wcQs/Usx0fU23od6wAK22kE6dhGAQbCLyUfvYn0+3fXJpFx/0cbCRGy5mukfoNtybEqBV+bZ7VwxOqs8357hVLvsvL3xRTHe9pXwzaFqAFi/2p14C5DLATzjB3aBfzKRexDraugToeUqZrm20+kqe9n0Oo70ecxMAAP//0tEICQAAAAZJREFUAwDQYMDuVKkB5gAAAABJRU5ErkJggg=='
marker.icon.size = { width: 32, height: 32 }
marker.offset = { width: 16, height: 32 }
})
//
this.baiduMarker[index].icon.url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAIN0lEQVR4AbQZW2wc1fWcWduktEX1ziYtKYJ61rJUBVKVGvWhVo3afjQqqOEV4IO3xBuFZ5TdNWQN3l1eAvIBSvgAJAQCAgQEQoAEfPB+BAGyAwTvJqBgIN51UECQ2N65nHNn7syd9czsw8vVPfe8z7nnzuzMnbsGdKmZQ7nfJq3MFSkr94pp5cbNdPYL08p+L0HSuXHWJcmGbbuUFhZdgJnOrKFJPQTzYg8C3iFA/B1ArAABy2mSP5MgabGCdWzDtuzDvqRfVO+4AFrhc8yBzMsgcBtN6ox2ZyF9yJdjcKx2/ZV9RwWkrOyzIOA+QFylAimMCFsJ1gPCuQYY/2NgGhHWE2xVdh7mGBRLxvSErRNtF2Ba2U8FwOpgCtwOgJf2GmJZtVxcS3BrrVy8f7oy9iwD0ywjWMs2bEtAPuA1jsmxPUGLRFsFUALKA4N+bJxAxAtrlcIwwd1fTZamfV04xTZsSzDMvgA4AX4bdHP4kiZUywVQ4CeCsSixbZ9SLRfuCcpb56QvxYBgEbAwF0S2lgowrVyJIpxI4HSEN2kFj67tLn3sCDofOQbHot/Jm1qUE92cmiicbFpAcjC7lh6LGzT3nbVy8a8a3xXSjbnTDyY2OLl9SRgVX8CqfA/a4hrN8YBhw2ka31XSjX1ABZW5aQ6KD8OxBZifz14NgMeBaig2Tu8uvq/YWPynC3rNo0Z+zwBEx9q6ShmbcrgsITzOmQORET22API5j0B2BHitVi7dIpmYIclbBSu73dyX+g4S9g4Gpk2SJUkX4ypVnINzScYZznNQ+BhZgPm7zJ/JZYjA6cJoOnnTyj2GtJ0gh2MJ+ghUZ/pY1rGNEkbiYK4hdy6h5pEFAOJ/NI/91UN7ntP4BaRpZW6mH/vJCxQLBOJkx3aBwhO4ufZ7guBcPDET0QWA8AtAeAMm8rPsEAZmeuRfALgeVEOYojfeqDBoA8cAMAokU2ogW8cHwhvn4pyeVpuLJ3OI6AIQLccEKDe8CLHNLmjqt3AehmcqxfzMZGmHBKJZRjZvEbg94OPKfISg5dTm4ls4VHgBq/I9pD6SQHYUxoQkQobUUdnDQcBflArBGKt+VvwSGhrLkHSgGvlIX8U3YAzmPBKcOTVYQfj3QOrzg97qs8ecPb+DcRjYCfybJt9frYw9o/EB0tV593aDb8C2MWfjnJRx+BUw8BfKgHHvz5dUGYcBCjhcyRFgNzRpuo3u2+jWc4hdC8qMw4K8w4UWUJ389AN6otQdE4D6gbmVim7EImG/pGT0w11pWhv8F59SuJh1bOOyoPsqmcJivi8Qp+9Ab+hdEFoAwNY6CHxbBTMERBbAP1II7CaNLEQ2XYcTjm+EsbD1AsanpvLfh1lGFECmhvA+OOhb9g8AJIvopH9MU61JpXMXHHZEPqlkTLOM+DUEsjf4SFnDMKx4umrjim7EkQXQvao7ecEaAzDPj0x61k4xzSCE2NLbN7uH3rrjDEyzjHUS6J0gfSSzcPh1Or+MpP57CCBy2x5ZACV8j4KoPpxKZ09XTBhGgf9vkNOJBJ1O8AkFANG+Fhfa+kqi5uyDJxD6FYHsCRuekkTIEFlArXLTO2T/NIHTBZzlEOFjtVJ4t9Zf5T3PXeEWUnoX27Ct5CIG+tQ83lMhPD8dswOOLIADIOAjjBnoPlxNTxH9srI4CNvvmatVipfZCMcgiosAxQMMTLOMdUA2Qacgt2xwJE1PQL4CjkKIyNVng9gCqv3Tj5KR/5WExpnEN+37ysXxarm0hbbGZzEwzbKmjmRg1+2zATABTtuPdXzSIcPH2AJ4tVCAdxWAbqOlA5l/hIdavJRXXxhwiYqEiI/wFkTxYTi+APKwEwm/AOIFYobQT9Ln7foltEimCk633b2KjsJNC5iZvHGCJn2bCiB/C+ns5YrvFu63rjsGAb3VB8AHZyYL+kkFhLWmBbBTjy24gDLTEoTILk1v0A64pHRRgwG0+gBLvCCITVefbVsqYO+u4te0OlwE+xDgb2yRiNkykEkbPTWQ46fORZrLtlp5zNtjafIFZEsFsFe1UtgMgC+A18S5yYHcqR7bIbF8ef5QgeI63R1BtLT67NNyAdJY2GOEvU9LNCCzdEU+sPUmfVv94JKDPHlv40a/sdFqpRT5TdEYvK0CpneVXqFHW84LIsQf7R9mO76VTCv7bwDUT/22xe2RIKS1VQD7V8sF/i08zrQLmWR65L8u3R5C4NVXPuWEYVyrmFZx2wVwYBQGX4U9TDOgsDfxS4jpViFpZTeDgH969ojX7p0c8590niKe6KiA6q6xT+ippF/6obot7oxP5WuTVuZ6BLhQSQTAaK1c2Kb4dnBHBXACeio9iIA3MO2AOJ7+JrrdoaPH5MDI+eQ3qlk81O59r/mGn0roBnE0FbGRVu9hZUP0lSkrpz/PlUri/nR2NWJ9i2Sc4d1DevrWOWRnY8dXQKXrRTkB7+OHPhU3hZ269Q9kV9K3Nb9LvJ0mbVHWTe3MR554qBxxeNEFfF3O77UBeRW/dRP1Af2ok4Mbj3B5+OVQPkWJeOW9wzJAWDdTLryubDrFFLdTV99vX6XwKt0+V3gSgKPRntuk+L752c00Ye/0juSlGv2LSXjRvSsF8CxmKkV6/eNNTLtwkmll72YgXj+1frxWKXb88qNYgd61AjhqrVLIEH6CQPWLiWAgBHQR4EPokbcbdKt1tQCeVF3gVTTTj5gOgICabdN/yjsLXwTki2S6XsA3uwqfCYQrG+b1HaJxzszuQtMPlAa/pmzXC+CMM5PF54VA+nsW6XQPt6Ntn1CtjLW8w+QYrcKPAAAA//9QcJOrAAAABklEQVQDALykzWblMgKeAAAAAElFTkSuQmCC'
this.baiduMarker[index].icon.size = { width: 48, height: 48 } //
this.baiduMarker[index].offset = { width: 24, height: 48 } //
//
this.activeMarkerIndex = index
},
// cesium
initCesium() {
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkY2Y5NWY2Ni1mODQ1LTQ3YTUtYjc4Zi1jYzhjMGI2YzcxMWYiLCJpZCI6MTI5ODMwLCJpYXQiOjE2Nzk0Njk5NTd9.DTH54ioOH-HLqeNIetBe9hFyrPOX2Vp1AQmZzw8TIZ4'
this.viewer = new Cesium.Viewer('cesiumContainer', {
sceneMode: Cesium.SceneMode.SCENE2D,
shadows: false,
timeline: false,
baseLayerPicker: false,
//
imageryProviderViewModels: this.imageryViewModels,
//
terrainProviderViewModels: [],
fullscreenButton: false,
selectionIndicator: false,
homeButton: false,
sceneModePicker: false,
animation: false,
infoBox: false,
geocoder: false,
navigationHelpButton: false
})
this.viewer._cesiumWidget._creditContainer.style.display = 'none'
this.viewer.scene.sun.show = false
this.viewer.scene.moon.show = false
this.viewer.scene.fog.enabled = false
this.viewer.scene.skyAtmosphere.show = false
this.viewer.scene.sun.show = false
this.viewer.scene.skyBox.show = false
this.viewer.scene.globe.enableLighting = false
this.viewer.shadowMap.darkness = 0.8
this.viewer.scene._sunBloom = false
this.viewer.scene.globe.showGroundAtmosphere = false
this.viewer.scene.postProcessStages.fxaa.enabled = true
this.viewer.camera.setView({
//
destination: Cesium.Cartesian3.fromDegrees(112, 18, 4000000)
})
},
//
addSingleLineImg(item) {
const trackLine = new Cesium.WebMapTileServiceImageryProvider(
{
url: `${this.CesiumHostAddr}geoserver/gwc/service/wmts/rest/dsds:${item.diving_sn}/{TileMatrixSet}/{TileMatrixSet}:{TileMatrix}/{TileRow}/{TileCol}?format=image/png`,
layer: `dsds:${item.diving_sn}`,
style: 'default',
format: 'image/png',
tileMatrixSetID: 'EPSG:4326',
tilingScheme: new Cesium.GeographicTilingScheme()
}
)
const trackLineLayer = this.viewer.imageryLayers.addImageryProvider(trackLine)
this.trackLineLayers.push(trackLineLayer)
},
//
removeSingleLineImg() {
if (this.trackLineLayers.length) {
this.trackLineLayers.forEach(line => {
this.viewer.imageryLayers.remove(line)
})
}
},
//
gitHovUnitYear(params = {}) {
hovUnitYear(params).then(res => {
const originalData = res.array
const sortedData = originalData.sort((a, b) => a.voyage_year - b.voyage_year)
this.barData2 = {
title: '年度参潜单位',
salesData: [
{
name: '参潜单位',
data: sortedData.map(item => item.unit_total),
stack: '新增单位数'
},
{
name: '新增单位',
data: sortedData.map(item => item.year_unit_total),
stack: '新增单位数'
}
],
productCategories: sortedData.map(item => item.voyage_year.toString())
}
})
},
//
getChartBar(params) {
platformHov(params).then(res => {
const originalData = res.array
const sortedData = originalData.sort((a, b) => a.year - b.year)
this.barData1 = {
title: '年度潜次数',
salesData: sortedData.map(item => item.diving_total),
productCategories: sortedData.map(item => item.year.toString())
}
this.barData3 = {
title: '样品集个数',
salesData: sortedData.map(item => item.dataset_total),
productCategories: sortedData.map(item => item.year.toString())
}
this.barData4 = {
title: '水中时间',
salesData: sortedData.map(item => item.water_total),
productCategories: sortedData.map(item => item.year.toString())
}
this.barData5 = {
title: '水中作业时间',
salesData: sortedData.map(item => item.job_total),
productCategories: sortedData.map(item => item.year.toString())
}
})
}
}
}
</script>
<style scoped lang="scss">
.statistics {
background-color: #ebf1f7;
.banner {
background-image: url(../../../static/images/bannerny1.jpg);
width: 100%;
height: 480px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #fff;
.titles {
margin-bottom: 40px;
font-size: 56px;
color: #fff;
font-weight: bold;
cursor: pointer;
}
.numbers {
display: flex;
justify-content: space-between;
gap: 80px;
.item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.value {
font-size: 56px;
font-weight: bold;
.unit {
font-size: 26px;
}
}
.label {
font-size: 18px;
}
}
}
}
.map-module {
padding: 50px 50px;
display: flex;
justify-content: space-between;
gap: 50px;
.bezel {
flex: 1;
width: 100%;
height: 520px;
border-radius: 10px;
overflow: hidden;
border: 1px solid #ccc;
position: relative;
.map-title {
position: absolute;
top: 20px;
left: 20px;
color: white;
z-index: 8888;
font-size: 22px;
}
.map-year {
position: absolute;
top: 20px;
right: 20px;
color: white;
z-index: 8888;
width: 100px;
}
}
}
.echarts-module {
padding: 0 50px 50px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 50px;
grid-auto-rows: 300px;
.bezel {
width: 100%;
height: 100%;
border-radius: 10px;
overflow: hidden;
border: 1px solid #ccc;
position: relative;
.map-title {
position: absolute;
top: 20px;
left: 20px;
color: white;
z-index: 8888;
font-size: 22px;
}
}
}
}
</style>
<style>
/* 隐藏百度地图Logo和版权信息 */
.anchorBL {
display: none !important;
}
.BMap_cpyCtrl {
display: none !important;
}
.el-icon-arrow-down {
font-size: 12px
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,435 @@
<template>
<div ref="chartContainer" style="width: 100%; height: 300px;"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
name: 'AdvancedMixedChart',
props: {
title: {
type: String,
default: '图表标题'
},
chartData: {
type: [Array, Object],
default: () => []
},
categories: {
type: Array,
default: () => []
},
chartType: {
type: String,
default: 'single', // 'single' | 'multiple' | 'mixed' | 'stacked'
validator: (value) => ['single', 'multiple', 'mixed', 'stacked'].includes(value)
},
colors: {
type: Array,
default: () => ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272']
},
// Y
dualYAxis: {
type: Boolean,
default: false
},
// stacked
stackName: {
type: String,
default: '总量'
}
},
data() {
return {
myChart: null
};
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (this.myChart) {
this.myChart.dispose();
}
window.removeEventListener('resize', this.handleResize);
},
methods: {
initChart() {
this.$nextTick(() => {
this.myChart = echarts.init(this.$refs.chartContainer);
this.renderChart();
window.addEventListener('resize', this.handleResize);
});
},
//
processChartData() {
if (this.chartType === 'single') {
//
const data = Array.isArray(this.chartData) && this.chartData.length > 0
? this.chartData
: new Array(this.categories.length).fill(0);
return [{
name: '数量',
type: 'bar',
data: data
}];
} else if (this.chartType === 'multiple') {
//
if (Array.isArray(this.chartData) && this.chartData.every(item => typeof item === 'number')) {
return [{
name: '系列1',
type: 'bar',
data: this.chartData
}];
} else if (Array.isArray(this.chartData)) {
return this.chartData.map((series, index) => ({
name: series.name || `系列${index + 1}`,
type: series.type || 'bar',
data: series.data || [],
yAxisIndex: series.yAxisIndex || 0
}));
}
} else if (this.chartType === 'mixed') {
// 线
if (Array.isArray(this.chartData)) {
return this.chartData.map((series, index) => ({
name: series.name || (series.type === 'line' ? '折线' : '柱状') + (index + 1),
type: series.type || (index === 0 ? 'bar' : 'line'),
data: series.data || [],
yAxisIndex: series.yAxisIndex || (this.dualYAxis && index > 0 ? 1 : 0)
}));
}
} else if (this.chartType === 'stacked') {
// [2,3](@ref)
if (Array.isArray(this.chartData) && this.chartData.every(item => typeof item === 'number')) {
//
return [{
name: '数量',
type: 'bar',
data: this.chartData,
stack: this.stackName // [2](@ref)
}];
} else if (Array.isArray(this.chartData)) {
//
return this.chartData.map((series, index) => ({
name: series.name || `系列${index + 1}`,
type: 'bar',
data: series.data || [],
yAxisIndex: series.yAxisIndex || 0,
stack: series.stack || this.stackName // stack [2,3](@ref)
}));
}
}
return [{
name: '数据',
type: 'bar',
data: new Array(this.categories.length).fill(0)
}];
},
// Y
getYAxisConfig() {
if (!this.dualYAxis) {
// Y
return [{
type: 'value',
axisLine: {
show: true,
lineStyle: {
color: '#999'
}
},
axisTick: {
show: false
},
axisLabel: {
color: '#666'
},
splitLine: {
lineStyle: {
color: '#f0f0f0',
type: 'solid'
}
}
}];
}
// Y
return [
{
type: 'value',
name: '左轴',
position: 'left',
axisLine: {
show: true,
lineStyle: {
color: this.colors[0]
}
},
axisLabel: {
color: '#666',
formatter: '{value}'
},
splitLine: {
show: false
}
},
{
type: 'value',
name: '右轴',
position: 'right',
axisLine: {
show: true,
lineStyle: {
color: this.colors[1]
}
},
axisLabel: {
color: '#666',
formatter: '{value}'
},
splitLine: {
show: false
}
}
];
},
//
getSeriesConfig(processedSeries) {
return processedSeries.map((series, index) => {
const isLine = series.type === 'line';
const isStacked = this.chartType === 'stacked';
// [2](@ref)
const baseConfig = {
name: series.name,
type: series.type,
data: series.data,
yAxisIndex: series.yAxisIndex || 0,
stack: series.stack //
};
if (isLine) {
// 线
return {
...baseConfig,
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
width: 3,
color: this.colors[index % this.colors.length]
},
itemStyle: {
color: this.colors[index % this.colors.length]
}
};
} else {
//
const barConfig = {
...baseConfig,
barWidth: this.chartType === 'single' ? '60%' : '40%',
itemStyle: {
color: this.colors[index % this.colors.length]
},
emphasis: {
itemStyle: {
color: this.colors[index % this.colors.length],
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
};
// [3](@ref)
if (isStacked) {
barConfig.barWidth = '50%';
barConfig.itemStyle.borderRadius = [2, 2, 0, 0]; //
}
return barConfig;
}
});
},
//
renderChart() {
const processedSeries = this.processChartData();
const seriesConfig = this.getSeriesConfig(processedSeries);
const yAxisConfig = this.getYAxisConfig();
//
const showLegend = this.chartType !== 'single';
// tooltip [2](@ref)
const tooltipFormatter = this.chartType === 'stacked'
? this.getStackedTooltipFormatter
: null;
const option = {
title: {
text: this.title,
left: 'left',
top: 10,
textStyle: {
color: '#333',
fontSize: 16,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: this.chartType === 'stacked' ? 'shadow' : 'line' // 使 [2](@ref)
},
formatter: tooltipFormatter || this.getDefaultTooltipFormatter(),
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#ddd',
borderWidth: 1,
textStyle: {
color: '#333'
}
},
legend: {
show: showLegend,
data: seriesConfig.map(s => s.name),
right: 10,
top: 10,
orient: 'horizontal',
type: 'scroll',
textStyle: {
color: '#666'
}
},
grid: {
left: '3%',
right: this.dualYAxis ? '8%' : '3%',
bottom: '8%',
top: showLegend ? 60 : 40,
containLabel: true
},
xAxis: {
type: 'category',
data: this.categories,
axisLine: {
lineStyle: {
color: '#999'
}
},
axisTick: {
show: false
},
axisLabel: {
color: '#666',
interval: 0,
rotate: this.categories.length > 6 ? 45 : 0,
margin: 10
}
},
yAxis: yAxisConfig,
series: seriesConfig
};
this.myChart.setOption(option);
},
// tooltip
getDefaultTooltipFormatter() {
return (params) => {
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${params[0].name}</div>`;
params.forEach(param => {
const color = param.color;
const seriesName = param.seriesName;
const value = param.value;
const marker = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${color};"></span>`;
result += `
<div style="display: flex; justify-content: space-between; align-items: center; margin: 4px 0; min-width: 120px;">
<div style="display: flex; align-items: center;">
${marker}
<span style="margin-right: 15px;">${seriesName}</span>
</div>
<span style="font-weight: bold; font-size: 13px;">${value}</span>
</div>
`;
});
return result;
};
},
// tooltip [2](@ref)
getStackedTooltipFormatter(params) {
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${params[0].name}</div>`;
let total = 0;
//
params.forEach(param => {
total += param.value || 0;
});
params.forEach(param => {
const color = param.color;
const seriesName = param.seriesName;
const value = param.value;
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : 0;
const marker = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${color};"></span>`;
result += `
<div style="display: flex; justify-content: space-between; align-items: center; margin: 4px 0; min-width: 150px;">
<div style="display: flex; align-items: center;">
${marker}
<span style="margin-right: 15px;">${seriesName}</span>
</div>
<div style="text-align: right;">
<span style="font-weight: bold; font-size: 13px;">${value}</span>
</div>
</div>
`;
});
return result;
},
handleResize() {
if (this.myChart) {
this.myChart.resize();
}
}
},
watch: {
chartData: {
handler() {
if (this.myChart) {
this.renderChart();
}
},
deep: true
},
categories() {
if (this.myChart) {
this.renderChart();
}
},
chartType() {
if (this.myChart) {
this.renderChart();
}
},
dualYAxis() {
if (this.myChart) {
this.renderChart();
}
},
stackName() {
if (this.myChart && this.chartType === 'stacked') {
this.renderChart();
}
}
}
};
</script>

View File

@ -0,0 +1,178 @@
<template>
<div ref="chartContainer" :style="{ width: width, height: height }"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
name: 'AdvancedPieChart',
props: {
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
},
chartData: {
type: Array,
required: true,
default: () => []
},
title: {
type: [String, Object],
default: ''
},
// 'normal', 'ring', 'nightingale'
chartType: {
type: String,
default: 'normal',
validator: (value) => ['normal', 'ring', 'nightingale'].includes(value)
},
//
showLabel: {
type: Boolean,
default: true
},
//
showValue: {
type: Boolean,
default: true
}
},
data() {
return {
myChart: null
};
},
watch: {
chartData: {
handler(newVal) {
this.updateChart(newVal);
},
deep: true
}
},
mounted() {
this.$nextTick(() => {
this.initChart();
});
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
if (this.myChart) {
this.myChart.dispose();
}
window.removeEventListener('resize', this.handleResize);
},
methods: {
initChart() {
if (!this.$refs.chartContainer) return;
this.myChart = echarts.init(this.$refs.chartContainer);
const option = {
title: {
text: typeof this.title === 'string' ? this.title : '',
left: 10,
top: 10,
textStyle: {
fontSize: 16,
color: '#333'
}
},
tooltip: {
trigger: 'item',
formatter: this.getTooltipFormatter()
},
legend: {
orient: 'vertical',
left: 'right',
textStyle: {
color: '#666'
}
},
series: this.getSeriesConfig()
};
this.myChart.setOption(option);
this.myChart.on('click', (params) => {
this.$emit('chartClick', params);
});
},
getSeriesConfig() {
const baseConfig = {
name: typeof this.title === 'string' ? this.title : '数据',
type: 'pie',
data: this.chartData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
label: this.getLabelConfig()
};
switch (this.chartType) {
case 'ring':
return {
...baseConfig,
radius: ['40%', '70%']
};
case 'nightingale':
return {
...baseConfig,
radius: [20, 140],
roseType: 'area'
};
default:
return {
...baseConfig,
radius: '70%'
};
}
},
getLabelConfig() {
if (!this.showLabel) {
return { show: false };
}
let formatter = '{b}';
if (this.showValue) {
formatter += ': {c}';
}
return {
show: true,
formatter: formatter
};
},
getTooltipFormatter() {
return '{a} <br/>{b}: {c} ({d}%)';
},
updateChart(newData) {
if (!this.myChart) return;
this.myChart.setOption({
series: [{
data: newData
}]
});
},
handleResize() {
if (this.myChart) {
this.myChart.resize();
}
}
}
};
</script>

View File

@ -19,8 +19,8 @@
</div> </div>
<div class="card-info"> <div class="card-info">
<h3 class="card-title">科考船汇总统计</h3> <h3 class="card-title">科考船汇总统计</h3>
<p class="card-count">4</p> <p class="card-count">{{ ship.length }}</p>
<p class="card-desc">探索一号探索二号探索三号海警101船</p> <p class="card-desc"><span v-for="(item, index) in ship" :key="index">{{ item.report_name }}<span v-if="index !== ship.length - 1"></span></span></p>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -39,8 +39,8 @@
</div> </div>
<div class="card-info"> <div class="card-info">
<h3 class="card-title">载人潜水器汇总统计</h3> <h3 class="card-title">载人潜水器汇总统计</h3>
<p class="card-count">2</p> <p class="card-count">{{ hov.length }}</p>
<p class="card-desc">深海勇士奋斗者等</p> <p class="card-desc"><span v-for="(item, index) in hov" :key="index">{{ item.report_name }}<span v-if="index !== hov.length - 1"></span></span> </p>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -59,8 +59,8 @@
</div> </div>
<div class="card-info"> <div class="card-info">
<h3 class="card-title">遥控潜水器汇总统计</h3> <h3 class="card-title">遥控潜水器汇总统计</h3>
<p class="card-count loading">数据加载中</p> <p class="card-count">{{ rov.length }}</p>
<p class="card-desc"></p> <p class="card-desc"><span v-for="(item, index) in rov" :key="index">{{ item.report_name }}<span v-if="index !== rov.length - 1"></span></span> </p>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -79,8 +79,8 @@
</div> </div>
<div class="card-info"> <div class="card-info">
<h3 class="card-title">无人航行潜水器汇总统计</h3> <h3 class="card-title">无人航行潜水器汇总统计</h3>
<p class="card-count loading">数据加载中</p> <p class="card-count">{{ uuv.length }}</p>
<p class="card-desc"></p> <p class="card-desc"><span v-for="(item, index) in uuv" :key="index">{{ item.report_name }}<span v-if="index !== uuv.length - 1"></span></span> </p>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -100,7 +100,6 @@
<div class="card-info"> <div class="card-info">
<h3 class="card-title">基站汇总统计</h3> <h3 class="card-title">基站汇总统计</h3>
<p class="card-count no-data">暂无数据</p> <p class="card-count no-data">暂无数据</p>
<p class="card-desc">深海站深渊站国际站</p>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -130,17 +129,34 @@
</template> </template>
<script> <script>
import { reportList } from '../../api/statistics'
import { filterDictItems } from '../../utils/index'
export default { export default {
name: 'MainDashboard', name: 'MainDashboard',
data() { data() {
return { return {
ship: [],
hov: [],
rov: [],
uuv: []
} }
}, },
created() {
this.getTypeList()
},
methods: { methods: {
goToDetail(type) { goToDetail(type) {
this.$router.push(`/container/${type}`); this.$router.push(`/container/${type}`);
} },
getTypeList() {
reportList().then(res => {
this.ship = filterDictItems(res.array, '科考船')
this.hov = filterDictItems(res.array, '载人潜水器')
this.rov = filterDictItems(res.array, '遥控潜水器')
this.uuv = filterDictItems(res.array, '无人航行潜水器')
})
},
} }
} }
</script> </script>

View File

@ -8,380 +8,71 @@
<el-breadcrumb-item>角色管理</el-breadcrumb-item> <el-breadcrumb-item>角色管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
</div> </div>
<div class="filter-container"> <el-table ref="tableData" stripe border :data="tableData" :header-cell-style="{ background: '#dddfe6',color: '#555960',textAlign: 'center' }" :cell-style="{ textAlign: 'center' }" height="600px">
<div class="checkBtn">
<el-button type="primary" size="medium" @click="batchImport">添加角色</el-button>
</div>
</div>
<el-table ref="tableData" stripe border :data="tableData" :header-cell-style="{ background: '#dddfe6',color: '#555960',textAlign: 'center' }" :cell-style="{ textAlign: 'center' }" height="600px" @row-click="openModal">
<el-table-column <el-table-column
type="index" type="index"
width="50" width="50"
/> />
<el-table-column prop="userName" label="角色名称" /> <el-table-column prop="role_name" label="角色名称" />
<el-table-column prop="describe" label="角色描述" /> <el-table-column prop="role_note" label="角色描述" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="修改" placement="top-end">
<el-button id="mod" size="mini" type="warning" class="el-icon-edit" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="删除" placement="top-end">
<el-button id="del" size="mini" type="danger" class="el-icon-delete" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="授权" placement="top-end">
<el-button id="permission" size="mini" type="warning" class="el-icon-orange" />
</el-tooltip>
</template>
</el-table-column>
</el-table> </el-table>
<div class="pagination-box"> <div class="pagination-box">
<el-pagination <el-pagination
background background
:current-page="currentPage" :page-sizes="[10, 20, 30]"
:page-sizes="[15, 30, 50]" :page-size="params.limit"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
:total="count" :total="total"
:current-page="pageNum"
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
</div> </div>
<div class="modal">
<el-dialog
:title="userStatus? '添加角色' : '修改角色'"
:visible.sync="showUserForm"
width="30%"
:before-close="handleClose"
>
<el-form ref="form" :model="form" label-width="140px" :rules="rules">
<el-form-item label="角色名称" prop="userName">
<el-input v-model="form.userName" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item>
<el-form-item label="角色描述" prop="describe">
<el-input v-model="form.describe" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item>
</el-form>
<el-row type="flex" justify="center">
<el-button
style="width: 100px"
type="primary"
size="medium"
@click="saveData"
>确定</el-button>
<el-button
size="medium"
style="margin-left: 10px; width: 100px"
type="info"
plain
@click="showUserForm = false;clearInput();resetForm();"
>取消</el-button>
</el-row>
</el-dialog>
<el-dialog
title="权限分配"
:visible.sync="showAuthorForm"
width="30%"
:before-close="handleCloseAuthor"
>
<el-form ref="form2" :model="form" label-width="10px" :rules="rules">
<el-form-item label="" prop="sex">
<el-select v-model="author" multiple placeholder="请选择" size="medium" style="width:100%">
<el-option
v-for="item in authorOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
<el-row type="flex" justify="center">
<el-button
style="width: 100px"
type="primary"
size="medium"
@click="saveAuthorData"
>确定</el-button>
<el-button
size="medium"
style="margin-left: 10px; width: 100px"
type="info"
plain
@click="showAuthorForm = false;resetForm2();"
>取消</el-button>
</el-row>
</el-dialog>
</div>
</div> </div>
</template> </template>
<script> <script>
import {roleList} from '../../api/sysManage'
export default { export default {
name: 'RoleManage', name: 'RoleManage',
data() { data() {
return { return {
// loading: false,
userStatus: true, //
// model params: {
recordEnabled: 0, start: 0,
showUserForm: false, limit: 10,
showAuthorForm: false, group: 10
delDialogVisible: false,
currentPage: 1,
pagesize: 15,
count: 0,
form: {
userName: '',
describe: ''
}, },
rules: { pageNum: 1,
userName: [ //
{ required: true, message: '请输入角色名称', trigger: 'blur' } tableData: [],
], total: 0
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' }
],
unit: [
{ required: true, message: '请输入单位', trigger: 'blur' }
]
},
author: ['用户管理','角色管理'],
tableData: [
{
userName: 'admin',
describe: '管理员',
createTime: '2022.3.4 12: 20: 00'
},
{
userName: 'admin',
describe: '管理员',
createTime: '2022.3.4 12: 20: 00'
},
{
userName: 'admin',
describe: '管理员',
createTime: '2022.3.4 12: 20: 00'
}
],
companyOption: [
{
label: '深海所',
value: '深海所'
},
{
label: '机构B',
value: '机构B'
}
],
sexOption: [
{
label: '男',
value: '男'
},
{
label: '女',
value: '女'
}
],
authorOption: [
{
label: '用户管理',
value: '用户管理'
},
{
label: '角色管理',
value: '角色管理'
},
{
label: '单位管理',
value: '单位管理'
},
{
label: '字典管理',
value: '字典管理'
},
{
label: '统计分析',
value: '统计分析'
},
{
label: '数据汇交',
value: '数据汇交'
},
{
label: '数据检索',
value: '数据检索'
}
]
} }
}, },
created() { created() {
const isLogin = localStorage.getItem('isLogin') this.getData()
if (isLogin == 'false' || isLogin == '' || isLogin == undefined) {
this.$router.push({ name: 'home' })
return
}
// this.init()
}, },
methods: { methods: {
batchImport() {
this.showUserForm = true
this.userStatus = true
},
tableIndex(index) {
return (this.currentPage - 1) * this.pagesize + index + 1
},
openModal(row, event, column) {
//
var opType = column.target.id
if (opType == 'del') {
this.$confirm('确定删除选中的数据吗?此操作不可撤销!', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
this.deleteData(row.id)
})
.catch((error) => {
console.log(error)
})
} else if (opType == 'mod') {
this.showUserForm = true
this.userStatus = false
this.form = JSON.parse(JSON.stringify(row))
} else if (opType == 'permission') {
this.showAuthorForm = true
}
},
async deleteData(id) {
this.loading = true
const data = {
id
}
try {
const res = await deletePlanPage(data)
if (res.code == 2000) {
this.init()
}
} catch (error) {
console.log(error)
this.loading = false
}
},
init() {
this.currentPage = 1
this.getData()
},
// currentPagepagesizedata
handleSizeChange: function(size) {
this.pagesize = size
this.getData()
},
handleCurrentChange: function(currentPage) {
this.currentPage = currentPage
this.getData()
},
fuzzyQueryFunction() {
this.currentPage = 1
this.getData()
},
reset() {
this.queryName = ''
},
getData() { getData() {
// this.loading = true
this.$http roleList(this.params).then(res => {
.post(process.env.baseUrl + '/node/findPage', { pageRequest: { pageNum: this.currentPage, pageSize: this.pagesize }, queryName: this.queryName }) this.tableData = res.page.records
.then(res => { this.total = Number(res.page.total)
if (res.code == 2000) { // 1 this.loading = false
this.tableData = res.data.page.content
this.count = res.data.page.totalSize
} else {
this.$message.error(res.msg) //
}
})
},
saveData() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.showStatus) {
this.addFunction()
} else {
this.updateFunction()
}
} else {
return false
}
}) })
}, },
saveAuthorData() { //
this.showAuthorForm = false handleCurrentChange(val) {
this.pageNum = val
this.params.start = val * this.params.limit - this.params.limit
this.getData()
}, },
async addFunction() { handleSizeChange(val) {
this.loading = true this.params.limit = val
const data = { this.getData()
deviceId: this.form.deviceId,
deviceName: this.form.deviceName,
description: this.form.description,
// userId: 123456,
userName: this.$store.state.user.user
}
try {
const res = await addPlanPage(data)
if (res.code === 2000) {
this.$emit('handleConfirm')
}
} catch (error) {
console.log(error)
}
this.loading = false
}, },
async updateFunction() {
this.loading = true
const data = {
deviceId: this.form.deviceId,
deviceName: this.form.deviceName,
description: this.form.description,
// userId: 123456,
userName: this.$store.state.user.user,
id: Number(this.id)
}
try {
const res = await updatePlanPage(data)
if (res.code === 2000) {
this.$emit('handleConfirm')
}
} catch (error) {
console.log(error)
}
this.loading = false
},
resetForm() {
this.$refs['form'].resetFields()
},
resetForm2() {
this.$refs['form2'].resetFields()
},
handleClose(done) { //
this.clearInput()
this.resetForm()
done()
},
handleCloseAuthor(done) {
done()
},
clearInput() {
this.form = {
userName: '',
name: '',
describe: '',
address: '',
unit: ''
}
}
} }
} }
</script> </script>

View File

@ -10,31 +10,33 @@
</div> </div>
<div class="filter-container"> <div class="filter-container">
<div class="checkBtn"> <div class="checkBtn">
<el-button type="primary" size="medium" @click="batchImport">添加用户</el-button> <el-button type="primary" size="medium" @click="handleAdd">添加用户</el-button>
</div> </div>
</div> </div>
<el-table ref="tableData" stripe border :data="tableData" :header-cell-style="{ background: '#dddfe6',color: '#555960',textAlign: 'center' }" :cell-style="{ textAlign: 'center' }" height="600px" @row-click="openModal"> <el-table ref="tableData" stripe border :data="tableData" :header-cell-style="{ background: '#dddfe6',color: '#555960',textAlign: 'center' }" :cell-style="{ textAlign: 'center' }" height="600px">
<el-table-column <el-table-column
type="index" type="index"
width="50" width="50"
/> />
<el-table-column prop="userName" label="用户名" /> <el-table-column prop="optr_name" label="操作员名称" />
<el-table-column prop="name" label="姓名" /> <el-table-column prop="login_name" label="登录名称" />
<el-table-column prop="sex" label="性别" /> <el-table-column prop="optr_alias" label="别名" />
<el-table-column prop="phone" label="电话" /> <el-table-column prop="optr_sex" label="性别" />
<el-table-column prop="address" label="邮箱" /> <el-table-column prop="optr_status" label="状态" />
<el-table-column prop="unit" label="单位" /> <el-table-column prop="optr_email" label="邮箱" />
<el-table-column prop="createTime" label="创建时间" /> <el-table-column prop="optr_mobile" label="联系电话" />
<el-table-column prop="optr_tel" label="办公电话" />
<el-table-column prop="role_names" label="角色" />
<el-table-column label="操作" width="200"> <el-table-column label="操作" width="200">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="修改" placement="top-end"> <el-tooltip class="item" effect="dark" content="修改" placement="top-end">
<el-button id="mod" size="mini" type="warning" class="el-icon-edit" /> <el-button size="mini" type="warning" class="el-icon-edit" @click="handleEdit(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip class="item" effect="dark" content="删除" placement="top-end"> <el-tooltip class="item" effect="dark" content="删除" placement="top-end">
<el-button id="del" size="mini" type="danger" class="el-icon-delete" /> <el-button size="mini" type="danger" class="el-icon-delete" @click="handleDelete(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip class="item" effect="dark" content="授权" placement="top-end"> <el-tooltip class="item" effect="dark" content="分配航次" placement="top-end">
<el-button id="permission" size="mini" type="warning" class="el-icon-orange" /> <el-button size="mini" type="primary" class="el-icon-s-unfold" @click="handleReg(scope.row)" />
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
@ -42,54 +44,64 @@
<div class="pagination-box"> <div class="pagination-box">
<el-pagination <el-pagination
background background
:current-page="currentPage" :page-sizes="[10, 20, 30]"
:page-sizes="[15, 30, 50]" :page-size="params.limit"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
:total="count" :total="total"
:current-page="pageNum"
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
</div> </div>
<div class="modal"> <div class="modal">
<!-- 新增编辑用户弹窗-->
<el-dialog <el-dialog
:title="userStatus? '添加用户' : '修改用户'" :title="title"
:visible.sync="showUserForm" :visible.sync="open"
width="30%" width="30%"
:before-close="handleClose" :before-close="cancel"
> >
<el-form ref="form" :model="form" label-width="140px" :rules="rules"> <el-form ref="form" :model="form" label-width="140px" :rules="rules">
<el-form-item label="用户名" prop="userName"> <el-form-item label="操作员名称" prop="optr_name">
<el-input v-model="form.userName" size="medium" auto-complete="off" placeholder="请输入" /> <el-input v-model="form.optr_name" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item> </el-form-item>
<el-form-item label="姓名" prop="name"> <el-form-item label="登录名称" prop="login_name">
<el-input v-model="form.name" size="medium" auto-complete="off" placeholder="请输入" /> <el-input v-model="form.login_name" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item> </el-form-item>
<el-form-item label="电话" prop="phone"> <el-form-item label="登录密码" prop="login_pass">
<el-input v-model="form.phone" size="medium" auto-complete="off" placeholder="请输入" /> <el-input v-model="form.login_pass" size="medium" auto-complete="off" placeholder="请输入" show-password />
</el-form-item> </el-form-item>
<el-form-item label="邮箱" prop="address"> <el-form-item label="别名" prop="optr_alias">
<el-input v-model="form.address" size="medium" auto-complete="off" placeholder="请输入" /> <el-input v-model="form.optr_alias" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item> </el-form-item>
<el-form-item label="性别" prop="sex"> <el-form-item label="邮箱" prop="optr_email">
<el-select v-model="form.sex" placeholder="请选择" size="medium"> <el-input v-model="form.optr_email" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item>
<el-form-item label="联系电话" prop="optr_mobile">
<el-input v-model="form.optr_mobile" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item>
<el-form-item label="办公电话" prop="optr_tel">
<el-input v-model="form.optr_tel" size="medium" auto-complete="off" placeholder="请输入" />
</el-form-item>
<el-form-item label="角色" prop="role_ids">
<el-select v-model="form.role_ids" placeholder="请选择" size="medium">
<el-option <el-option
v-for="item in sexOption" v-for="item in roleOptions"
:key="item.value" :key="item.role_id"
:label="item.label" :label="item.role_name"
:value="item.value" :value="item.role_id"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="单位" prop="unit"> <el-form-item label="性别" prop="optr_sex">
<el-select v-model="form.unit" placeholder="请选择" size="medium"> <el-radio-group v-model="form.optr_sex">
<el-option <el-radio v-for="(item,index) in frameSex" :key="index" :label="item.dict_value">{{ item.dict_text }}</el-radio>
v-for="item in companyOption" </el-radio-group>
:key="item.value" </el-form-item>
:label="item.label" <el-form-item label="状态" prop="optr_status">
:value="item.value" <el-radio-group v-model="form.optr_status">
/> <el-radio v-for="(item,index) in frameStatus" :key="index" :label="item.dict_value">{{ item.dict_text }}</el-radio>
</el-select> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
@ -97,347 +109,279 @@
style="width: 100px" style="width: 100px"
type="primary" type="primary"
size="medium" size="medium"
@click="saveData" @click="submitForm"
>确定</el-button> >确定</el-button>
<el-button <el-button
size="medium" size="medium"
style="margin-left: 10px; width: 100px" style="margin-left: 10px; width: 100px"
type="info" type="info"
plain plain
@click="showUserForm = false;clearInput();resetForm();" @click="cancel"
>取消</el-button> >取消</el-button>
</el-row> </el-row>
</el-dialog> </el-dialog>
<!-- 航次分配弹窗-->
<el-dialog <el-dialog
title="用户授权" title="航次列表"
:visible.sync="showAuthorForm" :visible.sync="openReg"
width="30%" width="80%"
:before-close="handleCloseAuthor" :before-close="cancelReg"
> >
<el-form ref="form2" :model="form" label-width="10px" :rules="rules"> <div class="top-btns">
<el-form-item label="" prop="sex"> <el-button size="mini" type="danger" @click="handleRegClear">清除</el-button>
<el-select v-model="author" multiple placeholder="请选择" size="medium" style="width:100%"> <el-button size="mini" type="primary" :disabled="multipleSelection.length === 0" @click="handleRegGrant">分配</el-button>
<el-option </div>
v-for="item in authorOption" <el-table ref="tableData" v-loading="regLoading" stripe border :data="regData" max-height="500" @selection-change="handleSelectionChange" :header-cell-style="{ background: '#dddfe6',color: '#555960',textAlign: 'center' }" :cell-style="{ textAlign: 'center' }">
:key="item.value" <el-table-column type="selection" width="55" />
:label="item.label" <el-table-column prop="voyage_name" label="航次名称" />
:value="item.value" <el-table-column prop="ship_name" label="科考船舶" />
/> <el-table-column prop="voyage_start_date" label="航次开始">
</el-select> <template slot-scope="scope">
</el-form-item> <span>{{ scope.row.voyage_start_date ? scope.row.voyage_start_date.slice(0,10) : '' }}</span>
</el-form> </template>
<el-row type="flex" justify="center"> </el-table-column>
<el-button <el-table-column prop="voyage_end_date" label="航次结束">
style="width: 100px" <template slot-scope="scope">
type="primary" <span>{{ scope.row.voyage_end_date ? scope.row.voyage_end_date.slice(0,10) : '' }}</span>
size="medium" </template>
@click="saveAuthorData" </el-table-column>
>确定</el-button> <el-table-column prop="voyage_officer" label="首席" />
<el-button <el-table-column prop="member_total" label="参航人数" />
size="medium" <el-table-column prop="funding_org" label="资助机构" />
style="margin-left: 10px; width: 100px" <el-table-column prop="funding_code" label="资助编号" />
type="info" </el-table>
plain
@click="showAuthorForm = false;resetForm2();"
>取消</el-button>
</el-row>
</el-dialog> </el-dialog>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { roleList, optrAdd, optrEdit, optrList, optrDelete, regList, regGrant, regClear } from '../../api/sysManage'
import { initInfo } from '../../api/login'
export default { export default {
name: 'UserManage', name: 'UserManage',
data() { data() {
return { return {
// loading: false,
userStatus: true, //
// model params: {
recordEnabled: 0, start: 0,
showUserForm: false, limit: 10,
showAuthorForm: false, group: 10,
delDialogVisible: false, query: ''
currentPage: 1, },
pagesize: 15, pageNum: 1,
count: 0, //
tableData: [],
total: 0,
//
open: false,
title: '新增',
form: { form: {
userName: '', optr_dept: 0,
name: '', optr_name: '',
phone: '', login_name: '',
address: '', login_pass: '',
unit: '' optr_alias: '',
optr_sex: '',
optr_status: '',
optr_email: '',
optr_mobile: '',
optr_tel: '',
role_ids: ''
}, },
rules: { rules: {
userName: [ optr_name: [
{ required: true, message: '请输入用户名', trigger: 'blur' } { required: true, message: '请输入操作员名称', trigger: 'blur' }
], ],
name: [ login_name: [
{ required: true, message: '请输入姓名', trigger: 'blur' } { required: true, message: '请输入登录名称', trigger: 'blur' }
], ],
unit: [ login_pass: [
{ required: true, message: '请输入单位', trigger: 'blur' } { required: true, message: '请输入登录密码', trigger: 'blur' }
],
optr_alias: [
{ required: true, message: '请输入别名', trigger: 'blur' }
],
optr_sex: [
{ required: true, message: '请选择操作员性别', trigger: 'change' }
],
optr_status: [
{ required: true, message: '请选择操作员状态', trigger: 'change' }
],
optr_email: [
{ required: true, message: '请输入电子邮件地址', trigger: 'blur' }
],
optr_mobile: [
{ required: true, message: '请输入联系电话', trigger: 'blur' }
],
optr_tel: [
{ required: true, message: '请输入办公电话', trigger: 'blur' }
],
role_ids: [
{ required: true, message: '请选择角色', trigger: 'change' }
] ]
}, },
author: ['系统管理员'], frameSex: [],
tableData: [ frameStatus: [],
{ roleOptions: [],
userName: 'admin', regLoading: false,
name: '管理员', openReg: false,
sex: '男', regData: [],
phone: '13827182976', multipleSelection: [],
address: '134@163.com', voyageIds: [],
unit: '深海所', currentUserId: ''
createTime: '2022.3.4 12: 20: 00'
},
{
userName: 'user',
name: 'USER',
sex: '男',
phone: '13827182976',
address: '134@163.com',
unit: '深海所',
createTime: '2022.3.4 12: 20: 00'
},
{
userName: 'user1',
name: 'USER1',
sex: '男',
phone: '13827182976',
address: '134@163.com',
unit: '深海所',
createTime: '2022.3.4 12: 20: 00'
}
],
companyOption: [
{
label: '深海所',
value: '深海所'
},
{
label: '机构B',
value: '机构B'
}
],
sexOption: [
{
label: '男',
value: '男'
},
{
label: '女',
value: '女'
}
],
authorOption: [
{
label: '超级管理员',
value: '超级管理员'
},
{
label: '系统管理员',
value: '系统管理员'
},
{
label: '普通用户',
value: '普通用户'
}
]
} }
}, },
created() { created() {
const isLogin = localStorage.getItem('isLogin') this.getFrameDictArray()
if (isLogin == 'false' || isLogin == '' || isLogin == undefined) { this.getRole()
this.$router.push({ name: 'home' }) this.getData()
return
}
// this.init()
const arr = [
{
key: 1,
value: 11
},
{
key: 2,
value: 22
},
{
key: 3,
value: 33
},
{
key: 4,
value: 44
},
{
key: 5,
value: 55
}
]
arr.map((item, index) => {
if (index < arr.length - 1) {
console.log(item, arr[index + 1])
}
})
}, },
methods: { methods: {
batchImport() {
this.showUserForm = true
this.userStatus = true
},
tableIndex(index) {
return (this.currentPage - 1) * this.pagesize + index + 1
},
openModal(row, event, column) {
//
var opType = column.target.id
if (opType == 'del') {
this.$confirm('确定删除选中的数据吗?此操作不可撤销!', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
this.deleteData(row.id)
})
.catch((error) => {
console.log(error)
})
} else if (opType == 'mod') {
this.showUserForm = true
this.userStatus = false
this.form = JSON.parse(JSON.stringify(row))
} else if (opType == 'permission') {
this.showAuthorForm = true
}
},
async deleteData(id) {
this.loading = true
const data = {
id
}
try {
const res = await deletePlanPage(data)
if (res.code == 2000) {
this.init()
}
} catch (error) {
console.log(error)
this.loading = false
}
},
init() {
this.currentPage = 1
this.getData()
},
// currentPagepagesizedata
handleSizeChange: function(size) {
this.pagesize = size
this.getData()
},
handleCurrentChange: function(currentPage) {
this.currentPage = currentPage
this.getData()
},
fuzzyQueryFunction() {
this.currentPage = 1
this.getData()
},
reset() {
this.queryName = ''
},
getData() { getData() {
// this.loading = true
this.$http optrList(this.params).then(res => {
.post(process.env.baseUrl + '/node/findPage', { pageRequest: { pageNum: this.currentPage, pageSize: this.pagesize }, queryName: this.queryName }) this.tableData = res.page.records
.then(res => { this.total = Number(res.page.total)
if (res.code == 2000) { // 1 this.loading = false
this.tableData = res.data.page.content })
this.count = res.data.page.totalSize
} else {
this.$message.error(res.msg) //
}
})
}, },
saveData() { //
this.$refs['form'].validate((valid) => { handleAdd() {
this.title = '新增'
this.open = true
},
//
handleEdit(row) {
this.title = '编辑'
this.open = true
this.form = JSON.parse(JSON.stringify(row))
this.form.role_ids = Number(this.form.role_id)
},
//
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) { if (valid) {
if (this.showStatus) { if (this.title === '新增') {
this.addFunction() optrAdd(this.form).then(res => {
this.$message.success('已添加新用户!')
this.getData()
this.cancel()
})
} else { } else {
this.updateFunction() optrEdit(this.form).then(res => {
this.$message.success('用户信息已更新!')
this.getData()
this.cancel()
})
} }
} else {
return false
} }
}) })
}, },
saveAuthorData() { //
this.showAuthorForm = false cancel() {
this.open = false
this.reset()
}, },
async addFunction() { cancelReg() {
this.loading = true this.openReg = false
const data = {
deviceId: this.form.deviceId,
deviceName: this.form.deviceName,
description: this.form.description,
// userId: 123456,
userName: this.$store.state.user.user
}
try {
const res = await addPlanPage(data)
if (res.code === 2000) {
this.$emit('handleConfirm')
}
} catch (error) {
console.log(error)
}
this.loading = false
}, },
async updateFunction() { //
this.loading = true reset() {
const data = { this.form = {
deviceId: this.form.deviceId, optr_dept: 0,
deviceName: this.form.deviceName, optr_name: '',
description: this.form.description, login_name: '',
// userId: 123456, login_pass: '',
userName: this.$store.state.user.user, optr_alias: '',
id: Number(this.id) optr_sex: '',
optr_status: '',
optr_email: '',
optr_mobile: '',
optr_tel: '',
role_ids: ''
} }
try {
const res = await updatePlanPage(data)
if (res.code === 2000) {
this.$emit('handleConfirm')
}
} catch (error) {
console.log(error)
}
this.loading = false
},
resetForm() {
this.$refs['form'].resetFields() this.$refs['form'].resetFields()
}, },
resetForm2() { getFrameDictArray() {
this.$refs['form2'].resetFields() initInfo().then(res => {
const frameDictArray = res.map.FrameDictArray
this.frameStatus = this.filterDictItems(frameDictArray, "FrameStatus")
this.frameSex = this.filterDictItems(frameDictArray, "FrameSex")
})
}, },
handleClose(done) { // filterDictItems(data, dictName) {
this.clearInput() return data.filter(item => item.dict_name === dictName)
this.resetForm()
done()
}, },
handleCloseAuthor(done) { getRole() {
done() roleList().then(res => {
this.roleOptions = res.page.records
})
}, },
clearInput() { //
this.form = { handleDelete(row) {
userName: '', this.$confirm('确定要删除该用户吗?', '提示', {
name: '', confirmButtonText: '确定',
phone: '', cancelButtonText: '取消',
address: '', type: 'warning'
unit: '' }).then(() => {
} optrDelete(row).then(() => {
this.$message.success('删除成功!')
this.getData()
})
}).catch(() => {})
},
//
handleCurrentChange(val) {
this.pageNum = val
this.params.start = val * this.params.limit - this.params.limit
this.getData()
},
handleSizeChange(val) {
this.params.limit = val
this.getData()
},
//
handleReg(row) {
this.openReg = true
this.regLoading = true
this.currentUserId = row.optr_id
regList().then(res => {
this.regData = res.array
this.regLoading = false
})
},
//
handleRegClear() {
regClear({ optr_id: this.currentUserId }).then(res => {
this.$message.success('清除成功!')
})
},
handleSelectionChange(val) {
this.multipleSelection = val
this.voyageIds = val.map(item => item.tsy_id)
console.log(val)
},
//
handleRegGrant() {
regGrant({
optr_id: this.currentUserId,
voyage_ids: this.voyageIds
}).then(res => {
this.$message.success('分配成功!')
})
} }
} }
} }
</script> </script>
<style lang="scss" scoped>
.top-btns {
margin-bottom: 10px;
text-align: right;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB