This commit is contained in:
80937518651731347 2026-04-07 08:27:40 +08:00
parent 104766dbe9
commit 9e56963d70
13 changed files with 497 additions and 197 deletions

View File

@ -17,8 +17,6 @@ module.exports = merge(prodEnv, {
// VUE_APP_API_URL: '"http://10.10.150.237/"' // VUE_APP_API_URL: '"http://10.10.150.237/"'
VUE_APP_BASE_API: '"/api"', VUE_APP_BASE_API: '"/api"',
/** 仅参航科学家报表;与生产一致直连 https服务端 CORS 已放开,避免本地代理 404 */
VUE_APP_UNIT_MEMBER_REPORT_BASE: '"https://weixin.bdsmart.cn/ds1"',
// VUE_APP_BASE_APIURL: '"http://ds2.hzzxq.com/api2"', // VUE_APP_BASE_APIURL: '"http://ds2.hzzxq.com/api2"',
VUE_APP_BASE_APIURL: '"http://test.gopmas.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"',

View File

@ -11,12 +11,12 @@ module.exports = {
// Paths // Paths
assetsSubDirectory: 'static', assetsSubDirectory: 'static',
assetsPublicPath: '/', assetsPublicPath: '/',
proxyTable: {
/** /**
* 参航科学家接口单独转发到 weixin须写在通用 /api 之前避免被旧后端吞掉 * 使用数组保证匹配顺序必须先于通配 `/api`否则 unit 会落到 gopmas 导致 404
* unitMember 使用 /ds1-report-proxy /api 时的兜底一致
*/ */
'/api/report/unit/member.htm': { proxyTable: [
{
context: '/api/report/unit/member.htm',
target: 'https://weixin.bdsmart.cn', target: 'https://weixin.bdsmart.cn',
changeOrigin: true, changeOrigin: true,
secure: true, secure: true,
@ -24,30 +24,34 @@ module.exports = {
'^/api': '/ds1' '^/api': '/ds1'
} }
}, },
'/api': { {
// target: 'http://120.48.105.88', // 线上 context: '/api/report/unit/total.htm',
// target: 'http://10.0.90.70', // 测试环境 target: 'https://weixin.bdsmart.cn',
// target: 'http://1.95.42.191:8080', // 测试环境 changeOrigin: true,
// target: 'http://ds2.hzzxq.com/api2', // 测试环境 secure: true,
pathRewrite: {
'^/api': '/ds1'
}
},
{
context: '/api',
target: 'http://test.gopmas.com/ds', // 测试环境 target: 'http://test.gopmas.com/ds', // 测试环境
changeOrigin: true, // 是否跨域 changeOrigin: true,
secure: false, // 是否使用https secure: false,
pathRewrite: { pathRewrite: {
'^/api': '' // 线上 '^/api': ''
// '^/service': '/service' //线上
} }
}, },
'/geoserver': { {
// target: 'http://120.48.105.88', // 线上 context: '/geoserver',
target: 'http://124.222.8.13:9801', // 测试环境 target: 'http://124.222.8.13:9801', // 测试环境
changeOrigin: true, // 是否跨域 changeOrigin: true,
secure: false, // 是否使用https secure: false,
pathRewrite: { pathRewrite: {
'^/geoserver': '/geoserver' // 线上 '^/geoserver': '/geoserver'
// '^/service': '/service' //线上
} }
} }
}, ],
// Various Dev Server settings // Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST host: 'localhost', // can be overwritten by process.env.HOST

View File

@ -16,8 +16,6 @@ module.exports = {
// VUE_APP_BASE_API: '"/api"', // VUE_APP_BASE_API: '"/api"',
// VUE_APP_BASE_APIURL: '"http://ds.hzzxq.com/api"', // VUE_APP_BASE_APIURL: '"http://ds.hzzxq.com/api"',
VUE_APP_BASE_API: '"./"', VUE_APP_BASE_API: '"./"',
/** 仅参航科学家报表 report/unit/member.htm 使用,与全局 VUE_APP_BASE_API 解耦 */
VUE_APP_UNIT_MEMBER_REPORT_BASE: '"https://weixin.bdsmart.cn/ds1"',
VUE_APP_BASE_APIURL: '"./"', VUE_APP_BASE_APIURL: '"./"',
VUE_APP_FULL_API: '"http://10.10.151.3:5555"', VUE_APP_FULL_API: '"http://10.10.151.3: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"',

BIN
dist.zip

Binary file not shown.

View File

@ -10,6 +10,20 @@ export function reportList(data) {
}) })
} }
/**
* 按平台类型查询平台名称列表统计页 banner 下拉
* 对应 `GET /report/platform/name.htm?platform_type=...`
* @param {string} [platformType='水下自主式无人潜器'] 与后端 platform_type 一致
* @returns {Promise}
*/
export function platformNameList(platformType = '水下自主式无人潜器') {
return request({
url: '/report/platform/name.htm',
method: 'get',
params: { platform_type: platformType }
})
}
// 科考船汇总统计属性 // 科考船汇总统计属性
export function shipTotal(data) { export function shipTotal(data) {
return request({ return request({
@ -166,47 +180,31 @@ export function platformRov(data) {
}) })
} }
/**
* ds1 报表独立域名参航科学家/单位汇总等与全局 VUE_APP_BASE_API 解耦
* @returns {string}
*/
function getDs1ReportBase() {
return (
process.env.VUE_APP_UNIT_MEMBER_REPORT_BASE ||
'https://weixin.bdsmart.cn/ds1'
)
}
/** /**
* 单位维度参航科学家明细按科学家汇总 * 单位维度参航科学家明细按科学家汇总
* 路径与其它报表一致`/report/unit/member.htm` `VUE_APP_BASE_API`生产 `./`解析
* @param {Object} data 请求体需包含 unit单位标识 * @param {Object} data 请求体需包含 unit单位标识
* @returns {Promise} * @returns {Promise}
*/ */
export function unitMember(data) { export function unitMember(data) {
return request({ return request({
baseURL: getDs1ReportBase(),
url: '/report/unit/member.htm', url: '/report/unit/member.htm',
method: 'post', method: 'post',
type: 'application/x-www-form-urlencoded', type: 'application/x-www-form-urlencoded',
/** 跨域且服务端返回 Access-Control-Allow-Origin: * 时不可带 cookie否则浏览器会拦截 */
withCredentials: false,
data data
}) })
} }
/** /**
* 单位维度汇总统计按单位一行含航次天数人数深潜等 * 单位维度汇总统计按单位一行含航次天数人数深潜等
* unitMember 共用 ds1 baseURL
* @param {Object} [data] 可选 unit 等筛选参数空对象表示全部单位 * @param {Object} [data] 可选 unit 等筛选参数空对象表示全部单位
* @returns {Promise} * @returns {Promise}
*/ */
export function unitTotal(data = {}) { export function unitTotal(data = {}) {
return request({ return request({
baseURL: getDs1ReportBase(),
url: '/report/unit/total.htm', url: '/report/unit/total.htm',
method: 'post', method: 'post',
type: 'application/x-www-form-urlencoded', type: 'application/x-www-form-urlencoded',
withCredentials: false,
data data
}) })
} }

View File

@ -29,7 +29,7 @@
</div> </div>
<div class="bot_right"> <div class="bot_right">
<div class="right1"> <div class="right1">
<p>依托两器船等各类海洋科考平台设备开展航次科考</p> <p>依托两器船等各类海洋科考平台设备开展航次科考</p>
</div> </div>
<div class="right2"> <div class="right2">
<div class="right2_1"> <div class="right2_1">
@ -95,12 +95,12 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<div class="dongtai_top"> <div class="dongtai_top">
<img src="../../static/images/shuju3.png" alt=""> <span class="dongtai_title">航次数据更新动态</span>
</div> </div>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<div class="dongtai_top"> <div class="dongtai_top">
<img src="../../static/images/shuju4.png" alt=""> <span class="dongtai_title">数据样品引用动态</span>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
@ -1027,6 +1027,28 @@ export default {
img { img {
width: auto; width: auto;
} }
.dongtai_title {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 14px;
font-size: 38px;
font-weight: 600;
color: #ffffff;
letter-spacing: 2px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
&::before,
&::after {
content: '';
flex-shrink: 0;
width: 55px;
height: 3px;
border-radius: 1px;
background-color: rgba(255, 255, 255, 0.23);
}
}
} }
.dongtai_bot { .dongtai_bot {

View File

@ -17,7 +17,6 @@
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<p class="banner-title-picker__hint">点击选择</p>
</div> </div>
<div class="numbers"> <div class="numbers">
<div v-for="(item, index) in numbers" :key="index" class="item"> <div v-for="(item, index) in numbers" :key="index" class="item">
@ -65,12 +64,11 @@
<script> <script>
/** /**
* 无人航行器汇总统计页请求 `/report/platform/auv` 系列接口交互与遥控潜水器汇总页一致ECharts * 水下自主式无人潜器汇总统计页Banner 平台名来自 `/report/platform/name.htm`其余请求 `/report/platform/auv` 系列交互与遥控潜水器汇总页一致ECharts
*/ */
import BarChart from './components/BarChart.vue' import BarChart from './components/BarChart.vue'
import PieChart from './components/PieChart.vue' import PieChart from './components/PieChart.vue'
import { reportList, platformAuv, auvUnit, auvUnitYear, auvDiving } from '../../api/statistics' import { platformNameList, platformAuv, auvUnit, auvUnitYear, auvDiving } from '../../api/statistics'
import { filterDictItems } from '../../utils/index'
import { initMapbox, handleWheel, initSprites, triggerLayerClick } from "@/utils/mapbox-utils"; import { initMapbox, handleWheel, initSprites, triggerLayerClick } from "@/utils/mapbox-utils";
import { loadVectorLayer, loadJsonLineFeature, loadJsonPointFeature, highlightFeaturesByProperty } from '@/utils/vector-layer-utils' import { loadVectorLayer, loadJsonLineFeature, loadJsonPointFeature, highlightFeaturesByProperty } from '@/utils/vector-layer-utils'
import { ColorGenerator } from '@/utils/color-generator'; import { ColorGenerator } from '@/utils/color-generator';
@ -151,10 +149,26 @@ export default {
} }
}, },
methods: { methods: {
// /**
* Banner 下拉选项`/report/platform/name.htm?platform_type=水下自主式无人潜器`
*/
getTypeList() { getTypeList() {
reportList().then(res => { platformNameList().then(res => {
this.titles = filterDictItems(res.array, '无人航行潜水器') const raw = res.array || []
this.titles = raw
.map((item, idx) => {
if (typeof item === 'string') {
return { tsy_id: `auv-name-${idx}`, report_name: item }
}
const name =
item.platform_name != null
? item.platform_name
: item.report_name != null ? item.report_name : ''
const id =
item.tsy_id != null ? item.tsy_id : item.id != null ? item.id : `auv-name-${idx}`
return { tsy_id: id, report_name: name }
})
.filter(i => i.report_name)
}) })
}, },
// //
@ -361,16 +375,6 @@ export default {
color: #ffffff; color: #ffffff;
} }
.banner-title-picker__hint {
margin: 14px 0 0;
padding: 0;
font-size: 14px;
font-weight: 400;
letter-spacing: 0.12em;
color: rgba(255, 255, 255, 0.78);
line-height: 1.4;
}
.numbers { .numbers {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -463,17 +467,71 @@ export default {
<style lang="scss"> <style lang="scss">
.banner-type-dropdown-popper.el-dropdown-menu { .banner-type-dropdown-popper.el-dropdown-menu {
min-width: 240px; min-width: 260px;
max-height: #{"min(60vh, 420px)"}; max-height: #{"min(60vh, 420px)"};
overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
padding: 6px 0; padding: 8px;
border-radius: 8px; border-radius: 14px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35); border: 1px solid rgba(255, 255, 255, 0.38);
background: linear-gradient(
155deg,
rgba(255, 255, 255, 0.48) 0%,
rgba(248, 252, 255, 0.28) 50%,
rgba(255, 255, 255, 0.16) 100%
);
backdrop-filter: blur(22px) saturate(175%);
-webkit-backdrop-filter: blur(22px) saturate(175%);
box-shadow:
0 4px 6px rgba(0, 0, 0, 0.05),
0 18px 48px rgba(10, 40, 90, 0.16),
inset 0 1px 0 rgba(255, 255, 255, 0.42);
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
margin: 6px 0;
background: rgba(255, 255, 255, 0.14);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: rgba(10, 132, 193, 0.38);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb:hover {
background: rgba(10, 132, 193, 0.55);
}
} }
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item { .banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item {
font-size: 16px; font-size: 18px;
line-height: 22px; font-weight: 500;
padding: 14px 22px; letter-spacing: 0.03em;
line-height: 1.45;
padding: 12px 18px;
margin: 2px 0;
border-radius: 10px;
color: rgba(28, 45, 63, 0.95);
transition:
background 0.2s ease,
color 0.2s ease;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):hover {
background: rgba(10, 132, 193, 0.2);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:focus {
background: rgba(10, 132, 193, 0.17);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item.is-disabled {
color: rgba(28, 45, 63, 0.35);
} }
</style> </style>

View File

@ -17,7 +17,6 @@
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<p class="banner-title-picker__hint">点击选择</p>
</div> </div>
<div class="numbers"> <div class="numbers">
<div v-for="(item, index) in numbers" :key="index" class="item"> <div v-for="(item, index) in numbers" :key="index" class="item">
@ -361,16 +360,6 @@ export default {
color: #ffffff; color: #ffffff;
} }
.banner-title-picker__hint {
margin: 14px 0 0;
padding: 0;
font-size: 14px;
font-weight: 400;
letter-spacing: 0.12em;
color: rgba(255, 255, 255, 0.78);
line-height: 1.4;
}
.numbers { .numbers {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -463,17 +452,71 @@ export default {
<style lang="scss"> <style lang="scss">
.banner-type-dropdown-popper.el-dropdown-menu { .banner-type-dropdown-popper.el-dropdown-menu {
min-width: 240px; min-width: 260px;
max-height: #{"min(60vh, 420px)"}; max-height: #{"min(60vh, 420px)"};
overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
padding: 6px 0; padding: 8px;
border-radius: 8px; border-radius: 14px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35); border: 1px solid rgba(255, 255, 255, 0.38);
background: linear-gradient(
155deg,
rgba(255, 255, 255, 0.48) 0%,
rgba(248, 252, 255, 0.28) 50%,
rgba(255, 255, 255, 0.16) 100%
);
backdrop-filter: blur(22px) saturate(175%);
-webkit-backdrop-filter: blur(22px) saturate(175%);
box-shadow:
0 4px 6px rgba(0, 0, 0, 0.05),
0 18px 48px rgba(10, 40, 90, 0.16),
inset 0 1px 0 rgba(255, 255, 255, 0.42);
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
margin: 6px 0;
background: rgba(255, 255, 255, 0.14);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: rgba(10, 132, 193, 0.38);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb:hover {
background: rgba(10, 132, 193, 0.55);
}
} }
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item { .banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item {
font-size: 16px; font-size: 18px;
line-height: 22px; font-weight: 500;
padding: 14px 22px; letter-spacing: 0.03em;
line-height: 1.45;
padding: 12px 18px;
margin: 2px 0;
border-radius: 10px;
color: rgba(28, 45, 63, 0.95);
transition:
background 0.2s ease,
color 0.2s ease;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):hover {
background: rgba(10, 132, 193, 0.2);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:focus {
background: rgba(10, 132, 193, 0.17);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item.is-disabled {
color: rgba(28, 45, 63, 0.35);
} }
</style> </style>

View File

@ -17,7 +17,6 @@
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<p class="banner-title-picker__hint">点击选择</p>
</div> </div>
<div class="numbers"> <div class="numbers">
<div v-for="(item, index) in numbers" :key="index" class="item"> <div v-for="(item, index) in numbers" :key="index" class="item">
@ -360,16 +359,6 @@ export default {
color: #ffffff; color: #ffffff;
} }
.banner-title-picker__hint {
margin: 14px 0 0;
padding: 0;
font-size: 14px;
font-weight: 400;
letter-spacing: 0.12em;
color: rgba(255, 255, 255, 0.78);
line-height: 1.4;
}
.numbers { .numbers {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -462,17 +451,71 @@ export default {
<style lang="scss"> <style lang="scss">
.banner-type-dropdown-popper.el-dropdown-menu { .banner-type-dropdown-popper.el-dropdown-menu {
min-width: 240px; min-width: 260px;
max-height: #{"min(60vh, 420px)"}; max-height: #{"min(60vh, 420px)"};
overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
padding: 6px 0; padding: 8px;
border-radius: 8px; border-radius: 14px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35); border: 1px solid rgba(255, 255, 255, 0.38);
background: linear-gradient(
155deg,
rgba(255, 255, 255, 0.48) 0%,
rgba(248, 252, 255, 0.28) 50%,
rgba(255, 255, 255, 0.16) 100%
);
backdrop-filter: blur(22px) saturate(175%);
-webkit-backdrop-filter: blur(22px) saturate(175%);
box-shadow:
0 4px 6px rgba(0, 0, 0, 0.05),
0 18px 48px rgba(10, 40, 90, 0.16),
inset 0 1px 0 rgba(255, 255, 255, 0.42);
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
margin: 6px 0;
background: rgba(255, 255, 255, 0.14);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: rgba(10, 132, 193, 0.38);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb:hover {
background: rgba(10, 132, 193, 0.55);
}
} }
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item { .banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item {
font-size: 16px; font-size: 18px;
line-height: 22px; font-weight: 500;
padding: 14px 22px; letter-spacing: 0.03em;
line-height: 1.45;
padding: 12px 18px;
margin: 2px 0;
border-radius: 10px;
color: rgba(28, 45, 63, 0.95);
transition:
background 0.2s ease,
color 0.2s ease;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):hover {
background: rgba(10, 132, 193, 0.2);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:focus {
background: rgba(10, 132, 193, 0.17);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item.is-disabled {
color: rgba(28, 45, 63, 0.35);
} }
</style> </style>

View File

@ -17,7 +17,6 @@
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<p class="banner-title-picker__hint">点击选择</p>
</div> </div>
<div class="numbers"> <div class="numbers">
<div v-for="(item, index) in numbers" :key="index" class="item"> <div v-for="(item, index) in numbers" :key="index" class="item">
@ -383,16 +382,6 @@ export default {
color: #ffffff; color: #ffffff;
} }
.banner-title-picker__hint {
margin: 14px 0 0;
padding: 0;
font-size: 14px;
font-weight: 400;
letter-spacing: 0.12em;
color: rgba(255, 255, 255, 0.78);
line-height: 1.4;
}
.numbers { .numbers {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -549,17 +538,71 @@ export default {
<style lang="scss"> <style lang="scss">
.banner-type-dropdown-popper.el-dropdown-menu { .banner-type-dropdown-popper.el-dropdown-menu {
min-width: 240px; min-width: 260px;
max-height: #{"min(60vh, 420px)"}; max-height: #{"min(60vh, 420px)"};
overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
padding: 6px 0; padding: 8px;
border-radius: 8px; border-radius: 14px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35); border: 1px solid rgba(255, 255, 255, 0.38);
background: linear-gradient(
155deg,
rgba(255, 255, 255, 0.48) 0%,
rgba(248, 252, 255, 0.28) 50%,
rgba(255, 255, 255, 0.16) 100%
);
backdrop-filter: blur(22px) saturate(175%);
-webkit-backdrop-filter: blur(22px) saturate(175%);
box-shadow:
0 4px 6px rgba(0, 0, 0, 0.05),
0 18px 48px rgba(10, 40, 90, 0.16),
inset 0 1px 0 rgba(255, 255, 255, 0.42);
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
margin: 6px 0;
background: rgba(255, 255, 255, 0.14);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: rgba(10, 132, 193, 0.38);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb:hover {
background: rgba(10, 132, 193, 0.55);
}
} }
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item { .banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item {
font-size: 16px; font-size: 18px;
line-height: 22px; font-weight: 500;
padding: 14px 22px; letter-spacing: 0.03em;
line-height: 1.45;
padding: 12px 18px;
margin: 2px 0;
border-radius: 10px;
color: rgba(28, 45, 63, 0.95);
transition:
background 0.2s ease,
color 0.2s ease;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):hover {
background: rgba(10, 132, 193, 0.2);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:focus {
background: rgba(10, 132, 193, 0.17);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item.is-disabled {
color: rgba(28, 45, 63, 0.35);
} }
</style> </style>

View File

@ -9,18 +9,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="unit-toolbar">
<div class="unit-toolbar-inner">
<div class="unit-filter-box">
<span class="banner-unit-label">统计单位</span>
<el-select v-model="selectedUnits" class="banner-unit-select" multiple filterable clearable size="small"
placeholder="请选择单位(不选表示全部)" value-key="unit_name" collapse-tags @change="onBannerUnitsChange">
<el-option v-for="unit in unitOptions" :key="unit.unit_id || unit.unit_name" :label="unit.unit_name"
:value="unit" />
</el-select>
</div>
</div>
</div>
<!-- 地图板块与科考船汇总统计页 ships 一致Mapbox + shipUnit + shipVoyage --> <!-- 地图板块与科考船汇总统计页 ships 一致Mapbox + shipUnit + shipVoyage -->
<div class="map-module"> <div class="map-module">
<div class="bezel"> <div class="bezel">
@ -37,27 +25,57 @@
<div id="voyageMap_uuv" ref="voyageMap_uuv" style="height: 100%;width:100%;" /> <div id="voyageMap_uuv" ref="voyageMap_uuv" style="height: 100%;width:100%;" />
</div> --> </div> -->
</div> </div>
<div class="unit-toolbar">
<div class="unit-toolbar-inner">
<div class="unit-filter-box">
<span class="banner-unit-label">统计单位</span>
<el-select v-model="selectedUnits" class="banner-unit-select" multiple filterable clearable size="small"
placeholder="请选择单位(不选表示全部)" value-key="unit_name" collapse-tags @change="onBannerUnitsChange">
<el-option v-for="unit in unitOptions" :key="unit.unit_id || unit.unit_name" :label="unit.unit_name"
:value="unit" />
</el-select>
</div>
</div>
</div>
<!-- echarts板块--> <!-- echarts板块-->
<div class="echarts-module"> <div class="echarts-module">
<div class="bezel"> <div class="bezel">
<BarChart :title="barVoyageCount.title" :chart-data="barVoyageCount.salesData" <BarChart :title="barVoyageCount.title"
:categories="barVoyageCount.productCategories" chart-type="single" /> :chart-data="barVoyageCount.salesData"
:categories="barVoyageCount.productCategories"
chart-type="single"
:hide-x-axis-labels="true" />
</div> </div>
<div class="bezel"> <div class="bezel">
<BarChart :title="barVoyageDays.title" :chart-data="barVoyageDays.salesData" <BarChart :title="barVoyageDays.title"
:categories="barVoyageDays.productCategories" chart-type="single" /> :chart-data="barVoyageDays.salesData"
:categories="barVoyageDays.productCategories"
chart-type="single"
:hide-x-axis-labels="true" />
</div> </div>
<div class="bezel"> <div class="bezel">
<BarChart :title="barPeopleDiving.title" :chart-data="barPeopleDiving.salesData" <BarChart :title="barPeopleDiving.title"
:categories="barPeopleDiving.productCategories" chart-type="multiple" /> :chart-data="barPeopleDiving.salesData"
:categories="barPeopleDiving.productCategories"
chart-type="multiple"
:hide-x-axis-labels="true" />
</div> </div>
<div class="bezel"> <div class="bezel">
<BarChart :title="barFirstPeopleDiving.title" :chart-data="barFirstPeopleDiving.salesData" <BarChart :title="barFirstPeopleDiving.title"
:categories="barFirstPeopleDiving.productCategories" chart-type="multiple" /> :chart-data="barFirstPeopleDiving.salesData"
:categories="barFirstPeopleDiving.productCategories"
chart-type="multiple"
:hide-x-axis-labels="true" />
</div> </div>
<div class="bezel"> <div class="bezel">
<BarChart :title="mixedMileage.title" :chart-data="mixedMileage.series" :categories="mixedMileage.categories" <BarChart :title="mixedMileage.title"
chart-type="mixed" :dual-y-axis="true" left-axis-name="次数" right-axis-name="天数" /> :chart-data="mixedMileage.series"
:categories="mixedMileage.categories"
chart-type="mixed"
:dual-y-axis="true"
left-axis-name="次数"
right-axis-name="天数"
:hide-x-axis-labels="true" />
</div> </div>
</div> </div>
</div> </div>
@ -112,34 +130,34 @@ export default {
unit: '次' unit: '次'
} }
], ],
/** 次数(柱状) */ /** 航次数(柱状) */
barVoyageCount: { title: '次数', salesData: [], productCategories: [] }, barVoyageCount: { title: '航次数', salesData: [], productCategories: [] },
/** 参航天数(柱状) */ /** 参航天数(柱状) */
barVoyageDays: { title: '参航天数', salesData: [], productCategories: [] }, barVoyageDays: { title: '参航天数', salesData: [], productCategories: [] },
/** 参航人数、深潜次数(柱状,多系列) */ /** 参航人数、深潜次数(柱状,多系列) */
barPeopleDiving: { barPeopleDiving: {
title: '参航人数、深潜次数', title: '参航人数、深潜次数',
salesData: [ salesData: [
{ name: '参航人数', type: 'bar', data: [] }, { name: '参航人数', type: 'bar', data: [] },
{ name: '深潜次数', type: 'bar', data: [] } { name: '深潜次数', type: 'bar', data: [] }
], ],
productCategories: [] productCategories: []
}, },
/** 首次参航、首次深潜(柱状,多系列) */ /** 首次参航、首次深潜(柱状,多系列) */
barFirstPeopleDiving: { barFirstPeopleDiving: {
title: '首次参航人数、首次深潜数', title: '首次参航人数、首次深潜数',
salesData: [ salesData: [
{ name: '首次参航人数', type: 'bar', data: [] }, { name: '首次参航人数', type: 'bar', data: [] },
{ name: '首次深潜数', type: 'bar', data: [] } { name: '首次深潜数', type: 'bar', data: [] }
], ],
productCategories: [] productCategories: []
}, },
/** 次数(柱)+ 参航天数(折) */ /** 航次数(柱)+ 参航天数(折) */
mixedMileage: { mixedMileage: {
title: '次数与参航天数', title: '航次数与参航天数',
categories: [], categories: [],
series: [ series: [
{ name: '次数', type: 'bar', data: [], yAxisIndex: 0 }, { name: '航次数', type: 'bar', data: [], yAxisIndex: 0 },
{ name: '参航天数', type: 'line', data: [], yAxisIndex: 1 } { name: '参航天数', type: 'line', data: [], yAxisIndex: 1 }
] ]
} }
@ -235,29 +253,29 @@ export default {
this.numbers[0].value = '' this.numbers[0].value = ''
this.numbers[1].value = '' this.numbers[1].value = ''
this.numbers[2].value = '' this.numbers[2].value = ''
this.barVoyageCount = { title: '次数', salesData: [], productCategories: [] } this.barVoyageCount = { title: '航次数', salesData: [], productCategories: [] }
this.barVoyageDays = { title: '参航天数', salesData: [], productCategories: [] } this.barVoyageDays = { title: '参航天数', salesData: [], productCategories: [] }
this.barPeopleDiving = { this.barPeopleDiving = {
title: '参航人数、深潜次数', title: '参航人数、深潜次数',
salesData: [ salesData: [
{ name: '参航人数', type: 'bar', data: [] }, { name: '参航人数', type: 'bar', data: [] },
{ name: '深潜次数', type: 'bar', data: [] } { name: '深潜次数', type: 'bar', data: [] }
], ],
productCategories: [] productCategories: []
} }
this.barFirstPeopleDiving = { this.barFirstPeopleDiving = {
title: '首次参航人数、首次深潜数', title: '首次参航人数、首次深潜数',
salesData: [ salesData: [
{ name: '首次参航人数', type: 'bar', data: [] }, { name: '首次参航人数', type: 'bar', data: [] },
{ name: '首次深潜数', type: 'bar', data: [] } { name: '首次深潜数', type: 'bar', data: [] }
], ],
productCategories: [] productCategories: []
} }
this.mixedMileage = { this.mixedMileage = {
title: '次数与参航天数', title: '航次数与参航天数',
categories: [], categories: [],
series: [ series: [
{ name: '次数', type: 'bar', data: [], yAxisIndex: 0 }, { name: '航次数', type: 'bar', data: [], yAxisIndex: 0 },
{ name: '参航天数', type: 'line', data: [], yAxisIndex: 1 } { name: '参航天数', type: 'line', data: [], yAxisIndex: 1 }
] ]
} }
@ -278,7 +296,7 @@ export default {
this.numbers[2].value = sorted.reduce((s, r) => s + num(r.drving_total), 0) this.numbers[2].value = sorted.reduce((s, r) => s + num(r.drving_total), 0)
this.barVoyageCount = { this.barVoyageCount = {
title: '次数', title: '航次数',
salesData: voyage, salesData: voyage,
productCategories: names productCategories: names
} }
@ -288,26 +306,26 @@ export default {
productCategories: names productCategories: names
} }
this.barPeopleDiving = { this.barPeopleDiving = {
title: '参航人数、深潜次数', title: '参航人数、深潜次数',
salesData: [ salesData: [
{ name: '参航人数', type: 'bar', data: member }, { name: '参航人数', type: 'bar', data: member },
{ name: '深潜次数', type: 'bar', data: drving } { name: '深潜次数', type: 'bar', data: drving }
], ],
productCategories: names productCategories: names
} }
this.barFirstPeopleDiving = { this.barFirstPeopleDiving = {
title: '首次参航人数、首次深潜数', title: '首次参航人数、首次深潜数',
salesData: [ salesData: [
{ name: '首次参航人数', type: 'bar', data: fVoyage }, { name: '首次参航人数', type: 'bar', data: fVoyage },
{ name: '首次深潜数', type: 'bar', data: fDrving } { name: '首次深潜数', type: 'bar', data: fDrving }
], ],
productCategories: names productCategories: names
} }
this.mixedMileage = { this.mixedMileage = {
title: '次数与参航天数', title: '航次数与参航天数',
categories: names, categories: names,
series: [ series: [
{ name: '次数', type: 'bar', data: voyage, yAxisIndex: 0 }, { name: '航次数', type: 'bar', data: voyage, yAxisIndex: 0 },
{ name: '参航天数', type: 'line', data: days, yAxisIndex: 1 } { name: '参航天数', type: 'line', data: days, yAxisIndex: 1 }
] ]
} }
@ -506,10 +524,10 @@ export default {
} }
} }
/** Banner 下方:统计单位筛选条,整行容器 + 右侧对齐独立框 */ /** 地图卡片下方:统计单位筛选条,整行容器 + 右侧对齐独立框 */
.unit-toolbar { .unit-toolbar {
width: 100%; width: 100%;
padding: 8px 50px 8px; padding: 20px 50px 12px;
box-sizing: border-box; box-sizing: border-box;
background-color: #ebf1f7; background-color: #ebf1f7;
@ -561,7 +579,7 @@ export default {
} }
.map-module { .map-module {
padding: 50px 50px; padding: 50px 50px 0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 50px; gap: 50px;
@ -661,11 +679,10 @@ export default {
} }
.echarts-module { .echarts-module {
padding: 0 50px 50px; padding: 16px 50px 50px;
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: 1fr;
gap: 50px; row-gap: 32px;
grid-auto-rows: 300px;
.bezel { .bezel {
width: 100%; width: 100%;
@ -690,17 +707,71 @@ export default {
<style lang="scss"> <style lang="scss">
.banner-type-dropdown-popper.el-dropdown-menu { .banner-type-dropdown-popper.el-dropdown-menu {
min-width: 240px; min-width: 260px;
max-height: #{"min(60vh, 420px)"}; max-height: #{"min(60vh, 420px)"};
overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
padding: 6px 0; padding: 8px;
border-radius: 8px; border-radius: 14px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35); border: 1px solid rgba(255, 255, 255, 0.38);
background: linear-gradient(
155deg,
rgba(255, 255, 255, 0.48) 0%,
rgba(248, 252, 255, 0.28) 50%,
rgba(255, 255, 255, 0.16) 100%
);
backdrop-filter: blur(22px) saturate(175%);
-webkit-backdrop-filter: blur(22px) saturate(175%);
box-shadow:
0 4px 6px rgba(0, 0, 0, 0.05),
0 18px 48px rgba(10, 40, 90, 0.16),
inset 0 1px 0 rgba(255, 255, 255, 0.42);
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
margin: 6px 0;
background: rgba(255, 255, 255, 0.14);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: rgba(10, 132, 193, 0.38);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb:hover {
background: rgba(10, 132, 193, 0.55);
}
} }
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item { .banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item {
font-size: 16px; font-size: 18px;
line-height: 22px; font-weight: 500;
padding: 14px 22px; letter-spacing: 0.03em;
line-height: 1.45;
padding: 12px 18px;
margin: 2px 0;
border-radius: 10px;
color: rgba(28, 45, 63, 0.95);
transition:
background 0.2s ease,
color 0.2s ease;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):hover {
background: rgba(10, 132, 193, 0.2);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item:focus {
background: rgba(10, 132, 193, 0.17);
color: #0a84c1;
}
.banner-type-dropdown-popper.el-dropdown-menu .el-dropdown-menu__item.is-disabled {
color: rgba(28, 45, 63, 0.35);
} }
</style> </style>

View File

@ -53,6 +53,11 @@ export default {
type: Number, type: Number,
default: 300 default: 300
}, },
/** 是否隐藏 X 轴类目标签 */
hideXAxisLabels: {
type: Boolean,
default: false
},
// stacked // stacked
stackName: { stackName: {
type: String, type: String,
@ -80,6 +85,17 @@ export default {
window.removeEventListener('resize', this.handleResize); window.removeEventListener('resize', this.handleResize);
}, },
methods: { methods: {
/**
* Y 轴刻度与提示框数值按整数展示不出现小数点
* @param {number|string} value 原始刻度或数据值
* @returns {string}
*/
formatAxisInteger(value) {
const n = Number(value)
if (!Number.isFinite(n)) return String(value)
return String(Math.round(n))
},
initChart() { initChart() {
this.$nextTick(() => { this.$nextTick(() => {
this.myChart = echarts.init(this.$refs.chartContainer); this.myChart = echarts.init(this.$refs.chartContainer);
@ -165,6 +181,7 @@ export default {
// Y // Y
return [{ return [{
type: 'value', type: 'value',
minInterval: 1,
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
@ -175,7 +192,8 @@ export default {
show: false show: false
}, },
axisLabel: { axisLabel: {
color: '#666' color: '#666',
formatter: (v) => this.formatAxisInteger(v)
}, },
splitLine: { splitLine: {
lineStyle: { lineStyle: {
@ -192,6 +210,7 @@ export default {
type: 'value', type: 'value',
name: this.leftAxisName || '左轴', name: this.leftAxisName || '左轴',
position: 'left', position: 'left',
minInterval: 1,
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
@ -200,7 +219,7 @@ export default {
}, },
axisLabel: { axisLabel: {
color: '#666', color: '#666',
formatter: '{value}' formatter: (v) => this.formatAxisInteger(v)
}, },
splitLine: { splitLine: {
show: false show: false
@ -210,6 +229,7 @@ export default {
type: 'value', type: 'value',
name: this.rightAxisName || '右轴', name: this.rightAxisName || '右轴',
position: 'right', position: 'right',
minInterval: 1,
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
@ -218,7 +238,7 @@ export default {
}, },
axisLabel: { axisLabel: {
color: '#666', color: '#666',
formatter: '{value}' formatter: (v) => this.formatAxisInteger(v)
}, },
splitLine: { splitLine: {
show: false show: false
@ -365,6 +385,7 @@ export default {
show: false show: false
}, },
axisLabel: { axisLabel: {
show: !this.hideXAxisLabels,
color: '#666', color: '#666',
interval: 0, interval: 0,
rotate: this.categories.length > 6 ? 45 : 0, rotate: this.categories.length > 6 ? 45 : 0,
@ -385,7 +406,7 @@ export default {
params.forEach(param => { params.forEach(param => {
const color = param.color; const color = param.color;
const seriesName = param.seriesName; const seriesName = param.seriesName;
const value = param.value; const value = this.formatAxisInteger(param.value);
const marker = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${color};"></span>`; const marker = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${color};"></span>`;
result += ` result += `
@ -415,8 +436,9 @@ export default {
params.forEach(param => { params.forEach(param => {
const color = param.color; const color = param.color;
const seriesName = param.seriesName; const seriesName = param.seriesName;
const value = param.value; const value = this.formatAxisInteger(param.value);
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : 0; const raw = Number(param.value);
const percentage = total > 0 && Number.isFinite(raw) ? ((raw / 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>`; const marker = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${color};"></span>`;
result += ` result += `

View File

@ -168,7 +168,7 @@
<span class="stat-metric__label">{{ m.label }}</span> <span class="stat-metric__label">{{ m.label }}</span>
</div> </div>
</div> </div>
<p class="stat-card__fleet" :title="uuvNamesJoined">单位汇总与参航单位详情页一致 · 点击查看</p>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
@ -202,7 +202,7 @@
<span class="stat-metric__label">{{ m.label }}</span> <span class="stat-metric__label">{{ m.label }}</span>
</div> </div>
</div> </div>
<p class="stat-card__fleet" :title="uuvNamesJoined">默认单位中国科学院深海科学与工程研究所 · 点击查看详情</p>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
@ -411,7 +411,7 @@ export default {
return this.uuvScientistValue return this.uuvScientistValue
}, },
/** /**
* 参航科学家卡片三项指标 Uuv 详情页 banner 一致 * 参航科学家卡片三项指标
* @returns {Array<{ label: string, display: string }>} * @returns {Array<{ label: string, display: string }>}
*/ */
uuvScientistMetrics() { uuvScientistMetrics() {
@ -420,13 +420,13 @@ export default {
return [ return [
{ label: '科学家人数', display: '—' }, { label: '科学家人数', display: '—' },
{ label: '累计参加航次', display: '—' }, { label: '累计参加航次', display: '—' },
{ label: '累计参航天数', display: '—' } { label: '累计参加人·天', display: '—' }
] ]
} }
return [ return [
{ label: '科学家人数', display: this.formatMetric(c.scientistCount) + ' 人' }, { label: '科学家人数', display: this.formatMetric(c.scientistCount) + ' 人' },
{ label: '累计参加航次', display: this.formatMetric(c.sumVoyage) + ' 次' }, { label: '累计参加航次', display: this.formatMetric(c.sumVoyage) + ' 次' },
{ label: '累计参航天数', display: this.formatMetric(c.sumDays) + ' 天' } { label: '累计参加人·天', display: this.formatMetric(c.sumDays) + ' 人·天' }
] ]
} }
}, },