更新若干事项
This commit is contained in:
1 parent
68cdaf38fa
commit
9898962836
30 files changed
+7715
No files matched your search
@@ -0,0 +1,51 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// 平台端排班模板管理(全局模板,只有平台可维护)
|
||||
// 系统预置模板只读:后端会拒绝对 is_system=1 的编辑 / 删除 / 停用
|
||||
const PREFIX = "/platform/schedTemplates";
|
||||
|
||||
export function getSchedTemplateList(params) {
|
||||
return request({
|
||||
url: PREFIX,
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function getSchedTemplateDetail(id) {
|
||||
return request({
|
||||
url: `${PREFIX}/${id}`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function createSchedTemplate(data) {
|
||||
return request({
|
||||
url: PREFIX,
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateSchedTemplate(id, data) {
|
||||
return request({
|
||||
url: `${PREFIX}/${id}`,
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteSchedTemplate(id) {
|
||||
return request({
|
||||
url: `${PREFIX}/${id}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleSchedTemplateStatus(id, status) {
|
||||
return request({
|
||||
url: `${PREFIX}/${id}/status`,
|
||||
method: "post",
|
||||
data: { status },
|
||||
});
|
||||
}
|
||||
@@ -73,6 +73,13 @@ const staticMainChildren = [
|
||||
component: () => import("@/views/template/tags.vue"),
|
||||
meta: { requiresAuth: true, title: "标签调用说明" }
|
||||
},
|
||||
// 排班模板(全局模板管理,只有平台可维护)
|
||||
{
|
||||
path: "/basicSettings/schedTemplate",
|
||||
name: "PlatformSchedTemplate",
|
||||
component: () => import("@/views/system/schedTemplate/index.vue"),
|
||||
meta: { requiresAuth: true, title: "排班模板" }
|
||||
},
|
||||
// 兼容拼写错误的路径重定向
|
||||
{
|
||||
path: "/apps/erp/dashborad",
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="dialogTitle"
|
||||
width="960px"
|
||||
top="6vh"
|
||||
destroy-on-close
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<div v-loading="loading">
|
||||
<el-form :model="form" label-width="110px" :disabled="readonly">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="模板名称" required>
|
||||
<el-input v-model="form.name" maxlength="50" placeholder="如:项目部大小周" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="模板类型">
|
||||
<el-select v-model="form.template_type" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排班方式" required>
|
||||
<el-radio-group v-model="form.align_mode">
|
||||
<el-radio :value="1">按自然周</el-radio>
|
||||
<el-radio :value="2">按开始日滚动</el-radio>
|
||||
<el-radio :value="3">按自然月</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="form.sort" :min="0" :max="9999" controls-position="right" />
|
||||
<span class="tip">数字越小越靠前</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col v-if="form.align_mode !== 3" :span="12">
|
||||
<el-form-item label="周期天数" required>
|
||||
<el-select v-if="form.align_mode === 1" v-model="form.cycle_days" style="width: 100%">
|
||||
<el-option :value="7" label="1 周(7 天)" />
|
||||
<el-option :value="14" label="2 周(14 天,大小周常用)" />
|
||||
<el-option :value="21" label="3 周(21 天)" />
|
||||
<el-option :value="28" label="4 周(28 天)" />
|
||||
</el-select>
|
||||
<el-input-number
|
||||
v-else
|
||||
v-model="form.cycle_days"
|
||||
:min="2"
|
||||
:max="31"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.align_mode === 1" :span="12">
|
||||
<el-form-item label="起始周">
|
||||
<el-select v-model="form.week_offset" style="width: 100%">
|
||||
<el-option :value="0" label="方案开始周按第 1 周算" />
|
||||
<el-option v-if="form.cycle_days > 7" :value="1" label="方案开始周按第 2 周算" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.align_mode === 3" :span="12">
|
||||
<el-form-item label="每月休息天数" required>
|
||||
<el-input-number
|
||||
v-model="form.month_rest_days"
|
||||
:min="1"
|
||||
:max="15"
|
||||
controls-position="right"
|
||||
/>
|
||||
<span class="tip">天</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.align_mode === 3" :span="12">
|
||||
<el-form-item label="休息日安排">
|
||||
<el-select v-model="form.month_rest_weekday" style="width: 100%">
|
||||
<el-option :value="0" label="不固定(从月末往前排)" />
|
||||
<el-option v-for="item in weekdayOptions" :key="item.value" :value="item.value" :label="`优先休${item.label}`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="规则摘要">
|
||||
<el-input
|
||||
v-model="form.summary"
|
||||
maxlength="255"
|
||||
placeholder="留空由系统自动生成,用于租户端列表展示"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<div class="rules-head">
|
||||
<div class="label">
|
||||
{{ form.align_mode === 3 ? "上班日规则" : "周期每日规则" }}
|
||||
</div>
|
||||
<div class="hint" v-if="form.align_mode === 1">
|
||||
序号按自然周固定:周一 → 周日;多周循环时第 2 周从第 8 天开始
|
||||
</div>
|
||||
<div class="hint" v-else-if="form.align_mode === 2">
|
||||
从方案开始日期起,按顺序循环使用下面 {{ form.cycle_days }} 天的规则
|
||||
</div>
|
||||
<div class="hint" v-else>
|
||||
除每月固定休息日外,其余日期都按下面这一天的班次上班
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="form.days" border size="small" :disabled="readonly">
|
||||
<el-table-column label="天数" width="120">
|
||||
<template #default="{ row, $index }">{{ dayLabel($index, row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-radio-group v-model="row.day_type" size="small" :disabled="readonly">
|
||||
<el-radio-button :value="1">上班</el-radio-button>
|
||||
<el-radio-button :value="2">休息</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="班次名称" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.shift_name"
|
||||
size="small"
|
||||
maxlength="50"
|
||||
:disabled="readonly || row.day_type === 2"
|
||||
placeholder="如:白班"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="简称" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.shift_short"
|
||||
size="small"
|
||||
maxlength="20"
|
||||
:disabled="readonly || row.day_type === 2"
|
||||
placeholder="白"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上班时间" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.work_start"
|
||||
size="small"
|
||||
placeholder="09:00"
|
||||
:disabled="readonly || row.day_type === 2"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下班时间" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.work_end"
|
||||
size="small"
|
||||
placeholder="18:00"
|
||||
:disabled="readonly || row.day_type === 2"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="休息开始" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.rest_start"
|
||||
size="small"
|
||||
placeholder="12:00"
|
||||
:disabled="readonly || row.day_type === 2"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="休息结束" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.rest_end"
|
||||
size="small"
|
||||
placeholder="13:30"
|
||||
:disabled="readonly || row.day_type === 2"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="颜色" width="70" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-color-picker
|
||||
v-model="row.color"
|
||||
size="small"
|
||||
:disabled="readonly || row.day_type === 2"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">{{ readonly ? "关闭" : "取消" }}</el-button>
|
||||
<el-button v-if="!readonly" type="primary" :loading="submitting" @click="submit">
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, nextTick } from "vue";
|
||||
import {
|
||||
getSchedTemplateDetail,
|
||||
createSchedTemplate,
|
||||
updateSchedTemplate,
|
||||
} from "@/api/schedTemplate";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
templateId: { type: Number, default: 0 },
|
||||
readonly: { type: Boolean, default: false },
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "saved"]);
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit("update:modelValue", value),
|
||||
});
|
||||
|
||||
const typeOptions = [
|
||||
{ value: 1, label: "双休" },
|
||||
{ value: 2, label: "单休" },
|
||||
{ value: 3, label: "大小周" },
|
||||
{ value: 4, label: "月休" },
|
||||
{ value: 5, label: "三班倒" },
|
||||
{ value: 6, label: "做一休一" },
|
||||
{ value: 9, label: "自定义" },
|
||||
];
|
||||
const weekdayOptions = [
|
||||
{ value: 1, label: "周一" },
|
||||
{ value: 2, label: "周二" },
|
||||
{ value: 3, label: "周三" },
|
||||
{ value: 4, label: "周四" },
|
||||
{ value: 5, label: "周五" },
|
||||
{ value: 6, label: "周六" },
|
||||
{ value: 7, label: "周日" },
|
||||
];
|
||||
const weekdayNames = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
|
||||
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
// 回填详情时避免 watch 把已加载的规则重新重建
|
||||
let skipSync = false;
|
||||
|
||||
function blankDay(index) {
|
||||
return {
|
||||
day_index: index,
|
||||
day_type: 1,
|
||||
shift_name: "白班",
|
||||
shift_short: "白",
|
||||
color: "#409EFF",
|
||||
work_start: "09:00",
|
||||
work_end: "18:00",
|
||||
rest_start: "12:00",
|
||||
rest_end: "13:30",
|
||||
work_hours: 7.5,
|
||||
remark: "",
|
||||
};
|
||||
}
|
||||
|
||||
function restDay(index) {
|
||||
return {
|
||||
day_index: index,
|
||||
day_type: 2,
|
||||
shift_name: "",
|
||||
shift_short: "",
|
||||
color: "",
|
||||
work_start: "",
|
||||
work_end: "",
|
||||
rest_start: "",
|
||||
rest_end: "",
|
||||
work_hours: 0,
|
||||
remark: "",
|
||||
};
|
||||
}
|
||||
|
||||
function defaultForm() {
|
||||
const days = [];
|
||||
for (let i = 0; i < 7; i += 1) {
|
||||
days.push(i >= 5 ? restDay(i) : blankDay(i));
|
||||
}
|
||||
return {
|
||||
id: 0,
|
||||
name: "",
|
||||
code: "",
|
||||
template_type: 9,
|
||||
align_mode: 1,
|
||||
cycle_days: 7,
|
||||
week_offset: 0,
|
||||
month_rest_days: 4,
|
||||
month_rest_weekday: 7,
|
||||
summary: "",
|
||||
status: 1,
|
||||
sort: 0,
|
||||
days,
|
||||
};
|
||||
}
|
||||
|
||||
const form = reactive(defaultForm());
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
if (props.readonly) return "查看排班模板";
|
||||
return props.templateId ? "编辑排班模板" : "新建排班模板";
|
||||
});
|
||||
|
||||
function dayLabel(index, row) {
|
||||
if (form.align_mode === 3) return "上班日";
|
||||
if (form.align_mode === 2) return `第 ${index + 1} 天`;
|
||||
const week = Math.floor(index / 7) + 1;
|
||||
const name = weekdayNames[index % 7] || row.day_index;
|
||||
return form.cycle_days > 7 ? `第 ${week} 周 ${name}` : name;
|
||||
}
|
||||
|
||||
// 周期天数或排班方式变化时,保留已填规则并补齐/裁剪天数
|
||||
function syncDays() {
|
||||
const cycle = form.align_mode === 3 ? 1 : Number(form.cycle_days) || 7;
|
||||
const next = [];
|
||||
for (let i = 0; i < cycle; i += 1) {
|
||||
const current = form.days[i];
|
||||
if (current) {
|
||||
next.push({ ...current, day_index: i });
|
||||
} else {
|
||||
next.push(blankDay(i));
|
||||
}
|
||||
}
|
||||
form.days = next;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => form.cycle_days,
|
||||
() => {
|
||||
if (!props.readonly && !skipSync) syncDays();
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => form.align_mode,
|
||||
(value) => {
|
||||
if (props.readonly || skipSync) return;
|
||||
if (value === 3) {
|
||||
form.month_rest_days = form.month_rest_days || 4;
|
||||
} else if (value === 1 && form.cycle_days % 7 !== 0) {
|
||||
form.cycle_days = 7;
|
||||
}
|
||||
syncDays();
|
||||
}
|
||||
);
|
||||
|
||||
// 月休的休息日安排由后端按月推算,这里只要保证至少有一天的上班规则
|
||||
watch(
|
||||
() => form.modelValue,
|
||||
(open) => {
|
||||
if (open) loadDetail();
|
||||
}
|
||||
);
|
||||
|
||||
async function loadDetail() {
|
||||
if (!props.templateId) {
|
||||
skipSync = true;
|
||||
Object.assign(form, defaultForm());
|
||||
await nextTick();
|
||||
skipSync = false;
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getSchedTemplateDetail(props.templateId);
|
||||
const data = res?.data || {};
|
||||
skipSync = true;
|
||||
Object.assign(form, {
|
||||
id: data.id || 0,
|
||||
name: data.name || "",
|
||||
code: data.code || "",
|
||||
template_type: data.template_type || 9,
|
||||
align_mode: data.align_mode || 1,
|
||||
cycle_days: data.cycle_days || 7,
|
||||
week_offset: data.week_offset || 0,
|
||||
month_rest_days: data.month_rest_days || 0,
|
||||
month_rest_weekday: data.month_rest_weekday || 0,
|
||||
summary: data.summary || "",
|
||||
status: data.status === 0 ? 0 : 1,
|
||||
sort: data.sort || 0,
|
||||
});
|
||||
form.days = (data.days || []).map((item) => ({
|
||||
day_index: item.day_index,
|
||||
day_type: item.day_type,
|
||||
shift_name: item.shift_name || "",
|
||||
shift_short: item.shift_short || "",
|
||||
color: item.color || "#409EFF",
|
||||
work_start: item.work_start || "",
|
||||
work_end: item.work_end || "",
|
||||
rest_start: item.rest_start || "",
|
||||
rest_end: item.rest_end || "",
|
||||
work_hours: item.work_hours || 0,
|
||||
remark: item.remark || "",
|
||||
}));
|
||||
if (form.days.length === 0) syncDays();
|
||||
await nextTick();
|
||||
skipSync = false;
|
||||
} catch (e) {
|
||||
skipSync = false;
|
||||
ElMessage.error(e?.message || "加载模板详情失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function validate() {
|
||||
if (!form.name.trim()) return "请填写模板名称";
|
||||
if (form.align_mode !== 3) {
|
||||
if (!form.cycle_days || form.cycle_days < 2) return "周期天数至少 2 天";
|
||||
if (form.align_mode === 1 && form.cycle_days % 7 !== 0) {
|
||||
return "按自然周排班时,周期天数必须是 7 的倍数";
|
||||
}
|
||||
if (form.days.length !== form.cycle_days) return "每日规则数量与周期天数不一致";
|
||||
} else if (!form.month_rest_days || form.month_rest_days < 1) {
|
||||
return "请填写每月休息天数";
|
||||
}
|
||||
const workDays = form.days.filter((item) => item.day_type === 1);
|
||||
if (workDays.length === 0) return "至少需要一天上班";
|
||||
for (const item of workDays) {
|
||||
if (!String(item.shift_name || "").trim()) return "上班日需要填写班次名称";
|
||||
if (!/^\d{1,2}:\d{2}$/.test(item.work_start) || !/^\d{1,2}:\d{2}$/.test(item.work_end)) {
|
||||
return "上下班时间格式应为 HH:mm";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
const message = validate();
|
||||
if (message) {
|
||||
ElMessage.warning(message);
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
name: form.name.trim(),
|
||||
code: form.code || "",
|
||||
template_type: form.template_type,
|
||||
align_mode: form.align_mode,
|
||||
cycle_days: form.align_mode === 3 ? 0 : form.cycle_days,
|
||||
week_offset: form.week_offset,
|
||||
month_rest_days: form.align_mode === 3 ? form.month_rest_days : 0,
|
||||
month_rest_weekday: form.align_mode === 3 ? form.month_rest_weekday : 0,
|
||||
summary: form.summary,
|
||||
status: form.status,
|
||||
sort: form.sort,
|
||||
days: form.days.map((item, index) => ({
|
||||
day_index: index,
|
||||
day_type: item.day_type,
|
||||
shift_name: item.day_type === 1 ? item.shift_name : "",
|
||||
shift_short: item.day_type === 1 ? item.shift_short : "",
|
||||
color: item.day_type === 1 ? item.color || "#409EFF" : "",
|
||||
work_start: item.day_type === 1 ? item.work_start : "",
|
||||
work_end: item.day_type === 1 ? item.work_end : "",
|
||||
rest_start: item.day_type === 1 ? item.rest_start : "",
|
||||
rest_end: item.day_type === 1 ? item.rest_end : "",
|
||||
work_hours: item.day_type === 1 ? item.work_hours : 0,
|
||||
remark: item.remark || "",
|
||||
})),
|
||||
};
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
if (props.templateId) {
|
||||
await updateSchedTemplate(props.templateId, payload);
|
||||
ElMessage.success("模板已更新");
|
||||
} else {
|
||||
await createSchedTemplate(payload);
|
||||
ElMessage.success("模板已创建");
|
||||
}
|
||||
visible.value = false;
|
||||
emit("saved");
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || "保存失败");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
skipSync = true;
|
||||
Object.assign(form, defaultForm());
|
||||
nextTick(() => {
|
||||
skipSync = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.rules-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
margin: 4px 0 8px;
|
||||
}
|
||||
.rules-head .label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rules-head .hint {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
.tip {
|
||||
margin-left: 8px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div class="sched-template-page">
|
||||
<div class="page-header">
|
||||
<div class="title">排班模板</div>
|
||||
<div class="actions">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
placeholder="搜索模板名称"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
@keyup.enter="loadList"
|
||||
@clear="loadList"
|
||||
/>
|
||||
<el-button @click="loadList">查询</el-button>
|
||||
<el-button type="primary" @click="openCreate">新建模板</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="系统预置模板全局生效、只读不可改;租户只能调用,调用后规则会复制到租户自己的方案里,可自由调整"
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
|
||||
<el-table :data="list" v-loading="loading" border>
|
||||
<el-table-column prop="sort" label="排序" width="70" align="center" />
|
||||
<el-table-column label="模板名称" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.name }}</span>
|
||||
<el-tag v-if="row.is_system === 1" size="small" type="warning" style="margin-left: 6px">
|
||||
预置
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="template_type_text" label="类型" width="100" />
|
||||
<el-table-column label="排班方式" width="190">
|
||||
<template #default="{ row }">{{ alignText(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="summary" label="规则摘要" min-width="280" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'info'">
|
||||
{{ row.status === 1 ? "启用" : "停用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creater" label="创建人" width="100" />
|
||||
<el-table-column label="操作" width="210" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button v-if="row.editable" link type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button
|
||||
v-if="row.editable"
|
||||
link
|
||||
:type="row.status === 1 ? 'warning' : 'success'"
|
||||
@click="toggleStatus(row)"
|
||||
>
|
||||
{{ row.status === 1 ? "停用" : "启用" }}
|
||||
</el-button>
|
||||
<el-button v-if="row.editable" link type="danger" @click="removeRow(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pager">
|
||||
<el-pagination
|
||||
v-model:current-page="page"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
background
|
||||
@current-change="loadList"
|
||||
@size-change="loadList"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TemplateEdit
|
||||
v-model="dialogVisible"
|
||||
:template-id="currentId"
|
||||
:readonly="readonly"
|
||||
@saved="loadList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import TemplateEdit from "./components/TemplateEdit.vue";
|
||||
import {
|
||||
getSchedTemplateList,
|
||||
deleteSchedTemplate,
|
||||
toggleSchedTemplateStatus,
|
||||
} from "@/api/schedTemplate";
|
||||
|
||||
const keyword = ref("");
|
||||
const list = ref([]);
|
||||
const total = ref(0);
|
||||
const page = ref(1);
|
||||
const pageSize = ref(20);
|
||||
const loading = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
const currentId = ref(0);
|
||||
const readonly = ref(false);
|
||||
|
||||
function alignText(row) {
|
||||
if (row.align_mode === 3) {
|
||||
return `按自然月 · 每月休 ${row.month_rest_days || 0} 天`;
|
||||
}
|
||||
if (row.align_mode === 2) {
|
||||
return `每 ${row.cycle_days || 0} 天滚动循环`;
|
||||
}
|
||||
const weeks = Math.max(1, Math.round((row.cycle_days || 7) / 7));
|
||||
return weeks > 1 ? `按自然周 · ${weeks} 周循环` : "按自然周";
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getSchedTemplateList({
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
keyword: keyword.value,
|
||||
});
|
||||
const data = res?.data || {};
|
||||
list.value = data.list || [];
|
||||
total.value = data.total || 0;
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || "加载排班模板失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
currentId.value = 0;
|
||||
readonly.value = false;
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
currentId.value = row.id;
|
||||
readonly.value = false;
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function openView(row) {
|
||||
currentId.value = row.id;
|
||||
readonly.value = true;
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function toggleStatus(row) {
|
||||
const next = row.status === 1 ? 0 : 1;
|
||||
try {
|
||||
await toggleSchedTemplateStatus(row.id, next);
|
||||
ElMessage.success(next === 1 ? "模板已启用" : "模板已停用");
|
||||
loadList();
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || "更新状态失败");
|
||||
}
|
||||
}
|
||||
|
||||
async function removeRow(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除模板「${row.name}」?删除后租户将无法再调用该模板,已生成的方案不受影响。`,
|
||||
"删除确认",
|
||||
{ type: "warning" }
|
||||
);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await deleteSchedTemplate(row.id);
|
||||
ElMessage.success("模板已删除");
|
||||
loadList();
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || "删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadList);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sched-template-page {
|
||||
padding: 16px;
|
||||
}
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.page-header .title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.page-header .actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user