1259 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			1259 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						||
  <view class="attendance-container">
 | 
						||
    <!-- 移动设备顶部状态栏 -->
 | 
						||
    <view class="top_bar flex w-full" v-if="isMobile">
 | 
						||
      <view class="chat-header">
 | 
						||
        <view class="header-left" @click="goBack">
 | 
						||
          <i class="fas fa-arrow-left"></i>
 | 
						||
        </view>
 | 
						||
        <view class="header-title">考勤打卡</view>
 | 
						||
        <view class="header-right" @click="showMoreOptions">
 | 
						||
          <i class="fas fa-ellipsis-v"></i>
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 浏览器环境下的导航栏 -->
 | 
						||
    <view class="chat-header" v-else>
 | 
						||
      <view class="header-left" @click="goBack">
 | 
						||
        <i class="fas fa-arrow-left"></i>
 | 
						||
      </view>
 | 
						||
      <view class="header-title">考勤打卡</view>
 | 
						||
      <view class="header-right" @click="showMoreOptions">
 | 
						||
        <i class="fas fa-ellipsis-v"></i>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 顶部日期与问候语 -->
 | 
						||
    <view class="header">
 | 
						||
      <!-- <view class="date">
 | 
						||
        {{ currentDate }}
 | 
						||
      </view>
 | 
						||
      <view class="greeting">
 | 
						||
        {{ greeting }}, {{ userName }}
 | 
						||
      </view> -->
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- Tab切换 -->
 | 
						||
    <view class="tab-container">
 | 
						||
      <view
 | 
						||
        class="tab-item"
 | 
						||
        :class="{ active: activeTab === 'work' }"
 | 
						||
        @tap="switchTab('work')"
 | 
						||
      >
 | 
						||
        上下班打卡
 | 
						||
      </view>
 | 
						||
      <view
 | 
						||
        class="tab-item"
 | 
						||
        :class="{ active: activeTab === 'out' }"
 | 
						||
        @tap="switchTab('out')"
 | 
						||
      >
 | 
						||
        外出打卡
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 考勤摘要模块 -->
 | 
						||
    <view class="attendance-summary flex-column">
 | 
						||
      <view class="flex-between underline pb-30">
 | 
						||
        <view class="status">
 | 
						||
        <view class="label">上班时间</view>
 | 
						||
        <view class="value">{{ workStart }}</view>
 | 
						||
      </view>
 | 
						||
      <view class="status">
 | 
						||
        <view class="label">下班时间</view>
 | 
						||
        <view class="value">{{ workEnd }}</view>
 | 
						||
      </view>
 | 
						||
      <view class="status">
 | 
						||
        <view class="label">已打卡天数</view>
 | 
						||
        <view class="value">{{ punchDays }}</view>
 | 
						||
      </view>
 | 
						||
      </view>
 | 
						||
      <view class="flex mt-30 tools-bar" >
 | 
						||
        <view @tap="goToRecord">记录</view>
 | 
						||
        <view @tap="goToStatistics">统计</view>
 | 
						||
        <view @tap="goToRule">规则</view>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 上下班打卡区域 -->
 | 
						||
    <view class="clockin-area" v-if="activeTab === 'work'">
 | 
						||
      <view class="circle" @tap="handleWorkPunch">
 | 
						||
        <view class="punch-time">{{ currentTime }}</view>
 | 
						||
        <view class="punch-status" v-if="workRecorded">
 | 
						||
          <text class="status-success">已打卡</text>
 | 
						||
        </view>
 | 
						||
        <view v-else class="punch-btn">
 | 
						||
          {{ workPunchText }}
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
      <view class="location">
 | 
						||
        <text class="iconfont icon-dizhi"></text>
 | 
						||
        {{ location }}
 | 
						||
        <button class="location-refresh" size="mini" @tap="refreshLocation">
 | 
						||
          刷新定位
 | 
						||
        </button>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 外出打卡区域 -->
 | 
						||
    <view class="out-area" v-if="activeTab === 'out'">
 | 
						||
      <view class="out-form">
 | 
						||
        <view class="form-item">
 | 
						||
          <text class="label">开始时间</text>
 | 
						||
          <picker
 | 
						||
            mode="time"
 | 
						||
            :value="startTime"
 | 
						||
            @change="onStartTimeChange"
 | 
						||
            class="time-picker"
 | 
						||
          >
 | 
						||
            <view class="picker-text">{{ startTime || "请选择开始时间" }}</view>
 | 
						||
          </picker>
 | 
						||
        </view>
 | 
						||
        <view class="form-item">
 | 
						||
          <text class="label">结束时间</text>
 | 
						||
          <picker
 | 
						||
            mode="time"
 | 
						||
            :value="endTime"
 | 
						||
            @change="onEndTimeChange"
 | 
						||
            class="time-picker"
 | 
						||
          >
 | 
						||
            <view class="picker-text">{{ endTime || "请选择结束时间" }}</view>
 | 
						||
          </picker>
 | 
						||
        </view>
 | 
						||
        <view class="form-item">
 | 
						||
          <text class="label">外出时长</text>
 | 
						||
          <view class="duration-display">
 | 
						||
            <text class="duration-text">{{ calculatedDuration }}</text>
 | 
						||
          </view>
 | 
						||
        </view>
 | 
						||
        <view class="form-item">
 | 
						||
          <text class="label">外出事由</text>
 | 
						||
          <textarea
 | 
						||
            class="textarea"
 | 
						||
            v-model="outReason"
 | 
						||
            placeholder="请输入外出事由(8000字以内)"
 | 
						||
            placeholder-style="color: #999"
 | 
						||
            :maxlength="8000"
 | 
						||
            :show-count="true"
 | 
						||
            auto-height
 | 
						||
          />
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
      <view class="out-punch-btn">
 | 
						||
        <button
 | 
						||
          class="punch-btn"
 | 
						||
          :disabled="!canOutPunch"
 | 
						||
          @tap="handleOutPunch"
 | 
						||
        >
 | 
						||
          提交外出申请
 | 
						||
        </button>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 打卡记录弹窗 -->
 | 
						||
    <view class="record-modal" v-if="showRecordModal" @tap="closeRecordModal">
 | 
						||
      <view class="modal-content" @tap.stop>
 | 
						||
        <view class="modal-header">
 | 
						||
          <view class="modal-title">打卡记录</view>
 | 
						||
          <view class="close-btn" @tap="closeRecordModal">
 | 
						||
            <i class="fas fa-times"></i>
 | 
						||
          </view>
 | 
						||
        </view>
 | 
						||
        <view class="modal-body">
 | 
						||
          <view v-if="todayRecords.length > 0">
 | 
						||
            <view
 | 
						||
              class="record-item"
 | 
						||
              v-for="(record, idx) in todayRecords"
 | 
						||
              :key="idx"
 | 
						||
            >
 | 
						||
              <view class="record-left">
 | 
						||
                <view class="record-time">{{ formatRecordTime(record.time) }}</view>
 | 
						||
                <view class="record-type">{{ record.type }}</view>
 | 
						||
                <view v-if="record.reason" class="record-detail"
 | 
						||
                  >事由:{{ record.reason }}</view
 | 
						||
                >
 | 
						||
                <view v-if="record.startTime" class="record-detail"
 | 
						||
                  >开始时间:{{ record.startTime }}</view
 | 
						||
                >
 | 
						||
                <view v-if="record.endTime" class="record-detail"
 | 
						||
                  >结束时间:{{ record.endTime }}</view
 | 
						||
                >
 | 
						||
                <view v-if="record.duration" class="record-detail"
 | 
						||
                  >外出时长:{{ record.duration }}</view
 | 
						||
                >
 | 
						||
              </view>
 | 
						||
            </view>
 | 
						||
          </view>
 | 
						||
          <view v-else class="no-record">暂无打卡记录</view>
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 考勤规则弹窗 -->
 | 
						||
    <view class="rule-modal" v-if="showRuleModal" @tap="closeRuleModal">
 | 
						||
      <view class="modal-content" @tap.stop>
 | 
						||
        <view class="modal-header">
 | 
						||
          <view class="modal-title">考勤规则</view>
 | 
						||
          <view class="close-btn" @tap="closeRuleModal">
 | 
						||
            <i class="fas fa-times"></i>
 | 
						||
          </view>
 | 
						||
        </view>
 | 
						||
        <view class="modal-body">
 | 
						||
          <view class="rule-sections">
 | 
						||
            <!-- 考勤时间 -->
 | 
						||
            <view class="rule-section">
 | 
						||
              <view class="rule-header" @tap="toggleRuleSection('attendance')">
 | 
						||
                <view class="rule-title">
 | 
						||
                  <i class="fas fa-clock"></i>
 | 
						||
                  考勤时间
 | 
						||
                </view>
 | 
						||
                <view class="rule-arrow" :class="{ expanded: expandedRules.attendance }">
 | 
						||
                  <i class="fas fa-chevron-down"></i>
 | 
						||
                </view>
 | 
						||
              </view>
 | 
						||
              <view class="rule-content" v-if="expandedRules.attendance">
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">上班时间:</text>
 | 
						||
                  <text class="rule-value">09:00</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">下班时间:</text>
 | 
						||
                  <text class="rule-value">18:00</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">午休时间:</text>
 | 
						||
                  <text class="rule-value">12:00 - 13:00</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">弹性时间:</text>
 | 
						||
                  <text class="rule-value">±30分钟</text>
 | 
						||
                </view>
 | 
						||
              </view>
 | 
						||
            </view>
 | 
						||
 | 
						||
            <!-- 补卡规则 -->
 | 
						||
            <view class="rule-section">
 | 
						||
              <view class="rule-header" @tap="toggleRuleSection('supplement')">
 | 
						||
                <view class="rule-title">
 | 
						||
                  <i class="fas fa-edit"></i>
 | 
						||
                  补卡规则
 | 
						||
                </view>
 | 
						||
                <view class="rule-arrow" :class="{ expanded: expandedRules.supplement }">
 | 
						||
                  <i class="fas fa-chevron-down"></i>
 | 
						||
                </view>
 | 
						||
              </view>
 | 
						||
              <view class="rule-content" v-if="expandedRules.supplement">
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">补卡时限:</text>
 | 
						||
                  <text class="rule-value">当日24:00前</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">补卡次数:</text>
 | 
						||
                  <text class="rule-value">每月最多3次</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">补卡原因:</text>
 | 
						||
                  <text class="rule-value">必须填写详细说明</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">审批流程:</text>
 | 
						||
                  <text class="rule-value">直属领导审批</text>
 | 
						||
                </view>
 | 
						||
              </view>
 | 
						||
            </view>
 | 
						||
 | 
						||
            <!-- 更多规则 -->
 | 
						||
            <view class="rule-section">
 | 
						||
              <view class="rule-header" @tap="toggleRuleSection('more')">
 | 
						||
                <view class="rule-title">
 | 
						||
                  <i class="fas fa-cog"></i>
 | 
						||
                  更多规则
 | 
						||
                </view>
 | 
						||
                <view class="rule-arrow" :class="{ expanded: expandedRules.more }">
 | 
						||
                  <i class="fas fa-chevron-down"></i>
 | 
						||
                </view>
 | 
						||
              </view>
 | 
						||
              <view class="rule-content" v-if="expandedRules.more">
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">迟到处理:</text>
 | 
						||
                  <text class="rule-value">超过30分钟算迟到</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">早退处理:</text>
 | 
						||
                  <text class="rule-value">提前30分钟以上算早退</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">外出申请:</text>
 | 
						||
                  <text class="rule-value">需提前1小时申请</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">请假制度:</text>
 | 
						||
                  <text class="rule-value">按公司请假制度执行</text>
 | 
						||
                </view>
 | 
						||
                <view class="rule-item">
 | 
						||
                  <text class="rule-label">加班规定:</text>
 | 
						||
                  <text class="rule-value">超过18:30算加班</text>
 | 
						||
                </view>
 | 
						||
              </view>
 | 
						||
            </view>
 | 
						||
          </view>
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
  </view>
 | 
						||
