|
|
@@ -2,7 +2,7 @@ |
|
|
|
<div class="attendance-group-container"> |
|
|
|
<div class="page-header"> |
|
|
|
<el-row> |
|
|
|
<el-col :span=24> |
|
|
|
<el-col :span="24"> |
|
|
|
<h1>考勤组配置</h1> |
|
|
|
<p>设置考勤规则、关联员工和打卡区域</p> |
|
|
|
</el-col> |
|
|
@@ -11,169 +11,210 @@ |
|
|
|
|
|
|
|
<div class="main-content"> |
|
|
|
<el-row :gutter="20"> |
|
|
|
<!-- 左侧:考勤组列表 --> |
|
|
|
<el-col :sm="24" :md="6"> |
|
|
|
<el-table :data="attendanceGroupData"> |
|
|
|
<el-table-column label="考勤组" align="center" prop="name" lable-size="100px"></el-table-column> |
|
|
|
<el-table-column label="描述" align="center" prop="description" ></el-table-column> |
|
|
|
<el-table-column label="考勤时间" align="center" prop="workDays" ></el-table-column> |
|
|
|
</el-table> |
|
|
|
<el-card class="group-list-card"> |
|
|
|
<template #header> |
|
|
|
<div class="card-header"> |
|
|
|
<h3>考勤组列表</h3> |
|
|
|
<el-button type="primary" size="small" @click="showAddGroupModal = true"> |
|
|
|
<i class="el-icon-plus"></i> 新建 |
|
|
|
</el-button> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<div class="group-list"> |
|
|
|
<div |
|
|
|
v-for="group in attendanceGroups" |
|
|
|
:key="group.id" |
|
|
|
class="group-item" |
|
|
|
:class="{'active': selectedGroupId === group.id}" |
|
|
|
@click="selectGroup(group.id)" |
|
|
|
> |
|
|
|
<div class="group-name">{{ group.name }}</div> |
|
|
|
<div class="group-desc">{{ group.description || '未设置描述' }}</div> |
|
|
|
<div class="group-meta"> |
|
|
|
<span>{{ group.members.length }}名员工</span> |
|
|
|
<span v-if="group.areaName">· {{ group.areaName }}</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-if="attendanceGroups.length === 0" class="empty-state"> |
|
|
|
<i class="el-icon-s-empty"></i> |
|
|
|
<p>暂无考勤组,点击"新建"按钮创建</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-card> |
|
|
|
</el-col> |
|
|
|
|
|
|
|
<!-- 右侧:考勤组表单 --> |
|
|
|
<el-col :sm="24" :md="18"> |
|
|
|
<div class="card-header"> |
|
|
|
<el-card class="group-form-card"> |
|
|
|
<template #header> |
|
|
|
<div class="card-header"> |
|
|
|
<h3>{{ isEditMode ? '编辑考勤组' : '新建考勤组' }}</h3> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<el-card class="group-form-card"> |
|
|
|
<template #header> |
|
|
|
<div class="card-header"> |
|
|
|
<h3>{{ isEditMode ? '编辑考勤组' : '新建考勤组' }}</h3> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<el-form :model="attendanceGroup" :rules="rules" ref="groupForm" label-width="120px"> |
|
|
|
<!-- 基本信息 --> |
|
|
|
<el-form-item label="考勤组名称" prop="name"> |
|
|
|
<el-input v-model="attendanceGroup.name" placeholder="请输入考勤组名称"></el-input> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="描述" prop="description"> |
|
|
|
<el-input v-model="attendanceGroup.description" placeholder="请输入描述信息"></el-input> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- 考勤时间设置 --> |
|
|
|
<el-form-item label="工作日设置"> |
|
|
|
<el-checkbox-group v-model="attendanceGroup.workDays"> |
|
|
|
<el-checkbox v-for="day in workDays" :key="day.value" :label="day.value">{{ day.label }}</el-checkbox> |
|
|
|
</el-checkbox-group> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="上班时间" prop="workStartTime"> |
|
|
|
<el-time-picker |
|
|
|
v-model="attendanceGroup.workStartTime" |
|
|
|
format="HH:mm" |
|
|
|
value-format="HH:mm" |
|
|
|
placeholder="选择上班时间" |
|
|
|
></el-time-picker> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="迟到浮动" prop="lateRange"> |
|
|
|
<el-col :span="12"> |
|
|
|
<el-switch |
|
|
|
v-model="attendanceGroup.allowLate" |
|
|
|
active-text="允许" |
|
|
|
inactive-text="不允许" |
|
|
|
@change="handleLateChange" |
|
|
|
></el-switch> |
|
|
|
</el-col> |
|
|
|
<el-col :span="12" v-if="attendanceGroup.allowLate"> |
|
|
|
<el-slider |
|
|
|
v-model="attendanceGroup.lateRange" |
|
|
|
:min="1" |
|
|
|
:max="30" |
|
|
|
:step="1" |
|
|
|
show-tooltip |
|
|
|
@change="validateLateRange" |
|
|
|
></el-slider> |
|
|
|
<div class="slider-value">{{ attendanceGroup.lateRange }}分钟</div> |
|
|
|
</el-col> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form :model="attendanceGroup" :rules="rules" ref="groupForm" label-width="120px"> |
|
|
|
<!-- 基本信息 --> |
|
|
|
<el-form-item label="考勤组名称" prop="name"> |
|
|
|
<el-input v-model="attendanceGroup.name" placeholder="请输入考勤组名称"></el-input> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="描述" prop="description"> |
|
|
|
<el-input v-model="attendanceGroup.description" placeholder="请输入描述信息"></el-input> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- 考勤时间设置 --> |
|
|
|
<el-form-item label="工作日设置"> |
|
|
|
<el-checkbox-group v-model="attendanceGroup.workDays"> |
|
|
|
<el-checkbox v-for="day in workDays" :key="day.value" :label="day.value">{{ day.label }}</el-checkbox> |
|
|
|
</el-checkbox-group> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="上班时间" prop="workStartTime"> |
|
|
|
<el-time-picker |
|
|
|
v-model="attendanceGroup.workStartTime" |
|
|
|
format="HH:mm" |
|
|
|
value-format="HH:mm" |
|
|
|
placeholder="选择上班时间" |
|
|
|
></el-time-picker> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="迟到浮动" prop="lateRange"> |
|
|
|
<el-col :span="12"> |
|
|
|
<el-switch |
|
|
|
v-model="attendanceGroup.allowLate" |
|
|
|
active-text="允许" |
|
|
|
inactive-text="不允许" |
|
|
|
@change="handleLateChange" |
|
|
|
></el-switch> |
|
|
|
</el-col> |
|
|
|
<el-col :span="12" v-if="attendanceGroup.allowLate"> |
|
|
|
<el-slider |
|
|
|
v-model="attendanceGroup.lateRange" |
|
|
|
:min="1" |
|
|
|
:max="30" |
|
|
|
:step="1" |
|
|
|
show-tooltip |
|
|
|
@change="validateLateRange" |
|
|
|
></el-slider> |
|
|
|
<div class="slider-value">{{ attendanceGroup.lateRange }}分钟</div> |
|
|
|
</el-col> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="下班时间" prop="workEndTime"> |
|
|
|
<el-time-picker |
|
|
|
v-model="attendanceGroup.workEndTime" |
|
|
|
format="HH:mm" |
|
|
|
value-format="HH:mm" |
|
|
|
placeholder="选择下班时间" |
|
|
|
></el-time-picker> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="早退浮动" prop="earlyRange"> |
|
|
|
<el-col :span="12"> |
|
|
|
<el-switch |
|
|
|
v-model="attendanceGroup.allowEarly" |
|
|
|
active-text="允许" |
|
|
|
inactive-text="不允许" |
|
|
|
@change="handleEarlyChange" |
|
|
|
></el-switch> |
|
|
|
</el-col> |
|
|
|
<el-col :span="12" v-if="attendanceGroup.allowEarly"> |
|
|
|
<el-slider |
|
|
|
v-model="attendanceGroup.earlyRange" |
|
|
|
:min="1" |
|
|
|
:max="30" |
|
|
|
:step="1" |
|
|
|
show-tooltip |
|
|
|
@change="validateEarlyRange" |
|
|
|
></el-slider> |
|
|
|
<div class="slider-value">{{ attendanceGroup.earlyRange }}分钟</div> |
|
|
|
</el-col> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- 打卡配置 --> |
|
|
|
<el-form-item label="打卡区域" prop="areaId"> |
|
|
|
<el-select |
|
|
|
v-model="attendanceGroup.areaId" |
|
|
|
placeholder="请选择打卡区域" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="area in checkInAreas" |
|
|
|
:key="area.id" |
|
|
|
:label="area.name" |
|
|
|
:value="area.id" |
|
|
|
></el-option> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- <el-form-item label="打卡方式" prop="checkInType"> |
|
|
|
<el-radio-group v-model="attendanceGroup.checkInType"> |
|
|
|
<el-radio label="location">位置打卡</el-radio> |
|
|
|
<el-radio label="qr">二维码打卡</el-radio> |
|
|
|
<el-radio label="both">两者都可</el-radio> |
|
|
|
</el-radio-group> |
|
|
|
</el-form-item> --> |
|
|
|
|
|
|
|
<!-- 关联员工 --> |
|
|
|
<el-form-item label="关联员工"> |
|
|
|
<el-button |
|
|
|
type="primary" |
|
|
|
size="small" |
|
|
|
@click="openUserSelectionDialog" |
|
|
|
> |
|
|
|
<i class="el-icon-plus"></i> 选择员工 |
|
|
|
</el-button> |
|
|
|
|
|
|
|
<el-table |
|
|
|
:data="attendanceGroup.members" |
|
|
|
style="width: 100%; margin-top: 20px" |
|
|
|
empty-text="暂无关联员工" |
|
|
|
> |
|
|
|
<el-table-column prop="name" label="姓名"></el-table-column> |
|
|
|
<el-table-column prop="employeeId" label="工号"></el-table-column> |
|
|
|
<el-table-column prop="department" label="部门"></el-table-column> |
|
|
|
<el-table-column prop="position" label="职位"></el-table-column> |
|
|
|
<el-table-column label="操作" width="80"> |
|
|
|
<template #default="scope"> |
|
|
|
<el-button |
|
|
|
type="danger" |
|
|
|
size="mini" |
|
|
|
@click="removeMember(scope.row)" |
|
|
|
> |
|
|
|
<i class="el-icon-delete"></i> |
|
|
|
</el-button> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
</el-table> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- 操作按钮 --> |
|
|
|
<el-form-item> |
|
|
|
<el-button type="primary" @click="saveGroup">保存</el-button> |
|
|
|
<el-button @click="cancelEdit">取消</el-button> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
</el-card> |
|
|
|
</div> |
|
|
|
<el-form-item label="下班时间" prop="workEndTime"> |
|
|
|
<el-time-picker |
|
|
|
v-model="attendanceGroup.workEndTime" |
|
|
|
format="HH:mm" |
|
|
|
value-format="HH:mm" |
|
|
|
placeholder="选择下班时间" |
|
|
|
></el-time-picker> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="早退浮动" prop="earlyRange"> |
|
|
|
<el-col :span="12"> |
|
|
|
<el-switch |
|
|
|
v-model="attendanceGroup.allowEarly" |
|
|
|
active-text="允许" |
|
|
|
inactive-text="不允许" |
|
|
|
@change="handleEarlyChange" |
|
|
|
></el-switch> |
|
|
|
</el-col> |
|
|
|
<el-col :span="12" v-if="attendanceGroup.allowEarly"> |
|
|
|
<el-slider |
|
|
|
v-model="attendanceGroup.earlyRange" |
|
|
|
:min="1" |
|
|
|
:max="30" |
|
|
|
:step="1" |
|
|
|
show-tooltip |
|
|
|
@change="validateEarlyRange" |
|
|
|
></el-slider> |
|
|
|
<div class="slider-value">{{ attendanceGroup.earlyRange }}分钟</div> |
|
|
|
</el-col> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- 打卡配置 --> |
|
|
|
<el-form-item label="打卡区域" prop="areaId"> |
|
|
|
<el-select |
|
|
|
v-model="attendanceGroup.areaId" |
|
|
|
placeholder="请选择打卡区域" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="area in checkInAreas" |
|
|
|
:key="area.id" |
|
|
|
:label="area.name" |
|
|
|
:value="area.id" |
|
|
|
></el-option> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- 关联员工 --> |
|
|
|
<el-form-item label="关联员工"> |
|
|
|
<el-button |
|
|
|
type="primary" |
|
|
|
size="small" |
|
|
|
@click="openUserSelectionDialog" |
|
|
|
> |
|
|
|
<i class="el-icon-plus"></i> 选择员工 |
|
|
|
</el-button> |
|
|
|
|
|
|
|
<el-table |
|
|
|
:data="attendanceGroup.members" |
|
|
|
style="width: 100%; margin-top: 20px" |
|
|
|
empty-text="暂无关联员工" |
|
|
|
> |
|
|
|
<el-table-column prop="name" label="姓名"></el-table-column> |
|
|
|
<el-table-column prop="employeeId" label="工号"></el-table-column> |
|
|
|
<el-table-column prop="department" label="部门"></el-table-column> |
|
|
|
<el-table-column prop="position" label="职位"></el-table-column> |
|
|
|
<el-table-column label="操作" width="80"> |
|
|
|
<template #default="scope"> |
|
|
|
<el-button |
|
|
|
type="danger" |
|
|
|
size="mini" |
|
|
|
@click="removeMember(scope.row)" |
|
|
|
> |
|
|
|
<i class="el-icon-delete"></i> |
|
|
|
</el-button> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
</el-table> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<!-- 操作按钮 --> |
|
|
|
<el-form-item> |
|
|
|
<el-button type="primary" @click="saveGroup">保存</el-button> |
|
|
|
<el-button @click="cancelEdit">取消</el-button> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
</el-card> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 新建考勤组对话框 --> |
|
|
|
<el-dialog |
|
|
|
title="新建考勤组" |
|
|
|
:visible.sync="showAddGroupModal" |
|
|
|
width="30%" |
|
|
|
:before-close="handleCloseAddGroupDialog" |
|
|
|
> |
|
|
|
<el-form :model="newGroup" :rules="newGroupRules" ref="newGroupForm" label-width="100px"> |
|
|
|
<el-form-item label="考勤组名称" prop="name"> |
|
|
|
<el-input v-model="newGroup.name" placeholder="请输入考勤组名称"></el-input> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="描述"> |
|
|
|
<el-input v-model="newGroup.description" placeholder="请输入描述信息"></el-input> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
<template #footer> |
|
|
|
<el-button @click="showAddGroupModal = false">取消</el-button> |
|
|
|
<el-button type="primary" @click="createNewGroup">创建</el-button> |
|
|
|
</template> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- 员工选择对话框 --> |
|
|
|
<el-dialog |
|
|
|
title="选择员工" |
|
|
@@ -181,41 +222,15 @@ |
|
|
|
width="60%" |
|
|
|
:before-close="handleCloseUserDialog" |
|
|
|
> |
|
|
|
<!-- <template #content> |
|
|
|
<div class="search-container"> |
|
|
|
<el-input |
|
|
|
v-model="userSearchKeyword" |
|
|
|
placeholder="搜索员工" |
|
|
|
prefix-icon="el-icon-search" |
|
|
|
@keyup.enter="searchUsers" |
|
|
|
></el-input> |
|
|
|
<el-button @click="searchUsers">搜索</el-button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<el-table |
|
|
|
:data="filteredUsers" |
|
|
|
@selection-change="handleUserSelectionChange" |
|
|
|
> |
|
|
|
<el-table-column type="selection" width="55"></el-table-column> |
|
|
|
<el-table-column prop="name" label="姓名"></el-table-column> |
|
|
|
<el-table-column prop="employeeId" label="工号"></el-table-column> |
|
|
|
<el-table-column prop="department" label="部门"></el-table-column> |
|
|
|
<el-table-column prop="position" label="职位"></el-table-column> |
|
|
|
</el-table> |
|
|
|
</template> --> |
|
|
|
<template #footer> |
|
|
|
<el-button @click="userSelectionDialogVisible = false">取消</el-button> |
|
|
|
<el-button type="primary" @click="confirmUserSelection">确定</el-button> |
|
|
|
</template> |
|
|
|
<div class="search-container"> |
|
|
|
<el-input |
|
|
|
v-model="userSearchKeyword" |
|
|
|
placeholder="搜索员工" |
|
|
|
prefix-icon="el-icon-search" |
|
|
|
@keyup.enter="searchUsers" |
|
|
|
></el-input> |
|
|
|
<el-button @click="searchUsers">搜索</el-button> |
|
|
|
</div> |
|
|
|
<el-input |
|
|
|
v-model="userSearchKeyword" |
|
|
|
placeholder="搜索员工" |
|
|
|
prefix-icon="el-icon-search" |
|
|
|
@keyup.enter="searchUsers" |
|
|
|
></el-input> |
|
|
|
<el-button @click="searchUsers">搜索</el-button> |
|
|
|
</div> |
|
|
|
<el-table |
|
|
|
:data="filteredUsers" |
|
|
|
@selection-change="handleUserSelectionChange" |
|
|
@@ -226,25 +241,31 @@ |
|
|
|
<el-table-column prop="department" label="部门"></el-table-column> |
|
|
|
<el-table-column prop="position" label="职位"></el-table-column> |
|
|
|
</el-table> |
|
|
|
<template #footer> |
|
|
|
<el-button @click="userSelectionDialogVisible = false">取消</el-button> |
|
|
|
<el-button type="primary" @click="confirmUserSelection">确定</el-button> |
|
|
|
</template> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { |
|
|
|
add, |
|
|
|
getOperationWarnresult, |
|
|
|
delOperationWarnresult, |
|
|
|
addOperationWarnresult, |
|
|
|
updateOperationWarnresult |
|
|
|
getList, |
|
|
|
update, |
|
|
|
del |
|
|
|
} from '@/api/daka/attendance-group' |
|
|
|
|
|
|
|
export default { |
|
|
|
name: 'AttendanceGroup', |
|
|
|
data() { |
|
|
|
return { |
|
|
|
// 考勤组数据 |
|
|
|
// 考勤组列表 |
|
|
|
attendanceGroups: [], |
|
|
|
// 当前选中的考勤组ID |
|
|
|
selectedGroupId: null, |
|
|
|
// 考勤组表单数据 |
|
|
|
attendanceGroup: { |
|
|
|
id: null, |
|
|
|
name: '', |
|
|
@@ -258,9 +279,9 @@ export default { |
|
|
|
earlyRange: 15, |
|
|
|
areaId: null, |
|
|
|
checkInType: 'location', |
|
|
|
members: [] |
|
|
|
members: [], |
|
|
|
areaName: '' |
|
|
|
}, |
|
|
|
|
|
|
|
// 工作日选项 |
|
|
|
workDays: [ |
|
|
|
{ value: 'mon', label: '周一' }, |
|
|
@@ -271,22 +292,26 @@ export default { |
|
|
|
{ value: 'sat', label: '周六' }, |
|
|
|
{ value: 'sun', label: '周日' } |
|
|
|
], |
|
|
|
|
|
|
|
// 员工列表 |
|
|
|
users: [], |
|
|
|
|
|
|
|
// 打卡区域列表 |
|
|
|
checkInAreas: [], |
|
|
|
|
|
|
|
// 新建考勤组表单 |
|
|
|
newGroup: { |
|
|
|
name: '', |
|
|
|
description: '' |
|
|
|
}, |
|
|
|
// 新建考勤组表单规则 |
|
|
|
newGroupRules: { |
|
|
|
name: [{ required: true, message: '请输入考勤组名称', trigger: 'blur' }] |
|
|
|
}, |
|
|
|
// 搜索关键词 |
|
|
|
userSearchKeyword: '', |
|
|
|
|
|
|
|
// 选中的用户 |
|
|
|
selectedUsers: [], |
|
|
|
|
|
|
|
// 对话框状态 |
|
|
|
userSelectionDialogVisible: false, |
|
|
|
|
|
|
|
showAddGroupModal: false, |
|
|
|
// 表单验证规则 |
|
|
|
rules: { |
|
|
|
name: [ |
|
|
@@ -302,20 +327,16 @@ export default { |
|
|
|
{ required: true, message: '请选择打卡区域', trigger: 'change' } |
|
|
|
] |
|
|
|
}, |
|
|
|
|
|
|
|
// 编辑模式 |
|
|
|
isEditMode: false, |
|
|
|
|
|
|
|
// 提示信息 |
|
|
|
successMessage: '', |
|
|
|
successMessageVisible: false, |
|
|
|
attendanceGroupData: [{name: 'sssss', description: 'dddd', workDays: '[mon, tue, wed, thu, fri]'}] |
|
|
|
successMessageVisible: false |
|
|
|
}; |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
// 过滤后的用户列表 |
|
|
|
filteredUsers() { |
|
|
|
debugger |
|
|
|
// 获取不在当前考勤组中的用户 |
|
|
|
const groupMemberIds = this.attendanceGroup.members.map(member => member.id); |
|
|
|
const availableUsers = this.users.filter(user => |
|
|
@@ -337,13 +358,52 @@ export default { |
|
|
|
mounted() { |
|
|
|
// 加载模拟数据 |
|
|
|
this.loadMockData(); |
|
|
|
|
|
|
|
// 默认进入新建模式 |
|
|
|
this.resetForm(); |
|
|
|
// 加载打卡区域数据(实际项目中从API获取) |
|
|
|
this.loadCheckInAreas(); |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
// 加载模拟数据 |
|
|
|
loadMockData() { |
|
|
|
// 模拟考勤组数据 |
|
|
|
this.attendanceGroups = [ |
|
|
|
{ |
|
|
|
id: 1, |
|
|
|
name: '研发部考勤组', |
|
|
|
description: '研发部门日常考勤规则', |
|
|
|
workDays: ['mon', 'tue', 'wed', 'thu', 'fri'], |
|
|
|
workStartTime: '09:00', |
|
|
|
workEndTime: '18:00', |
|
|
|
allowLate: true, |
|
|
|
lateRange: 15, |
|
|
|
allowEarly: true, |
|
|
|
earlyRange: 15, |
|
|
|
areaId: 1, |
|
|
|
areaName: '总部办公楼', |
|
|
|
members: [ |
|
|
|
{ id: 1, name: '张三', employeeId: 'EMP001', department: '研发部', position: '高级工程师' }, |
|
|
|
{ id: 2, name: '李四', employeeId: 'EMP002', department: '研发部', position: '产品经理' } |
|
|
|
] |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 2, |
|
|
|
name: '销售部考勤组', |
|
|
|
description: '销售部门考勤规则', |
|
|
|
workDays: ['mon', 'tue', 'wed', 'thu', 'fri'], |
|
|
|
workStartTime: '09:30', |
|
|
|
workEndTime: '18:30', |
|
|
|
allowLate: true, |
|
|
|
lateRange: 10, |
|
|
|
allowEarly: false, |
|
|
|
earlyRange: 0, |
|
|
|
areaId: 2, |
|
|
|
areaName: '销售办公室', |
|
|
|
members: [ |
|
|
|
{ id: 3, name: '王五', employeeId: 'EMP003', department: '销售部', position: '销售代表' }, |
|
|
|
{ id: 4, name: '赵六', employeeId: 'EMP004', department: '销售部', position: '销售经理' } |
|
|
|
] |
|
|
|
} |
|
|
|
]; |
|
|
|
|
|
|
|
// 模拟员工数据 |
|
|
|
this.users = [ |
|
|
|
{ id: 1, name: '张三', employeeId: 'EMP001', department: '研发部', position: '高级工程师' }, |
|
|
@@ -357,8 +417,10 @@ export default { |
|
|
|
{ id: 9, name: '郑十一', employeeId: 'EMP009', department: '市场部', position: '市场专员' }, |
|
|
|
{ id: 10, name: '王十二', employeeId: 'EMP010', department: '市场部', position: '市场总监' } |
|
|
|
]; |
|
|
|
|
|
|
|
// 模拟打卡区域数据 |
|
|
|
}, |
|
|
|
|
|
|
|
// 加载打卡区域数据(实际项目中从API获取) |
|
|
|
loadCheckInAreas() { |
|
|
|
this.checkInAreas = [ |
|
|
|
{ id: 1, name: '总部办公楼', lng: 116.404, lat: 39.915, radius: 300, enabled: true }, |
|
|
|
{ id: 2, name: '销售办公室', lng: 116.414, lat: 39.905, radius: 200, enabled: true }, |
|
|
@@ -366,6 +428,17 @@ export default { |
|
|
|
]; |
|
|
|
}, |
|
|
|
|
|
|
|
// 选择考勤组 |
|
|
|
selectGroup(id) { |
|
|
|
this.selectedGroupId = id; |
|
|
|
const group = this.attendanceGroups.find(g => g.id === id); |
|
|
|
if (group) { |
|
|
|
// 复制考勤组数据 |
|
|
|
this.attendanceGroup = { ...group }; |
|
|
|
this.isEditMode = true; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 重置表单 |
|
|
|
resetForm() { |
|
|
|
this.attendanceGroup = { |
|
|
@@ -381,56 +454,91 @@ export default { |
|
|
|
earlyRange: 15, |
|
|
|
areaId: null, |
|
|
|
checkInType: 'location', |
|
|
|
members: [] |
|
|
|
members: [], |
|
|
|
areaName: '' |
|
|
|
}; |
|
|
|
|
|
|
|
this.isEditMode = false; |
|
|
|
this.selectedGroupId = null; |
|
|
|
}, |
|
|
|
|
|
|
|
// 打开编辑模式(示例:编辑第一个考勤组) |
|
|
|
openEditMode() { |
|
|
|
// 实际项目中,这里应该接收考勤组ID并从API获取数据 |
|
|
|
// 这里仅作示例,使用模拟数据 |
|
|
|
const exampleGroup = { |
|
|
|
id: 1, |
|
|
|
name: '研发部考勤组', |
|
|
|
description: '研发部门日常考勤规则', |
|
|
|
workDays: ['mon', 'tue', 'wed', 'thu', 'fri'], |
|
|
|
workStartTime: '09:00', |
|
|
|
workEndTime: '18:00', |
|
|
|
allowLate: true, |
|
|
|
lateRange: 15, |
|
|
|
allowEarly: true, |
|
|
|
earlyRange: 15, |
|
|
|
areaId: 1, |
|
|
|
checkInType: 'location', |
|
|
|
members: this.users.slice(0, 2) |
|
|
|
}; |
|
|
|
|
|
|
|
this.attendanceGroup = { ...exampleGroup }; |
|
|
|
this.isEditMode = true; |
|
|
|
// 打开新建考勤组对话框 |
|
|
|
openAddGroupDialog() { |
|
|
|
this.resetForm(); |
|
|
|
this.showAddGroupModal = true; |
|
|
|
}, |
|
|
|
|
|
|
|
// 创建新考勤组 |
|
|
|
createNewGroup() { |
|
|
|
this.$refs.newGroupForm.validate(valid => { |
|
|
|
if (valid) { |
|
|
|
// 实际项目中调用API创建 |
|
|
|
const newGroup = { |
|
|
|
name: this.newGroup.name, |
|
|
|
description: this.newGroup.description, |
|
|
|
workDays: ['mon', 'tue', 'wed', 'thu', 'fri'], |
|
|
|
workStartTime: '09:00', |
|
|
|
workEndTime: '18:00', |
|
|
|
allowLate: true, |
|
|
|
lateRange: 15, |
|
|
|
allowEarly: true, |
|
|
|
earlyRange: 15, |
|
|
|
areaId: null, |
|
|
|
areaName: '', |
|
|
|
members: [] |
|
|
|
}; |
|
|
|
add(newGroup).then((res) => { |
|
|
|
console.log(res) |
|
|
|
}) |
|
|
|
this.attendanceGroups.push(newGroup); |
|
|
|
this.selectGroup(newGroup.id); |
|
|
|
this.showAddGroupModal = false; |
|
|
|
this.$message.success('考勤组创建成功'); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理关闭新建考勤组对话框 |
|
|
|
handleCloseAddGroupDialog(done) { |
|
|
|
if (this.newGroup.name) { |
|
|
|
this.$confirm('确定要取消创建吗?', '提示', { |
|
|
|
type: 'warning' |
|
|
|
}).then(() => { |
|
|
|
done(); |
|
|
|
}).catch(() => { |
|
|
|
// 取消关闭 |
|
|
|
}); |
|
|
|
} else { |
|
|
|
done(); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 保存考勤组 |
|
|
|
saveGroup() { |
|
|
|
this.$refs.groupForm.validate(valid => { |
|
|
|
if (valid) { |
|
|
|
// 这里应该调用API保存数据 |
|
|
|
// 示例代码仅做演示 |
|
|
|
this.attendanceGroup.workDaysList = this.attendanceGroup.workDays |
|
|
|
// 查找打卡区域名称 |
|
|
|
const area = this.checkInAreas.find(a => a.id === this.attendanceGroup.areaId); |
|
|
|
if (area) { |
|
|
|
this.attendanceGroup.areaName = area.name; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.isEditMode) { |
|
|
|
console.log('更新考勤组:', this.attendanceGroup); |
|
|
|
this.showSuccessMessage('考勤组更新成功'); |
|
|
|
// 更新现有考勤组 |
|
|
|
const index = this.attendanceGroups.findIndex(g => g.id === this.attendanceGroup.id); |
|
|
|
if (index !== -1) { |
|
|
|
this.attendanceGroups.splice(index, 1, this.attendanceGroup); |
|
|
|
} |
|
|
|
this.$message.success('考勤组更新成功'); |
|
|
|
} else { |
|
|
|
console.log('创建考勤组:', this.attendanceGroup); |
|
|
|
this.showSuccessMessage('考勤组创建成功'); |
|
|
|
add(this.attendanceGroup).then((res) => { |
|
|
|
console.log(res) |
|
|
|
}) |
|
|
|
// 添加新考勤组 |
|
|
|
this.attendanceGroups.push(this.attendanceGroup); |
|
|
|
this.selectedGroupId = this.attendanceGroup.id; |
|
|
|
this.$message.success('考勤组创建成功'); |
|
|
|
} |
|
|
|
|
|
|
|
// 重置表单 |
|
|
|
this.resetForm(); |
|
|
|
// this.resetForm(); |
|
|
|
} else { |
|
|
|
console.log('表单验证失败'); |
|
|
|
return false; |
|
|
@@ -440,7 +548,14 @@ export default { |
|
|
|
|
|
|
|
// 取消编辑 |
|
|
|
cancelEdit() { |
|
|
|
this.resetForm(); |
|
|
|
if (this.isEditMode && this.selectedGroupId) { |
|
|
|
const group = this.attendanceGroups.find(g => g.id === this.selectedGroupId); |
|
|
|
if (group) { |
|
|
|
this.attendanceGroup = { ...group }; |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.resetForm(); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 打开员工选择对话框 |
|
|
@@ -457,7 +572,6 @@ export default { |
|
|
|
// 搜索用户 |
|
|
|
searchUsers() { |
|
|
|
// 搜索逻辑已在computed属性中实现 |
|
|
|
// 这里只需触发重新计算 |
|
|
|
}, |
|
|
|
|
|
|
|
// 确认选择员工 |
|
|
@@ -549,10 +663,6 @@ export default { |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
.equal-width .left, .equal-width .right { |
|
|
|
flex: 1; |
|
|
|
} |
|
|
|
|
|
|
|
.attendance-group-container { |
|
|
|
padding: 20px; |
|
|
|
} |
|
|
@@ -572,12 +682,11 @@ export default { |
|
|
|
} |
|
|
|
|
|
|
|
.main-content { |
|
|
|
display: flex; |
|
|
|
max-width: 800px; |
|
|
|
max-width: 1200px; |
|
|
|
margin: 0 auto; |
|
|
|
} |
|
|
|
|
|
|
|
.group-form-card { |
|
|
|
.group-list-card, .group-form-card { |
|
|
|
border-radius: 8px; |
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
@@ -627,4 +736,75 @@ export default { |
|
|
|
.el-message { |
|
|
|
top: 80px; |
|
|
|
} |
|
|
|
</style> |
|
|
|
|
|
|
|
/* 考勤组列表样式 */ |
|
|
|
.group-list { |
|
|
|
max-height: 400px; |
|
|
|
overflow-y: auto; |
|
|
|
padding: 10px 0; |
|
|
|
} |
|
|
|
|
|
|
|
.group-item { |
|
|
|
padding: 15px; |
|
|
|
margin-bottom: 10px; |
|
|
|
border-radius: 4px; |
|
|
|
cursor: pointer; |
|
|
|
background-color: #fff; |
|
|
|
border: 1px solid #ebeef5; |
|
|
|
transition: all 0.3s; |
|
|
|
} |
|
|
|
|
|
|
|
.group-item:hover { |
|
|
|
border-color: #409eff; |
|
|
|
background-color: #f5f7fa; |
|
|
|
} |
|
|
|
|
|
|
|
.group-item.active { |
|
|
|
border-color: #409eff; |
|
|
|
background-color: #ecf5ff; |
|
|
|
} |
|
|
|
|
|
|
|
.group-name { |
|
|
|
font-weight: 500; |
|
|
|
margin-bottom: 5px; |
|
|
|
color: #303133; |
|
|
|
} |
|
|
|
|
|
|
|
.group-desc { |
|
|
|
font-size: 13px; |
|
|
|
color: #606266; |
|
|
|
margin-bottom: 5px; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
|
|
|
|
.group-meta { |
|
|
|
font-size: 12px; |
|
|
|
color: #909399; |
|
|
|
} |
|
|
|
|
|
|
|
.empty-state { |
|
|
|
text-align: center; |
|
|
|
padding: 30px 0; |
|
|
|
color: #909399; |
|
|
|
} |
|
|
|
|
|
|
|
.empty-state i { |
|
|
|
font-size: 36px; |
|
|
|
margin-bottom: 10px; |
|
|
|
display: block; |
|
|
|
color: #dcdfe6; |
|
|
|
} |
|
|
|
|
|
|
|
/* 响应式布局 */ |
|
|
|
@media (max-width: 768px) { |
|
|
|
.main-content { |
|
|
|
flex-direction: column; |
|
|
|
} |
|
|
|
|
|
|
|
.group-list-card, .group-form-card { |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |