修改移动端内容,后端添加了所有查询接口

This commit is contained in:
2025-10-31 17:18:30 +08:00
parent 9800a37055
commit ac541472c2
9 changed files with 2530 additions and 1262 deletions

View File

@@ -1,54 +1,90 @@
<template>
<view>
<view class="month-picker">
<picker
mode="date"
fields="month"
:value="selectedMonth"
:start="startDate"
:end="endDate"
@change="handleMonthChange"
>
<view class="picker-content">
<!-- 左箭头 -->
<view
class="arrow"
@click.stop="changeMonth(-1)"
:class="{ disabled: isMinMonth }"
>
</view>
<view class="month-display">
<text class="label">选择月份</text>
<text class="month-text">{{ formattedMonth }}</text>
</view>
<!-- 右箭头 -->
<view
class="arrow"
@click.stop="changeMonth(1)"
:class="{ disabled: isMaxMonth }"
>
</view>
<text class="icon"></text>
<view class="page-container">
<!-- 月份选择器 -->
<view class="month-selector">
<view class="month-arrow" @click="changeMonth(-1)" :class="{ disabled: isMinMonth }">
<text></text>
</view>
<picker mode="date" fields="month" :value="selectedMonth" :start="startDate" :end="endDate" @change="handleMonthChange">
<view class="month-display">
<text class="month-text">{{ formattedMonth }}</text>
<text class="picker-icon"></text>
</view>
</picker>
<view class="month-arrow" @click="changeMonth(1)" :class="{ disabled: isMaxMonth }">
<text></text>
</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>
</template>
<script>
// 1. 导入组件(路径需根据实际项目结构调整)
import klpTable from "../klp-ui/k-table/k-table.vue";
import { getTeamPerformance } from '@/api/pocket/plantState'
// 2. 独立工具函数避免data初始化时调用this.methods的问题
// 工具函数
function getCurrentMonth() {
const date = new Date();
const year = date.getFullYear();
@@ -56,104 +92,215 @@ function getCurrentMonth() {
return `${year}-${month}`;
}
function getDefaultMonth() {
return getCurrentMonth();
}
export default {
// 3. 显式注册局部组件Vue 2 必需)
components: {
klpTable
},
// 4. 响应式数据(替代 Vue 3 的 ref
data() {
return {
// 默认选中当前月份
selectedMonth: getDefaultMonth(),
// 可选月份范围2020年1月 - 当前月
selectedMonth: getCurrentMonth(),
startDate: "2020-01",
endDate: getCurrentMonth(),
// 表格列配置
columns: [
{ title: '班组', key: 'team' },
{ title: '作业次数', key: 'workCount' },
{ title: '原始产量', key: 'originalOutput' },
{ title: '产量(t)', key: 'output' },
{ title: '成材率', key: 'yieldRate' },
{ title: '厚度合格率', key: 'thicknessPassRate' }
{ title: '合格率', key: 'passRate' },
{ title: '综合评分', key: 'score' }
],
// 表格数据
tableData: [
{ team: '班组1', workCount: 100, originalOutput: 1000, yieldRate: '95%', thicknessPassRate: '90%' },
{ team: '班组2', workCount: 120, originalOutput: 1200, yieldRate: '92%', thicknessPassRate: '88%' },
{ team: '班组3', workCount: 90, originalOutput: 900, yieldRate: '93%', thicknessPassRate: '91%' },
{ team: '班组4', workCount: 110, originalOutput: 1100, yieldRate: '94%', thicknessPassRate: '89%' }
]
tableData: [],
// 班组排名数据
teamRankingData: [
{ team: '班', shift: '早班', score: 96.8 },
{ 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: {
// 是否为最小可选月份2020-01
isMinMonth() {
return this.selectedMonth === this.startDate;
},
// 是否为最大可选月份(当前月)
isMaxMonth() {
return this.selectedMonth === this.endDate;
},
// 格式化月份显示2024年05月
formattedMonth() {
const [year, month] = this.selectedMonth.split("-");
return `${year}${month}`;
}
},
// 6. 方法定义(所有函数需放在 methods 中)
mounted() {
this.loadTeamPerformance();
},
methods: {
// 选择器切换月份picker组件自带事件
handleMonthChange(e) {
this.selectedMonth = e.detail.value;
this.loadTeamPerformance();
},
// 箭头切换月份左减1右加1
changeMonth(step) {
// 解析当前选中的年月
const [year, month] = this.selectedMonth.split("-").map(Number);
// 计算目标月份month从0开始需-1后+step
const targetDate = new Date(year, month - 1 + step);
// 格式化目标月份为 YYYY-MM
const targetMonth = `${targetDate.getFullYear()}-${(targetDate.getMonth() + 1).toString().padStart(2, "0")}`;
// 边界校验:不能小于最小月,不能大于最大月
if (this.compareMonths(targetMonth, this.startDate) < 0) return;
if (this.compareMonths(targetMonth, this.endDate) > 0) return;
// 更新选中月份
this.selectedMonth = targetMonth;
this.loadTeamPerformance();
},
// 比较两个月份a > b 返回1a = b 返回0a < b 返回-1
compareMonths(a, b) {
const [aYear, aMonth] = a.split("-").map(Number);
const [bYear, bMonth] = b.split("-").map(Number);
if (aYear !== bYear) return aYear - bYear;
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>
<style scoped>
/* 月份选择器 */
.month-picker {
padding: 20rpx;
<style scoped lang="scss">
/* 页面容器 */
.page-container {
background: #f5f7fa;
padding: 24rpx;
}
.picker-content {
/* 月份选择器 */
.month-selector {
display: flex;
align-items: center;
padding: 24rpx 20rpx;
background: #fff;
border-radius: 12rpx;
border: 1rpx solid #e8e8e8;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
border-radius: 8rpx;
padding: 20rpx 24rpx;
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 {
@@ -161,42 +308,176 @@ export default {
display: flex;
align-items: center;
justify-content: center;
}
.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;
gap: 12rpx;
}
.month-text {
color: #1a73e8;
font-size: 32rpx;
color: #0066cc;
font-weight: 600;
font-size: 30rpx;
margin: 0 20rpx;
}
.icon {
color: #999;
font-size: 24rpx;
margin-left: 20rpx;
.picker-icon {
font-size: 20rpx;
color: #909399;
}
</style>
/* 区块样式 */
.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;
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>