迁移班次管理功能
This commit is contained in:
19
docs/功能测试-班次管理.md
Normal file
19
docs/功能测试-班次管理.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 功能测试任务列表 - 班次管理
|
||||||
|
|
||||||
|
> 路由:`/system_settings/organization/production_shift_management`
|
||||||
|
|
||||||
|
| 序号 | 测试项 | 操作步骤 | 预期结果 | 结果 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 1 | 页面入口 | 从菜单进入“班次管理” | 页面正常打开,展示班次计划名称、编码、起止时间、状态、创建/更新时间 | ☐ |
|
||||||
|
| 2 | 条件查询 | 输入计划名称、编码或创建时间后查询 | 列表按条件刷新 | ☐ |
|
||||||
|
| 3 | 新增校验 | 点击新增,不填名称、编码或时间范围直接确认 | 显示必填校验 | ☐ |
|
||||||
|
| 4 | 新增班次计划 | 填写计划信息、选择班组、添加班次明细后确认 | 保存成功,列表出现新计划 | ☐ |
|
||||||
|
| 5 | 班组绑定唯一性 | 两条班次明细绑定同一个班组 | 第二次绑定被阻止并提示 | ☐ |
|
||||||
|
| 6 | 明细必填校验 | 添加班次明细但缺少名称、开始时间或结束时间 | 提示对应行缺失字段 | ☐ |
|
||||||
|
| 7 | 编辑班次计划 | 点击编辑,修改状态、休息日、明细后确认 | 保存成功,重新打开可回显新数据 | ☐ |
|
||||||
|
| 8 | 单条删除 | 点击删除并确认 | 计划删除成功 | ☐ |
|
||||||
|
| 9 | 批量删除 | 勾选多条计划后批量删除 | 所选计划删除成功 | ☐ |
|
||||||
|
| 10 | 导入模板 | 点击导入后下载模板 | 浏览器下载班次计划导入模板 | ☐ |
|
||||||
|
| 11 | 导入数据 | 选择合法 xls/xlsx 文件并确认导入 | 预览展示后提交成功,列表刷新 | ☐ |
|
||||||
|
| 12 | 导出任务 | 点击导出并确认 | 提示下载任务创建成功 | ☐ |
|
||||||
|
| 13 | 权限按钮 | 使用缺少权限账号进入页面 | 对应新增/编辑/删除/导入/导出按钮隐藏或不可操作 | ☐ |
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
|
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
|
||||||
|
|
||||||
- 总功能数:79
|
- 总功能数:79
|
||||||
- 已迁移:76
|
- 已迁移:77
|
||||||
- 未迁移:3
|
- 未迁移:2
|
||||||
|
|
||||||
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|
||||||
|:---:|---|---|---|---|---|
|
|:---:|---|---|---|---|---|
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
| ✅ | 生产配置 (Production Master Data) | 物料模型 (Material Model) | 计量单位 (Unit of Measure) | 计量单位配置与管理 | `src/views/production-master-data/material-model/material-unit/` |
|
| ✅ | 生产配置 (Production Master Data) | 物料模型 (Material Model) | 计量单位 (Unit of Measure) | 计量单位配置与管理 | `src/views/production-master-data/material-model/material-unit/` |
|
||||||
| ✅ | 生产配置 (Production Master Data) | SPC采集模型 (SPC Configuration) | SPC采集配置 (Data Collection Configuration) | 配置SPC采集参数 | `src/views/production-master-data/spc-configuration/data-collection-configuration/` |
|
| ✅ | 生产配置 (Production Master Data) | SPC采集模型 (SPC Configuration) | SPC采集配置 (Data Collection Configuration) | 配置SPC采集参数 | `src/views/production-master-data/spc-configuration/data-collection-configuration/` |
|
||||||
| ✅ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 班组管理 (Team Management) | 管理生产班组 | `src/views/production-master-data/team-model/team-management/` |
|
| ✅ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 班组管理 (Team Management) | 管理生产班组 | `src/views/production-master-data/team-model/team-management/` |
|
||||||
| ⬜ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 班次管理 (Shift Management) | 管理生产班次 | 待确认 |
|
| ✅ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 班次管理 (Shift Management) | 管理生产班次 | `src/views/production-master-data/team-model/shift-management/` |
|
||||||
| ⬜ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 排班日历 (Scheduling Calendar) | 查看排班日历 | 待确认 |
|
| ⬜ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 排班日历 (Scheduling Calendar) | 查看排班日历 | 待确认 |
|
||||||
| ✅ | 设备模型 (Equipment Management) | 设备类别 (Equipment Category) | 设备类别 (Equipment Category) | 管理设备类别 | `src/views/equipment-management/equipment-model/equipment-category/` |
|
| ✅ | 设备模型 (Equipment Management) | 设备类别 (Equipment Category) | 设备类别 (Equipment Category) | 管理设备类别 | `src/views/equipment-management/equipment-model/equipment-category/` |
|
||||||
| ✅ | 设备模型 (Equipment Management) | 设备信息 (Equipment Management) | 设备信息 (Equipment Registry) | 管理设备信息 | `src/views/equipment-management/equipment-model/equipment-registry/` |
|
| ✅ | 设备模型 (Equipment Management) | 设备信息 (Equipment Management) | 设备信息 (Equipment Registry) | 管理设备信息 | `src/views/equipment-management/equipment-model/equipment-registry/` |
|
||||||
|
|||||||
17
src/api/production-master-data/shift-management.js
Normal file
17
src/api/production-master-data/shift-management.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'system_settings/organization/production_shift_management/'
|
||||||
|
|
||||||
|
function params (method, data = {}) {
|
||||||
|
return { method: `system_settings_organization_production_shift_management_${method}`, platform: 'background', ...data }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShiftAll (data) { return request({ url: BASE + 'all', method: 'get', params: params('all', data) }) }
|
||||||
|
export function getShiftList (data) { return request({ url: BASE + 'list', method: 'get', params: params('list', data) }) }
|
||||||
|
export function createShift (data) { return request({ url: BASE + 'create', method: 'post', data: params('create', data) }) }
|
||||||
|
export function editShift (data) { return request({ url: BASE + 'edit', method: 'put', data: params('edit', data) }) }
|
||||||
|
export function deleteShift (data) { return request({ url: BASE + 'delete', method: 'delete', data: params('delete', data) }) }
|
||||||
|
export function getShiftImportTemplate (data) { return request({ url: BASE + 'get_import_template', method: 'post', responseType: 'blob', data: params('get_import_template', data) }) }
|
||||||
|
export function importShiftData (data) { return request({ url: BASE + 'data_import', method: 'post', data: params('data_import', data) }) }
|
||||||
|
export function exportShiftTask (data) { return request({ url: BASE + 'data_export_task', method: 'post', data: params('data_export_task', data) }) }
|
||||||
|
export function getShiftCalendarByDateRange (data) { return request({ url: BASE + 'get_shift_by_date_range', method: 'get', params: params('get_shift_by_date_range', data) }) }
|
||||||
@@ -549,6 +549,87 @@
|
|||||||
"please_import_department_data": "Please import team data first",
|
"please_import_department_data": "Please import team data first",
|
||||||
"team_data_import_template": "Team Import Template",
|
"team_data_import_template": "Team Import Template",
|
||||||
"upload_format_error": "Please upload xls or xlsx file"
|
"upload_format_error": "Please upload xls or xlsx file"
|
||||||
|
},
|
||||||
|
"shift_management": {
|
||||||
|
"search": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"add": "Add",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete",
|
||||||
|
"batch_delete": "Batch Delete",
|
||||||
|
"import": "Import",
|
||||||
|
"export": "Export",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"prompt": "Notice",
|
||||||
|
"operation": "Operation",
|
||||||
|
"shift_plan_name": "Shift Plan Name",
|
||||||
|
"shift_plan_code": "Shift Plan Code",
|
||||||
|
"enter_shift_plan_name": "Please enter shift plan name",
|
||||||
|
"enter_shift_plan_code": "Please enter shift plan code",
|
||||||
|
"last_create_time": "Create Time",
|
||||||
|
"serial_number": "No.",
|
||||||
|
"start_time": "Start Time",
|
||||||
|
"end_time": "End Time",
|
||||||
|
"start_date": "Start Date",
|
||||||
|
"end_date": "End Date",
|
||||||
|
"status": "Status",
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"creator": "Creator",
|
||||||
|
"create_time": "Created At",
|
||||||
|
"update_time": "Updated At",
|
||||||
|
"add_shift_plan": "Add Shift Plan",
|
||||||
|
"edit_shift_plan": "Edit Shift Plan",
|
||||||
|
"shift_name": "Shift Name",
|
||||||
|
"shift_code": "Shift Code",
|
||||||
|
"enter_shift_name": "Please enter shift name",
|
||||||
|
"enter_shift_code": "Please enter shift code",
|
||||||
|
"shift_time_range": "Shift Time Range",
|
||||||
|
"please_select_shift_time_range": "Please select shift time range",
|
||||||
|
"rotation_mode": "Rotation Mode",
|
||||||
|
"enter_content": "Please enter content",
|
||||||
|
"day": "Day",
|
||||||
|
"week": "Week",
|
||||||
|
"month": "Month",
|
||||||
|
"rest_day_setting": "Rest Days",
|
||||||
|
"monday": "Mon",
|
||||||
|
"tuesday": "Tue",
|
||||||
|
"wednesday": "Wed",
|
||||||
|
"thursday": "Thu",
|
||||||
|
"friday": "Fri",
|
||||||
|
"saturday": "Sat",
|
||||||
|
"sunday": "Sun",
|
||||||
|
"production_team": "Production Team",
|
||||||
|
"please_select": "Please select",
|
||||||
|
"remark": "Remark",
|
||||||
|
"enter_remark": "Please enter remark",
|
||||||
|
"add_shift": "Add Shift",
|
||||||
|
"shift_start_time": "Shift Start Time",
|
||||||
|
"shift_end_time": "Shift End Time",
|
||||||
|
"select_shift_start_time": "Please select shift start time",
|
||||||
|
"select_shift_end_time": "Please select shift end time",
|
||||||
|
"production_team_binding": "Team Binding",
|
||||||
|
"production_team_can_only_bind_one_shift": "One team can only bind one shift",
|
||||||
|
"please_enter_shift_plan_name": "Please enter shift plan name",
|
||||||
|
"please_enter_shift_plan_code": "Please enter shift plan code",
|
||||||
|
"please_enter_shift_name_row": "Please enter shift name, row: ",
|
||||||
|
"please_select_shift_start_time_row": "Please select shift start time, row: ",
|
||||||
|
"please_select_shift_end_time_row": "Please select shift end time, row: ",
|
||||||
|
"operation_successful": "Operation successful",
|
||||||
|
"delete_department_confirm_message": "Are you sure to delete this shift plan?",
|
||||||
|
"batch_delete_confirm_message": "Are you sure to delete selected shift plans?",
|
||||||
|
"please_select_table_data": "Please select table data first",
|
||||||
|
"export_confirm_message": "Export current query result?",
|
||||||
|
"download_task_created": "Download task created",
|
||||||
|
"shift_plan_data_import": "Import Shift Plan Data",
|
||||||
|
"upload_file_alert_title": "Import using the template format",
|
||||||
|
"upload_file_alert_description": "Download the template before importing",
|
||||||
|
"select_file": "Select File",
|
||||||
|
"download_template": "Download Template",
|
||||||
|
"please_import_department_data": "Please import shift plan data first",
|
||||||
|
"shift_plan_data_import_template": "Shift Plan Import Template",
|
||||||
|
"upload_format_error": "Please upload xls or xlsx file"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spc_configuration": {
|
"spc_configuration": {
|
||||||
|
|||||||
@@ -549,6 +549,87 @@
|
|||||||
"please_import_department_data": "请先导入班组数据",
|
"please_import_department_data": "请先导入班组数据",
|
||||||
"team_data_import_template": "班组数据导入模板",
|
"team_data_import_template": "班组数据导入模板",
|
||||||
"upload_format_error": "请上传 xls 或 xlsx 文件"
|
"upload_format_error": "请上传 xls 或 xlsx 文件"
|
||||||
|
},
|
||||||
|
"shift_management": {
|
||||||
|
"search": "查询",
|
||||||
|
"reset": "重置",
|
||||||
|
"add": "新增",
|
||||||
|
"edit": "编辑",
|
||||||
|
"delete": "删除",
|
||||||
|
"batch_delete": "批量删除",
|
||||||
|
"import": "导入",
|
||||||
|
"export": "导出",
|
||||||
|
"confirm": "确定",
|
||||||
|
"cancel": "取消",
|
||||||
|
"prompt": "提示",
|
||||||
|
"operation": "操作",
|
||||||
|
"shift_plan_name": "班次计划名称",
|
||||||
|
"shift_plan_code": "班次计划编码",
|
||||||
|
"enter_shift_plan_name": "请输入班次计划名称",
|
||||||
|
"enter_shift_plan_code": "请输入班次计划编码",
|
||||||
|
"last_create_time": "创建时间",
|
||||||
|
"serial_number": "序号",
|
||||||
|
"start_time": "开始时间",
|
||||||
|
"end_time": "结束时间",
|
||||||
|
"start_date": "开始日期",
|
||||||
|
"end_date": "结束日期",
|
||||||
|
"status": "状态",
|
||||||
|
"enabled": "启用",
|
||||||
|
"disabled": "禁用",
|
||||||
|
"creator": "创建人",
|
||||||
|
"create_time": "创建时间",
|
||||||
|
"update_time": "更新时间",
|
||||||
|
"add_shift_plan": "新增班次计划",
|
||||||
|
"edit_shift_plan": "编辑班次计划",
|
||||||
|
"shift_name": "班次名称",
|
||||||
|
"shift_code": "班次编码",
|
||||||
|
"enter_shift_name": "请输入班次名称",
|
||||||
|
"enter_shift_code": "请输入班次编码",
|
||||||
|
"shift_time_range": "班次计划时间范围",
|
||||||
|
"please_select_shift_time_range": "请选择班次时间范围",
|
||||||
|
"rotation_mode": "轮转模式",
|
||||||
|
"enter_content": "请输入内容",
|
||||||
|
"day": "天",
|
||||||
|
"week": "周",
|
||||||
|
"month": "月",
|
||||||
|
"rest_day_setting": "休息日设置",
|
||||||
|
"monday": "周一",
|
||||||
|
"tuesday": "周二",
|
||||||
|
"wednesday": "周三",
|
||||||
|
"thursday": "周四",
|
||||||
|
"friday": "周五",
|
||||||
|
"saturday": "周六",
|
||||||
|
"sunday": "周日",
|
||||||
|
"production_team": "生产班组",
|
||||||
|
"please_select": "请选择",
|
||||||
|
"remark": "备注",
|
||||||
|
"enter_remark": "请输入备注",
|
||||||
|
"add_shift": "添加班次",
|
||||||
|
"shift_start_time": "班次开始时间",
|
||||||
|
"shift_end_time": "班次结束时间",
|
||||||
|
"select_shift_start_time": "请选择班次开始时间",
|
||||||
|
"select_shift_end_time": "请选择班次结束时间",
|
||||||
|
"production_team_binding": "生产班组绑定",
|
||||||
|
"production_team_can_only_bind_one_shift": "一个生产班组只能绑定一个班次",
|
||||||
|
"please_enter_shift_plan_name": "请输入班次计划名称",
|
||||||
|
"please_enter_shift_plan_code": "请输入班次计划编码",
|
||||||
|
"please_enter_shift_name_row": "请输入班次名称,行号:",
|
||||||
|
"please_select_shift_start_time_row": "请选择班次开始时间,行号:",
|
||||||
|
"please_select_shift_end_time_row": "请选择班次结束时间,行号:",
|
||||||
|
"operation_successful": "操作成功",
|
||||||
|
"delete_department_confirm_message": "确定要删除该班次计划吗?",
|
||||||
|
"batch_delete_confirm_message": "确定要删除所选班次计划吗?",
|
||||||
|
"please_select_table_data": "请先选择表格数据",
|
||||||
|
"export_confirm_message": "确定要导出当前查询结果吗?",
|
||||||
|
"download_task_created": "下载任务创建成功",
|
||||||
|
"shift_plan_data_import": "班次计划数据导入",
|
||||||
|
"upload_file_alert_title": "请按模板格式导入文件",
|
||||||
|
"upload_file_alert_description": "导入前请先下载模板并按模板字段填写",
|
||||||
|
"select_file": "选择文件",
|
||||||
|
"download_template": "下载模板",
|
||||||
|
"please_import_department_data": "请先导入班次计划数据",
|
||||||
|
"shift_plan_data_import_template": "班次计划数据导入模板",
|
||||||
|
"upload_format_error": "请上传 xls 或 xlsx 文件"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spc_configuration": {
|
"spc_configuration": {
|
||||||
|
|||||||
@@ -62,5 +62,11 @@ export default {
|
|||||||
meta: { ...meta, cache: true, title: '班组管理' },
|
meta: { ...meta, cache: true, title: '班组管理' },
|
||||||
component: _import('production-master-data/team-model/team-management')
|
component: _import('production-master-data/team-model/team-management')
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
path: 'organization/production_shift_management',
|
||||||
|
name: `${pre}organization-production_shift_management`,
|
||||||
|
meta: { ...meta, cache: true, title: '班次管理' },
|
||||||
|
component: _import('production-master-data/team-model/shift-management')
|
||||||
|
}
|
||||||
])('system_settings-')
|
])('system_settings-')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container>
|
||||||
|
<template #header>
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-form :inline="true" size="mini">
|
||||||
|
<el-form-item :label="$t(key('shift_plan_name'))"><el-input v-model.trim="search.name" :placeholder="$t(key('enter_shift_plan_name'))" clearable style="width:200px" @keyup.enter.native="onSearch" /></el-form-item>
|
||||||
|
<el-form-item :label="$t(key('shift_plan_code'))"><el-input v-model.trim="search.code" :placeholder="$t(key('enter_shift_plan_code'))" clearable style="width:200px" @keyup.enter.native="onSearch" /></el-form-item>
|
||||||
|
<el-form-item :label="$t(key('last_create_time'))"><el-date-picker v-model="search.create_time" type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss" :start-placeholder="$t(key('start_date'))" :end-placeholder="$t(key('end_date'))" style="width:330px" /></el-form-item>
|
||||||
|
<el-form-item><el-button type="primary" icon="el-icon-search" @click="onSearch">{{ $t(key('search')) }}</el-button><el-button icon="el-icon-refresh" @click="onReset">{{ $t(key('reset')) }}</el-button></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<page-table :columns="columns" :data="tableData" :loading="loading" :toolbar-buttons="toolbarButtons" :row-buttons="rowButtons" :pagination="pagination" auto-height @page-change="onPageChange" @selection-change="selectedRows = $event">
|
||||||
|
<template #col-status="{ row }"><span v-if="Number(row.status) === 1" class="status-on">{{ $t(key('enabled')) }}</span><span v-else class="status-off">{{ $t(key('disabled')) }}</span></template>
|
||||||
|
</page-table>
|
||||||
|
|
||||||
|
<el-dialog :title="$t(dialogTitle)" :visible.sync="dialogVisible" width="80%" :close-on-click-modal="false" @close="closeDialog">
|
||||||
|
<el-form ref="form" :model="formData" :rules="translatedRules" label-width="150px" size="small">
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('shift_name'))" prop="name"><el-input v-model="formData.name" :placeholder="$t(key('enter_shift_name'))" /></el-form-item></el-col>
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('shift_code'))" prop="code"><el-input v-model="formData.code" :placeholder="$t(key('enter_shift_code'))" /></el-form-item></el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('shift_time_range'))" prop="time_range"><el-date-picker v-model="formData.time_range" type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss" :start-placeholder="$t(key('start_date'))" :end-placeholder="$t(key('end_date'))" style="width:100%" /></el-form-item></el-col>
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('status'))"><el-switch v-model="formData.status" :active-text="$t(key('enabled'))" :inactive-text="$t(key('disabled'))" :active-value="1" :inactive-value="0" /></el-form-item></el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('rotation_mode'))"><el-input v-model="formData.cycle.length" class="input-with-select" :placeholder="$t(key('enter_content'))"><el-select slot="append" v-model="formData.cycle.unit" style="width:90px"><el-option :label="$t(key('day'))" value="day" /><el-option :label="$t(key('week'))" value="week" /><el-option :label="$t(key('month'))" value="month" /></el-select></el-input></el-form-item></el-col>
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('rest_day_setting'))"><el-checkbox-group v-model="formData.weekly_rest_days"><el-checkbox v-for="day in weekOptions" :key="day.value" :label="day.value">{{ $t(day.label) }}</el-checkbox></el-checkbox-group></el-form-item></el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('production_team'))"><el-select v-model="formData.productionTeamIds" multiple collapse-tags clearable filterable style="width:100%" :placeholder="$t(key('please_select'))"><el-option v-for="item in teamOptions" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item></el-col>
|
||||||
|
<el-col :span="12"><el-form-item :label="$t(key('remark'))"><el-input v-model="formData.remark" type="textarea" :rows="2" :placeholder="$t(key('enter_remark'))" /></el-form-item></el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<el-divider />
|
||||||
|
<el-button size="mini" type="success" icon="el-icon-plus" @click="addShiftDetail">{{ $t(key('add_shift')) }}</el-button>
|
||||||
|
<el-table :data="shiftsData" border style="width:100%;margin-top:10px">
|
||||||
|
<el-table-column :label="$t(key('shift_name'))"><template slot-scope="scope"><el-input v-model="shiftsData[scope.$index].name" :placeholder="$t(key('enter_shift_name'))" /></template></el-table-column>
|
||||||
|
<el-table-column :label="$t(key('shift_start_time'))" width="190"><template slot-scope="scope"><el-time-picker v-model="shiftsData[scope.$index].start_time" format="HH:mm" value-format="HH:mm" :placeholder="$t(key('select_shift_start_time'))" /></template></el-table-column>
|
||||||
|
<el-table-column :label="$t(key('shift_end_time'))" width="190"><template slot-scope="scope"><el-time-picker v-model="shiftsData[scope.$index].finish_time" format="HH:mm" value-format="HH:mm" :placeholder="$t(key('select_shift_end_time'))" /></template></el-table-column>
|
||||||
|
<el-table-column :label="$t(key('production_team_binding'))"><template slot-scope="scope"><el-select v-model="shiftsData[scope.$index].production_team_id" clearable filterable :placeholder="$t(key('please_select'))" @change="val => changeProductionTeam(val, scope.$index)"><el-option v-for="item in selectableTeams" :key="item.id" :label="item.name" :value="item.id" /></el-select></template></el-table-column>
|
||||||
|
<el-table-column :label="$t(key('operation'))" width="120"><template slot-scope="scope"><el-button type="danger" size="mini" icon="el-icon-delete" @click="shiftsData.splice(scope.$index, 1)">{{ $t(key('delete')) }}</el-button></template></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<span slot="footer"><el-button @click="closeDialog">{{ $t(key('cancel')) }}</el-button><el-button type="primary" :loading="submitting" @click="submitDialog">{{ $t(key('confirm')) }}</el-button></span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :title="$t(key('shift_plan_data_import'))" :visible.sync="importVisible" width="80%" :close-on-click-modal="false">
|
||||||
|
<el-alert :title="$t(key('upload_file_alert_title'))" :description="$t(key('upload_file_alert_description'))" :closable="false" type="warning" />
|
||||||
|
<el-upload action="" :multiple="false" :auto-upload="false" :show-file-list="true" :file-list="importFileList" accept=".xls,.xlsx" :on-change="onImportFileChange"><el-button slot="trigger" size="mini" type="success">{{ $t(key('select_file')) }}</el-button><el-button style="margin-left:10px" size="mini" type="primary" :loading="importLoading" @click.stop="downloadTemplate">{{ $t(key('download_template')) }}</el-button></el-upload>
|
||||||
|
<el-table :data="importRows" height="330" border style="margin-top:12px" v-loading="importTableLoading"><el-table-column prop="name" :label="$t(key('shift_plan_name'))" /><el-table-column prop="code" :label="$t(key('shift_plan_code'))" /><el-table-column prop="start_time" :label="$t(key('start_time'))" /><el-table-column prop="finish_time" :label="$t(key('end_time'))" /><el-table-column prop="status" :label="$t(key('status'))" /><el-table-column prop="shift_name" :label="$t(key('shift_name'))" /></el-table>
|
||||||
|
<span slot="footer"><el-button @click="importVisible = false">{{ $t(key('cancel')) }}</el-button><el-button type="primary" @click="submitImport">{{ $t(key('confirm')) }}</el-button></span>
|
||||||
|
</el-dialog>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useTableColumns } from '@/composables/useTableColumns'
|
||||||
|
import { useTableButtons } from '@/composables/useTableButtons'
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import { confirmMixin } from '@/composables/useConfirmHandle'
|
||||||
|
import PageTable from '@/components/page-table'
|
||||||
|
import { downloadRename, readExcel } from '@/utils/file'
|
||||||
|
import { getTeamAll } from '@/api/production-master-data/team-management'
|
||||||
|
import { getShiftList, createShift, editShift, deleteShift, getShiftImportTemplate, importShiftData, exportShiftTask } from '@/api/production-master-data/shift-management'
|
||||||
|
|
||||||
|
function readPageData (res) {
|
||||||
|
const data = res && res.data ? res.data : res
|
||||||
|
if (!data) return { list: [], total: 0 }
|
||||||
|
if (Array.isArray(data)) return { list: data, total: data.length }
|
||||||
|
return { list: data.data || data.list || [], total: Number(data.count || data.total || 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeJson (value, fallback) {
|
||||||
|
if (!value) return fallback
|
||||||
|
if (typeof value !== 'string') return value
|
||||||
|
try { return JSON.parse(value) } catch { return fallback }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'production-master-data-shift-management',
|
||||||
|
components: { PageTable },
|
||||||
|
mixins: [i18nMixin('page.production_master_data.team_model.shift_management'), confirmMixin],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
tableData: [],
|
||||||
|
selectedRows: [],
|
||||||
|
columns: [],
|
||||||
|
toolbarButtons: [],
|
||||||
|
rowButtons: [],
|
||||||
|
search: { name: '', code: '', create_time: '' },
|
||||||
|
pagination: { current: 1, size: 10, total: 0 },
|
||||||
|
teamOptions: [],
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogTitle: '',
|
||||||
|
handleType: 'create',
|
||||||
|
editId: '',
|
||||||
|
formData: this.defaultFormData(),
|
||||||
|
shiftsData: [],
|
||||||
|
importVisible: false,
|
||||||
|
importFileList: [],
|
||||||
|
importRows: [],
|
||||||
|
importLoading: false,
|
||||||
|
importTableLoading: false,
|
||||||
|
weekOptions: [{ value: 1, label: this.key('monday') }, { value: 2, label: this.key('tuesday') }, { value: 3, label: this.key('wednesday') }, { value: 4, label: this.key('thursday') }, { value: 5, label: this.key('friday') }, { value: 6, label: this.key('saturday') }, { value: 7, label: this.key('sunday') }],
|
||||||
|
rules: { name: [{ required: true, message: this.key('please_enter_shift_plan_name'), trigger: 'blur' }], code: [{ required: true, message: this.key('please_enter_shift_plan_code'), trigger: 'blur' }], time_range: [{ required: true, message: this.key('please_select_shift_time_range'), trigger: 'change' }] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
translatedRules () { const next = {}; Object.keys(this.rules).forEach(key => { next[key] = this.rules[key].map(rule => ({ ...rule, message: this.$t(rule.message) })) }); return next },
|
||||||
|
selectableTeams () { return this.teamOptions.filter(item => this.formData.productionTeamIds.indexOf(item.id) > -1) }
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.loadTeams()
|
||||||
|
this.columns = useTableColumns([{ prop: 'sort', label: this.key('serial_number'), width: 80 }, { prop: 'name', label: this.key('shift_plan_name'), minWidth: 150 }, { prop: 'code', label: this.key('shift_plan_code'), minWidth: 130 }, { prop: 'start_time', label: this.key('start_time'), minWidth: 150 }, { prop: 'finish_time', label: this.key('end_time'), minWidth: 150 }, { prop: 'status', label: this.key('status'), slot: 'status', width: 100 }, { prop: 'nickname', label: this.key('creator'), minWidth: 110 }, { prop: 'create_time', label: this.key('create_time'), minWidth: 160 }, { prop: 'update_time', label: this.key('update_time'), minWidth: 160 }, { prop: '_actions', label: this.key('operation'), width: 180, fixed: 'right' }])
|
||||||
|
const btns = useTableButtons({ toolbar: [{ key: 'add', label: this.key('add'), icon: 'el-icon-plus', type: 'primary', auth: '/system_settings/organization/production_shift_management/create', onClick: this.openAdd }, { key: 'batch_delete', label: this.key('batch_delete'), icon: 'el-icon-delete', color: 'danger', auth: '/system_settings/organization/production_shift_management/batch-delete', needSelection: true, onClick: this.handleBatchDelete }, { key: 'import', label: this.key('import'), icon: 'el-icon-upload2', type: 'success', auth: '/system_settings/organization/production_shift_management/import', onClick: this.openImport }, { key: 'export', label: this.key('export'), icon: 'el-icon-download', type: 'primary', auth: '/system_settings/organization/production_shift_management/export', onClick: this.handleExport }], row: [{ key: 'edit', label: this.key('edit'), icon: 'el-icon-edit', auth: '/system_settings/organization/production_shift_management/edit', onClick: this.openEdit }, { key: 'delete', label: this.key('delete'), icon: 'el-icon-delete', color: 'danger', auth: '/system_settings/organization/production_shift_management/delete', onClick: this.handleDelete }] }, this.$permission)
|
||||||
|
this.toolbarButtons = btns.toolbarButtons; this.rowButtons = btns.rowButtons; this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
defaultFormData () { return { name: '', code: '', time_range: '', status: 1, remark: '', weekly_rest_days: [6, 7], cycle: { unit: 'day', length: 0 }, productionTeamIds: [] } },
|
||||||
|
async loadTeams () { const res = await getTeamAll({}); this.teamOptions = (res && res.data) || res || [] },
|
||||||
|
async fetchData () { this.loading = true; try { const res = await getShiftList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size }); const { list, total } = readPageData(res); this.tableData = list; this.pagination.total = total } finally { this.loading = false } },
|
||||||
|
onSearch () { this.pagination.current = 1; this.fetchData() },
|
||||||
|
onReset () { this.search = { name: '', code: '', create_time: '' }; this.pagination.current = 1; this.fetchData() },
|
||||||
|
onPageChange (page) { this.pagination.current = page.current; this.pagination.size = page.size; this.fetchData() },
|
||||||
|
openAdd () { this.handleType = 'create'; this.dialogTitle = this.key('add_shift_plan'); this.dialogVisible = true },
|
||||||
|
openEdit (row) { this.handleType = 'edit'; this.dialogTitle = this.key('edit_shift_plan'); this.editId = row.id; this.formData = { name: row.name, code: row.code, time_range: [row.start_time, row.finish_time], status: Number(row.status), remark: row.remark || '', weekly_rest_days: safeJson(row.weekly_rest_days, []), cycle: safeJson(row.cycle, { unit: 'day', length: 0 }), productionTeamIds: safeJson(row.production_team_ids, []) }; this.shiftsData = safeJson(row.shifts, []); this.dialogVisible = true },
|
||||||
|
addShiftDetail () { this.shiftsData.push({ name: '', start_time: '', finish_time: '', production_team_id: '' }) },
|
||||||
|
changeProductionTeam (value, index) { if (!value) return; if (this.shiftsData.some((item, i) => i !== index && item.production_team_id === value)) { this.shiftsData[index].production_team_id = ''; this.$message.warning(this.$t(this.key('production_team_can_only_bind_one_shift'))) } },
|
||||||
|
validateShifts () { for (let i = 0; i < this.shiftsData.length; i++) { const item = this.shiftsData[i]; if (!item.name) return this.$t(this.key('please_enter_shift_name_row')) + (i + 1); if (!item.start_time) return this.$t(this.key('please_select_shift_start_time_row')) + (i + 1); if (!item.finish_time) return this.$t(this.key('please_select_shift_end_time_row')) + (i + 1) } return '' },
|
||||||
|
submitDialog () { this.$refs.form.validate(async valid => { if (!valid) return; const error = this.validateShifts(); if (error) { this.$message.error(error); return } this.submitting = true; try { const payload = { ...this.formData, start_time: this.formData.time_range[0], finish_time: this.formData.time_range[1], is_shift: Number(this.formData.cycle.length) > 0 ? 1 : 0, rest_enabled: this.formData.weekly_rest_days.length !== 7 ? 1 : 0, shiftsData: JSON.stringify(this.shiftsData) }; if (this.handleType === 'create') await createShift(payload); else await editShift({ ...payload, id: this.editId }); this.$message.success(this.$t(this.key('operation_successful'))); this.closeDialog(); this.fetchData() } finally { this.submitting = false } }) },
|
||||||
|
closeDialog () { this.dialogVisible = false; this.formData = this.defaultFormData(); this.shiftsData = []; this.editId = '' },
|
||||||
|
async handleDelete (row) { const cancelled = await this.$confirmAction({ message: this.key('delete_department_confirm_message'), title: this.key('prompt'), confirmButtonText: this.key('confirm'), cancelButtonText: this.key('cancel') }, () => deleteShift({ id: [row.id] })); if (cancelled) return; this.$message.success(this.$t(this.key('operation_successful'))); this.fetchData() },
|
||||||
|
async handleBatchDelete () { if (!this.selectedRows.length) { this.$message.error(this.$t(this.key('please_select_table_data'))); return } const cancelled = await this.$confirmAction({ message: this.key('batch_delete_confirm_message'), title: this.key('prompt'), confirmButtonText: this.key('confirm'), cancelButtonText: this.key('cancel') }, () => deleteShift({ id: this.selectedRows.map(item => item.id) })); if (cancelled) return; this.$message.success(this.$t(this.key('operation_successful'))); this.fetchData() },
|
||||||
|
openImport () { this.importFileList = []; this.importRows = []; this.importVisible = true },
|
||||||
|
async downloadTemplate () { this.importLoading = true; try { const res = await getShiftImportTemplate({}); downloadRename(res, 'xlsx', this.$t(this.key('shift_plan_data_import_template'))) } finally { this.importLoading = false } },
|
||||||
|
async onImportFileChange (file) { if (!file || !/\.(xls|xlsx)$/i.test(file.name)) { this.$message.error(this.$t(this.key('upload_format_error'))); return } this.importFileList = [file]; this.importTableLoading = true; try { const rows = await readExcel(file.raw); this.importRows = rows.map(row => ({ name: row[this.$t(this.key('shift_plan_name'))], code: row[this.$t(this.key('shift_plan_code'))], start_time: row[this.$t(this.key('start_time'))], finish_time: row[this.$t(this.key('end_time'))], status: row[this.$t(this.key('status'))], shift_name: row[this.$t(this.key('shift_name'))], shifts: { name: row[this.$t(this.key('shift_name'))], start_time: row[this.$t(this.key('shift_start_time'))], finish_time: row[this.$t(this.key('shift_end_time'))], production_team_id: row[this.$t(this.key('production_team_binding'))] } })) } finally { this.importTableLoading = false } },
|
||||||
|
async submitImport () { if (!this.importRows.length) { this.$message.error(this.$t(this.key('please_import_department_data'))); return } await importShiftData({ import_data: JSON.stringify(this.importRows) }); this.$message.success(this.$t(this.key('operation_successful'))); this.importVisible = false; this.fetchData() },
|
||||||
|
async handleExport () { const cancelled = await this.$confirmAction({ message: this.key('export_confirm_message'), title: this.key('prompt'), confirmButtonText: this.key('confirm'), cancelButtonText: this.key('cancel') }, () => exportShiftTask({ ...this.search, action: 'download' })); if (cancelled) return; this.$message.success(this.$t(this.key('download_task_created'))) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-bar { padding: 10px 0; }
|
||||||
|
.status-on { color: #67C23A; }
|
||||||
|
.status-off { color: #909399; }
|
||||||
|
.input-with-select /deep/ .el-input-group__append { width: 90px; }
|
||||||
|
/deep/ .el-form-item--mini.el-form-item { margin-bottom: 4px; }
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user