210 lines
5.1 KiB
Vue
210 lines
5.1 KiB
Vue
<template>
|
||
<view class="standard-container">
|
||
<view class="standard-header">
|
||
<text class="standard-title">履职标准</text>
|
||
<view>
|
||
<DatePicker
|
||
:modelValue="dateTimestamp"
|
||
format="YYYY年MM月"
|
||
:showIcon="true"
|
||
:showArrow="false"
|
||
iconColor="#333"
|
||
@change="handleDateChange"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="standard-cards">
|
||
<view class="standard-card">
|
||
<text class="card-label">现场履职</text>
|
||
<text class="card-value">实到: {{ standardDataInfo.real_xclz_num }}</text>
|
||
</view>
|
||
<view class="standard-card">
|
||
<text class="card-label">班组履职</text>
|
||
<text class="card-value">实到: {{ standardDataInfo.real_bzlz_num }}</text>
|
||
</view>
|
||
<view class="standard-card problem-card" @tap="handleProblemClick">
|
||
<text class="card-label">发现问题</text>
|
||
<text class="card-value problem-value">{{ standardDataInfo.problemNum }}项</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="standard-note">
|
||
<text class="note-text">*单日督察多个现场统计为1次</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from 'vue'
|
||
import dayjs from 'dayjs'
|
||
import DatePicker from '@/components/DatePicker/index.vue'
|
||
import { getHomeStatisticsApi } from '@/services/leader/home'
|
||
|
||
/**
|
||
* 履职标准组件
|
||
* 业务背景:展示当前月份的履职标准数据,包括现场履职、班组履职和发现问题数量
|
||
* 设计决策:
|
||
* 1. 支持日期选择,查看不同月份的数据
|
||
* 2. 三个指标卡片横向排列,使用绿色背景突出显示
|
||
* 3. 发现问题数量可点击,进入详情页面
|
||
* 4. 底部显示说明文字,解释统计规则
|
||
*/
|
||
|
||
const standardDataInfo = ref({
|
||
bzlz_unit_type: '次',
|
||
problemNum: 0,
|
||
real_bzlz_num: 0,
|
||
real_xclz_num: 0,
|
||
xclz_unit_type: '次',
|
||
})
|
||
|
||
const props = defineProps({
|
||
// 选中的日期(YYYY-MM格式)
|
||
selectedDate: {
|
||
type: String,
|
||
required: true,
|
||
},
|
||
// 履职标准数据
|
||
standardData: {
|
||
type: Object,
|
||
required: true,
|
||
default: () => ({
|
||
onSitePerformance: { actual: 0, required: 0 },
|
||
teamPerformance: { actual: 0, required: 0 },
|
||
problemsFound: { count: 0 },
|
||
}),
|
||
},
|
||
})
|
||
|
||
const emit = defineEmits(['date-change', 'problem-click'])
|
||
|
||
/**
|
||
* 计算日期时间戳
|
||
* 业务背景:DatePicker 组件需要时间戳格式
|
||
* 设计决策:将 YYYY-MM 格式转换为时间戳
|
||
*/
|
||
const dateTimestamp = computed(() => {
|
||
return dayjs(props.selectedDate).valueOf()
|
||
})
|
||
|
||
/**
|
||
* 处理日期变更
|
||
* @param {Number} timestamp - 时间戳
|
||
*/
|
||
const handleDateChange = (timestamp) => {
|
||
emit('date-change', timestamp)
|
||
getPerformanceStandardData()
|
||
}
|
||
|
||
/**
|
||
* 处理发现问题点击
|
||
* 业务背景:点击发现问题数量后,进入详情页面
|
||
*/
|
||
const handleProblemClick = () => {
|
||
emit('problem-click')
|
||
}
|
||
|
||
// 获取履职标准数据
|
||
const getPerformanceStandardData = async () => {
|
||
const res = await getHomeStatisticsApi({
|
||
yyyyMM: props.selectedDate.split('-').join(''),
|
||
})
|
||
if (res.code === 200) {
|
||
standardDataInfo.value = res.data
|
||
}
|
||
}
|
||
|
||
getPerformanceStandardData()
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.standard-container {
|
||
margin: 0 32rpx 32rpx;
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
padding: 40rpx 32rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.standard-header {
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 32rpx;
|
||
padding-bottom: 24rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.standard-title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #333;
|
||
letter-spacing: 0.5rpx;
|
||
}
|
||
|
||
.standard-cards {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.standard-card {
|
||
flex: 1;
|
||
background: linear-gradient(135deg, #07c160 0%, #06a050 100%);
|
||
border-radius: 16rpx;
|
||
padding: 36rpx 20rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 180rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(7, 193, 96, 0.25);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.problem-card {
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.problem-card:active {
|
||
transform: translateY(-2rpx) scale(0.98);
|
||
box-shadow: 0 6rpx 16rpx rgba(7, 193, 96, 0.35);
|
||
}
|
||
|
||
.card-label {
|
||
font-size: 28rpx;
|
||
color: rgba(255, 255, 255, 0.95);
|
||
margin-bottom: 20rpx;
|
||
font-weight: 600;
|
||
letter-spacing: 0.5rpx;
|
||
}
|
||
|
||
.card-value {
|
||
font-size: 36rpx;
|
||
color: #fff;
|
||
font-weight: 700;
|
||
letter-spacing: 1rpx;
|
||
}
|
||
|
||
.problem-value {
|
||
color: #fff;
|
||
background: rgba(255, 68, 68, 0.9);
|
||
padding: 4rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.standard-note {
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.note-text {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
line-height: 1.5;
|
||
}
|
||
</style>
|