更新角色管理相关内容

This commit is contained in:
2026-09-09 15:04:34 +08:00
parent de7225b61e
commit fef0233cc4
24 changed files with 1442 additions and 116 deletions
@@ -77,6 +77,68 @@
placeholder="补充说明(可选)"
/>
</el-form-item>
<el-divider>提醒设置</el-divider>
<el-form-item label="开启提醒">
<el-switch v-model="form.reminder_enabled" @change="onReminderToggle" />
<span class="form-tip">开启后将在「提醒工具」生成一条提醒任务,到点时按渠道通知你</span>
</el-form-item>
<template v-if="form.reminder_enabled">
<el-form-item label="提醒时间" prop="reminder_time">
<el-date-picker
v-model="form.reminder_time"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="选择提醒时间"
style="width: 100%"
/>
<span class="form-tip"
>默认取日程发生时间,可修改;提醒将在"提前提醒分钟"之前开始送达</span
>
</el-form-item>
<el-form-item label="提醒渠道" prop="remind_channels">
<el-checkbox-group v-model="form.remind_channels">
<el-checkbox value="SMS">短信</el-checkbox>
<el-checkbox value="EMAIL">邮件</el-checkbox>
<el-checkbox value="BARK">Bark</el-checkbox>
<el-checkbox value="SITE_MSG">站内信</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="提前提醒(分钟)" prop="advance_minutes">
<el-input-number
v-model="form.advance_minutes"
:min="0"
:max="1440"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
<el-alert
v-if="missingChannels.length"
type="warning"
:closable="false"
show-icon
class="reminder-alert"
>
<template #title>
未配置「{{ missingChannels.join("、") }}」的接收方式
</template>
<div class="alert-body">
<span>请先在通知设置中配置对应账号,否则该渠道提醒无法送达。</span>
<el-button
type="primary"
size="small"
link
@click="saveDraftAndGotoNotify"
>去设置并暂存当前日程</el-button
>
</div>
</el-alert>
</template>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
@@ -89,8 +151,11 @@
<script setup>
import { computed, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import { createSchedule, updateSchedule } from "@/api/oaSchedule";
import { getNotifyConfig } from "@/api/notifyConfig";
import { getReminderDetail } from "@/api/reminder";
const props = defineProps({
modelValue: {
@@ -106,10 +171,17 @@ const props = defineProps({
defaultDate: {
type: String,
default: ""
},
// 从通知设置页返回时恢复的草稿(含提醒设置)
draft: {
type: Object,
default: null
}
});
const emit = defineEmits(["update:modelValue", "saved"]);
const emit = defineEmits(["update:modelValue", "saved", "draft-applied"]);
const router = useRouter();
const visible = computed({
get: () => props.modelValue,
@@ -137,9 +209,18 @@ const form = reactive({
end_time: "",
priority: 0,
color: colorOptions[0],
content: ""
content: "",
// 提醒绑定
reminder_enabled: false,
reminder_time: "",
remind_channels: [],
advance_minutes: 0
});
// 用户通知配置:用于判断所选渠道是否已配置接收账号
const notifyConfig = ref({});
const notifyLoading = ref(false);
const rules = {
title: [{ required: true, message: "请输入日程标题", trigger: "blur" }],
schedule_date: [
@@ -149,7 +230,7 @@ const rules = {
const isEdit = computed(() => !!props.schedule);
// 打开抽屉时根据编辑对象/默认日期重建表单。
// 打开抽屉时根据编辑对象/默认日期/草稿重建表单。
// 用 watch 替代 el-drawer 的 @open + destroy-on-close:
// open 事件早于抽屉内容挂载,时序不稳定会导致表单控件状态错乱、点击无效。
watch(
@@ -161,7 +242,112 @@ watch(
}
);
function resetReminderForm() {
form.reminder_enabled = false;
form.reminder_time = "";
form.remind_channels = [];
form.advance_minutes = 0;
}
// 由日程日期/时间推算默认提醒时间:全天取 09:00,按时段取开始时间。
function defaultReminderTime() {
if (!form.schedule_date) {
return "";
}
if (form.all_day) {
return `${form.schedule_date} 09:00:00`;
}
const t = form.start_time || "09:00";
return `${form.schedule_date} ${t}:00`;
}
// 日程时间变化时,若提醒已开启且用户未手动填写提醒时间,则同步默认值
watch(
() => [form.schedule_date, form.all_day, form.start_time],
() => {
if (form.reminder_enabled && !form.reminder_time) {
form.reminder_time = defaultReminderTime();
}
}
);
function onReminderToggle(val) {
if (val && !form.reminder_time) {
form.reminder_time = defaultReminderTime();
}
}
async function loadNotifyConfig() {
notifyLoading.value = true;
try {
const res = await getNotifyConfig();
if (res?.code === 200) {
notifyConfig.value = res.data || {};
}
} catch {
notifyConfig.value = {};
} finally {
notifyLoading.value = false;
}
}
// 当前选中渠道中,缺少对应接收账号配置的渠道
const missingChannels = computed(() => {
if (!form.reminder_enabled) {
return [];
}
const cfg = notifyConfig.value || {};
const need = [];
for (const ch of form.remind_channels) {
if (ch === "EMAIL" && !cfg.email) {
need.push("邮件");
} else if (ch === "SMS" && !cfg.phone) {
need.push("短信");
} else if (ch === "BARK" && !cfg.bark_device_key) {
need.push("Bark");
}
// SITE_MSG(站内信)无需额外配置
}
return need;
});
// 暂存当前填写内容并跳转到通知设置页
function saveDraftAndGotoNotify() {
const draft = { ...form, schedule_id: undefined };
sessionStorage.setItem("oa_schedule_draft", JSON.stringify(draft));
ElMessage.info("已暂存当前日程,去设置通知后返回即可继续");
router.push("/user/notifyConfig");
}
// 编辑已绑定提醒的日程时,回填提醒设置
async function loadReminderForEdit(reminderId) {
try {
const res = await getReminderDetail(reminderId);
if (res?.code === 200 && res.data) {
const d = res.data;
form.reminder_time = d.schedule_time || defaultReminderTime();
form.remind_channels = d.remind_channels || [];
form.advance_minutes = d.advance_minutes ?? 0;
}
} catch {
// 读取失败时不阻断编辑,退化为基础默认值
form.reminder_time = defaultReminderTime();
}
}
function initForm() {
// 优先应用从通知设置页返回的草稿
if (props.draft) {
editingId.value = 0;
Object.assign(form, props.draft);
if (!form.reminder_time) {
form.reminder_time = defaultReminderTime();
}
emit("draft-applied");
loadNotifyConfig();
return;
}
if (props.schedule) {
editingId.value = props.schedule.id;
form.title = props.schedule.title;
@@ -172,6 +358,13 @@ function initForm() {
form.priority = props.schedule.priority || 0;
form.color = props.schedule.color || colorOptions[0];
form.content = props.schedule.content || "";
// 回显已绑定的提醒任务
if (props.schedule.reminder_id > 0) {
form.reminder_enabled = true;
loadReminderForEdit(props.schedule.reminder_id);
} else {
resetReminderForm();
}
} else {
editingId.value = 0;
form.title = "";
@@ -182,7 +375,9 @@ function initForm() {
form.priority = 0;
form.color = colorOptions[0];
form.content = "";
resetReminderForm();
}
loadNotifyConfig();
}
async function handleSave() {
@@ -202,6 +397,22 @@ async function handleSave() {
}
}
// 开启提醒但所选渠道缺少接收配置:阻断保存,引导先去通知设置
if (form.reminder_enabled && missingChannels.value.length) {
ElMessage.warning(
`请先配置${missingChannels.value.join("、")}的接收方式,或取消该渠道`
);
return;
}
if (form.reminder_enabled && !form.reminder_time) {
ElMessage.warning("请选择提醒时间");
return;
}
if (form.reminder_enabled && form.remind_channels.length === 0) {
ElMessage.warning("请至少选择一个提醒渠道");
return;
}
saving.value = true;
try {
const payload = {
@@ -212,7 +423,13 @@ async function handleSave() {
end_time: form.all_day ? "" : form.end_time || "",
all_day: form.all_day ? 1 : 0,
color: form.color,
priority: form.priority
priority: form.priority,
reminder_enabled: !!form.reminder_enabled,
reminder_time: form.reminder_enabled ? form.reminder_time : "",
remind_channels: form.reminder_enabled ? form.remind_channels : [],
advance_minutes: form.reminder_enabled
? Number(form.advance_minutes || 0)
: 0
};
const res =
isEdit.value
@@ -231,7 +448,7 @@ async function handleSave() {
}
</script>
<style scoped>
<style lang="less" scoped>
.time-range {
display: flex;
align-items: center;
@@ -272,4 +489,38 @@ async function handleSave() {
border-color: #fff;
outline: 2px solid #409eff;
}
.form-tip {
display: block;
font-size: 12px;
color: #909399;
line-height: 1.5;
margin-top: 4px;
}
.reminder-alert {
margin-bottom: 8px;
}
.reminder-alert .alert-body {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
flex-wrap: wrap;
}
.reminder-alert .alert-body span {
font-size: 13px;
}
// ===== 暗色主题适配 =====
html.dark & {
.time-sep {
color: var(--el-text-color-secondary);
}
.color-dot.active {
border-color: var(--el-bg-color);
outline-color: var(--el-color-primary);
}
}
</style>