|
|
@@ -0,0 +1,406 @@
|
|
|
+<template>
|
|
|
+ <view class="attendance-page">
|
|
|
+ <view class="header">
|
|
|
+ <text class="title">我的考勤记录</text>
|
|
|
+ <picker mode="selector" :range="timeRanges" @change="onTimeRangeChange">
|
|
|
+ <view class="picker">
|
|
|
+ {{ selectedTimeRange }}
|
|
|
+ </view>
|
|
|
+ </picker>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="statistics" :class="{'show': chartShow}">
|
|
|
+ <view class="statistic-card">
|
|
|
+ <view class="statistic-title">考勤统计</view>
|
|
|
+ <view class="statistic-item">
|
|
|
+ <text class="label">出勤:</text>
|
|
|
+ <text class="value">{{ attendanceCount }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="statistic-item">
|
|
|
+ <text class="label">请假:</text>
|
|
|
+ <text class="value">{{ leaveCount }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="statistic-item">
|
|
|
+ <text class="label">出差:</text>
|
|
|
+ <text class="value">{{ businessTripCount }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="statistic-item">
|
|
|
+ <text class="label">打卡:</text>
|
|
|
+ <text class="value">{{ clockInCount }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="statistic-item">
|
|
|
+ <text class="label">补卡:</text>
|
|
|
+ <text class="value">{{ makeUpCount }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="todayCheckIn" :class="{'show': shouldShow}">
|
|
|
+ <view class="check-in-container">
|
|
|
+ <text class="title2">今日签到信息</text>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">日期:</text>
|
|
|
+ <text class="value">{{todayData.day}}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">晨签时间:</text>
|
|
|
+ <text class="value">{{todayData.startTime || '未打卡'}} </text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">晚签时间:</text>
|
|
|
+ <text class="value">{{todayData.endTime || '未打卡'}} </text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">状态:</text>
|
|
|
+ <text class="value">{{todayData.status}}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="chart-container" :class="{'show': chartShow}">
|
|
|
+ <view class="">
|
|
|
+ <text class="chart-title">考勤趋势图</text>
|
|
|
+ <view class="charts-box">
|
|
|
+ <qiun-data-charts :type="chartsType" :opts="opts" :chartData="chartData" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+ import {
|
|
|
+ ref,
|
|
|
+ onMounted,
|
|
|
+ reactive
|
|
|
+ } from 'vue';
|
|
|
+
|
|
|
+ const todayData = reactive({
|
|
|
+ day: '2024-11-11',
|
|
|
+ startTime: '08:59:59',
|
|
|
+ endTime: '',
|
|
|
+ status: '待补卡'
|
|
|
+ })
|
|
|
+ const shouldShow = ref(true)
|
|
|
+ const chartShow = ref(false)
|
|
|
+ //图表类型
|
|
|
+ const chartsType = ref('pie')
|
|
|
+ const chartData = ref({})
|
|
|
+ const opts = ref({
|
|
|
+ color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"],
|
|
|
+ padding: [15, 15, 0, 5],
|
|
|
+ enableScroll: false,
|
|
|
+ legend: {},
|
|
|
+ xAxis: {
|
|
|
+ disableGrid: true
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ data: [{
|
|
|
+ min: 0
|
|
|
+ }]
|
|
|
+ },
|
|
|
+ extra: {
|
|
|
+ column: {
|
|
|
+ type: "group",
|
|
|
+ width: 30,
|
|
|
+ activeBgColor: "#000000",
|
|
|
+ activeBgOpacity: 0.08
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const timeRanges = ref(['日', '周', '月']);
|
|
|
+ const selectedTimeRange = ref('日');
|
|
|
+
|
|
|
+ //出勤...补卡统计
|
|
|
+ const attendanceCount = ref(0);
|
|
|
+ const leaveCount = ref(0);
|
|
|
+ const businessTripCount = ref(0);
|
|
|
+ const clockInCount = ref(0);
|
|
|
+ const makeUpCount = ref(0);
|
|
|
+
|
|
|
+ const currentData = ref({
|
|
|
+ attendance: 0,
|
|
|
+ leave: 0,
|
|
|
+ businessTrip: 0,
|
|
|
+ clockIn: 0,
|
|
|
+ makeUp: 0,
|
|
|
+ res: {},
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //日周月切换
|
|
|
+ const onTimeRangeChange = (event) => {
|
|
|
+
|
|
|
+ const selectedIndex = event.detail.value;
|
|
|
+ console.log('selectedIndex', selectedIndex)
|
|
|
+ selectedTimeRange.value = timeRanges.value[selectedIndex];
|
|
|
+ fetchAttendanceData();
|
|
|
+ switch (selectedTimeRange.value) {
|
|
|
+ case '日':
|
|
|
+ shouldShow.value = true;
|
|
|
+ chartShow.value = false;
|
|
|
+ break;
|
|
|
+ case '周':
|
|
|
+ shouldShow.value = false;
|
|
|
+ chartShow.value = true;
|
|
|
+ console.log(shouldShow.value)
|
|
|
+ getData1();
|
|
|
+ break;
|
|
|
+ case '月':
|
|
|
+ shouldShow.value = false;
|
|
|
+ chartShow.value = true;
|
|
|
+ getData2();
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ const fetchAttendanceData = () => {
|
|
|
+ const mockData = {
|
|
|
+ 日: {
|
|
|
+ attendance: 5,
|
|
|
+ leave: 0,
|
|
|
+ businessTrip: 1,
|
|
|
+ clockIn: 5,
|
|
|
+ makeUp: 0,
|
|
|
+ res: {}
|
|
|
+ },
|
|
|
+ 周: {
|
|
|
+ attendance: 30,
|
|
|
+ leave: 2,
|
|
|
+ businessTrip: 1,
|
|
|
+ clockIn: 32,
|
|
|
+ makeUp: 1,
|
|
|
+ res: {
|
|
|
+ series: [{
|
|
|
+ data: [{
|
|
|
+ "name": "出勤",
|
|
|
+ "value": 50,
|
|
|
+ "labelText": "出勤:50次"
|
|
|
+ }, {
|
|
|
+ "name": "请假",
|
|
|
+ "value": 30
|
|
|
+ }, {
|
|
|
+ "name": "出差",
|
|
|
+ "value": 20
|
|
|
+ }, {
|
|
|
+ "name": "打卡",
|
|
|
+ "value": 18,
|
|
|
+ "labelText": "打卡:18次"
|
|
|
+ }, {
|
|
|
+ "name": "补卡",
|
|
|
+ "value": 8
|
|
|
+ }]
|
|
|
+ }],
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 月: {
|
|
|
+ attendance: 100,
|
|
|
+ leave: 5,
|
|
|
+ businessTrip: 2,
|
|
|
+ clockIn: 105,
|
|
|
+ makeUp: 3,
|
|
|
+ res: {
|
|
|
+ categories: ["1周", "2周", "3周", "4周"],
|
|
|
+ series: [{
|
|
|
+ name: "出勤",
|
|
|
+ data: [35, 8, 25, 37]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "出差",
|
|
|
+ data: [70, 40, 65, 100]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "请假",
|
|
|
+ data: [100, 80, 95, 150]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const currentData1 = mockData[selectedTimeRange.value];
|
|
|
+ currentData.value=currentData1;
|
|
|
+
|
|
|
+ if (currentData1) {
|
|
|
+ attendanceCount.value = currentData1.attendance;
|
|
|
+ leaveCount.value = currentData1.leave;
|
|
|
+ businessTripCount.value = currentData1.businessTrip;
|
|
|
+ clockInCount.value = currentData1.clockIn;
|
|
|
+ makeUpCount.value = currentData1.makeUp;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ console.error('没有找到相关的考勤数据:', selectedTimeRange.value);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ //初始化数据
|
|
|
+ fetchAttendanceData();
|
|
|
+ });
|
|
|
+
|
|
|
+ function getData1() {
|
|
|
+ chartsType.value = 'pie';
|
|
|
+ opts.value.yAxis = {
|
|
|
+ data: [{
|
|
|
+ min: 0
|
|
|
+ }]
|
|
|
+ };
|
|
|
+ opts.value.extra = {
|
|
|
+ column: {
|
|
|
+ type: "group",
|
|
|
+ width: 30,
|
|
|
+ activeBgColor: "#000000",
|
|
|
+ activeBgOpacity: 0.08
|
|
|
+ }
|
|
|
+ };
|
|
|
+ // console.log(currentData.value.res)
|
|
|
+ let res =currentData.value.res;
|
|
|
+ chartData.value = JSON.parse(JSON.stringify(res));
|
|
|
+ }
|
|
|
+
|
|
|
+ function getData2() {
|
|
|
+ chartsType.value = 'column';
|
|
|
+ let res =currentData.value.res;
|
|
|
+ chartData.value = JSON.parse(JSON.stringify(res));
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+ .attendance-page {
|
|
|
+ padding: 20px;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .title {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker {
|
|
|
+ border: 1px solid #3498db;
|
|
|
+ border-radius: 5px;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #ecf6fc;
|
|
|
+ color: #3498db;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statistics {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statistic-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 10px;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
|
+ /* width: 100%; */
|
|
|
+ max-width: 600px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statistic-title {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statistic-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .label {
|
|
|
+ font-size: 18px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+
|
|
|
+ .value {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-container {
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-title {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bold;
|
|
|
+ text-align: center;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .charts-box {
|
|
|
+ width: 100%;
|
|
|
+ height: 300px;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ .todayCheckIn {
|
|
|
+
|
|
|
+ background-color: #ffffff;
|
|
|
+ padding: 20px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ .check-in-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .title2 {
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ .info-row {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ .todayCheckIn,
|
|
|
+ .chart-container,
|
|
|
+ .statistics {
|
|
|
+ /* 默认隐藏 */
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 当需要显示时,可以添加一个额外的类 */
|
|
|
+ .todayCheckIn.show,
|
|
|
+ .chart-container.show,
|
|
|
+ .statistics.show {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+</style>
|