修改移动端内容,后端添加了所有查询接口
This commit is contained in:
12
.idea/klp-mono.iml
generated
Normal file
12
.idea/klp-mono.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/klp-mono.iml" filepath="$PROJECT_DIR$/.idea/klp-mono.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
162
apps/hand-factory/api/pocket/plantState.js
Normal file
162
apps/hand-factory/api/pocket/plantState.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// =========================== 状态定义相关 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有状态定义及其当前值(用于初始化缓存)
|
||||||
|
* 返回数据结构:
|
||||||
|
* {
|
||||||
|
* id: 定义ID (对应VALUE字段编号,如id=14对应VALUE14),
|
||||||
|
* name: 指标名称,
|
||||||
|
* units: 单位,
|
||||||
|
* comments: 说明,
|
||||||
|
* currentValue: 当前值,
|
||||||
|
* currentInsdate: 当前值时间
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export function getAllPlantStateDefines() {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/proPlantStateDefine/allWithValues',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================== 当前数据相关 ===========================
|
||||||
|
|
||||||
|
// 查询设备状态当前数据列表
|
||||||
|
export function listPlantStateCurrent(query) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/proPlantStateCurrent/selectAll',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询单条设备状态当前数据
|
||||||
|
export function getPlantStateCurrent(type, insdate) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/proPlantStateCurrent/one',
|
||||||
|
method: 'get',
|
||||||
|
params: { type, insdate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询设备状态历史数据列表
|
||||||
|
export function listPlantStateHistory(query) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/proPlantStateHistory/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询设备状态历史数据详情
|
||||||
|
export function getPlantStateHistory(insdate) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/proPlantStateHistory/' + insdate,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询停机列表
|
||||||
|
export function listStoppage(query) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/proStoppage/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询停机详情
|
||||||
|
export function getStoppage(stopId) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/proStoppage/' + stopId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================== 班组信息相关 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前班组信息
|
||||||
|
* 返回数据结构:
|
||||||
|
* {
|
||||||
|
* shift: 班次 (如:A/B/C 或 早/中/晚),
|
||||||
|
* crew: 班组编号,
|
||||||
|
* seqNum: 序列号,
|
||||||
|
* sysTime: 系统时间
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export function getCurrentShift() {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/shiftCurrent/current',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================== 生产统计相关 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取生产统计汇总数据
|
||||||
|
* @param {string} startDate - 开始日期 (yyyy-MM-dd)
|
||||||
|
* @param {string} endDate - 结束日期 (yyyy-MM-dd)
|
||||||
|
*/
|
||||||
|
export function getProductionSummary(startDate, endDate) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/productionStatistics/summary',
|
||||||
|
method: 'get',
|
||||||
|
params: { startDate, endDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取班组产量统计
|
||||||
|
* @param {string} startDate - 开始日期
|
||||||
|
* @param {string} endDate - 结束日期
|
||||||
|
*/
|
||||||
|
export function getCrewProduction(startDate, endDate) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/productionStatistics/crewProduction',
|
||||||
|
method: 'get',
|
||||||
|
params: { startDate, endDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取厚度分布统计
|
||||||
|
* @param {string} startDate - 开始日期
|
||||||
|
* @param {string} endDate - 结束日期
|
||||||
|
*/
|
||||||
|
export function getThicknessDistribution(startDate, endDate) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/productionStatistics/thicknessDistribution',
|
||||||
|
method: 'get',
|
||||||
|
params: { startDate, endDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取宽度分布统计
|
||||||
|
* @param {string} startDate - 开始日期
|
||||||
|
* @param {string} endDate - 结束日期
|
||||||
|
*/
|
||||||
|
export function getWidthDistribution(startDate, endDate) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/productionStatistics/widthDistribution',
|
||||||
|
method: 'get',
|
||||||
|
params: { startDate, endDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取班组绩效统计
|
||||||
|
* @param {string} startDate - 开始日期
|
||||||
|
* @param {string} endDate - 结束日期
|
||||||
|
*/
|
||||||
|
export function getTeamPerformance(startDate, endDate) {
|
||||||
|
return request({
|
||||||
|
url: '/pocket/productionStatistics/teamPerformance',
|
||||||
|
method: 'get',
|
||||||
|
params: { startDate, endDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,142 +1,136 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="page-container">
|
||||||
<!-- 日月年汇总单选 -->
|
<!-- 时间维度切换 -->
|
||||||
<view class="tab-container">
|
<view class="time-tab-bar">
|
||||||
<view
|
<view
|
||||||
v-for="item in timeTabs"
|
v-for="item in timeTabs"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
class="tab-item"
|
class="time-tab-item"
|
||||||
:class="{ active: activeTab === item.value }"
|
:class="{ 'time-tab-active': activeTab === item.value }"
|
||||||
@click="handleTabChange(item.value)"
|
@click="handleTabChange(item.value)"
|
||||||
>
|
>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 日期选择器 -->
|
<!-- 日期选择区 -->
|
||||||
<view class="date-picker">
|
<view class="date-selector">
|
||||||
<!-- 日模式 -->
|
<!-- 日模式 -->
|
||||||
<view v-if="activeTab === 'day'" class="date-item">
|
<picker v-if="activeTab === 'day'" mode="date" :value="startDate" @change="handleDateChange">
|
||||||
<picker
|
<view class="date-input">
|
||||||
mode="date"
|
<text class="date-label">日期</text>
|
||||||
:value="startDate"
|
<text class="date-value">{{ startDate }}</text>
|
||||||
@change="handleDateChange"
|
|
||||||
>
|
|
||||||
<view class="picker-text">选择日期:{{ startDate }}</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
</view>
|
||||||
|
</picker>
|
||||||
|
|
||||||
<!-- 月模式 -->
|
<!-- 月模式 -->
|
||||||
<view v-else-if="activeTab === 'month'" class="date-range">
|
<view v-else-if="activeTab === 'month'" class="date-range-group">
|
||||||
<view class="date-item">
|
<picker mode="date" fields="month" :value="startDate" @change="handleStartMonthChange">
|
||||||
<picker
|
<view class="date-input">
|
||||||
mode="date"
|
<text class="date-label">起</text>
|
||||||
fields="month"
|
<text class="date-value">{{ startDate }}</text>
|
||||||
:value="startDate"
|
|
||||||
@change="handleStartMonthChange"
|
|
||||||
>
|
|
||||||
<view class="picker-text">开始月份:{{ startDate }}</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="date-item">
|
|
||||||
<picker
|
|
||||||
mode="date"
|
|
||||||
fields="month"
|
|
||||||
:value="endDate"
|
|
||||||
:start="startDate"
|
|
||||||
:end="maxMonthEnd"
|
|
||||||
@change="handleEndMonthChange"
|
|
||||||
>
|
|
||||||
<view class="picker-text">结束月份:{{ endDate }}</view>
|
|
||||||
</picker>
|
</picker>
|
||||||
|
<view class="date-separator">至</view>
|
||||||
|
<picker mode="date" fields="month" :value="endDate" :start="startDate" :end="maxMonthEnd" @change="handleEndMonthChange">
|
||||||
|
<view class="date-input">
|
||||||
|
<text class="date-label">止</text>
|
||||||
|
<text class="date-value">{{ endDate }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
</picker>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 年模式 -->
|
<!-- 年模式 -->
|
||||||
<view v-else class="date-range">
|
<view v-else class="date-range-group">
|
||||||
<view class="date-item">
|
<picker mode="date" fields="year" :value="startDate" @change="handleStartYearChange">
|
||||||
<picker
|
<view class="date-input">
|
||||||
mode="date"
|
<text class="date-label">起</text>
|
||||||
fields="year"
|
<text class="date-value">{{ startDate }}</text>
|
||||||
:value="startDate"
|
</view>
|
||||||
@change="handleStartYearChange"
|
</picker>
|
||||||
>
|
<view class="date-separator">至</view>
|
||||||
<view class="picker-text">开始年份:{{ startDate }}</view>
|
<picker mode="date" fields="year" :value="endDate" @change="handleEndYearChange">
|
||||||
</picker>
|
<view class="date-input">
|
||||||
|
<text class="date-label">止</text>
|
||||||
|
<text class="date-value">{{ endDate }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="date-item">
|
|
||||||
<picker
|
|
||||||
mode="date"
|
|
||||||
fields="year"
|
|
||||||
:value="endDate"
|
|
||||||
@change="handleEndYearChange"
|
|
||||||
>
|
|
||||||
<view class="picker-text">结束年份:{{ endDate }}</view>
|
|
||||||
</picker>
|
</picker>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 生产汇总数据 -->
|
||||||
|
<view class="summary-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">生产汇总</text>
|
||||||
|
<text class="section-date">{{ displayDateRange }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-grid">
|
||||||
|
<view class="summary-card" v-for="(item, index) in summaryData" :key="index">
|
||||||
|
<text class="summary-label">{{ item.label }}</text>
|
||||||
|
<view class="summary-value-box">
|
||||||
|
<text class="summary-value">{{ item.value }}</text>
|
||||||
|
<text v-if="item.unit" class="summary-unit">{{ item.unit }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 统计图轮播(产量相关) -->
|
<!-- 产量趋势图 -->
|
||||||
<view class="chart-container">
|
<view class="chart-section">
|
||||||
<swiper
|
<view class="section-header">
|
||||||
class="chart-swiper"
|
<text class="section-title">产量趋势</text>
|
||||||
:current="currentSwiperIndex"
|
</view>
|
||||||
@change="onSwiperChange"
|
<view class="chart-wrapper">
|
||||||
:autoplay="false"
|
<qiun-data-charts type="mix" :chartData="productionChartData" :opts="productionChartOpts"/>
|
||||||
:circular="false"
|
</view>
|
||||||
:indicator-dots="true"
|
|
||||||
indicator-active-color="#1a73e8"
|
|
||||||
indicator-color="#e5e5e5"
|
|
||||||
>
|
|
||||||
<swiper-item v-for="(item, index) in chanLiangChartConfig" :key="index">
|
|
||||||
<qiun-data-charts :type="item.type" :chartData="item.chartData" :title="item.title"/>
|
|
||||||
</swiper-item>
|
|
||||||
</swiper>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 日期范围汇总 -->
|
<!-- 班组产量对比 -->
|
||||||
<klp-collapse-panel :title="'汇总(' + displayDateRange + ')'">
|
<view class="chart-section">
|
||||||
<k-metric-card
|
<view class="section-header">
|
||||||
:items="summaryData"
|
<text class="section-title">班组产量对比</text>
|
||||||
:columns="3"
|
</view>
|
||||||
/>
|
<view class="chart-wrapper">
|
||||||
|
<qiun-data-charts type="column" :chartData="crewChartData" :opts="columnChartOpts"/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 汇总饼图轮播 -->
|
<!-- 规格分布 -->
|
||||||
<swiper
|
<view class="chart-section">
|
||||||
class="chart-swiper"
|
<view class="section-header">
|
||||||
:autoplay="false"
|
<text class="section-title">规格分布</text>
|
||||||
:circular="false"
|
</view>
|
||||||
:indicator-dots="true"
|
<view class="pie-charts-row">
|
||||||
indicator-active-color="#1a73e8"
|
<view class="pie-chart-item">
|
||||||
indicator-color="#e5e5e5"
|
<text class="pie-title">厚度分布</text>
|
||||||
>
|
<qiun-data-charts type="pie" :chartData="thicknessPieData" :opts="pieChartOpts"/>
|
||||||
<swiper-item v-for="(item, index) in summaryChartConfig" :key="index">
|
</view>
|
||||||
<qiun-data-charts type="pie" :opts="item.opts" :chartData="item.chartData" :title="item.title"/>
|
|
||||||
</swiper-item>
|
</view>
|
||||||
</swiper>
|
<view class="pie-charts-row">
|
||||||
</klp-collapse-panel>
|
<view class="pie-chart-item">
|
||||||
|
<text class="pie-title">宽度分布</text>
|
||||||
|
<qiun-data-charts type="pie" :chartData="widthPieData" :opts="pieChartOpts"/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// 2. 独立工具函数(避免Vue2 data初始化时调用this.methods的问题)
|
import {
|
||||||
/**
|
getProductionSummary,
|
||||||
* 获取默认日期(根据视图类型)
|
getCrewProduction,
|
||||||
* @param {string} type - 视图类型:day/month/year
|
getThicknessDistribution,
|
||||||
* @returns {string} 格式化后的日期
|
getWidthDistribution
|
||||||
*/
|
} from '@/api/pocket/plantState'
|
||||||
|
|
||||||
|
// 工具函数
|
||||||
function getDefaultDate(type = "day") {
|
function getDefaultDate(type = "day") {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
return formatDate(date, type);
|
return formatDate(date, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化日期
|
|
||||||
* @param {Date} date - 日期对象
|
|
||||||
* @param {string} type - 视图类型:day/month/year
|
|
||||||
* @returns {string} 格式化后的日期
|
|
||||||
*/
|
|
||||||
function formatDate(date, type) {
|
function formatDate(date, type) {
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||||
@@ -151,128 +145,97 @@ function formatDate(date, type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 4. 响应式数据(替代 Vue3 的 ref)
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 激活的视图类型(日/月/年)
|
|
||||||
activeTab: "day",
|
activeTab: "day",
|
||||||
// 开始/结束日期(月份/年份)
|
|
||||||
startDate: getDefaultDate(),
|
startDate: getDefaultDate(),
|
||||||
endDate: getDefaultDate(),
|
endDate: getDefaultDate(),
|
||||||
// 视图切换选项
|
|
||||||
timeTabs: [
|
timeTabs: [
|
||||||
{ label: "日视图", value: "day" },
|
{ label: "日", value: "day" },
|
||||||
{ label: "月视图", value: "month" },
|
{ label: "月", value: "month" },
|
||||||
{ label: "年视图", value: "year" }
|
{ label: "年", value: "year" }
|
||||||
],
|
],
|
||||||
// 产量相关图表配置(轮播)
|
// 汇总数据
|
||||||
chanLiangChartConfig: [
|
|
||||||
{
|
|
||||||
title: "规格曲线",
|
|
||||||
type: "column",
|
|
||||||
chartData: {} // 初始空对象,后续加载数据
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "超产-欠产统计",
|
|
||||||
type: "line",
|
|
||||||
chartData: {} // 初始空对象,后续加载数据
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "产量-成材率统计",
|
|
||||||
type: "column",
|
|
||||||
chartData: {
|
|
||||||
categories: ["2016", "2017", "2018", "2019", "2020", "2021"],
|
|
||||||
series: [
|
|
||||||
{ name: "目标值", data: [35, 36, 31, 33, 13, 34] },
|
|
||||||
{ name: "完成量", data: [18, 27, 21, 24, 6, 28] }
|
|
||||||
]
|
|
||||||
} // 固定初始数据
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// 汇总饼图配置(轮播)
|
|
||||||
summaryChartConfig: [
|
|
||||||
{
|
|
||||||
title: "成品厚度统计(mm)",
|
|
||||||
opts: {
|
|
||||||
color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
|
|
||||||
padding: [5,5,5,5],
|
|
||||||
enableScroll: false,
|
|
||||||
position: "left",
|
|
||||||
float: "left",
|
|
||||||
extra: {
|
|
||||||
pie: {
|
|
||||||
activeOpacity: 0.5,
|
|
||||||
activeRadius: 10,
|
|
||||||
offsetAngle: 0,
|
|
||||||
labelWidth: 15,
|
|
||||||
border: false,
|
|
||||||
borderWidth: 3,
|
|
||||||
borderColor: "#FFFFFF"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartData: JSON.parse(JSON.stringify({
|
|
||||||
series: [{ data: [{"name":"一班","value":50},{"name":"二班","value":30},{"name":"三班","value":20},{"name":"四班","value":18},{"name":"五班","value":8}] }]
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "钢种统计",
|
|
||||||
opts: {
|
|
||||||
color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
|
|
||||||
padding: [5,5,5,5],
|
|
||||||
enableScroll: false,
|
|
||||||
extra: {
|
|
||||||
pie: {
|
|
||||||
activeOpacity: 0.5,
|
|
||||||
activeRadius: 10,
|
|
||||||
offsetAngle: 0,
|
|
||||||
labelWidth: 15,
|
|
||||||
border: false,
|
|
||||||
borderWidth: 3,
|
|
||||||
borderColor: "#FFFFFF"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartData: JSON.parse(JSON.stringify({
|
|
||||||
series: [{ data: [{"name":"一班","value":50},{"name":"二班","value":30},{"name":"三班","value":20},{"name":"四班","value":18},{"name":"五班","value":8}] }]
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "班组统计",
|
|
||||||
opts: {
|
|
||||||
color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
|
|
||||||
padding: [5,5,5,5],
|
|
||||||
enableScroll: false,
|
|
||||||
extra: {
|
|
||||||
pie: {
|
|
||||||
activeOpacity: 0.5,
|
|
||||||
activeRadius: 10,
|
|
||||||
offsetAngle: 0,
|
|
||||||
labelWidth: 15,
|
|
||||||
border: false,
|
|
||||||
borderWidth: 3,
|
|
||||||
borderColor: "#FFFFFF"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartData: JSON.parse(JSON.stringify({
|
|
||||||
series: [{ data: [{"name":"一班","value":50},{"name":"二班","value":30},{"name":"三班","value":20},{"name":"四班","value":18},{"name":"五班","value":8}] }]
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// 轮播图当前索引
|
|
||||||
currentSwiperIndex: 0,
|
|
||||||
// 服务器加载的图表基础数据
|
|
||||||
chartData: {},
|
|
||||||
// 汇总指标卡数据
|
|
||||||
summaryData: [
|
summaryData: [
|
||||||
{ label: "生产钢卷数", value: 186 },
|
{ label: "生产钢卷数", value: 0, unit: "卷" },
|
||||||
{ label: "平均宽度", value: 1054, unit: "mm" },
|
{ label: "平均宽度", value: 0, unit: "mm" },
|
||||||
{ label: "平均厚度", value: 0.93, unit: "mm" },
|
{ label: "平均厚度", value: 0, unit: "mm" },
|
||||||
{ label: "原料总量", value: 3396.47, unit: "t" },
|
{ label: "原料总量", value: 0, unit: "t" },
|
||||||
{ label: "成品总量", value: 4360.18, unit: "t" },
|
{ label: "成品总量", value: 0, unit: "t" },
|
||||||
{ label: "成材率", value: 95.99, unit: "%" } // 原代码unit写t,推测是笔误,修正为%
|
{ label: "成材率", value: 0, unit: "%" }
|
||||||
|
],
|
||||||
|
// 产量趋势图(柱状+折线)
|
||||||
|
productionChartData: {},
|
||||||
|
productionChartOpts: {
|
||||||
|
color: ["#0066cc", "#00b96b"],
|
||||||
|
padding: [15, 15, 0, 15],
|
||||||
|
enableScroll: false,
|
||||||
|
legend: { position: "top" },
|
||||||
|
xAxis: { disableGrid: true },
|
||||||
|
yAxis: {
|
||||||
|
gridType: "dash",
|
||||||
|
dashLength: 4,
|
||||||
|
gridColor: "#e4e7ed",
|
||||||
|
data: [
|
||||||
|
{ position: "left", title: "产量(t)" },
|
||||||
|
{ position: "right", title: "成材率(%)" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
mix: {
|
||||||
|
column: { width: 20 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 班组产量对比
|
||||||
|
crewChartData: {},
|
||||||
|
columnChartOpts: {
|
||||||
|
color: ["#0066cc", "#409eff", "#66b1ff"],
|
||||||
|
padding: [15, 15, 0, 15],
|
||||||
|
enableScroll: false,
|
||||||
|
legend: {},
|
||||||
|
xAxis: { disableGrid: true },
|
||||||
|
yAxis: {
|
||||||
|
gridType: "dash",
|
||||||
|
dashLength: 4,
|
||||||
|
gridColor: "#e4e7ed",
|
||||||
|
data: [{ title: "产量(t)" }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 饼图配置
|
||||||
|
pieChartOpts: {
|
||||||
|
color: ["#0066cc", "#409eff", "#66b1ff", "#a0cfff", "#d9ecff"],
|
||||||
|
padding: [15, 15, 15, 15],
|
||||||
|
enableScroll: false,
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
position: "bottom",
|
||||||
|
lineHeight: 16,
|
||||||
|
fontSize: 10,
|
||||||
|
fontColor: "#666",
|
||||||
|
margin: 5,
|
||||||
|
itemGap: 8
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
pie: {
|
||||||
|
activeOpacity: 0.5,
|
||||||
|
activeRadius: 10,
|
||||||
|
labelWidth: 15,
|
||||||
|
border: false,
|
||||||
|
ringWidth: 0,
|
||||||
|
offsetAngle: 0,
|
||||||
|
disablePieStroke: true,
|
||||||
|
linearType: "none",
|
||||||
|
customRadius: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
thicknessPieData: {
|
||||||
|
series: [{ data: [] }]
|
||||||
|
},
|
||||||
|
widthPieData: {
|
||||||
|
series: [{ data: [] }]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// 5. 计算属性(替代 Vue3 的 computed 函数)
|
// 5. 计算属性(替代 Vue3 的 computed 函数)
|
||||||
@@ -298,168 +261,427 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 6. 生命周期钩子(替代 Vue3 的 onMounted)
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getServerData(); // 页面挂载后加载图表数据
|
this.loadProductionData();
|
||||||
},
|
},
|
||||||
// 7. 方法定义(所有交互逻辑与数据处理)
|
|
||||||
methods: {
|
methods: {
|
||||||
// 切换视图(日/月/年)
|
// 切换时间维度
|
||||||
handleTabChange(tab) {
|
handleTabChange(tab) {
|
||||||
this.activeTab = tab;
|
this.activeTab = tab;
|
||||||
// 重置日期:日视图首尾一致,月/年视图首尾默认当前
|
|
||||||
const defaultDate = getDefaultDate();
|
const defaultDate = getDefaultDate();
|
||||||
this.startDate = defaultDate;
|
this.startDate = defaultDate;
|
||||||
this.endDate = tab === "day" ? defaultDate : getDefaultDate(tab);
|
this.endDate = tab === "day" ? defaultDate : getDefaultDate(tab);
|
||||||
|
this.loadProductionData();
|
||||||
},
|
},
|
||||||
// 日模式:日期选择器变更
|
|
||||||
|
// 日期选择变更
|
||||||
handleDateChange(e) {
|
handleDateChange(e) {
|
||||||
this.startDate = e.detail.value;
|
this.startDate = e.detail.value;
|
||||||
this.endDate = e.detail.value; // 日视图首尾日期同步
|
this.endDate = e.detail.value;
|
||||||
|
this.loadProductionData();
|
||||||
},
|
},
|
||||||
// 月模式:开始月份变更
|
|
||||||
handleStartMonthChange(e) {
|
handleStartMonthChange(e) {
|
||||||
this.startDate = e.detail.value;
|
this.startDate = e.detail.value;
|
||||||
// 自动调整结束月份:不超过开始月份+1年
|
|
||||||
const maxEndDate = new Date(this.startDate);
|
const maxEndDate = new Date(this.startDate);
|
||||||
maxEndDate.setFullYear(maxEndDate.getFullYear() + 1);
|
maxEndDate.setFullYear(maxEndDate.getFullYear() + 1);
|
||||||
const maxEndStr = formatDate(maxEndDate, "month");
|
const maxEndStr = formatDate(maxEndDate, "month");
|
||||||
if (new Date(this.endDate) > maxEndDate) {
|
if (new Date(this.endDate) > maxEndDate) {
|
||||||
this.endDate = maxEndStr;
|
this.endDate = maxEndStr;
|
||||||
}
|
}
|
||||||
|
this.loadProductionData();
|
||||||
},
|
},
|
||||||
// 月模式:结束月份变更
|
|
||||||
handleEndMonthChange(e) {
|
handleEndMonthChange(e) {
|
||||||
this.endDate = e.detail.value;
|
this.endDate = e.detail.value;
|
||||||
|
this.loadProductionData();
|
||||||
},
|
},
|
||||||
// 年模式:开始年份变更
|
|
||||||
handleStartYearChange(e) {
|
handleStartYearChange(e) {
|
||||||
this.startDate = e.detail.value;
|
this.startDate = e.detail.value;
|
||||||
|
this.loadProductionData();
|
||||||
},
|
},
|
||||||
// 年模式:结束年份变更
|
|
||||||
handleEndYearChange(e) {
|
handleEndYearChange(e) {
|
||||||
this.endDate = e.detail.value;
|
this.endDate = e.detail.value;
|
||||||
|
this.loadProductionData();
|
||||||
},
|
},
|
||||||
// 轮播图切换回调(更新当前索引)
|
|
||||||
onSwiperChange(e) {
|
|
||||||
this.currentSwiperIndex = e.detail.current;
|
|
||||||
},
|
|
||||||
// 模拟从服务器加载图表数据
|
|
||||||
getServerData() {
|
|
||||||
setTimeout(() => {
|
|
||||||
// 模拟服务器返回数据
|
|
||||||
const serverRes = {
|
|
||||||
categories: ["2016", "2017", "2018", "2019", "2020", "2021"],
|
|
||||||
series: [
|
|
||||||
{ name: "目标值", data: [35, 36, 31, 33, 13, 34] },
|
|
||||||
{ name: "完成量", data: [18, 27, 21, 24, 6, 28] }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
// 深拷贝避免引用问题,确保Vue2响应式
|
|
||||||
this.chartData = JSON.parse(JSON.stringify(serverRes));
|
|
||||||
|
|
||||||
// 更新产量图表配置的前两个图表数据(第三个固定)
|
// 加载生产数据
|
||||||
this.chanLiangChartConfig.forEach((item, index) => {
|
loadProductionData() {
|
||||||
if (index < 2) { // 仅前两个图表用服务器数据
|
uni.showLoading({ title: '加载中' })
|
||||||
item.chartData = JSON.parse(JSON.stringify(this.chartData));
|
|
||||||
|
// 并行请求所有数据
|
||||||
|
Promise.all([
|
||||||
|
this.loadSummaryData(),
|
||||||
|
this.loadCrewProductionData(),
|
||||||
|
this.loadSpecDistribution()
|
||||||
|
]).finally(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载汇总数据
|
||||||
|
loadSummaryData() {
|
||||||
|
// 转换为完整日期格式
|
||||||
|
const start = this.formatFullDate(this.startDate, true)
|
||||||
|
const end = this.formatFullDate(this.endDate, false)
|
||||||
|
|
||||||
|
return getProductionSummary(start, end).then(response => {
|
||||||
|
if (response.code === 200 && response.data) {
|
||||||
|
const data = response.data
|
||||||
|
this.summaryData = [
|
||||||
|
{ label: "生产钢卷数", value: data.coilCount || 0, unit: "卷" },
|
||||||
|
{ label: "平均宽度", value: data.avgWidth || 0, unit: "mm" },
|
||||||
|
{ label: "平均厚度", value: data.avgThick || 0, unit: "mm" },
|
||||||
|
{ label: "原料总量", value: data.totalEntryWeight || 0, unit: "t" },
|
||||||
|
{ label: "成品总量", value: data.totalExitWeight || 0, unit: "t" },
|
||||||
|
{ label: "成材率", value: data.yieldRate || 0, unit: "%" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
});
|
}).catch(error => {
|
||||||
}, 500);
|
console.error('加载汇总数据失败:', error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载班组产量数据
|
||||||
|
loadCrewProductionData() {
|
||||||
|
const start = this.formatFullDate(this.startDate, true)
|
||||||
|
const end = this.formatFullDate(this.endDate, false)
|
||||||
|
|
||||||
|
return getCrewProduction(start, end).then(response => {
|
||||||
|
if (response.code === 200 && response.data && response.data.length > 0) {
|
||||||
|
const crewData = response.data
|
||||||
|
|
||||||
|
// 按班组和班次分组
|
||||||
|
const categories = []
|
||||||
|
const crewMap = {}
|
||||||
|
|
||||||
|
crewData.forEach(item => {
|
||||||
|
const label = `${item.crew}-${item.shift}`
|
||||||
|
categories.push(label)
|
||||||
|
|
||||||
|
if (!crewMap[item.shift]) {
|
||||||
|
crewMap[item.shift] = []
|
||||||
|
}
|
||||||
|
crewMap[item.shift].push(Number(item.totalWeight) || 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 构建班组产量对比图
|
||||||
|
const series = Object.keys(crewMap).map(shift => ({
|
||||||
|
name: shift,
|
||||||
|
data: crewMap[shift]
|
||||||
|
}))
|
||||||
|
|
||||||
|
this.crewChartData = {
|
||||||
|
categories: categories,
|
||||||
|
series: series
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建产量趋势图(使用班组数据)
|
||||||
|
this.productionChartData = {
|
||||||
|
categories: categories,
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "产量",
|
||||||
|
type: "column",
|
||||||
|
index: 0,
|
||||||
|
data: crewData.map(item => Number(item.totalWeight) || 0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "平均厚度",
|
||||||
|
type: "line",
|
||||||
|
index: 1,
|
||||||
|
style: "curve",
|
||||||
|
data: crewData.map(item => Number(item.avgThick) || 0)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('加载班组产量失败:', error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载规格分布
|
||||||
|
loadSpecDistribution() {
|
||||||
|
const start = this.formatFullDate(this.startDate, true)
|
||||||
|
const end = this.formatFullDate(this.endDate, false)
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
getThicknessDistribution(start, end),
|
||||||
|
getWidthDistribution(start, end)
|
||||||
|
]).then(([thicknessRes, widthRes]) => {
|
||||||
|
// 厚度分布
|
||||||
|
if (thicknessRes.code === 200 && thicknessRes.data) {
|
||||||
|
this.thicknessPieData = {
|
||||||
|
series: [{
|
||||||
|
data: thicknessRes.data.map(item => ({
|
||||||
|
name: item.category,
|
||||||
|
value: item.count
|
||||||
|
}))
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 宽度分布
|
||||||
|
if (widthRes.code === 200 && widthRes.data) {
|
||||||
|
this.widthPieData = {
|
||||||
|
series: [{
|
||||||
|
data: widthRes.data.map(item => ({
|
||||||
|
name: item.category,
|
||||||
|
value: item.count
|
||||||
|
}))
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('加载规格分布失败:', error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 格式化为完整日期(用于查询)
|
||||||
|
formatFullDate(dateStr, isStart) {
|
||||||
|
if (!dateStr) return ''
|
||||||
|
|
||||||
|
// 如果已经是完整日期格式 yyyy-MM-dd,直接返回
|
||||||
|
if (dateStr.length === 10) {
|
||||||
|
return dateStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 月份格式 yyyy-MM
|
||||||
|
if (dateStr.length === 7) {
|
||||||
|
if (isStart) {
|
||||||
|
// 开始日期:该月第一天
|
||||||
|
return `${dateStr}-01`
|
||||||
|
} else {
|
||||||
|
// 结束日期:该月最后一天
|
||||||
|
const [year, month] = dateStr.split('-')
|
||||||
|
const lastDay = new Date(year, month, 0).getDate()
|
||||||
|
return `${dateStr}-${String(lastDay).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 年份格式 yyyy
|
||||||
|
if (dateStr.length === 4) {
|
||||||
|
if (isStart) {
|
||||||
|
// 开始日期:该年第一天
|
||||||
|
return `${dateStr}-01-01`
|
||||||
|
} else {
|
||||||
|
// 结束日期:该年最后一天
|
||||||
|
return `${dateStr}-12-31`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
.container {
|
/* 页面容器 */
|
||||||
padding: 20rpx;
|
.page-container {
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 简洁Tab容器 */
|
/* 时间维度切换 */
|
||||||
.tab-container {
|
.time-tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
box-sizing: border-box;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin-bottom: 20rpx;
|
border-radius: 8rpx;
|
||||||
border-radius: 12rpx;
|
padding: 8rpx;
|
||||||
overflow: hidden;
|
margin-bottom: 24rpx;
|
||||||
border: 1rpx solid #e8e8e8;
|
border: 1rpx solid #e4e7ed;
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item {
|
.time-tab-item {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 24rpx 20rpx;
|
padding: 16rpx 0;
|
||||||
color: #666;
|
font-size: 26rpx;
|
||||||
font-size: 28rpx;
|
color: #606266;
|
||||||
font-weight: 500;
|
border-radius: 6rpx;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.2s;
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:not(:last-child)::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
width: 1rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
background: #e8e8e8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item.active {
|
.time-tab-active {
|
||||||
|
background: #0066cc;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #1a73e8;
|
font-weight: 500;
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 日期选择器 */
|
/* 日期选择区 */
|
||||||
.date-picker {
|
.date-selector {
|
||||||
padding: 20rpx;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 12rpx;
|
border-radius: 8rpx;
|
||||||
margin-bottom: 20rpx;
|
padding: 24rpx;
|
||||||
border: 1rpx solid #e8e8e8;
|
margin-bottom: 24rpx;
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
border: 1rpx solid #e4e7ed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-range {
|
.date-range-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20rpx;
|
align-items: stretch;
|
||||||
|
gap: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-item {
|
.date-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border: 1rpx solid #e4e7ed;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-radius: 6rpx 0 0 6rpx;
|
||||||
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-text {
|
&:last-child {
|
||||||
padding: 20rpx;
|
border-radius: 0 6rpx 6rpx 0;
|
||||||
background: #f8f9fa;
|
border-left: none;
|
||||||
border-radius: 10rpx;
|
}
|
||||||
color: #333;
|
}
|
||||||
|
|
||||||
|
.date-label {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #909399;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-value {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
border: 1rpx solid #e8e8e8;
|
color: #303133;
|
||||||
transition: all 0.2s ease;
|
font-weight: 500;
|
||||||
|
flex: 1;
|
||||||
&:active {
|
text-align: right;
|
||||||
background: #f0f2f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.date-separator {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
background: #0066cc;
|
||||||
|
padding: 24rpx 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 区块样式 */
|
||||||
|
.summary-section,
|
||||||
|
.chart-section {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-left: 16rpx;
|
||||||
|
border-left: 4rpx solid #0066cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-date {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 汇总卡片网格 */
|
||||||
|
.summary-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1rpx solid #e4e7ed;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 28rpx 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #909399;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0066cc;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-unit {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #909399;
|
||||||
|
margin-left: 6rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 图表容器 */
|
/* 图表容器 */
|
||||||
.chart-container {
|
.chart-wrapper {
|
||||||
margin-bottom: 20rpx;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 16rpx;
|
border: 1rpx solid #e4e7ed;
|
||||||
padding: 30rpx;
|
border-radius: 8rpx;
|
||||||
border: 1rpx solid #e8e8e8;
|
padding: 24rpx 16rpx;
|
||||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
min-height: 450rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-swiper {
|
/* 饼图行布局 */
|
||||||
height: 500rpx;
|
.pie-charts-row {
|
||||||
box-sizing: border-box;
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie-chart-item {
|
||||||
|
flex: 1;
|
||||||
|
background: #fff;
|
||||||
|
border: 1rpx solid #e4e7ed;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 24rpx 0 32rpx 0;
|
||||||
|
min-height: 520rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie-title {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie-chart-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,90 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view class="page-container">
|
||||||
<view class="month-picker">
|
<!-- 月份选择器 -->
|
||||||
<picker
|
<view class="month-selector">
|
||||||
mode="date"
|
<view class="month-arrow" @click="changeMonth(-1)" :class="{ disabled: isMinMonth }">
|
||||||
fields="month"
|
<text>◀</text>
|
||||||
:value="selectedMonth"
|
|
||||||
:start="startDate"
|
|
||||||
:end="endDate"
|
|
||||||
@change="handleMonthChange"
|
|
||||||
>
|
|
||||||
<view class="picker-content">
|
|
||||||
<!-- 左箭头 -->
|
|
||||||
<view
|
|
||||||
class="arrow"
|
|
||||||
@click.stop="changeMonth(-1)"
|
|
||||||
:class="{ disabled: isMinMonth }"
|
|
||||||
>
|
|
||||||
◀
|
|
||||||
</view>
|
</view>
|
||||||
|
<picker mode="date" fields="month" :value="selectedMonth" :start="startDate" :end="endDate" @change="handleMonthChange">
|
||||||
<view class="month-display">
|
<view class="month-display">
|
||||||
<text class="label">选择月份:</text>
|
|
||||||
<text class="month-text">{{ formattedMonth }}</text>
|
<text class="month-text">{{ formattedMonth }}</text>
|
||||||
</view>
|
<text class="picker-icon">▼</text>
|
||||||
|
|
||||||
<!-- 右箭头 -->
|
|
||||||
<view
|
|
||||||
class="arrow"
|
|
||||||
@click.stop="changeMonth(1)"
|
|
||||||
:class="{ disabled: isMaxMonth }"
|
|
||||||
>
|
|
||||||
▶
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<text class="icon">▼</text>
|
|
||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
|
<view class="month-arrow" @click="changeMonth(1)" :class="{ disabled: isMaxMonth }">
|
||||||
|
<text>▶</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view>
|
<!-- 班组绩效排名 -->
|
||||||
<k-table :columns="columns" :data="tableData"></k-table>
|
<view class="ranking-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">绩效排名</text>
|
||||||
|
</view>
|
||||||
|
<view class="ranking-list">
|
||||||
|
<view
|
||||||
|
class="ranking-item"
|
||||||
|
v-for="(item, index) in teamRankingData"
|
||||||
|
:key="index"
|
||||||
|
:class="'rank-' + (index + 1)"
|
||||||
|
>
|
||||||
|
<view class="rank-badge">{{ index + 1 }}</view>
|
||||||
|
<view class="rank-info">
|
||||||
|
<text class="team-name">{{ item.team }}</text>
|
||||||
|
<text class="team-shift">{{ item.shift }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="rank-score">
|
||||||
|
<text class="score-value">{{ item.score }}</text>
|
||||||
|
<text class="score-label">分</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 班组产量对比 -->
|
||||||
|
<view class="chart-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">产量对比</text>
|
||||||
|
</view>
|
||||||
|
<view class="chart-wrapper">
|
||||||
|
<qiun-data-charts type="column" :chartData="outputChartData" :opts="columnChartOpts"/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 质量指标对比 -->
|
||||||
|
<view class="chart-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">质量指标</text>
|
||||||
|
</view>
|
||||||
|
<view class="chart-wrapper">
|
||||||
|
<qiun-data-charts type="radar" :chartData="qualityRadarData" :opts="radarChartOpts"/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 班组详细数据 -->
|
||||||
|
<view class="detail-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">详细数据</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-table">
|
||||||
|
<view class="table-header">
|
||||||
|
<text class="table-cell header-cell" v-for="col in columns" :key="col.key">{{ col.title }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="table-row" v-for="(row, index) in tableData" :key="index">
|
||||||
|
<text class="table-cell" v-for="col in columns" :key="col.key">{{ row[col.key] }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="tableData.length === 0" class="empty-state">
|
||||||
|
<text class="empty-text">暂无数据</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// 1. 导入组件(路径需根据实际项目结构调整)
|
import { getTeamPerformance } from '@/api/pocket/plantState'
|
||||||
import klpTable from "../klp-ui/k-table/k-table.vue";
|
|
||||||
|
|
||||||
// 2. 独立工具函数(避免data初始化时调用this.methods的问题)
|
// 工具函数
|
||||||
function getCurrentMonth() {
|
function getCurrentMonth() {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
@@ -56,104 +92,215 @@ function getCurrentMonth() {
|
|||||||
return `${year}-${month}`;
|
return `${year}-${month}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultMonth() {
|
|
||||||
return getCurrentMonth();
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 3. 显式注册局部组件(Vue 2 必需)
|
|
||||||
components: {
|
|
||||||
klpTable
|
|
||||||
},
|
|
||||||
// 4. 响应式数据(替代 Vue 3 的 ref)
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 默认选中当前月份
|
selectedMonth: getCurrentMonth(),
|
||||||
selectedMonth: getDefaultMonth(),
|
|
||||||
// 可选月份范围:2020年1月 - 当前月
|
|
||||||
startDate: "2020-01",
|
startDate: "2020-01",
|
||||||
endDate: getCurrentMonth(),
|
endDate: getCurrentMonth(),
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
columns: [
|
columns: [
|
||||||
{ title: '班组', key: 'team' },
|
{ title: '班组', key: 'team' },
|
||||||
{ title: '作业次数', key: 'workCount' },
|
{ title: '产量(t)', key: 'output' },
|
||||||
{ title: '原始产量', key: 'originalOutput' },
|
|
||||||
{ title: '成材率', key: 'yieldRate' },
|
{ title: '成材率', key: 'yieldRate' },
|
||||||
{ title: '厚度合格率', key: 'thicknessPassRate' }
|
{ title: '合格率', key: 'passRate' },
|
||||||
|
{ title: '综合评分', key: 'score' }
|
||||||
],
|
],
|
||||||
// 表格数据
|
// 表格数据
|
||||||
tableData: [
|
tableData: [],
|
||||||
{ team: '班组1', workCount: 100, originalOutput: 1000, yieldRate: '95%', thicknessPassRate: '90%' },
|
// 班组排名数据
|
||||||
{ team: '班组2', workCount: 120, originalOutput: 1200, yieldRate: '92%', thicknessPassRate: '88%' },
|
teamRankingData: [
|
||||||
{ team: '班组3', workCount: 90, originalOutput: 900, yieldRate: '93%', thicknessPassRate: '91%' },
|
{ team: '甲班', shift: '早班', score: 96.8 },
|
||||||
{ team: '班组4', workCount: 110, originalOutput: 1100, yieldRate: '94%', thicknessPassRate: '89%' }
|
{ team: '乙班', shift: '中班', score: 95.5 },
|
||||||
]
|
{ team: '丙班', shift: '晚班', score: 94.2 },
|
||||||
|
{ team: '丁班', shift: '早班', score: 93.6 }
|
||||||
|
],
|
||||||
|
// 产量对比图
|
||||||
|
outputChartData: {},
|
||||||
|
columnChartOpts: {
|
||||||
|
color: ["#0066cc"],
|
||||||
|
padding: [15, 15, 0, 15],
|
||||||
|
enableScroll: false,
|
||||||
|
xAxis: { disableGrid: true },
|
||||||
|
yAxis: {
|
||||||
|
gridType: "dash",
|
||||||
|
dashLength: 4,
|
||||||
|
gridColor: "#e4e7ed",
|
||||||
|
data: [{ title: "产量(t)" }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 质量指标雷达图
|
||||||
|
qualityRadarData: {
|
||||||
|
categories: [],
|
||||||
|
series: []
|
||||||
|
},
|
||||||
|
radarChartOpts: {
|
||||||
|
color: ["#0066cc", "#409eff", "#66b1ff", "#a0cfff"],
|
||||||
|
padding: [15, 15, 15, 15],
|
||||||
|
enableScroll: false,
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
position: "bottom",
|
||||||
|
lineHeight: 16,
|
||||||
|
fontSize: 10,
|
||||||
|
fontColor: "#666"
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
radar: {
|
||||||
|
gridType: "radar",
|
||||||
|
gridColor: "#e4e7ed",
|
||||||
|
gridCount: 5,
|
||||||
|
opacity: 0.2,
|
||||||
|
labelShow: true,
|
||||||
|
max: 100,
|
||||||
|
labelColor: "#666666",
|
||||||
|
labelFontSize: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// 5. 计算属性(替代 Vue 3 的 computed 函数)
|
|
||||||
computed: {
|
computed: {
|
||||||
// 是否为最小可选月份(2020-01)
|
|
||||||
isMinMonth() {
|
isMinMonth() {
|
||||||
return this.selectedMonth === this.startDate;
|
return this.selectedMonth === this.startDate;
|
||||||
},
|
},
|
||||||
// 是否为最大可选月份(当前月)
|
|
||||||
isMaxMonth() {
|
isMaxMonth() {
|
||||||
return this.selectedMonth === this.endDate;
|
return this.selectedMonth === this.endDate;
|
||||||
},
|
},
|
||||||
// 格式化月份显示(如:2024年05月)
|
|
||||||
formattedMonth() {
|
formattedMonth() {
|
||||||
const [year, month] = this.selectedMonth.split("-");
|
const [year, month] = this.selectedMonth.split("-");
|
||||||
return `${year}年${month}月`;
|
return `${year}年${month}月`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 6. 方法定义(所有函数需放在 methods 中)
|
mounted() {
|
||||||
|
this.loadTeamPerformance();
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 选择器切换月份(picker组件自带事件)
|
|
||||||
handleMonthChange(e) {
|
handleMonthChange(e) {
|
||||||
this.selectedMonth = e.detail.value;
|
this.selectedMonth = e.detail.value;
|
||||||
|
this.loadTeamPerformance();
|
||||||
},
|
},
|
||||||
// 箭头切换月份(左减1,右加1)
|
|
||||||
changeMonth(step) {
|
changeMonth(step) {
|
||||||
// 解析当前选中的年月
|
|
||||||
const [year, month] = this.selectedMonth.split("-").map(Number);
|
const [year, month] = this.selectedMonth.split("-").map(Number);
|
||||||
// 计算目标月份(month从0开始,需-1后+step)
|
|
||||||
const targetDate = new Date(year, month - 1 + step);
|
const targetDate = new Date(year, month - 1 + step);
|
||||||
// 格式化目标月份为 YYYY-MM
|
|
||||||
const targetMonth = `${targetDate.getFullYear()}-${(targetDate.getMonth() + 1).toString().padStart(2, "0")}`;
|
const targetMonth = `${targetDate.getFullYear()}-${(targetDate.getMonth() + 1).toString().padStart(2, "0")}`;
|
||||||
|
|
||||||
// 边界校验:不能小于最小月,不能大于最大月
|
|
||||||
if (this.compareMonths(targetMonth, this.startDate) < 0) return;
|
if (this.compareMonths(targetMonth, this.startDate) < 0) return;
|
||||||
if (this.compareMonths(targetMonth, this.endDate) > 0) return;
|
if (this.compareMonths(targetMonth, this.endDate) > 0) return;
|
||||||
|
|
||||||
// 更新选中月份
|
|
||||||
this.selectedMonth = targetMonth;
|
this.selectedMonth = targetMonth;
|
||||||
|
this.loadTeamPerformance();
|
||||||
},
|
},
|
||||||
// 比较两个月份(a > b 返回1,a = b 返回0,a < b 返回-1)
|
|
||||||
compareMonths(a, b) {
|
compareMonths(a, b) {
|
||||||
const [aYear, aMonth] = a.split("-").map(Number);
|
const [aYear, aMonth] = a.split("-").map(Number);
|
||||||
const [bYear, bMonth] = b.split("-").map(Number);
|
const [bYear, bMonth] = b.split("-").map(Number);
|
||||||
|
|
||||||
if (aYear !== bYear) return aYear - bYear;
|
if (aYear !== bYear) return aYear - bYear;
|
||||||
return aMonth - bMonth;
|
return aMonth - bMonth;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载班组绩效数据
|
||||||
|
loadTeamPerformance() {
|
||||||
|
uni.showLoading({ title: '加载中' })
|
||||||
|
|
||||||
|
// 转换月份为完整日期范围
|
||||||
|
const startDate = `${this.selectedMonth}-01`
|
||||||
|
const [year, month] = this.selectedMonth.split('-')
|
||||||
|
const lastDay = new Date(year, month, 0).getDate()
|
||||||
|
const endDate = `${this.selectedMonth}-${String(lastDay).padStart(2, '0')}`
|
||||||
|
|
||||||
|
console.log('查询班组绩效:', startDate, endDate)
|
||||||
|
|
||||||
|
getTeamPerformance(startDate, endDate).then(response => {
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (response.code === 200 && response.data && response.data.length > 0) {
|
||||||
|
const teamData = response.data
|
||||||
|
console.log('班组绩效数据:', teamData)
|
||||||
|
|
||||||
|
// 排名数据(取前4名)
|
||||||
|
this.teamRankingData = teamData.slice(0, 4).map(item => ({
|
||||||
|
team: item.crew,
|
||||||
|
shift: item.shift,
|
||||||
|
score: Number(item.score) || 0
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
this.tableData = teamData.map(item => ({
|
||||||
|
team: item.teamName,
|
||||||
|
output: Number(item.output) || 0,
|
||||||
|
yieldRate: `${Number(item.yieldRate) || 0}%`,
|
||||||
|
passRate: `${Number(item.passRate) || 0}%`,
|
||||||
|
score: Number(item.score) || 0
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 产量对比柱状图
|
||||||
|
this.outputChartData = {
|
||||||
|
categories: teamData.map(item => item.teamName),
|
||||||
|
series: [
|
||||||
|
{ name: '产量', data: teamData.map(item => Number(item.output) || 0) }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 质量指标雷达图
|
||||||
|
this.qualityRadarData = {
|
||||||
|
categories: ['产量指标', '成材率', '合格率', '厚度质量', '板形质量'],
|
||||||
|
series: teamData.map(item => ({
|
||||||
|
name: item.teamName,
|
||||||
|
data: [
|
||||||
|
Number(this.normalizeValue(item.output, 1500)), // 产量归一化到100
|
||||||
|
Number(item.yieldRate) || 0,
|
||||||
|
Number(item.passRate) || 0,
|
||||||
|
Number(item.avgThickQuality) || 0,
|
||||||
|
Number(item.avgShapeQuality) || 0
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('暂无班组绩效数据')
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('加载班组绩效失败:', error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 数值归一化到100(用于雷达图)
|
||||||
|
normalizeValue(value, max) {
|
||||||
|
return Math.min(100, (Number(value) / max) * 100).toFixed(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
/* 月份选择器 */
|
/* 页面容器 */
|
||||||
.month-picker {
|
.page-container {
|
||||||
padding: 20rpx;
|
background: #f5f7fa;
|
||||||
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-content {
|
/* 月份选择器 */
|
||||||
|
.month-selector {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 24rpx 20rpx;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 12rpx;
|
border-radius: 8rpx;
|
||||||
border: 1rpx solid #e8e8e8;
|
padding: 20rpx 24rpx;
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
margin-bottom: 24rpx;
|
||||||
|
border: 1rpx solid #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.month-arrow {
|
||||||
|
padding: 12rpx 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #0066cc;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: #dcdfe6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.month-display {
|
.month-display {
|
||||||
@@ -161,42 +308,176 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
gap: 12rpx;
|
||||||
|
|
||||||
.arrow {
|
|
||||||
padding: 0 24rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: #1a73e8;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.7;
|
|
||||||
transform: scale(0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
color: #ccc;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
color: #666;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.month-text {
|
.month-text {
|
||||||
color: #1a73e8;
|
font-size: 32rpx;
|
||||||
|
color: #0066cc;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 30rpx;
|
|
||||||
margin: 0 20rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.picker-icon {
|
||||||
color: #999;
|
font-size: 20rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 区块样式 */
|
||||||
|
.ranking-section,
|
||||||
|
.chart-section,
|
||||||
|
.detail-section {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-left: 16rpx;
|
||||||
|
border-left: 4rpx solid #0066cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 排名列表 */
|
||||||
|
.ranking-list {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 1rpx solid #e4e7ed;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ranking-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-bottom: 1rpx solid #f5f7fa;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.rank-1 .rank-badge {
|
||||||
|
background: #0066cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.rank-2 .rank-badge {
|
||||||
|
background: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.rank-3 .rank-badge {
|
||||||
|
background: #66b1ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-badge {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #a0cfff;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-shift {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
margin-left: 20rpx;
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-score {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
font-size: 48rpx;
|
||||||
|
color: #0066cc;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-label {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图表容器 */
|
||||||
|
.chart-wrapper {
|
||||||
|
background: #fff;
|
||||||
|
border: 1rpx solid #e4e7ed;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 24rpx 16rpx;
|
||||||
|
min-height: 450rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 详细数据表格 */
|
||||||
|
.detail-table {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 1rpx solid #e4e7ed;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
display: flex;
|
||||||
|
background: #0066cc;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-cell {
|
||||||
|
color: #fff !important;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1rpx solid #f5f7fa;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24rpx 12rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态 */
|
||||||
|
.empty-state {
|
||||||
|
padding: 100rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #909399;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,141 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<!-- 简洁标签栏 -->
|
|
||||||
<view class="tab-container">
|
|
||||||
<view
|
|
||||||
v-for="item in tabData"
|
|
||||||
:key="item.value"
|
|
||||||
@click="currentTab = item.value"
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ 'tab-active': currentTab === item.value }"
|
|
||||||
>
|
|
||||||
<text class="tab-label">{{ item.text }}</text>
|
|
||||||
<view class="tab-indicator" v-if="currentTab === item.value"></view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<scroll-view scroll-y v-if="currentTab === 1">
|
|
||||||
<view class="content-card">
|
|
||||||
<k-metric-card
|
|
||||||
:items="status"
|
|
||||||
:columns="2"
|
|
||||||
></k-metric-card>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="content-card">
|
|
||||||
<klp-collapse-panel title="状态统计">
|
|
||||||
<qiun-data-charts type="line" :chartData="chartData" />
|
|
||||||
</klp-collapse-panel>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="content-card">
|
|
||||||
<klp-collapse-panel title="轧机状态">
|
|
||||||
<qiun-data-charts type="column" :chartData="chartData" />
|
|
||||||
</klp-collapse-panel>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="content-card">
|
|
||||||
<klp-collapse-panel title="机组跟踪">
|
|
||||||
<view style="padding: 30rpx; display: flex; flex-direction: column; align-items: stretch; justify-content: center; gap: 20rpx;">
|
|
||||||
<view style="display: flex; justify-content: space-between; align-items: center; padding: 20rpx; background-color: #d9edf6; border-radius: 10rpx;">
|
|
||||||
<text>轧机</text>
|
|
||||||
<text>6390000</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view style="border-radius: 10rpx; overflow: hidden;">
|
|
||||||
<view style="display: flex; justify-content: space-between; align-items: center; padding: 20rpx; background-color: #d9edf6; border: 1px solid #d9edf6;">
|
|
||||||
<text>圆剪盘</text>
|
|
||||||
<text>6390000</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-container">
|
|
||||||
<view class="info-row">
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">设备编号:</text>
|
|
||||||
<text class="info-value">M-001</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">运行状态:</text>
|
|
||||||
<text class="info-value">正常</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="info-row">
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">当前产量:</text>
|
|
||||||
<text class="info-value">2580 件</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">合格率:</text>
|
|
||||||
<text class="info-value">98.5%</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view style="display: flex; justify-content: space-between; align-items: center; padding: 20rpx; background-color: #d9edf6; border-radius: 10rpx;">
|
|
||||||
<text>酸洗</text>
|
|
||||||
<text>6390000</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view style="border-radius: 10rpx; overflow: hidden;">
|
|
||||||
<view style="display: flex; justify-content: space-between; align-items: center; padding: 20rpx; background-color: #d9edf6; border: 1px solid #d9edf6;">
|
|
||||||
<text>入口活套</text>
|
|
||||||
<text>6390000</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-container">
|
|
||||||
<view class="info-row">
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">设备编号:</text>
|
|
||||||
<text class="info-value">M-001</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">运行状态:</text>
|
|
||||||
<text class="info-value">正常</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="info-row">
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">当前产量:</text>
|
|
||||||
<text class="info-value">2580 件</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="info-label">合格率:</text>
|
|
||||||
<text class="info-value">98.5%</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</klp-collapse-panel>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="content-card">
|
|
||||||
<klp-collapse-panel title="能耗">
|
|
||||||
<view class="metric-container">
|
|
||||||
<!-- 网络状态指标 -->
|
|
||||||
<view class="metric-item">
|
|
||||||
<view class="metric-value">
|
|
||||||
9.9
|
|
||||||
</view>
|
|
||||||
<view class="metric-label">
|
|
||||||
工艺缎带钢线速度
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 班组指标 -->
|
|
||||||
<view class="metric-item">
|
|
||||||
<view class="metric-value team-number">
|
|
||||||
126.0
|
|
||||||
</view>
|
|
||||||
<view class="metric-label">
|
|
||||||
轧机出口带钢线速度
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</klp-collapse-panel>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
|
|
||||||
<scroll-view scroll-y v-if="currentTab == 2">
|
<scroll-view scroll-y v-if="currentTab == 2">
|
||||||
<klp-product-statistic></klp-product-statistic>
|
<klp-product-statistic></klp-product-statistic>
|
||||||
@@ -152,7 +16,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listPlantStateCurrent, listPlantStateHistory } from '@/api/pocket/plantState'
|
import { getAllPlantStateDefines, listPlantStateHistory } from '@/api/pocket/plantState'
|
||||||
|
import config from '@/config'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 响应式数据(替代 Vue 3 的 ref)
|
// 响应式数据(替代 Vue 3 的 ref)
|
||||||
@@ -166,91 +31,77 @@ export default {
|
|||||||
{ text: "班组绩效", value: 4 }
|
{ text: "班组绩效", value: 4 }
|
||||||
],
|
],
|
||||||
status: [ // 状态指标数据(供 k-metric-card 使用)
|
status: [ // 状态指标数据(供 k-metric-card 使用)
|
||||||
{ label: '网络状态', value: '正常' },
|
{ label: '网络状态', value: '检测中...' },
|
||||||
{ label: '当前班组', value: '乙 / 中' }
|
{ label: '当前班组', value: '—' }
|
||||||
],
|
],
|
||||||
chartData: {} // 图表数据(初始化空对象)
|
plantStateDefines: [] // 缓存所有的状态定义
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 生命周期钩子(替代 Vue 3 的 onMounted)
|
// 生命周期钩子(替代 Vue 3 的 onMounted)
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getServerData() // 页面挂载后加载数据
|
this.checkNetworkStatus() // 检测网络状态
|
||||||
this.loadCurrentState() // 加载当前状态
|
this.initPlantStateDefines() // 先加载所有定义
|
||||||
},
|
},
|
||||||
// 方法定义(所有函数需放在 methods 中)
|
// 方法定义(所有函数需放在 methods 中)
|
||||||
methods: {
|
methods: {
|
||||||
// 加载当前设备状态
|
// 检测网络状态
|
||||||
loadCurrentState() {
|
checkNetworkStatus() {
|
||||||
uni.showLoading({ title: '加载中' })
|
const startTime = Date.now()
|
||||||
listPlantStateCurrent({ pageNum: 1, pageSize: 10 }).then(response => {
|
|
||||||
uni.hideLoading()
|
|
||||||
if (response.code === 200 && response.rows) {
|
|
||||||
// 处理当前状态数据
|
|
||||||
console.log('当前状态数据:', response.rows)
|
|
||||||
// 可以根据实际需求更新status数据
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
uni.hideLoading()
|
|
||||||
console.error('加载设备状态失败:', error)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 从服务器获取历史数据用于图表
|
// 使用uni.request测试网络连接速度
|
||||||
getServerData() {
|
uni.request({
|
||||||
uni.showLoading({ title: '加载中' })
|
url: config.baseUrl + '/pocket/proPlantStateDefine/allWithValues',
|
||||||
|
method: 'GET',
|
||||||
|
timeout: 5000,
|
||||||
|
success: (res) => {
|
||||||
|
const responseTime = Date.now() - startTime
|
||||||
|
|
||||||
listPlantStateHistory({ pageNum: 1, pageSize: 20 }).then(response => {
|
// 根据响应时间判断网络状态
|
||||||
uni.hideLoading()
|
if (responseTime < 500) {
|
||||||
|
this.status[0].value = '通畅'
|
||||||
if (response.code === 200 && response.rows && response.rows.length > 0) {
|
} else if (responseTime < 2000) {
|
||||||
// 处理历史数据,转换为图表需要的格式
|
this.status[0].value = '卡顿'
|
||||||
const categories = []
|
|
||||||
const targetData = []
|
|
||||||
const completeData = []
|
|
||||||
|
|
||||||
response.rows.forEach(item => {
|
|
||||||
// 根据实际数据结构调整
|
|
||||||
const dateStr = this.formatDate(item.insdate)
|
|
||||||
categories.push(dateStr)
|
|
||||||
// 这里使用value1和value2作为示例,实际应根据业务需求调整
|
|
||||||
targetData.push(item.value1 || 0)
|
|
||||||
completeData.push(item.value2 || 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = {
|
|
||||||
categories: categories,
|
|
||||||
series: [
|
|
||||||
{ name: '目标值', data: targetData },
|
|
||||||
{ name: '完成量', data: completeData }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartData = JSON.parse(JSON.stringify(res))
|
|
||||||
} else {
|
} else {
|
||||||
// 如果没有数据,使用默认数据
|
this.status[0].value = '异常'
|
||||||
this.loadDefaultChartData()
|
}
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
this.status[0].value = '异常'
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
|
||||||
uni.hideLoading()
|
|
||||||
console.error('加载历史数据失败:', error)
|
|
||||||
// 加载失败时使用默认数据
|
|
||||||
this.loadDefaultChartData()
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 加载默认图表数据
|
// 初始化:加载所有状态定义及其当前值
|
||||||
loadDefaultChartData() {
|
initPlantStateDefines() {
|
||||||
const res = {
|
uni.showLoading({ title: '加载中' })
|
||||||
categories: ['2016', '2017', '2018', '2019', '2020', '2021'],
|
|
||||||
series: [
|
getAllPlantStateDefines().then(response => {
|
||||||
{ name: '目标值', data: [35, 36, 31, 33, 13, 34] },
|
if (response.code === 200 && response.data) {
|
||||||
{ name: '完成量', data: [18, 27, 21, 24, 6, 28] }
|
// 缓存所有定义
|
||||||
]
|
this.plantStateDefines = response.data
|
||||||
|
console.log('状态定义已加载:', this.plantStateDefines)
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 这里可以根据 plantStateDefines 来处理首页数据
|
||||||
|
// 例如:this.initHomePageData()
|
||||||
|
} else {
|
||||||
|
uni.hideLoading()
|
||||||
}
|
}
|
||||||
this.chartData = JSON.parse(JSON.stringify(res))
|
}).catch(error => {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('加载状态定义失败:', error)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 格式化日期
|
|
||||||
|
// 根据define ID获取对应的值(工具方法)
|
||||||
|
getValueByDefineId(dataRow, defineId) {
|
||||||
|
const fieldName = `value${defineId}`
|
||||||
|
return dataRow[fieldName] || null
|
||||||
|
},
|
||||||
|
|
||||||
|
// 格式化日期(工具方法)
|
||||||
formatDate(dateStr) {
|
formatDate(dateStr) {
|
||||||
if (!dateStr) return ''
|
if (!dateStr) return ''
|
||||||
const date = new Date(dateStr)
|
const date = new Date(dateStr)
|
||||||
@@ -320,6 +171,17 @@ page {
|
|||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 空状态占位 */
|
||||||
|
.empty-placeholder {
|
||||||
|
padding: 100rpx 40rpx;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 指标容器 */
|
/* 指标容器 */
|
||||||
.metric-container {
|
.metric-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -348,6 +210,12 @@ page {
|
|||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #666;
|
color: #666;
|
||||||
letter-spacing: 1rpx;
|
letter-spacing: 1rpx;
|
||||||
|
|
||||||
|
.metric-unit {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 信息容器 */
|
/* 信息容器 */
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 应用全局配置
|
// 应用全局配置
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// baseUrl: 'http://192.168.31.116:8080',
|
baseUrl: 'http://192.168.31.116:8080',
|
||||||
baseUrl: 'http://140.143.206.120:8080',
|
// baseUrl: 'http://140.143.206.120:8080',
|
||||||
// baseUrl: 'http://localhost:8080',
|
// baseUrl: 'http://localhost:8080',
|
||||||
// 应用信息
|
// 应用信息
|
||||||
appInfo: {
|
appInfo: {
|
||||||
|
|||||||
Reference in New Issue
Block a user