1. 新增AttendanceTemplateManager组件,实现考勤模板的本地管理 2. 在排班页面添加模板管理入口和相关操作按钮 3. 支持将班次配置保存为模板、从模板快速导入人员 4. 优化班次选择器的交互体验,添加清空功能 5. 新增单个班次的人员导入导出功能
318 lines
7.2 KiB
Vue
318 lines
7.2 KiB
Vue
<template>
|
|
<div class="template-manager">
|
|
<div class="left-panel">
|
|
<div class="panel-header">
|
|
<span class="panel-title">模板列表</span>
|
|
<el-button icon="el-icon-plus" size="mini" type="primary" @click="addTemplate">新增</el-button>
|
|
</div>
|
|
<div class="template-list">
|
|
<div
|
|
v-for="template in templateList"
|
|
:key="template.id"
|
|
:class="['template-item', { active: currentTemplate.id === template.id }]"
|
|
@click="selectTemplate(template)"
|
|
>
|
|
<div class="template-name">{{ template.name }}</div>
|
|
<div class="template-meta">
|
|
<span>{{ template.employeeCount }}人</span>
|
|
<span class="create-time">{{ template.createTime }}</span>
|
|
</div>
|
|
<div class="template-actions">
|
|
<el-button size="mini" type="text" @click.stop="deleteTemplate(template)">删除</el-button>
|
|
</div>
|
|
</div>
|
|
<div v-if="templateList.length === 0" class="empty-list">
|
|
<el-empty description="暂无模板" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="right-panel">
|
|
<div class="panel-header">
|
|
<span class="panel-title">员工配置</span>
|
|
</div>
|
|
<div class="template-form">
|
|
<el-form :model="currentTemplate" label-width="70px" inline>
|
|
<el-form-item label="模板名称">
|
|
<el-input
|
|
v-model="currentTemplate.name"
|
|
placeholder="请输入模板名称"
|
|
style="width: 200px;"
|
|
/>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
<div class="transfer-container">
|
|
<el-transfer
|
|
v-model="selectedEmployeeIds"
|
|
:data="employeeList"
|
|
:titles="['可选员工', '已选员工']"
|
|
:button-texts="['移除', '添加']"
|
|
filterable
|
|
filter-placeholder="搜索员工"
|
|
:width="['160px', '160px']"
|
|
:height="280"
|
|
/>
|
|
</div>
|
|
<div class="right-panel-footer">
|
|
<el-button type="primary" @click="saveTemplate" :disabled="!currentTemplate.name">保存</el-button>
|
|
<el-button @click="resetForm">重置</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'AttendanceTemplateManager',
|
|
props: {
|
|
employeeList: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
templateList: [],
|
|
currentTemplate: {
|
|
id: '',
|
|
name: '',
|
|
employeeIds: [],
|
|
employeeCount: 0,
|
|
createTime: ''
|
|
},
|
|
selectedEmployeeIds: [],
|
|
}
|
|
},
|
|
mounted() {
|
|
this.loadTemplates()
|
|
},
|
|
methods: {
|
|
loadTemplates() {
|
|
try {
|
|
const templates = localStorage.getItem('attendanceTemplates')
|
|
this.templateList = templates ? JSON.parse(templates) : []
|
|
} catch (e) {
|
|
this.templateList = []
|
|
}
|
|
},
|
|
|
|
saveTemplates() {
|
|
localStorage.setItem('attendanceTemplates', JSON.stringify(this.templateList))
|
|
this.$emit('update')
|
|
},
|
|
|
|
addTemplate() {
|
|
this.currentTemplate = {
|
|
id: '',
|
|
name: '',
|
|
employeeIds: [],
|
|
employeeCount: 0,
|
|
createTime: ''
|
|
}
|
|
this.selectedEmployeeIds = []
|
|
},
|
|
|
|
editTemplate(template) {
|
|
this.currentTemplate = JSON.parse(JSON.stringify(template))
|
|
this.selectedEmployeeIds = [...template.employeeIds]
|
|
},
|
|
|
|
deleteTemplate(template) {
|
|
this.$confirm('确定删除?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
const index = this.templateList.findIndex(t => t.id === template.id)
|
|
if (index > -1) {
|
|
this.templateList.splice(index, 1)
|
|
this.saveTemplates()
|
|
this.$message.success('删除成功')
|
|
if (this.currentTemplate.id === template.id) {
|
|
this.resetForm()
|
|
}
|
|
}
|
|
}).catch(() => {
|
|
this.$message.info('已取消')
|
|
})
|
|
},
|
|
|
|
selectTemplate(template) {
|
|
this.currentTemplate = JSON.parse(JSON.stringify(template))
|
|
this.selectedEmployeeIds = [...template.employeeIds]
|
|
},
|
|
|
|
saveTemplate() {
|
|
if (!this.currentTemplate.name.trim()) {
|
|
this.$message.warning('请输入模板名称')
|
|
return
|
|
}
|
|
|
|
const templateData = {
|
|
id: this.currentTemplate.id || Date.now().toString(),
|
|
name: this.currentTemplate.name.trim(),
|
|
employeeIds: [...this.selectedEmployeeIds],
|
|
employeeCount: this.selectedEmployeeIds.length,
|
|
createTime: this.currentTemplate.id ? this.currentTemplate.createTime : new Date().toLocaleString('zh-CN')
|
|
}
|
|
|
|
if (this.currentTemplate.id) {
|
|
const index = this.templateList.findIndex(t => t.id === this.currentTemplate.id)
|
|
if (index > -1) {
|
|
this.templateList[index] = templateData
|
|
}
|
|
this.$message.success('修改成功')
|
|
} else {
|
|
this.templateList.push(templateData)
|
|
this.$message.success('新增成功')
|
|
}
|
|
|
|
this.saveTemplates()
|
|
},
|
|
|
|
resetForm() {
|
|
this.currentTemplate = {
|
|
id: '',
|
|
name: '',
|
|
employeeIds: [],
|
|
employeeCount: 0,
|
|
createTime: ''
|
|
}
|
|
this.selectedEmployeeIds = []
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.template-manager {
|
|
display: flex;
|
|
height: 520px;
|
|
gap: 24px;
|
|
}
|
|
|
|
.left-panel {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
border-right: 1px solid #e4e7ed;
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.right-panel {
|
|
width: 600px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.panel-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 14px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #e4e7ed;
|
|
}
|
|
|
|
.panel-title {
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
color: #303133;
|
|
}
|
|
|
|
.template-list {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding-right: 4px;
|
|
}
|
|
|
|
.template-list::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.template-list::-webkit-scrollbar-thumb {
|
|
background-color: #dcdfe6;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.template-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px 12px;
|
|
margin-bottom: 6px;
|
|
background-color: #fafafa;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.template-item:hover {
|
|
background-color: #f0f0f0;
|
|
}
|
|
|
|
.template-item.active {
|
|
background-color: #e6f7ff;
|
|
border-left: 3px solid #1890ff;
|
|
}
|
|
|
|
.template-name {
|
|
flex: 1;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.template-meta {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
gap: 2px;
|
|
margin-right: 12px;
|
|
font-size: 11px;
|
|
color: #909399;
|
|
}
|
|
|
|
.create-time {
|
|
font-size: 10px;
|
|
}
|
|
|
|
.template-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.template-actions .el-button {
|
|
padding: 0 4px;
|
|
font-size: 11px;
|
|
color: #606266;
|
|
}
|
|
|
|
.template-actions .el-button:hover {
|
|
color: #409eff;
|
|
}
|
|
|
|
.empty-list {
|
|
padding: 40px 0;
|
|
}
|
|
|
|
.template-form {
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.transfer-container {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.right-panel-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 10px;
|
|
margin-top: 14px;
|
|
padding-top: 10px;
|
|
border-top: 1px solid #e4e7ed;
|
|
}
|
|
</style> |