spring
2 天以前 69e864bcfa2547138af1780afac44345d340dfd9
fix: 完成用户评价、打卡签到页面
已添加2个文件
872 ■■■■■ 文件已修改
src/views/equipmentManagement/attendanceManagement/index.vue 403 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/index.vue 469 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/attendanceManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,403 @@
<template>
  <div class="app-container">
    <!-- æœåŠ¡è¯„ä»·æ¦‚è§ˆï¼šæ¨¡æ‹Ÿå‘˜å·¥ä¸šç»©è¯„åˆ† -->
    <el-row :gutter="16" class="mb16">
      <el-col :span="8">
        <el-card shadow="never">
          <div class="kpi-title">本月平均评分</div>
          <div class="kpi-value">
            {{ overallAvgScore.toFixed(1) }}
            <span class="kpi-unit">分</span>
          </div>
          <el-rate v-model="overallAvgScore" disabled show-score score-template="{value} / 5" />
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card shadow="never">
          <div class="kpi-title">已评价维修工单</div>
          <div class="kpi-value">
            {{ ratedCount }}
            <span class="kpi-unit">单</span>
          </div>
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card shadow="never">
          <div class="kpi-title">待评价维修工单</div>
          <div class="kpi-value kpi-warning">
            {{ pendingCount }}
            <span class="kpi-unit">单</span>
          </div>
        </el-card>
      </el-col>
    </el-row>
    <!-- æŸ¥è¯¢æ¡ä»¶ï¼šç®¡ç†å‘˜æŒ‰å·¥ç¨‹å¸ˆ / å®¢æˆ· / æ—¶é—´è¿½æº¯è¯„ä»· -->
    <div class="search_form">
      <div>
        <span class="search_title">维修工程师:</span>
        <el-input
          v-model="searchForm.engineerName"
          placeholder="请输入工程师姓名"
          style="width: 180px"
          clearable
          @keyup.enter.native="handleQuery"
        />
        <span class="search_title ml10">客户名称:</span>
        <el-input
          v-model="searchForm.customerName"
          placeholder="请输入客户名称"
          style="width: 180px"
          clearable
          @keyup.enter.native="handleQuery"
        />
        <span class="search_title ml10">完成时间:</span>
        <el-date-picker
          v-model="searchForm.dateRange"
          type="daterange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          clearable
        />
        <span class="search_title ml10">评价状态:</span>
        <el-select
          v-model="searchForm.status"
          placeholder="请选择"
          style="width: 140px"
          clearable
        >
          <el-option label="待评价" value="pending" />
          <el-option label="已评价" value="rated" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
          æœç´¢
        </el-button>
        <el-button @click="resetSearch">重置</el-button>
      </div>
      <div>
        <el-button icon="Download" @click="handleExport">
          å¯¼å‡ºè¯„价统计
        </el-button>
      </div>
    </div>
    <!-- ç»´ä¿®è¯„价列表:模拟“维修完成后触发评价”场景 -->
    <div class="table_list">
      <el-table
        :data="tableData"
        border
        style="width: 100%"
        height="calc(100vh - 24em)"
        :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
      >
        <el-table-column type="index" label="序号" width="60" align="center" />
        <el-table-column prop="orderNo" label="维修工单号" width="160" show-overflow-tooltip />
        <el-table-column prop="deviceName" label="设备名称" width="160" show-overflow-tooltip />
        <el-table-column prop="customerName" label="客户名称" width="180" show-overflow-tooltip />
        <el-table-column prop="engineerName" label="维修工程师" width="120" />
        <el-table-column prop="completeTime" label="维修完成时间" width="180" />
        <el-table-column prop="score" label="星级评分" width="140" align="center">
          <template #default="scope">
            <el-rate v-if="scope.row.score" v-model="scope.row.score" disabled />
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column prop="status" label="评价状态" width="100" align="center">
          <template #default="scope">
            <el-tag
              :type="scope.row.status === 'rated' ? 'success' : 'warning'"
              size="small"
            >
              {{ scope.row.status === 'rated' ? '已评价' : '待评价' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="feedback" label="客户反馈" show-overflow-tooltip />
        <el-table-column label="操作" width="160" align="center" fixed="right">
          <template #default="scope">
            <el-button
              v-if="scope.row.status === 'pending'"
              type="primary"
              link
              size="small"
              @click="openEvaluate(scope.row)"
            >
              åŽ»è¯„ä»·
            </el-button>
            <el-button
              v-else
              type="primary"
              link
              size="small"
              @click="openEvaluate(scope.row)"
            >
              æŸ¥çœ‹ / ä¿®æ”¹è¯„ä»·
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!-- è¯„价弹框:模拟“维修完成后弹出客户端评价” -->
    <el-dialog
      v-model="dialogVisible"
      :title="dialogTitle"
      width="520px"
      destroy-on-close
    >
      <div class="dialog-order-info" v-if="currentOrder">
        <div>维修工单:{{ currentOrder.orderNo }}</div>
        <div>设备名称:{{ currentOrder.deviceName }}</div>
        <div>维修工程师:{{ currentOrder.engineerName }}</div>
      </div>
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="星级评分:" prop="score">
          <el-rate v-model="form.score" :max="5" />
        </el-form-item>
        <el-form-item label="文字反馈:" prop="feedback">
          <el-input
            v-model="form.feedback"
            type="textarea"
            :rows="4"
            placeholder="请填写对本次维修服务的评价,如响应速度、专业程度、沟通体验等"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="dialogVisible = false">取 æ¶ˆ</el-button>
          <el-button type="primary" @click="handleSubmit">提 äº¤ è¯„ ä»·</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, computed } from "vue";
import { ElMessage } from "element-plus";
// æ¨¡æ‹Ÿç»´ä¿®å·¥å• + å®¢æˆ·è¯„价数据
const rawOrders = ref([
  {
    id: 1,
    orderNo: "WX-2024-1201-001",
    deviceName: "空压机 A1 å·",
    customerName: "华南电子科技有限公司",
    engineerName: "王师傅",
    completeTime: "2024-12-01 10:30:00",
    completeDate: "2024-12-01",
    status: "rated",
    score: 5,
    feedback: "维修非常专业,响应速度快,现场解释也很清晰,满意。",
  },
  {
    id: 2,
    orderNo: "WX-2024-1201-002",
    deviceName: "注塑机 B3 å·",
    customerName: "华东精密制造有限公司",
    engineerName: "李师傅",
    completeTime: "2024-12-01 15:20:00",
    completeDate: "2024-12-01",
    status: "rated",
    score: 4,
    feedback: "整体还不错,就是到场时间稍微长了一点,希望后面能再快一些。",
  },
  {
    id: 3,
    orderNo: "WX-2024-1202-003",
    deviceName: "焊接机器人 C2 å·",
    customerName: "西南新能源科技股份",
    engineerName: "张师傅",
    completeTime: "2024-12-02 11:05:00",
    completeDate: "2024-12-02",
    status: "pending",
    score: null,
    feedback: "",
  },
  {
    id: 4,
    orderNo: "WX-2024-1203-005",
    deviceName: "测试台 D1 å·",
    customerName: "北方汽车零部件有限公司",
    engineerName: "王师傅",
    completeTime: "2024-12-03 09:50:00",
    completeDate: "2024-12-03",
    status: "pending",
    score: null,
    feedback: "",
  },
]);
// æŸ¥è¯¢è¡¨å•
const searchForm = reactive({
  engineerName: "",
  customerName: "",
  dateRange: [],
  status: "",
});
// åˆ—表数据
const tableData = ref([...rawOrders.value]);
// ç»Ÿè®¡ï¼šæ•´ä½“评分、已评价 / å¾…评价数量
const ratedOrders = computed(() =>
  rawOrders.value.filter((o) => o.status === "rated" && o.score)
);
const overallAvgScore = computed(() => {
  if (!ratedOrders.value.length) return 0;
  const sum = ratedOrders.value.reduce((acc, cur) => acc + (cur.score || 0), 0);
  return sum / ratedOrders.value.length;
});
const ratedCount = computed(() => ratedOrders.value.length);
const pendingCount = computed(
  () => rawOrders.value.filter((o) => o.status === "pending").length
);
// æŸ¥è¯¢ / é‡ç½®
const recomputeTable = () => {
  const list = rawOrders.value.filter((item) => {
    if (
      searchForm.engineerName &&
      !item.engineerName.includes(searchForm.engineerName.trim())
    ) {
      return false;
    }
    if (
      searchForm.customerName &&
      !item.customerName.includes(searchForm.customerName.trim())
    ) {
      return false;
    }
    if (searchForm.status && item.status !== searchForm.status) {
      return false;
    }
    if (Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
      const [start, end] = searchForm.dateRange;
      if (item.completeDate < start || item.completeDate > end) {
        return false;
      }
    }
    return true;
  });
  tableData.value = list;
};
const handleQuery = () => {
  recomputeTable();
};
const resetSearch = () => {
  searchForm.engineerName = "";
  searchForm.customerName = "";
  searchForm.dateRange = [];
  searchForm.status = "";
  recomputeTable();
};
// å¯¼å‡ºï¼ˆæ¼”示)
const handleExport = () => {
  ElMessage.success("当前为演示页面,评价导出功能未对接实际接口");
};
// è¯„价弹框
const dialogVisible = ref(false);
const dialogTitle = ref("维修服务评价");
const currentOrder = ref(null);
const formRef = ref(null);
const form = reactive({
  score: 0,
  feedback: "",
});
const rules = {
  score: [{ required: true, message: "请选择星级评分", trigger: "change" }],
  feedback: [{ required: true, message: "请填写文字反馈", trigger: "blur" }],
};
// æ‰“开评价:模拟“维修完成确认后弹出评价弹框”
const openEvaluate = (row) => {
  currentOrder.value = row;
  dialogTitle.value =
    row.status === "pending" ? "维修服务评价" : "查看 / ä¿®æ”¹è¯„ä»·";
  form.score = row.score || 0;
  form.feedback = row.feedback || "";
  dialogVisible.value = true;
};
// æäº¤è¯„价:同步到本地“员工业绩统计”
const handleSubmit = () => {
  if (!formRef.value) return;
  formRef.value.validate((valid) => {
    if (!valid || !currentOrder.value) return;
    const target = rawOrders.value.find((o) => o.id === currentOrder.value.id);
    if (target) {
      target.score = form.score;
      target.feedback = form.feedback;
      target.status = "rated";
    }
    ElMessage.success("评价提交成功,已同步至员工业绩统计");
    dialogVisible.value = false;
    recomputeTable();
  });
};
// åˆå§‹åŒ–列表
recomputeTable();
</script>
<style scoped lang="scss">
.mb16 {
  margin-bottom: 16px;
}
.kpi-title {
  font-size: 13px;
  color: #909399;
}
.kpi-value {
  margin-top: 6px;
  font-size: 24px;
  font-weight: 600;
  color: #303133;
}
.kpi-unit {
  font-size: 12px;
  margin-left: 4px;
  color: #909399;
}
.kpi-warning {
  color: #e6a23c;
}
.dialog-order-info {
  margin-bottom: 12px;
  font-size: 13px;
  color: #606266;
  line-height: 1.8;
}
.dialog-footer {
  text-align: right;
}
</style>
src/views/personnelManagement/attendanceCheckin/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,469 @@
<template>
  <div class="app-container">
    <!-- å‘˜å·¥æ‰“卡区 -->
    <el-card shadow="never" class="mb16">
      <div class="attendance-header">
        <div>
          <div class="title">打卡签到</div>
          <div class="sub-title">支持一键打卡,自动记录上下班时间</div>
        </div>
        <div class="attendance-actions">
          <div class="time-block">
            <div class="label">当前时间</div>
            <div class="value">{{ nowTime }}</div>
          </div>
          <el-button type="primary" size="large" @click="handleCheckInOut">
            {{ checkInOutText }}
          </el-button>
        </div>
      </div>
      <el-descriptions border :column="4" class="mt10">
        <el-descriptions-item label="员工姓名">
          {{ currentUser.name }}
        </el-descriptions-item>
        <el-descriptions-item label="工号">
          {{ currentUser.no }}
        </el-descriptions-item>
        <el-descriptions-item label="所属部门">
          {{ currentUser.dept }}
        </el-descriptions-item>
        <el-descriptions-item label="今日状态">
          <el-tag :type="todayStatusTag" size="small">
            {{ todayStatusText }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="上班时间">
          {{ todayRecord?.checkInTime || '-' }}
        </el-descriptions-item>
        <el-descriptions-item label="下班时间">
          {{ todayRecord?.checkOutTime || '-' }}
        </el-descriptions-item>
        <el-descriptions-item label="工时(小时)">
          {{ todayRecord?.workHours ?? '-' }}
        </el-descriptions-item>
        <el-descriptions-item label="异常标记">
          <span v-if="todayRecord?.status === 'normal'">-</span>
          <el-tag v-else type="danger" size="small">
            {{ todayRecord?.statusText }}
          </el-tag>
        </el-descriptions-item>
      </el-descriptions>
    </el-card>
    <!-- æŸ¥è¯¢æ¡ä»¶ï¼ˆç®¡ç†å‘˜è€ƒå‹¤æ—¥æŠ¥ï¼‰ -->
    <div class="search_form">
      <div>
        <span class="search_title">部门:</span>
        <el-select
          v-model="searchForm.dept"
          placeholder="请选择部门"
          style="width: 180px"
          clearable
        >
          <el-option
            v-for="item in deptOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
        <span class="search_title ml10">日期:</span>
        <el-date-picker
          v-model="searchForm.date"
          type="date"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          placeholder="请选择日期"
          clearable
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
          æœç´¢
        </el-button>
        <el-button @click="resetSearch">重置</el-button>
      </div>
      <div>
        <el-button icon="Download" @click="handleExport">
          å¯¼å‡ºè€ƒå‹¤æ—¥æŠ¥
        </el-button>
      </div>
    </div>
    <!-- è€ƒå‹¤æ—¥æŠ¥è¡¨æ ¼ -->
    <div class="table_list">
      <el-table
        :data="tableData"
        border
        style="width: 100%"
        height="calc(100vh - 24em)"
        :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
        :row-class-name="rowClassName"
      >
        <el-table-column type="index" label="序号" width="60" align="center" />
        <el-table-column
          prop="date"
          label="日期"
          width="120"
        />
        <el-table-column
          prop="dept"
          label="部门"
          width="140"
        />
        <el-table-column
          prop="name"
          label="姓名"
          width="120"
        />
        <el-table-column
          prop="no"
          label="工号"
          width="120"
        />
        <el-table-column
          prop="checkInTime"
          label="上班时间"
          width="140"
        />
        <el-table-column
          prop="checkOutTime"
          label="下班时间"
          width="140"
        />
        <el-table-column
          prop="workHours"
          label="工时(小时)"
          width="110"
          align="center"
        />
        <el-table-column
          prop="statusText"
          label="考勤状态"
          width="120"
          align="center"
        >
          <template #default="scope">
            <el-tag
              v-if="scope.row.status === 'normal'"
              type="success"
              size="small"
            >
              æ­£å¸¸
            </el-tag>
            <el-tag
              v-else
              type="danger"
              size="small"
            >
              {{ scope.row.statusText }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          prop="remark"
          label="备注"
          show-overflow-tooltip
        />
      </el-table>
    </div>
  </div>
</template>
<script setup>
import { ref, reactive, computed, onMounted, onBeforeUnmount } from "vue";
import { ElMessage } from "element-plus";
// æ¨¡æ‹Ÿå½“前登录员工
const currentUser = reactive({
  id: 1,
  name: "张三",
  no: "E10001",
  dept: "生产一部",
});
// éƒ¨é—¨é€‰é¡¹
const deptOptions = [
  { label: "生产一部", value: "生产一部" },
  { label: "生产二部", value: "生产二部" },
  { label: "设备维护部", value: "设备维护部" },
  { label: "质检部", value: "质检部" },
];
// æ¨¡æ‹Ÿè€ƒå‹¤åŽŸå§‹æ•°æ®
const rawAttendance = ref([
  {
    id: 1,
    date: "2024-12-01",
    userId: 1,
    name: "张三",
    no: "E10001",
    dept: "生产一部",
    checkInTime: "08:58",
    checkOutTime: "18:10",
    workHours: 9.2,
    status: "normal",
    statusText: "正常",
    remark: "",
  },
  {
    id: 2,
    date: "2024-12-01",
    userId: 2,
    name: "李四",
    no: "E10002",
    dept: "生产一部",
    checkInTime: "09:15",
    checkOutTime: "18:05",
    workHours: 8.8,
    status: "late",
    statusText: "迟到",
    remark: "因交通拥堵迟到",
  },
  {
    id: 3,
    date: "2024-12-01",
    userId: 3,
    name: "王五",
    no: "E20001",
    dept: "设备维护部",
    checkInTime: "08:50",
    checkOutTime: "17:20",
    workHours: 8.5,
    status: "early",
    statusText: "早退",
    remark: "外出处理紧急故障",
  },
  {
    id: 4,
    date: "2024-12-02",
    userId: 1,
    name: "张三",
    no: "E10001",
    dept: "生产一部",
    checkInTime: "08:45",
    checkOutTime: "18:30",
    workHours: 9.7,
    status: "normal",
    statusText: "正常",
    remark: "加班0.5小时",
  },
]);
// æŸ¥è¯¢è¡¨å•
const searchForm = reactive({
  dept: "",
  date: "",
});
// è¡¨æ ¼æ•°æ®
const tableData = ref([]);
// å½“前时间展示
const nowTime = ref("");
let timer = null;
const updateNowTime = () => {
  const now = new Date();
  const Y = now.getFullYear();
  const M = String(now.getMonth() + 1).padStart(2, "0");
  const D = String(now.getDate()).padStart(2, "0");
  const h = String(now.getHours()).padStart(2, "0");
  const m = String(now.getMinutes()).padStart(2, "0");
  const s = String(now.getSeconds()).padStart(2, "0");
  nowTime.value = `${Y}-${M}-${D} ${h}:${m}:${s}`;
};
// ä»Šæ—¥æ—¥æœŸ
const todayStr = computed(() => nowTime.value.slice(0, 10));
// å½“日当前员工考勤记录
const todayRecord = computed(() =>
  rawAttendance.value.find(
    (item) =>
      item.userId === currentUser.id && item.date === todayStr.value
  )
);
// æ‰“卡按钮文本
const checkInOutText = computed(() => {
  if (!todayRecord.value || !todayRecord.value.checkInTime) {
    return "上班打卡";
  }
  if (!todayRecord.value.checkOutTime) {
    return "下班打卡";
  }
  return "今日已打卡完成";
});
// ä»Šæ—¥çŠ¶æ€å±•ç¤º
const todayStatusTag = computed(() => {
  if (!todayRecord.value) return "info";
  if (todayRecord.value.status === "normal") return "success";
  return "danger";
});
const todayStatusText = computed(() => {
  if (!todayRecord.value) return "未打卡";
  return todayRecord.value.statusText || "正常";
});
// è¡Œæ ·å¼ï¼šå¼‚常高亮
const rowClassName = ({ row }) => {
  if (row.status === "late" || row.status === "early") {
    return "row-abnormal";
  }
  return "";
};
// æŸ¥è¯¢
const recomputeTable = () => {
  const list = rawAttendance.value.filter((item) => {
    if (searchForm.dept && item.dept !== searchForm.dept) {
      return false;
    }
    if (searchForm.date && item.date !== searchForm.date) {
      return false;
    }
    return true;
  });
  tableData.value = list;
};
const handleQuery = () => {
  recomputeTable();
};
const resetSearch = () => {
  searchForm.dept = "";
  searchForm.date = "";
  recomputeTable();
};
// å¯¼å‡ºï¼ˆæ¼”示)
const handleExport = () => {
  ElMessage.success("当前为演示页面,导出功能未对接实际接口");
};
// æ‰“卡逻辑(仅前端模拟)
const handleCheckInOut = () => {
  const [dateStr, timeStr] = nowTime.value.split(" ");
  if (!dateStr || !timeStr) return;
  // ä¸Šç­æ‰“卡
  if (!todayRecord.value) {
    const newId = rawAttendance.value.length
      ? Math.max(...rawAttendance.value.map((i) => i.id)) + 1
      : 1;
    const status =
      timeStr > "09:00:00" ? "late" : "normal";
    const statusText = status === "late" ? "迟到" : "正常";
    rawAttendance.value.push({
      id: newId,
      date: dateStr,
      userId: currentUser.id,
      name: currentUser.name,
      no: currentUser.no,
      dept: currentUser.dept,
      checkInTime: timeStr.slice(0, 5),
      checkOutTime: "",
      workHours: null,
      status,
      statusText,
      remark: "",
    });
    ElMessage.success("上班打卡成功");
  } else if (!todayRecord.value.checkOutTime) {
    // ä¸‹ç­æ‰“卡
    todayRecord.value.checkOutTime = timeStr.slice(0, 5);
    // ç®€å•按 9:00-18:00 è®¡ç®—工时
    const start = todayRecord.value.checkInTime || "09:00";
    const [sh, sm] = start.split(":").map((v) => parseInt(v, 10));
    const [eh, em] = todayRecord.value.checkOutTime
      .split(":")
      .map((v) => parseInt(v, 10));
    const diff = (eh * 60 + em - (sh * 60 + sm)) / 60;
    todayRecord.value.workHours = Number(Math.max(diff, 0).toFixed(1));
    // æ—©é€€åˆ¤æ–­ï¼š18:00 å‰ç¦»å¼€è§†ä¸ºæ—©é€€ï¼ˆåªç¤ºæ„ï¼‰
    if (timeStr < "18:00:00") {
      todayRecord.value.status = "early";
      todayRecord.value.statusText = "早退";
    } else if (todayRecord.value.status === "normal") {
      todayRecord.value.statusText = "正常";
    }
    ElMessage.success("下班打卡成功");
  } else {
    ElMessage.info("今日已完成上下班打卡");
  }
  recomputeTable();
};
onMounted(() => {
  updateNowTime();
  timer = setInterval(updateNowTime, 1000);
  // é»˜è®¤å±•示当天数据
  const today = new Date();
  const Y = today.getFullYear();
  const M = String(today.getMonth() + 1).padStart(2, "0");
  const D = String(today.getDate()).padStart(2, "0");
  searchForm.date = `${Y}-${M}-${D}`;
  recomputeTable();
});
onBeforeUnmount(() => {
  if (timer) {
    clearInterval(timer);
  }
});
</script>
<style scoped lang="scss">
.mb16 {
  margin-bottom: 16px;
}
.attendance-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.attendance-header .title {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 4px;
}
.attendance-header .sub-title {
  font-size: 13px;
  color: #909399;
}
.attendance-actions {
  display: flex;
  align-items: center;
  gap: 16px;
}
.time-block {
  text-align: right;
}
.time-block .label {
  font-size: 12px;
  color: #909399;
}
.time-block .value {
  font-size: 18px;
  font-weight: 600;
  color: #333;
}
::v-deep(.row-abnormal) {
  background-color: #fff5f5;
}
</style>