</template>
 | 
						||
 | 
						||
<script>
 | 
						||
export default {
 | 
						||
  data() {
 | 
						||
    return {
 | 
						||
      userName: "张三",
 | 
						||
      currentDate: "",
 | 
						||
      greeting: "",
 | 
						||
      currentTime: "",
 | 
						||
      workStart: "09:00",
 | 
						||
      workEnd: "18:00",
 | 
						||
      punchDays: 12,
 | 
						||
      workRecorded: false,
 | 
						||
      location: "正在定位中...",
 | 
						||
      todayRecords: [],
 | 
						||
      activeTab: "work", // 当前激活的tab
 | 
						||
      // 外出打卡相关数据
 | 
						||
      outReason: "",
 | 
						||
      startTime: "",
 | 
						||
      endTime: "",
 | 
						||
      outRecorded: false,
 | 
						||
      // 弹窗相关数据
 | 
						||
      showRecordModal: false,
 | 
						||
      showRuleModal: false,
 | 
						||
      expandedRules: {
 | 
						||
        attendance: true,  // 默认展开第一个
 | 
						||
        supplement: false,
 | 
						||
        more: false
 | 
						||
      },
 | 
						||
    };
 | 
						||
  },
 | 
						||
  onLoad() {
 | 
						||
    this.updateDateTime();
 | 
						||
    this.greeting = this.getGreeting();
 | 
						||
    this.refreshLocation();
 | 
						||
    this.loadTodayRecords();
 | 
						||
    // 更新时间
 | 
						||
    this.timer = setInterval(this.updateDateTime, 1000);
 | 
						||
  },
 | 
						||
  onUnload() {
 | 
						||
    clearInterval(this.timer);
 | 
						||
  },
 | 
						||
  computed: {
 | 
						||
    // 从全局数据获取设备信息
 | 
						||
    isMobile() {
 | 
						||
      return getApp().globalData.isMobile;
 | 
						||
    },
 | 
						||
    workPunchText() {
 | 
						||
      // 根据今日打卡记录判断是上班还是下班
 | 
						||
      const workRecords = this.todayRecords.filter(
 | 
						||
        (record) => record.type === "上班打卡" || record.type === "下班打卡"
 | 
						||
      );
 | 
						||
      if (workRecords.length === 0) {
 | 
						||
        return "上班打卡";
 | 
						||
      } else if (workRecords.length === 1) {
 | 
						||
        return "下班打卡";
 | 
						||
      } else {
 | 
						||
        return "今日已打卡";
 | 
						||
      }
 | 
						||
    },
 | 
						||
    canOutPunch() {
 | 
						||
      return (
 | 
						||
        this.startTime && 
 | 
						||
        this.endTime && 
 | 
						||
        this.outReason.trim() &&
 | 
						||
        this.calculatedDuration !== "时间无效"
 | 
						||
      );
 | 
						||
    },
 | 
						||
    calculatedDuration() {
 | 
						||
      if (!this.startTime || !this.endTime) {
 | 
						||
        return "请选择开始和结束时间";
 | 
						||
      }
 | 
						||
      
 | 
						||
      const start = this.parseTime(this.startTime);
 | 
						||
      const end = this.parseTime(this.endTime);
 | 
						||
      
 | 
						||
      if (!start || !end) {
 | 
						||
        return "时间格式错误";
 | 
						||
      }
 | 
						||
      
 | 
						||
      if (end <= start) {
 | 
						||
        return "结束时间必须晚于开始时间";
 | 
						||
      }
 | 
						||
      
 | 
						||
      const diffMs = end - start;
 | 
						||
      const hours = Math.floor(diffMs / (1000 * 60 * 60));
 | 
						||
      const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
 | 
						||
      
 | 
						||
      if (hours > 0) {
 | 
						||
        return `${hours}小时${minutes}分钟`;
 | 
						||
      } else {
 | 
						||
        return `${minutes}分钟`;
 | 
						||
      }
 | 
						||
    },
 | 
						||
  },
 | 
						||
  methods: {
 | 
						||
    updateDateTime() {
 | 
						||
      const now = new Date();
 | 
						||
      this.currentDate = `${now.getFullYear()}-${(now.getMonth() + 1)
 | 
						||
        .toString()
 | 
						||
        .padStart(2, "0")}-${now.getDate().toString().padStart(2, "0")}`;
 | 
						||
      this.currentTime = now.toTimeString().slice(0, 8);
 | 
						||
    },
 | 
						||
    getGreeting() {
 | 
						||
      const hour = new Date().getHours();
 | 
						||
      if (hour < 6) return "夜深了";
 | 
						||
      if (hour < 9) return "早上好";
 | 
						||
      if (hour < 12) return "上午好";
 | 
						||
      if (hour < 14) return "中午好";
 | 
						||
      if (hour < 18) return "下午好";
 | 
						||
      if (hour < 22) return "晚上好";
 | 
						||
      return "夜深了";
 | 
						||
    },
 | 
						||
    switchTab(tab) {
 | 
						||
      this.activeTab = tab;
 | 
						||
    },
 | 
						||
    handleWorkPunch() {
 | 
						||
      const now = new Date();
 | 
						||
      const year = now.getFullYear();
 | 
						||
      const month = String(now.getMonth() + 1).padStart(2, '0');
 | 
						||
      const day = String(now.getDate()).padStart(2, '0');
 | 
						||
      const time = `${year}-${month}-${day} ${this.currentTime}`;
 | 
						||
      
 | 
						||
      // 这里只做前端模拟,实际要调用接口
 | 
						||
      let type = "";
 | 
						||
      const workRecords = this.todayRecords.filter(
 | 
						||
        (record) => record.type === "上班打卡" || record.type === "下班打卡"
 | 
						||
      );
 | 
						||
 | 
						||
      if (workRecords.length === 0) {
 | 
						||
        type = "上班打卡";
 | 
						||
      } else if (workRecords.length === 1) {
 | 
						||
        type = "下班打卡";
 | 
						||
      } else {
 | 
						||
        uni.showToast({ title: "今日已打卡完成", icon: "none" });
 | 
						||
        return;
 | 
						||
      }
 | 
						||
 | 
						||
      this.todayRecords.push({ time, type });
 | 
						||
      this.workRecorded = true;
 | 
						||
      uni.showToast({ title: `${type}成功`, icon: "success" });
 | 
						||
      setTimeout(() => {
 | 
						||
        this.workRecorded = false;
 | 
						||
      }, 2000);
 | 
						||
    },
 | 
						||
    handleOutPunch() {
 | 
						||
      if (!this.canOutPunch) {
 | 
						||
        if (!this.startTime || !this.endTime) {
 | 
						||
          uni.showToast({ title: "请选择开始和结束时间", icon: "none" });
 | 
						||
        } else if (this.calculatedDuration === "结束时间必须晚于开始时间") {
 | 
						||
          uni.showToast({ title: "结束时间必须晚于开始时间", icon: "none" });
 | 
						||
        } else if (!this.outReason.trim()) {
 | 
						||
          uni.showToast({ title: "请填写外出事由", icon: "none" });
 | 
						||
        } else {
 | 
						||
          uni.showToast({ title: "请填写完整信息", icon: "none" });
 | 
						||
        }
 | 
						||
        return;
 | 
						||
      }
 | 
						||
 | 
						||
      const now = new Date();
 | 
						||
      const year = now.getFullYear();
 | 
						||
      const month = String(now.getMonth() + 1).padStart(2, '0');
 | 
						||
      const day = String(now.getDate()).padStart(2, '0');
 | 
						||
      const time = `${year}-${month}-${day} ${this.currentTime}`;
 | 
						||
      
 | 
						||
      const type = "外出申请";
 | 
						||
      const record = {
 | 
						||
        time,
 | 
						||
        type,
 | 
						||
        reason: this.outReason,
 | 
						||
        startTime: this.startTime,
 | 
						||
        endTime: this.endTime,
 | 
						||
        duration: this.calculatedDuration,
 | 
						||
      };
 | 
						||
 | 
						||
      this.todayRecords.push(record);
 | 
						||
      this.outRecorded = true;
 | 
						||
      uni.showToast({ title: "外出申请提交成功", icon: "success" });
 | 
						||
 | 
						||
      // 清空表单
 | 
						||
      this.outReason = "";
 | 
						||
      this.startTime = "";
 | 
						||
      this.endTime = "";
 | 
						||
 | 
						||
      setTimeout(() => {
 | 
						||
        this.outRecorded = false;
 | 
						||
      }, 2000);
 | 
						||
    },
 | 
						||
    onStartTimeChange(e) {
 | 
						||
      this.startTime = e.detail.value;
 | 
						||
    },
 | 
						||
    onEndTimeChange(e) {
 | 
						||
      this.endTime = e.detail.value;
 | 
						||
    },
 | 
						||
    parseTime(timeStr) {
 | 
						||
      if (!timeStr) return null;
 | 
						||
      const [hours, minutes] = timeStr.split(':').map(Number);
 | 
						||
      if (isNaN(hours) || isNaN(minutes)) return null;
 | 
						||
      
 | 
						||
      const today = new Date();
 | 
						||
      today.setHours(hours, minutes, 0, 0);
 | 
						||
      return today;
 | 
						||
    },
 | 
						||
    refreshLocation() {
 | 
						||
      // 模拟定位
 | 
						||
      this.location = "北京·中关村";
 | 
						||
      // 若已集成定位api可替换
 | 
						||
      // uni.getLocation({...});
 | 
						||
    },
 | 
						||
    loadTodayRecords() {
 | 
						||
      // 模拟从本地加载今日打卡
 | 
						||
      const today = new Date();
 | 
						||
      const year = today.getFullYear();
 | 
						||
      const month = String(today.getMonth() + 1).padStart(2, '0');
 | 
						||
      const day = String(today.getDate()).padStart(2, '0');
 | 
						||
      
 | 
						||
      this.todayRecords = [
 | 
						||
        {
 | 
						||
          time: `${year}-${month}-${day} 09:15:30`,
 | 
						||
          type: "上班打卡"
 | 
						||
        },
 | 
						||
        {
 | 
						||
          time: `${year}-${month}-${day} 12:30:45`,
 | 
						||
          type: "外出申请",
 | 
						||
          reason: "客户拜访",
 | 
						||
          startTime: "12:30",
 | 
						||
          endTime: "14:00",
 | 
						||
          duration: "1小时30分钟"
 | 
						||
        },
 | 
						||
        {
 | 
						||
          time: `${year}-${month}-${day} 18:20:15`,
 | 
						||
          type: "下班打卡"
 | 
						||
        }
 | 
						||
      ];
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 返回
 | 
						||
     */
 | 
						||
    goBack() {
 | 
						||
      uni.navigateBack();
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 显示更多选项
 | 
						||
     */
 | 
						||
    showMoreOptions() {
 | 
						||
      uni.showActionSheet({
 | 
						||
        itemList: ["考勤记录", "设置", "帮助"],
 | 
						||
        success: (res) => {
 | 
						||
          switch (res.tapIndex) {
 | 
						||
            case 0:
 | 
						||
              // 考勤记录
 | 
						||
              this.showRecordModal = true;
 | 
						||
              break;
 | 
						||
            case 1:
 | 
						||
              // 设置
 | 
						||
              uni.showToast({ title: "设置功能开发中", icon: "none" });
 | 
						||
              break;
 | 
						||
            case 2:
 | 
						||
              // 帮助
 | 
						||
              uni.showToast({ title: "帮助功能开发中", icon: "none" });
 | 
						||
              break;
 | 
						||
          }
 | 
						||
        },
 | 
						||
      });
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 显示打卡记录
 | 
						||
     */
 | 
						||
    goToRecord() {
 | 
						||
      this.showRecordModal = true;
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 关闭打卡记录弹窗
 | 
						||
     */
 | 
						||
    closeRecordModal() {
 | 
						||
      this.showRecordModal = false;
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 跳转到统计页面
 | 
						||
     */
 | 
						||
    goToStatistics() {
 | 
						||
      uni.navigateTo({
 | 
						||
        url: "/pages/hr/attendance/statistics",
 | 
						||
      });
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 显示考勤规则弹窗
 | 
						||
     */
 | 
						||
    goToRule() {
 | 
						||
      this.showRuleModal = true;
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 关闭考勤规则弹窗
 | 
						||
     */
 | 
						||
    closeRuleModal() {
 | 
						||
      this.showRuleModal = false;
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 切换规则栏目展开状态
 | 
						||
     */
 | 
						||
    toggleRuleSection(section) {
 | 
						||
      this.expandedRules[section] = !this.expandedRules[section];
 | 
						||
    },
 | 
						||
    /**
 | 
						||
     * 格式化记录时间显示
 | 
						||
     */
 | 
						||
    formatRecordTime(timeStr) {
 | 
						||
      if (!timeStr) return '';
 | 
						||
      
 | 
						||
      // 如果是完整的时间字符串(包含年月日)
 | 
						||
      if (timeStr.includes('-') || timeStr.includes('/')) {
 | 
						||
        const date = new Date(timeStr);
 | 
						||
        return date.toLocaleString('zh-CN', {
 | 
						||
          year: 'numeric',
 | 
						||
          month: '2-digit',
 | 
						||
          day: '2-digit',
 | 
						||
          hour: '2-digit',
 | 
						||
          minute: '2-digit',
 | 
						||
          second: '2-digit'
 | 
						||
        });
 | 
						||
      }
 | 
						||
      
 | 
						||
      // 如果只是时间字符串,添加今天的日期
 | 
						||
      const today = new Date();
 | 
						||
      const year = today.getFullYear();
 | 
						||
      const month = String(today.getMonth() + 1).padStart(2, '0');
 | 
						||
      const day = String(today.getDate()).padStart(2, '0');
 | 
						||
      
 | 
						||
      return `${year}-${month}-${day} ${timeStr}`;
 | 
						||
    },
 | 
						||
  },
 | 
						||
};
 | 
						||
</script>
 | 
						||
 | 
						||
<style lang="scss" scoped>
 | 
						||
.attendance-container {
 | 
						||
  min-height: 100vh;
 | 
						||
  background: #f4f6fa;
 | 
						||
  padding: 0;
 | 
						||
}
 | 
						||
 | 
						||
/* 移动设备顶部状态栏 */
 | 
						||
.top_bar {
 | 
						||
  background: var(--gradient-primary);
 | 
						||
  box-shadow: var(--shadow-lg);
 | 
						||
  z-index: 9999;
 | 
						||
  position: fixed;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  right: 0;
 | 
						||
  height: calc(var(--status-bar-height) + 88rpx);
 | 
						||
  display: flex;
 | 
						||
  align-items: flex-end;
 | 
						||
  padding-top: var(--status-bar-height);
 | 
						||
  box-sizing: border-box;
 | 
						||
}
 | 
						||
 | 
						||
/* 支持安全区域的设备 */
 | 
						||
@supports (padding: max(0px)) {
 | 
						||
  .top_bar {
 | 
						||
    height: calc(var(--status-bar-height) + 88rpx + env(safe-area-inset-top));
 | 
						||
    padding-top: calc(var(--status-bar-height) + env(safe-area-inset-top));
 | 
						||
  }
 | 
						||
 | 
						||
  .top_bar + .header {
 | 
						||
    margin-top: calc(
 | 
						||
      var(--status-bar-height) + 88rpx + env(safe-area-inset-top)
 | 
						||
    );
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/* 顶部导航栏 */
 | 
						||
.chat-header {
 | 
						||
  display: flex;
 | 
						||
  justify-content: space-between;
 | 
						||
  align-items: center;
 | 
						||
  height: 88rpx;
 | 
						||
  background-color: var(--surface);
 | 
						||
  border-bottom: 1rpx solid var(--border);
 | 
						||
  padding: 0 20rpx;
 | 
						||
  box-sizing: border-box;
 | 
						||
  box-shadow: var(--shadow);
 | 
						||
}
 | 
						||
 | 
						||
/* 浏览器环境下的固定定位 */
 | 
						||
.attendance-container .chat-header:not(.top_bar .chat-header) {
 | 
						||
  position: fixed;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  right: 0;
 | 
						||
  z-index: 9999;
 | 
						||
  height: calc(var(--status-bar-height) + 88rpx);
 | 
						||
  padding-top: var(--status-bar-height);
 | 
						||
}
 | 
						||
 | 
						||
/* 移动设备下的导航栏样式 */
 | 
						||
.top_bar .chat-header {
 | 
						||
  background: transparent;
 | 
						||
  border-bottom: none;
 | 
						||
  box-shadow: none;
 | 
						||
  width: 100%;
 | 
						||
  height: 88rpx;
 | 
						||
  padding: 0 20rpx;
 | 
						||
  box-sizing: border-box;
 | 
						||
}
 | 
						||
 | 
						||
.top_bar .header-title {
 | 
						||
  color: var(--white);
 | 
						||
  font-weight: 600;
 | 
						||
  font-size: 36rpx;
 | 
						||
}
 | 
						||
 | 
						||
.top_bar .header-left i,
 | 
						||
.top_bar .header-right i {
 | 
						||
  color: var(--white);
 | 
						||
  font-size: 40rpx;
 | 
						||
}
 | 
						||
 | 
						||
.header-left,
 | 
						||
.header-right {
 | 
						||
  width: 80rpx;
 | 
						||
  height: 88rpx;
 | 
						||
  display: flex;
 | 
						||
  align-items: center;
 | 
						||
  justify-content: center;
 | 
						||
  transition: background-color 0.2s ease;
 | 
						||
}
 | 
						||
 | 
						||
.header-right:active {
 | 
						||
  background-color: var(--surface-hover);
 | 
						||
  border-radius: 8rpx;
 | 
						||
}
 | 
						||
 | 
						||
/* 移动设备下的按钮悬停效果 */
 | 
						||
.top_bar .header-left:active,
 | 
						||
.top_bar .header-right:active {
 | 
						||
  background-color: rgba(255, 255, 255, 0.2);
 | 
						||
  border-radius: 8rpx;
 | 
						||
}
 | 
						||
 | 
						||
.header-title {
 | 
						||
  font-size: 32rpx;
 | 
						||
  font-weight: 600;
 | 
						||
  color: var(--title-color);
 | 
						||
}
 | 
						||
 | 
						||
/* 浏览器环境下为内容添加顶部间距 */
 | 
						||
.attendance-container .chat-header:not(.top_bar .chat-header) + .header {
 | 
						||
  margin-top: calc(var(--status-bar-height) + 88rpx);
 | 
						||
}
 | 
						||
 | 
						||
@supports (padding: max(0px)) {
 | 
						||
  .attendance-container .chat-header:not(.top_bar .chat-header) {
 | 
						||
    height: calc(var(--status-bar-height) + 88rpx + env(safe-area-inset-top));
 | 
						||
    padding-top: calc(var(--status-bar-height) + env(safe-area-inset-top));
 | 
						||
  }
 | 
						||
 | 
						||
  .attendance-container .chat-header:not(.top_bar .chat-header) + .header {
 | 
						||
    margin-top: calc(
 | 
						||
      var(--status-bar-height) + 88rpx + env(safe-area-inset-top)
 | 
						||
    );
 | 
						||
  }
 | 
						||
}
 | 
						||
.header {
 | 
						||
  padding: 48rpx 36rpx 12rpx 36rpx;
 | 
						||
//   background: linear-gradient(90deg, #5497ff 0%, #88bafe 100%);
 | 
						||
  color: #fff;
 | 
						||
  .date {
 | 
						||
    font-size: 28rpx;
 | 
						||
    margin-bottom: 8rpx;
 | 
						||
    opacity: 0.9;
 | 
						||
  }
 | 
						||
  .greeting {
 | 
						||
    font-size: 40rpx;
 | 
						||
    font-weight: bold;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.tab-container {
 | 
						||
  background: #fff;
 | 
						||
  margin: -32rpx 36rpx 0 36rpx;
 | 
						||
  border-radius: 16rpx 16rpx 0 0;
 | 
						||
  border-bottom: 1rpx solid #efefefef;
 | 
						||
  display: flex;
 | 
						||
  box-shadow: 0 -6rpx 18rpx 0 rgba(74, 144, 226, 0.04);
 | 
						||
  position: relative;
 | 
						||
  z-index: 1;
 | 
						||
  .tab-item {
 | 
						||
    flex: 1;
 | 
						||
    text-align: center;
 | 
						||
    padding: 24rpx 0;
 | 
						||
    font-size: 28rpx;
 | 
						||
    color: #9eaab7;
 | 
						||
    position: relative;
 | 
						||
    transition: all 0.3s ease;
 | 
						||
    &.active {
 | 
						||
      color: #388bff;
 | 
						||
      font-weight: bold;
 | 
						||
      &::after {
 | 
						||
        content: "";
 | 
						||
        position: absolute;
 | 
						||
        bottom: 0;
 | 
						||
        left: 50%;
 | 
						||
        transform: translateX(-50%);
 | 
						||
        width: 60rpx;
 | 
						||
        height: 4rpx;
 | 
						||
        background: #388bff;
 | 
						||
        border-radius: 2rpx;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.attendance-summary {
 | 
						||
  background: #fff;
 | 
						||
  box-shadow: 0 6rpx 18rpx 0 rgba(74, 144, 226, 0.04);
 | 
						||
  border-radius: 16rpx;
 | 
						||
  padding: 36rpx 30rpx;
 | 
						||
  margin: -32rpx 36rpx 32rpx 36rpx;
 | 
						||
  display: flex;
 | 
						||
  justify-content: space-between;
 | 
						||
  .status {
 | 
						||
    text-align: center;
 | 
						||
    margin-top: 30rpx;
 | 
						||
    .label {
 | 
						||
      color: #9eaab7;
 | 
						||
      font-size: 24rpx;
 | 
						||
    }
 | 
						||
    .value {
 | 
						||
      color: #2d4259;
 | 
						||
      font-size: 32rpx;
 | 
						||
      margin-top: 8rpx;
 | 
						||
      font-weight: bold;
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  .tools-bar {
 | 
						||
    font-size: 28rpx;
 | 
						||
    color: #388bff;
 | 
						||
    font-weight: bold;
 | 
						||
    display: flex;
 | 
						||
    justify-content: space-around;
 | 
						||
    align-items: center;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.clockin-area {
 | 
						||
  display: flex;
 | 
						||
  flex-direction: column;
 | 
						||
  align-items: center;
 | 
						||
  margin: 240rpx 0 0 0;
 | 
						||
  .circle {
 | 
						||
    width: 320rpx;
 | 
						||
    height: 320rpx;
 | 
						||
    border-radius: 50%;
 | 
						||
    background: #fff;
 | 
						||
    box-shadow: 0 8rpx 18rpx 0 rgba(74, 144, 226, 0.06);
 | 
						||
    display: flex;
 | 
						||
    flex-direction: column;
 | 
						||
    align-items: center;
 | 
						||
    justify-content: center;
 | 
						||
    position: relative;
 | 
						||
    .punch-time {
 | 
						||
      font-size: 60rpx;
 | 
						||
      font-weight: bold;
 | 
						||
      color: #2d4259;
 | 
						||
      margin-bottom: 8rpx;
 | 
						||
    }
 | 
						||
    .punch-status {
 | 
						||
      margin-top: 16rpx;
 | 
						||
      .status-success {
 | 
						||
        color: #66c27c;
 | 
						||
        font-size: 32rpx;
 | 
						||
      }
 | 
						||
    }
 | 
						||
    .punch-btn {
 | 
						||
      border: none;
 | 
						||
      color: #388bff;
 | 
						||
      font-size: 32rpx;
 | 
						||
    }
 | 
						||
  }
 | 
						||
  .location {
 | 
						||
    margin-top: 26rpx;
 | 
						||
    color: #90a4b7;
 | 
						||
    font-size: 24rpx;
 | 
						||
    display: flex;
 | 
						||
    align-items: center;
 | 
						||
    .iconfont {
 | 
						||
      margin-right: 10rpx;
 | 
						||
      font-size: 26rpx;
 | 
						||
    }
 | 
						||
    .location-refresh {
 | 
						||
      background: none;
 | 
						||
      color: #388bff;
 | 
						||
      margin-left: 18rpx;
 | 
						||
      font-size: 22rpx;
 | 
						||
      border: none;
 | 
						||
      padding: 0 10rpx;
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.out-area {
 | 
						||
  margin: 24rpx 36rpx 0 36rpx;
 | 
						||
  background: #fff;
 | 
						||
  border-radius: 16rpx;
 | 
						||
  box-shadow: 0 8rpx 18rpx 0 rgba(74, 144, 226, 0.06);
 | 
						||
  padding: 36rpx 30rpx;
 | 
						||
 | 
						||
  .out-form {
 | 
						||
    .form-item {
 | 
						||
      margin-bottom: 32rpx;
 | 
						||
      .label {
 | 
						||
        display: block;
 | 
						||
        font-size: 28rpx;
 | 
						||
        color: #2d4259;
 | 
						||
        margin-bottom: 16rpx;
 | 
						||
        font-weight: 500;
 | 
						||
      }
 | 
						||
      .input {
 | 
						||
        width: 100%;
 | 
						||
        height: 80rpx;
 | 
						||
        background: #f8f9fa;
 | 
						||
        border: 1rpx solid #e9ecef;
 | 
						||
        border-radius: 12rpx;
 | 
						||
        padding: 0 24rpx;
 | 
						||
        font-size: 28rpx;
 | 
						||
        color: #2d4259;
 | 
						||
        box-sizing: border-box;
 | 
						||
        &:focus {
 | 
						||
          border-color: #388bff;
 | 
						||
          background: #fff;
 | 
						||
        }
 | 
						||
      }
 | 
						||
      .textarea {
 | 
						||
        width: 100%;
 | 
						||
        min-height: 120rpx;
 | 
						||
        background: #f8f9fa;
 | 
						||
        border: 1rpx solid #e9ecef;
 | 
						||
        border-radius: 12rpx;
 | 
						||
        padding: 20rpx 24rpx;
 | 
						||
        font-size: 28rpx;
 | 
						||
        color: #2d4259;
 | 
						||
        box-sizing: border-box;
 | 
						||
        line-height: 1.5;
 | 
						||
        &:focus {
 | 
						||
          border-color: #388bff;
 | 
						||
          background: #fff;
 | 
						||
        }
 | 
						||
      }
 | 
						||
      .duration-display {
 | 
						||
        width: 100%;
 | 
						||
        height: 80rpx;
 | 
						||
        background: #f8f9fa;
 | 
						||
        border: 1rpx solid #e9ecef;
 | 
						||
        border-radius: 12rpx;
 | 
						||
        display: flex;
 | 
						||
        align-items: center;
 | 
						||
        padding: 0 24rpx;
 | 
						||
        box-sizing: border-box;
 | 
						||
        .duration-text {
 | 
						||
          font-size: 28rpx;
 | 
						||
          color: #2d4259;
 | 
						||
          font-weight: 500;
 | 
						||
        }
 | 
						||
      }
 | 
						||
      .time-picker {
 | 
						||
        width: 100%;
 | 
						||
        height: 80rpx;
 | 
						||
        background: #f8f9fa;
 | 
						||
        border: 1rpx solid #e9ecef;
 | 
						||
        border-radius: 12rpx;
 | 
						||
        display: flex;
 | 
						||
        align-items: center;
 | 
						||
        padding: 0 24rpx;
 | 
						||
        box-sizing: border-box;
 | 
						||
        .picker-text {
 | 
						||
          font-size: 28rpx;
 | 
						||
          color: #2d4259;
 | 
						||
        }
 | 
						||
        &:active {
 | 
						||
          border-color: #388bff;
 | 
						||
          background: #fff;
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  .out-punch-btn {
 | 
						||
    margin-top: 40rpx;
 | 
						||
    text-align: center;
 | 
						||
    .punch-btn {
 | 
						||
      background: linear-gradient(90deg, #388bff 0%, #62b3ff 100%);
 | 
						||
      border: none;
 | 
						||
      color: #fff;
 | 
						||
      font-size: 32rpx;
 | 
						||
      border-radius: 64rpx;
 | 
						||
      width: 200rpx;
 | 
						||
      height: 80rpx;
 | 
						||
      box-shadow: 0 4rpx 10rpx 0 rgba(56, 139, 255, 0.17);
 | 
						||
      &:disabled {
 | 
						||
        background: #e9ecef;
 | 
						||
        color: #9eaab7;
 | 
						||
        box-shadow: none;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.record-section {
 | 
						||
  margin: 48rpx 36rpx 0 36rpx;
 | 
						||
  background: #fff;
 | 
						||
  border-radius: 16rpx;
 | 
						||
  box-shadow: 0 4rpx 10rpx 0 rgba(74, 144, 226, 0.04);
 | 
						||
  padding: 30rpx 24rpx;
 | 
						||
  .record-title {
 | 
						||
    font-size: 30rpx;
 | 
						||
    font-weight: bold;
 | 
						||
    margin-bottom: 18rpx;
 | 
						||
    color: #2d4259;
 | 
						||
  }
 | 
						||
  .record-item {
 | 
						||
    padding: 24rpx 0;
 | 
						||
    border-bottom: 1px solid #f2f3f8;
 | 
						||
    &:last-child {
 | 
						||
      border-bottom: none;
 | 
						||
    }
 | 
						||
    .record-left {
 | 
						||
      .record-time {
 | 
						||
        font-size: 28rpx;
 | 
						||
        color: #2d4259;
 | 
						||
        font-weight: 500;
 | 
						||
        margin-bottom: 8rpx;
 | 
						||
      }
 | 
						||
      .record-type {
 | 
						||
        color: #388bff;
 | 
						||
        font-size: 26rpx;
 | 
						||
        font-weight: 500;
 | 
						||
        margin-bottom: 8rpx;
 | 
						||
      }
 | 
						||
      .record-detail {
 | 
						||
        font-size: 24rpx;
 | 
						||
        color: #9eaab7;
 | 
						||
        margin-bottom: 4rpx;
 | 
						||
        line-height: 1.4;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
  .no-record {
 | 
						||
    color: #aaa;
 | 
						||
    text-align: center;
 | 
						||
    padding: 26rpx 0;
 | 
						||
    font-size: 28rpx;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/* 打卡记录弹窗 */
 | 
						||
.record-modal {
 | 
						||
  position: fixed;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  right: 0;
 | 
						||
  bottom: 0;
 | 
						||
  background: rgba(0, 0, 0, 0.5);
 | 
						||
  z-index: 10000;
 | 
						||
  display: flex;
 | 
						||
  align-items: center;
 | 
						||
  justify-content: center;
 | 
						||
  padding: 40rpx;
 | 
						||
  box-sizing: border-box;
 | 
						||
}
 | 
						||
 | 
						||
.modal-content {
 | 
						||
  background: #fff;
 | 
						||
  border-radius: 16rpx;
 | 
						||
  width: 100%;
 | 
						||
  max-width: 600rpx;
 | 
						||
  max-height: 80vh;
 | 
						||
  overflow: hidden;
 | 
						||
  box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.15);
 | 
						||
}
 | 
						||
 | 
						||
.modal-header {
 | 
						||
  display: flex;
 | 
						||
  align-items: center;
 | 
						||
  justify-content: space-between;
 | 
						||
  padding: 30rpx 40rpx;
 | 
						||
  border-bottom: 1rpx solid #f0f0f0;
 | 
						||
  background: #fff;
 | 
						||
  position: sticky;
 | 
						||
  top: 0;
 | 
						||
  z-index: 1;
 | 
						||
}
 | 
						||
 | 
						||
.modal-title {
 | 
						||
  font-size: 32rpx;
 | 
						||
  font-weight: 600;
 | 
						||
  color: #2d4259;
 | 
						||
}
 | 
						||
 | 
						||
.close-btn {
 | 
						||
  width: 60rpx;
 | 
						||
  height: 60rpx;
 | 
						||
  display: flex;
 | 
						||
  align-items: center;
 | 
						||
  justify-content: center;
 | 
						||
  border-radius: 50%;
 | 
						||
  background: #f5f5f5;
 | 
						||
  color: #666;
 | 
						||
  font-size: 28rpx;
 | 
						||
  transition: all 0.2s ease;
 | 
						||
}
 | 
						||
 | 
						||
.close-btn:active {
 | 
						||
  background: #e0e0e0;
 | 
						||
  transform: scale(0.95);
 | 
						||
}
 | 
						||
 | 
						||
.modal-body {
 | 
						||
  padding: 0 40rpx 40rpx 40rpx;
 | 
						||
  max-height: 60vh;
 | 
						||
  overflow-y: auto;
 | 
						||
}
 | 
						||
 | 
						||
.modal-body .record-item {
 | 
						||
  padding: 24rpx 0;
 | 
						||
  border-bottom: 1rpx solid #f2f3f8;
 | 
						||
  &:last-child {
 | 
						||
    border-bottom: none;
 | 
						||
  }
 | 
						||
  .record-left {
 | 
						||
    .record-time {
 | 
						||
      font-size: 28rpx;
 | 
						||
      color: #2d4259;
 | 
						||
      font-weight: 500;
 | 
						||
      margin-bottom: 8rpx;
 | 
						||
    }
 | 
						||
    .record-type {
 | 
						||
      color: #388bff;
 | 
						||
      font-size: 26rpx;
 | 
						||
      font-weight: 500;
 | 
						||
      margin-bottom: 8rpx;
 | 
						||
    }
 | 
						||
    .record-detail {
 | 
						||
      font-size: 24rpx;
 | 
						||
      color: #9eaab7;
 | 
						||
      margin-bottom: 4rpx;
 | 
						||
      line-height: 1.4;
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.modal-body .no-record {
 | 
						||
  color: #aaa;
 | 
						||
  text-align: center;
 | 
						||
  padding: 60rpx 0;
 | 
						||
  font-size: 28rpx;
 | 
						||
}
 | 
						||
 | 
						||
/* 考勤规则弹窗 */
 | 
						||
.rule-modal {
 | 
						||
  position: fixed;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  right: 0;
 | 
						||
  bottom: 0;
 | 
						||
  background: rgba(0, 0, 0, 0.5);
 | 
						||
  z-index: 10000;
 | 
						||
  display: flex;
 | 
						||
  align-items: center;
 | 
						||
  justify-content: center;
 | 
						||
  padding: 40rpx;
 | 
						||
  box-sizing: border-box;
 | 
						||
}
 | 
						||
 | 
						||
.rule-sections {
 | 
						||
  .rule-section {
 | 
						||
    margin-bottom: 20rpx;
 | 
						||
    border-radius: 12rpx;
 | 
						||
    overflow: hidden;
 | 
						||
    background: #f8f9fa;
 | 
						||
    border: 1rpx solid #e9ecef;
 | 
						||
    
 | 
						||
    .rule-header {
 | 
						||
      display: flex;
 | 
						||
      align-items: center;
 | 
						||
      justify-content: space-between;
 | 
						||
      padding: 24rpx 30rpx;
 | 
						||
      background: #fff;
 | 
						||
      border-bottom: 1rpx solid #e9ecef;
 | 
						||
      cursor: pointer;
 | 
						||
      transition: all 0.2s ease;
 | 
						||
      
 | 
						||
      &:active {
 | 
						||
        background: #f5f5f5;
 | 
						||
      }
 | 
						||
      
 | 
						||
      .rule-title {
 | 
						||
        display: flex;
 | 
						||
        align-items: center;
 | 
						||
        font-size: 30rpx;
 | 
						||
        font-weight: 600;
 | 
						||
        color: #2d4259;
 | 
						||
        
 | 
						||
        i {
 | 
						||
          margin-right: 16rpx;
 | 
						||
          color: #388bff;
 | 
						||
          font-size: 28rpx;
 | 
						||
        }
 | 
						||
      }
 | 
						||
      
 | 
						||
      .rule-arrow {
 | 
						||
        color: #9eaab7;
 | 
						||
        font-size: 24rpx;
 | 
						||
        transition: transform 0.3s ease;
 | 
						||
        
 | 
						||
        &.expanded {
 | 
						||
          transform: rotate(180deg);
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
    
 | 
						||
    .rule-content {
 | 
						||
      padding: 0 30rpx 24rpx 30rpx;
 | 
						||
      background: #fff;
 | 
						||
      
 | 
						||
      .rule-item {
 | 
						||
        display: flex;
 | 
						||
        justify-content: space-between;
 | 
						||
        align-items: center;
 | 
						||
        padding: 16rpx 0;
 | 
						||
        border-bottom: 1rpx solid #f0f0f0;
 | 
						||
        
 | 
						||
        &:last-child {
 | 
						||
          border-bottom: none;
 | 
						||
        }
 | 
						||
        
 | 
						||
        .rule-label {
 | 
						||
          font-size: 26rpx;
 | 
						||
          color: #666;
 | 
						||
          flex: 1;
 | 
						||
        }
 | 
						||
        
 | 
						||
        .rule-value {
 | 
						||
          font-size: 26rpx;
 | 
						||
          color: #2d4259;
 | 
						||
          font-weight: 500;
 | 
						||
          text-align: right;
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
</style>
 |