babyhealth/pages/businessDatas/portData.vue
2026-02-06 20:21:10 +08:00

811 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="data-detail-page" :class="{ mobile: isMobile }">
<!-- 页面头部 -->
<sub-header
title="港口数据"
@goBack="goBack"
@showMoreOptions="showMoreOptions"
/>
<!-- 数据卡片 -->
<view class="cards-section">
<view class="section-title">数据概览</view>
<view class="card-grid">
<view v-for="card in cardData" :key="card.id" class="data-card">
<view class="card-icon">
<i :class="card.icon"></i>
</view>
<view class="card-content">
<text class="card-value">{{ card.value }}</text>
<text class="card-label">{{ card.title }}</text>
</view>
</view>
</view>
</view>
<!-- 图表分析 -->
<view class="charts-section">
<view class="section-title">图表分析</view>
<view class="chart-tabs">
<view
class="chart-tab"
:class="{ active: activeTab === 'cargo' }"
@click="switchTab('cargo')"
>
船舶作业货物信息
</view>
<view
class="chart-tab"
:class="{ active: activeTab === 'customer' }"
@click="switchTab('customer')"
>
客户排名
</view>
<view
class="chart-tab"
:class="{ active: activeTab === 'location' }"
@click="switchTab('location')"
>
作业地点作业量
</view>
</view>
<view class="chart-content">
<!-- 船舶作业货物信息 -->
<view v-show="activeTab === 'cargo'" class="charts-container">
<view class="chartsMain">
<l-echart ref="chartRef" type="2d" :height="'auto'" />
</view>
<view class="chartsMain">
<l-echart ref="cargoChartRef" type="2d" :height="'auto'" />
</view>
</view>
<!-- 客户排名 -->
<view v-show="activeTab === 'customer'" class="charts-container">
<view class="chartsMain">
<l-echart ref="customerChartRef" type="2d" :height="'auto'" />
</view>
</view>
<!-- 作业地点作业量 -->
<view v-show="activeTab === 'location'" class="charts-container">
<view class="chartsMain">
<l-echart ref="locationChartRef" type="2d" :height="'auto'" />
</view>
</view>
</view>
</view>
<!-- 数据表格 -->
<view class="table-section">
<view class="section-title">详细数据</view>
<view class="table-container">
<view class="data-table">
<view class="table-header">
<view
v-for="header in tableHeaders"
:key="header.key"
class="table-cell header-cell"
>
{{ header.title }}
</view>
</view>
<view
v-for="(row, index) in tableData"
:key="index"
class="table-row"
>
<view
v-for="header in tableHeaders"
:key="header.key"
class="table-cell"
>
{{ row[header.key] }}
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, nextTick } from "vue";
import SubHeader from "../components/subHeader.vue";
import * as echarts from "echarts";
// 声明uni全局对象
declare const uni: any;
// 声明全局类型
declare global {
interface Window {
uni: any;
}
}
// 类型定义
interface CardData {
id: number;
title: string;
value: string;
icon: string;
}
interface TableHeader {
key: string;
title: string;
}
interface TableData {
port: string;
berth: string;
ship: string;
cargo: string;
weight: string;
status: string;
}
// 响应式数据
const pageTitle = ref("港口数据");
const activeTab = ref("cargo");
const chartRef = ref<any>(null);
const cargoChartRef = ref<any>(null);
const customerChartRef = ref<any>(null);
const locationChartRef = ref<any>(null);
// 设备信息
const isMobile = ref(false);
const cardData = ref<CardData[]>([
{ id: 1, title: "报港次数", value: "173次", icon: "fas fa-ship" },
{ id: 2, title: "完船次数", value: "197次", icon: "fas fa-anchor" },
{
id: 3,
title: "作业票作业量",
value: "260193.687吨",
icon: "fas fa-weight",
},
{
id: 4,
title: "船舶报港吨数",
value: "218928.400吨",
icon: "fas fa-ship",
},
{
id: 5,
title: "完船作业吨数",
value: "252643.653吨",
icon: "fas fa-anchor",
},
]);
const tableHeaders = ref<TableHeader[]>([
{ key: "port", title: "港口" },
{ key: "berth", title: "泊位" },
{ key: "ship", title: "船舶" },
{ key: "cargo", title: "货物" },
{ key: "weight", title: "重量(吨)" },
{ key: "status", title: "状态" },
]);
const tableData = ref<TableData[]>([
{
port: "上海港",
berth: "A1",
ship: "中远海运",
cargo: "集装箱",
weight: "50000",
status: "作业中",
},
{
port: "宁波港",
berth: "B2",
ship: "马士基",
cargo: "散货",
weight: "75000",
status: "已完成",
},
{
port: "青岛港",
berth: "C3",
ship: "长荣海运",
cargo: "油品",
weight: "30000",
status: "待作业",
},
]);
// 方法
const goBack = () => {
uni.navigateBack();
};
const showMoreOptions = () => {
uni.showActionSheet({
itemList: ["刷新", "返回首页"],
success: (res) => {
switch (res.tapIndex) {
case 0:
// 刷新数据
uni.reLaunch({
url: "/pages/businessDatas/portData",
});
uni.showToast({ title: "数据已刷新", icon: "success" });
break;
//返回首页
case 1:
uni.reLaunch({
url: "/pages/index/index",
});
uni.showToast({ title: "已返回工作台", icon: "success" });
break;
}
},
});
};
// 客户排名原始数据
const customerRankingRawData = [
{ name: "江苏鑫顺通新材料有限公司", value: 64641.52 },
{ name: "连云港耀捷建设工程有限公司", value: 44569.54 },
{ name: "江苏德邦兴华化工科技有限公司", value: 27887.37 },
{ name: "连云港海鲲物流有限公司", value: 19028.98 },
{ name: "张家港红喜物流有限公司", value: 16538.4 },
{ name: "成忠", value: 15557.77 },
{ name: "江苏米耘环保科技有限公司", value: 13952.03 },
{ name: "南京恒之通船务有限公司", value: 12684 },
{ name: "连云港市瑞豪贸易有限公司", value: 8098 },
{ name: "佰鼎风禾(江苏)供应链管理有限公司", value: 6546.48 },
];
// 作业地点作业量原始数据
const locationWorkloadRawData = [
{ name: "抓1", value: 12163.26 },
{ name: "抓2", value: 11872.35 },
{ name: "倒台", value: 93673.52 },
{ name: "1#", value: 59712.06 },
{ name: "2#", value: 9359.546 },
{ name: "3#", value: 8825.58 },
{ name: "4#", value: 14898.896 },
{ name: "5#", value: 19450.075 },
{ name: "6#", value: 17650.25 },
{ name: "7#", value: 21368.56 },
{ name: "8#", value: 35275.316 },
{ name: "抓2对面", value: 222.0 },
{ name: "二期大库", value: 9.1 },
{ name: "调度室后", value: 679.966 },
{ name: "直灌", value: 0 },
];
// 自动排序客户排名数据
const customerRankingData = (() => {
// 按数值从小到大排序
const sortedData = customerRankingRawData.sort((a, b) => a.value - b.value);
return {
legend: ["10月"],
yAxisData: sortedData.map((item) => item.name),
seriesData: sortedData.map((item) => item.value),
};
})();
// 作业地点作业量数据(不排序,按原始顺序)
const locationWorkloadData = {
legend: ["10月"],
yAxisData: locationWorkloadRawData.map((item) => item.name),
seriesData: locationWorkloadRawData.map((item) => item.value),
};
// 初始化客户排名图表
const initCustomerChart = (chart: any) => {
const option = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
legend: {
show: false,
},
grid: {
left: "3%",
right: "8%",
bottom: "3%",
top: "5%",
containLabel: true,
},
xAxis: {
type: "value",
boundaryGap: [0, 0.01],
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
splitLine: {
show: false,
},
},
yAxis: {
type: "category",
data: customerRankingData.yAxisData,
axisLabel: {
fontSize: 12,
width: 80,
overflow: "truncate",
},
},
series: [
{
name: customerRankingData.legend[0],
type: "bar",
data: customerRankingData.seriesData,
label: {
show: true,
position: "right",
formatter: "{c}",
fontSize: 11,
color: "#333",
},
},
],
};
chart.setOption(option);
console.log("客户排名图表初始化成功");
};
// 初始化作业地点作业量图表
const initLocationChart = (chart: any) => {
const option = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
legend: {
show: false,
},
grid: {
left: "3%",
right: "8%",
bottom: "3%",
top: "5%",
containLabel: true,
},
xAxis: {
type: "value",
boundaryGap: [0, 0.01],
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
splitLine: {
show: false,
},
},
yAxis: {
type: "category",
data: locationWorkloadData.yAxisData,
axisLabel: {
fontSize: 12,
width: 80,
overflow: "truncate",
},
},
series: [
{
name: locationWorkloadData.legend[0],
type: "bar",
data: locationWorkloadData.seriesData,
label: {
show: true,
position: "right",
formatter: "{c}",
fontSize: 11,
color: "#333",
},
},
],
};
chart.setOption(option);
console.log("作业地点作业量图表初始化成功");
};
// 切换tab
const switchTab = (tab: string) => {
activeTab.value = tab;
// 如果切换到客户排名tab确保图表正确显示
if (tab === "customer") {
nextTick(() => {
setTimeout(() => {
if (customerChartRef.value) {
customerChartRef.value.init(echarts, initCustomerChart);
}
}, 100);
});
}
// 如果切换到作业地点作业量tab确保图表正确显示
if (tab === "location") {
nextTick(() => {
setTimeout(() => {
if (locationChartRef.value) {
locationChartRef.value.init(echarts, initLocationChart);
}
}, 100);
});
}
};
// 生命周期
onMounted(() => {
// 检测设备类型
try {
const systemInfo = uni.getSystemInfoSync();
isMobile.value =
systemInfo.platform !== "devtools" &&
(systemInfo.platform === "ios" || systemInfo.platform === "android");
} catch (e) {
isMobile.value = false;
}
// 初始化第一个图表 - 货物类型统计
chartRef.value.init(echarts, (chart) => {
const option = {
title: {
text: "货物类型统计",
subtext: "各类型作业量分布",
left: "center",
},
tooltip: {
trigger: "item",
},
legend: {
orient: "horizontal",
bottom: "bottom",
itemGap: 10,
itemWidth: 14,
itemHeight: 14,
textStyle: {
fontSize: 12,
},
},
series: [
{
name: "作业类型",
type: "pie",
radius: "50%",
center: ["50%", "50%"],
data: [
{ value: 67187.753, name: "门机件杂货" },
{ value: 26582.55, name: "门机散货" },
{ value: 15576.03, name: "抓机散货" },
{ value: 82319.55, name: "倒台散货" },
{ value: 45963.8, name: "挖机散货" },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
};
chart.setOption(option);
});
// 初始化第二个图表 - 货物品类统计
cargoChartRef.value.init(echarts, (chart) => {
const option = {
title: {
text: "货物品类统计",
subtext: "各类货物占比分布",
left: "center",
},
tooltip: {
trigger: "item",
},
legend: {
orient: "vertical",
right: "right",
itemGap: 5,
itemWidth: 14,
itemHeight: 14,
textStyle: {
fontSize: 12,
},
},
series: [
{
name: "货物品类",
type: "pie",
radius: "50%",
center: ["50%", "55%"],
data: [
{ value: 64641.52, name: "铁精粉" },
{ value: 44734.8, name: "PTA" },
{ value: 44569.54, name: "粉质粘土" },
{ value: 33427.37, name: "盐" },
{ value: 15557.77, name: "石粉砂" },
{ value: 13952.03, name: "低卡煤" },
{ value: 4804.04, name: "氯化钾" },
{ value: 4140.0, name: "元明粉" },
{ value: 3664.0, name: "轻碱" },
{ value: 23152.583, name: "其他" },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
};
chart.setOption(option);
});
// 初始化客户排名图表 - 延迟初始化
setTimeout(() => {
if (customerChartRef.value) {
customerChartRef.value.init(echarts, initCustomerChart);
}
}, 500);
// 初始化作业地点作业量图表 - 延迟初始化
setTimeout(() => {
if (locationChartRef.value) {
locationChartRef.value.init(echarts, initLocationChart);
}
}, 500);
});
</script>
<style scoped>
.data-detail-page {
min-height: 100vh;
background: var(--background);
padding-top: 88rpx;
}
/* 移动设备需要额外的状态栏高度 */
.data-detail-page.mobile {
padding-top: calc(var(--status-bar-height) + 88rpx);
}
/* 支持安全区域的设备 */
@supports (padding: max(0px)) {
.data-detail-page.mobile {
padding-top: calc(
var(--status-bar-height) + 88rpx + env(safe-area-inset-top)
);
}
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: var(--title-color);
margin-bottom: 24rpx;
padding: 0 30rpx;
}
/* 卡片样式 */
.cards-section {
padding: 30rpx 0;
}
.card-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
padding: 0 30rpx;
}
.data-card {
background: var(--surface);
border-radius: 16rpx;
padding: 30rpx;
box-shadow: var(--shadow);
display: flex;
align-items: center;
gap: 20rpx;
border: 1rpx solid var(--border-light);
}
.card-icon {
width: 80rpx;
height: 80rpx;
background: var(--gradient-primary);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.card-icon i {
font-size: 32rpx;
color: var(--white);
}
.card-content {
flex: 1;
min-width: 0;
}
.card-value {
display: block;
font-size: 28rpx;
font-weight: 600;
color: var(--title-color);
margin-bottom: 8rpx;
}
.card-label {
display: block;
font-size: 24rpx;
color: var(--text-color);
}
/* 图表样式 */
.charts-section {
padding: 30rpx 0;
}
.chart-tabs {
display: flex;
padding: 0 30rpx;
margin-bottom: 30rpx;
overflow-x: auto;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
white-space: nowrap;
}
.chart-tabs::-webkit-scrollbar {
display: none; /* Chrome, Safari and Opera */
}
.chart-tab {
flex-shrink: 0;
padding: 16rpx 32rpx;
margin-right: 16rpx;
background: var(--gray-light);
border-radius: 32rpx;
font-size: 28rpx;
color: var(--text-color);
transition: all 0.3s ease;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.chart-tab:last-child {
margin-right: 0;
}
.chart-tab.active {
background: var(--primary-color);
color: var(--white);
box-shadow: var(--shadow-md);
}
.chart-content {
padding: 0 30rpx;
}
/* vchart样式 */
.chartsMain {
width: 100%;
min-height: 400rpx;
padding: 15rpx;
background: var(--surface);
margin-bottom: 24rpx;
border-radius: 12rpx;
box-shadow: var(--shadow-md);
border: 1rpx solid var(--border-light);
box-sizing: border-box;
position: relative;
overflow: hidden;
}
/* 移动端优化 */
@media screen and (max-width: 768px) {
.chartsMain {
min-height: 300rpx;
}
}
/* 柱状图特殊样式 - 支持横向滚动 */
.chartsMain.bar-chart {
overflow-x: auto;
overflow-y: hidden;
}
/* l-echart组件样式 */
.chartsMain :deep(.l-echart) {
width: 100% !important;
height: auto !important;
min-height: 300px !important;
background-color: transparent !important;
}
/* 柱状图横向滚动时的样式 */
.chartsMain.bar-chart :deep(.l-echart) {
min-width: 800rpx;
width: auto !important;
}
/* 表格样式 */
.table-section {
padding: 30rpx 0;
}
.table-container {
padding: 0 30rpx;
}
.data-table {
background: var(--surface);
border-radius: 16rpx;
overflow: hidden;
box-shadow: var(--shadow);
border: 1rpx solid var(--border-light);
}
.table-header {
background: var(--gray-lighter);
display: flex;
}
.table-row {
display: flex;
border-bottom: 1rpx solid var(--border-light);
}
.table-row:last-child {
border-bottom: none;
}
.table-cell {
flex: 1;
padding: 24rpx 16rpx;
font-size: 26rpx;
color: var(--text-color);
text-align: center;
border-right: 1rpx solid var(--border-light);
word-break: break-all;
}
.table-cell:last-child {
border-right: none;
}
.header-cell {
font-weight: 600;
color: var(--title-color);
}
</style>