gaoluyang
2026-03-12 fe167dd71a1300aeae07522db990d6b3fdb77a0e
src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,316 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜å’Œæ“ä½œæŒ‰é’® -->
    <div class="page-header">
      <div class="title">班次配置</div>
      <div class="actions">
        <el-button type="primary"
                   @click="openForm('add')">
          <el-icon>
            <Plus />
          </el-icon>
          æ–°å¢žç­æ¬¡
        </el-button>
      </div>
    </div>
    <!-- æŸ¥è¯¢æ¡ä»¶ -->
    <!-- <el-form :model="searchForm"
             :inline="true"
             class="search-form mb16">
      <el-form-item label="部门:"
                    prop="countId">
        <el-tree-select v-model="searchForm.countId"
                        :data="deptOptions"
                        :props="{ value: 'id', label: 'label', children: 'children' }"
                        value-key="id"
                        placeholder="请选择部门"
                        check-strictly
                        style="width: 200px" />
      </el-form-item>
      <el-form-item label="地点:"
                    prop="locationName">
        <el-input v-model="searchForm.locationName"
                  placeholder="请输入地点名称"
                  clearable
                  style="width: 200px" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary"
                   @click="fetchData">
          <el-icon>
            <Search />
          </el-icon>
          æœç´¢
        </el-button>
        <el-button @click="resetSearch">
          <el-icon>
            <Refresh />
          </el-icon>
          é‡ç½®
        </el-button>
      </el-form-item>
    </el-form> -->
    <!-- ç­æ¬¡åˆ—表 -->
    <el-card shadow="never"
             class="mb16">
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                height="calc(100vh - 18.5em)"
                style="width: 100%"
                row-key="id">
        <el-table-column type="index"
                         label="序号"
                         width="60"
                         align="center" />
        <el-table-column label="部门">
          <template #default="scope">
            {{ getDeptNameById(scope.row.sysDeptId) }}
          </template>
        </el-table-column>
        <el-table-column label="班次">
          <template #default="scope">
            {{ getShiftNameByValue(scope.row.shift) }}
          </template>
        </el-table-column>
        <el-table-column prop="locationName"
                         label="地点名称" />
        <el-table-column prop="longitude"
                         label="经度" />
        <el-table-column prop="latitude"
                         label="纬度" />
        <el-table-column prop="radius"
                         label="打卡范围(m)" />
        <el-table-column prop="startAt"
                         label="上班时间">
          <template #default="scope">
            {{ scope.row.startAt }}
          </template>
        </el-table-column>
        <el-table-column prop="endAt"
                         label="下班时间">
          <template #default="scope">
            {{ scope.row.endAt }}
          </template>
        </el-table-column>
        <el-table-column label="操作"
                         width="180"
                         fixed="right"
                         align="center">
          <template #default="scope">
            <el-button type="primary"
                       size="small"
                       link
                       @click="openForm('edit', scope.row)">编辑</el-button>
            <el-button type="danger"
                       size="small"
                       link
                       @click="handleDelete(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination :total="page.total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange"
                  class="mt10" />
    </el-card>
    <!-- æ–°å¢ž/编辑班次弹窗 -->
    <rule-form ref="ruleFormRef"
               v-model="dialogVisible"
               :operation-type="operationType"
               :row="currentRow"
               @close="dialogVisible = false; fetchData()" />
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    Plus,
    Edit,
    Delete,
    Search,
    Refresh,
    ArrowLeft,
  } from "@element-plus/icons-vue";
  import Pagination from "@/components/Pagination/index.vue";
  import RuleForm from "./components/form.vue";
  import { deptTreeSelect } from "@/api/system/user.js";
  import {
    getAttendanceRules,
    deleteAttendanceRule,
  } from "@/api/personnelManagement/attendanceRules.js";
  import { useDict } from "@/utils/dict";
  const { proxy } = getCurrentInstance();
  // è¡¨æ ¼æ•°æ®
  const tableData = ref([]);
  const tableLoading = ref(false);
  // åˆ†é¡µå‚æ•°
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // æŸ¥è¯¢è¡¨å•
  const searchForm = reactive({
    countId: "",
    locationName: "",
  });
  // éƒ¨é—¨é€‰é¡¹
  const deptOptions = ref([]);
  // èŽ·å–ç­æ¬¡å­—å…¸å€¼
  const { shifts_list } = useDict("shifts_list");
  // å¼¹çª—控制
  const dialogVisible = ref(false);
  const operationType = ref("add");
  const currentRow = ref({});
  const ruleFormRef = ref();
  // æ ¼å¼åŒ–æ—¶é—´
  const formatTime = timestamp => {
    if (!timestamp) return "";
    const date = new Date(timestamp);
    return `${String(date.getHours()).padStart(2, "0")}:${String(
      date.getMinutes()
    ).padStart(2, "0")}`;
  };
  // æ ¹æ®ç­æ¬¡å€¼èŽ·å–ç­æ¬¡åç§°
  const getShiftNameByValue = value => {
    if (!value) return "";
    const shift = shifts_list.value.find(item => item.value === value);
    return shift ? shift.label : value;
  };
  // èŽ·å–éƒ¨é—¨åˆ—è¡¨
  const fetchDeptOptions = () => {
    deptTreeSelect().then(response => {
      deptOptions.value = filterDisabledDept(
        JSON.parse(JSON.stringify(response.data))
      );
    });
  };
  // è¿‡æ»¤ç¦ç”¨çš„部门
  const filterDisabledDept = deptList => {
    return deptList.filter(dept => {
      if (dept.disabled) {
        return false;
      }
      if (dept.children && dept.children.length) {
        dept.children = filterDisabledDept(dept.children);
      }
      return true;
    });
  };
  // æ ¹æ®éƒ¨é—¨ID查找部门名称
  const getDeptNameById = (deptId, deptList = deptOptions.value) => {
    for (const dept of deptList) {
      if (dept.id === deptId) {
        return dept.label;
      }
      if (dept.children && dept.children.length) {
        const name = getDeptNameById(deptId, dept.children);
        if (name) {
          return name;
        }
      }
    }
    return "";
  };
  // æŸ¥è¯¢ç­æ¬¡åˆ—表
  const fetchData = () => {
    tableLoading.value = true;
    getAttendanceRules({ ...page, ...searchForm })
      .then(res => {
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  // åˆ†é¡µå˜æ›´
  const paginationChange = pagination => {
    page.current = pagination.page;
    page.size = pagination.limit;
    fetchData();
  };
  // é‡ç½®æœç´¢
  const resetSearch = () => {
    searchForm.countId = "";
    searchForm.locationName = "";
    fetchData();
  };
  // æ‰“开表单
  const openForm = (type, row = {}) => {
    operationType.value = type;
    currentRow.value = row;
    dialogVisible.value = true;
  };
  // åˆ é™¤ç­æ¬¡
  const handleDelete = id => {
    ElMessageBox.confirm("确定要删除这条班次吗?", "删除确认", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        deleteAttendanceRule([id]).then(() => {
          ElMessage.success("删除成功");
          fetchData();
        });
      })
      .catch(() => {
        // å–消删除
      });
  };
  // åˆå§‹åŒ–
  onMounted(() => {
    fetchDeptOptions();
    fetchData();
  });
</script>
<style scoped lang="scss">
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    .title {
      font-size: 18px;
      font-weight: 600;
    }
    .actions {
      display: flex;
      gap: 10px;
    }
  }
  .mb16 {
    margin-bottom: 16px;
  }
  .mt10 {
    margin-top: 10px;
  }
</style>