refactor event

This commit is contained in:
Timi
2026-08-06 20:53:22 +08:00
parent 1243068fc3
commit 9df6dfaf59
37 changed files with 1584 additions and 2316 deletions
+103 -86
View File
@@ -95,8 +95,8 @@
class="delete bg-white"
theme="danger"
variant="outline"
:loading="removing"
:disabled="saving || removing"
:loading="isRemoving"
:disabled="isSaving || isRemoving"
@click="confirmRemoveEventItem"
>
删除事件
@@ -105,8 +105,8 @@
v-can="savePermissionCode"
class="save"
theme="primary"
:loading="saving"
:disabled="removing"
:loading="isSaving"
:disabled="isRemoving"
@click="saveEventItem"
>
保存
@@ -201,6 +201,7 @@ defineOptions({
const route = useRoute();
const router = useRouter();
const routeId = computed(() => String(route.params.id || ""));
const isCreateMode = computed(() => !routeId.value);
const savePermissionCode = computed(() => isCreateMode.value ? permissionCodes.event.create : permissionCodes.event.update);
const deletePermissionCode = permissionCodes.event.delete;
@@ -208,44 +209,12 @@ const pageAccess = computed(() => ({
value: [permissionCodes.event.read, permissionCodes.user.read, savePermissionCode.value],
mode: "and" as const
}));
// ---------- 基础表单 ----------
const roleOptions = ref<Role[]>([]);
const colorPickerVisible = ref(false);
const colorPickerRenderKey = ref(0);
const colorDraft = ref("#2f88ff");
const beginAtPickerVisible = ref(false);
const endAtPickerVisible = ref(false);
const beginAtDraftText = ref("");
const endAtDraftText = ref("");
const saving = ref(false);
const removing = ref(false);
const colorSwatchList = [
"#2F88FF",
"#0EA5E9",
"#10B981",
"#22C55E",
"#F59E0B",
"#F97316",
"#EF4444",
"#EC4899",
"#8B5CF6",
"#64748B"
];
const limitOptions: LimitOption[] = [
{ label: "不限", value: "NONE" },
{ label: "上午和下午各一次", value: "DAILY_HALF_DAY" },
{ label: "间隔小时内一次", value: "INTERVAL_HOUR" },
{ label: "每个自然日一次", value: "NATURAL_DAY" },
{ label: "每个自然月一次", value: "NATURAL_MONTH" },
{ label: "事件总次数", value: "TOTAL_LIMIT" },
{ label: "单客户总次数", value: "CUSTOMER_TOTAL_LIMIT" }
];
const statusOptions: Array<{ label: string; value: EventStatus }> = [
{ label: "草稿", value: "DRAFT" },
{ label: "可登记", value: "ACTIVE" },
{ label: "不可登记", value: "INACTIVE" }
];
const limitPickerColumns = [limitOptions];
const statusPickerColumns = [statusOptions];
const isSaving = ref(false);
const isRemoving = ref(false);
const formModel = reactive({
id: "",
name: "",
@@ -259,10 +228,57 @@ const formModel = reactive({
endAtText: "",
roleCodeList: [] as string[]
});
// ---------- 颜色 ----------
const colorPickerVisible = ref(false);
const colorPickerRenderKey = ref(0);
const colorDraft = ref("#2f88ff");
const colorSwatchList = [
"#2F88FF",
"#0EA5E9",
"#10B981",
"#22C55E",
"#F59E0B",
"#F97316",
"#EF4444",
"#EC4899",
"#8B5CF6",
"#64748B"
];
// ---------- 次数限制 ----------
const limitOptions: LimitOption[] = [
{ label: "不限", value: "NONE" },
{ label: "上午和下午各一次", value: "DAILY_HALF_DAY" },
{ label: "间隔小时内一次", value: "INTERVAL_HOUR" },
{ label: "每个自然日一次", value: "NATURAL_DAY" },
{ label: "每个自然月一次", value: "NATURAL_MONTH" },
{ label: "事件总次数", value: "TOTAL_LIMIT" },
{ label: "单客户总次数", value: "CUSTOMER_TOTAL_LIMIT" }
];
const limitPickerColumns = [limitOptions];
const limitPickerVisible = ref(false);
const limitPickerDraft = ref<string[]>([formModel.limitType]);
// ---------- 状态 ----------
const statusOptions: Array<{ label: string; value: EventStatus }> = [
{ label: "草稿", value: "DRAFT" },
{ label: "可登记", value: "ACTIVE" },
{ label: "不可登记", value: "INACTIVE" }
];
const statusPickerColumns = [statusOptions];
const statusPickerVisible = ref(false);
const statusPickerDraft = ref<EventStatus[]>([formModel.status]);
// ---------- 生效时间 ----------
const beginAtPickerVisible = ref(false);
const endAtPickerVisible = ref(false);
const beginAtDraftText = ref("");
const endAtDraftText = ref("");
const minimumDateTimeText = "1970-01-01 00:00";
const nowDateTimeText = computed(() => Time.formatDateTime(new Date()));
const endAtMinimumDateTimeText = computed(() => formModel.beginAtText || minimumDateTimeText);
@@ -281,6 +297,8 @@ const limitValuePlaceholder = computed(() => {
return "请输入总次数上限";
});
// ---------- 颜色 ----------
function openColorPicker(): void {
colorDraft.value = formModel.color;
colorPickerVisible.value = true;
@@ -304,6 +322,8 @@ function updateColorDraftFromPalette(context: { color: ColorObject }): void {
colorDraft.value = context.color.hex8;
}
// ---------- 选项文案 ----------
function getLimitText(value: EventLimitType): string {
return limitOptions.find(option => option.value === value)?.label || "不限";
}
@@ -312,6 +332,8 @@ function getStatusText(value: EventStatus): string {
return statusOptions.find(option => option.value === value)?.label || "可登记";
}
// ---------- 次数限制 ----------
function openLimitPicker(): void {
limitPickerDraft.value = [formModel.limitType];
limitPickerVisible.value = true;
@@ -325,6 +347,8 @@ function confirmLimitPicker(value: Array<string | number>): void {
limitPickerVisible.value = false;
}
// ---------- 状态 ----------
function openStatusPicker(): void {
statusPickerDraft.value = [formModel.status];
statusPickerVisible.value = true;
@@ -351,6 +375,8 @@ function resolveLimitValue(): number | undefined {
return 0 < value ? Math.floor(value) : undefined;
}
// ---------- 表单转换 ----------
function resolveTimestamp(valueText: string): number | undefined {
if (!valueText.trim()) {
return undefined;
@@ -363,41 +389,27 @@ function resolveTimestamp(valueText: string): number | undefined {
function validateForm(): boolean {
if (!formModel.name.trim()) {
showToast("请输入登记事件名称", "warning");
Toast.warning("请输入登记事件名称");
return false;
}
if (!formModel.roleCodeList.length) {
showToast("请至少选择一个可登记角色", "warning");
Toast.warning("请至少选择一个可登记角色");
return false;
}
if (isValueLimitType(formModel.limitType) && !resolveLimitValue()) {
showToast("请输入有效的约束数值", "warning");
Toast.warning("请输入有效的约束数值");
return false;
}
const beginAt = resolveTimestamp(formModel.beginAtText);
const endAt = resolveTimestamp(formModel.endAtText);
if (beginAt !== undefined && endAt !== undefined && endAt < beginAt) {
showToast("终止时间不能早于起始时间", "warning");
Toast.warning("终止时间不能早于起始时间");
return false;
}
return true;
}
function buildPayload(): EventItem {
return {
id: formModel.id || undefined,
name: formModel.name.trim(),
description: formModel.description,
color: formModel.color,
status: formModel.status,
requirePhoto: formModel.requirePhoto,
limitType: formModel.limitType,
limitValue: resolveLimitValue(),
beginAt: resolveTimestamp(formModel.beginAtText),
endAt: resolveTimestamp(formModel.endAtText),
roleCodeList: [...formModel.roleCodeList]
};
}
// ---------- 生效时间 ----------
function openBeginAtPicker(): void {
beginAtDraftText.value = formModel.beginAtText || nowDateTimeText.value;
@@ -431,34 +443,48 @@ function clearEndAt(): void {
endAtPickerVisible.value = false;
}
// ---------- 保存 ----------
async function saveEventItem(): Promise<void> {
if (!validateForm()) {
return;
}
saving.value = true;
isSaving.value = true;
try {
const payload = buildPayload();
const payload: EventItem = {
id: formModel.id || undefined,
name: formModel.name.trim(),
description: formModel.description,
color: formModel.color,
status: formModel.status,
requirePhoto: formModel.requirePhoto,
limitType: formModel.limitType,
limitValue: resolveLimitValue(),
beginAt: resolveTimestamp(formModel.beginAtText),
endAt: resolveTimestamp(formModel.endAtText),
roleCodeList: [...formModel.roleCodeList]
};
if (isCreateMode.value) {
await EventAPI.create(payload);
} else {
await EventAPI.update(payload);
}
showToast("登记事件已保存");
Toast.success("登记事件已保存");
await router.replace("/management/registration-events");
} catch {
showToast("登记事件保存失败", "error");
Toast.error("登记事件保存失败");
} finally {
saving.value = false;
isSaving.value = false;
}
}
// ---------- 删除 ----------
async function confirmRemoveEventItem(): Promise<void> {
const id = formModel.id || routeId.value;
if (!id || removing.value) {
if (!id || isRemoving.value) {
return;
}
const eventName = formModel.name.trim() || "未命名事件";
const dialog = DialogPlugin.confirm({
title: "删除登记事件",
@@ -476,36 +502,28 @@ async function confirmRemoveEventItem(): Promise<void> {
}
async function removeEventItem(id: string): Promise<void> {
if (removing.value) {
if (isRemoving.value) {
return;
}
removing.value = true;
isRemoving.value = true;
try {
await EventAPI.remove(id);
showToast("登记事件已删除");
Toast.success("登记事件已删除");
await router.replace("/management/registration-events");
} catch {
showToast("登记事件删除失败", "error");
Toast.error("登记事件删除失败");
} finally {
removing.value = false;
isRemoving.value = false;
}
}
function showToast(message: string, theme: "success" | "error" | "warning" = "success"): void {
Toast({
theme,
direction: "row",
message
});
}
// ---------- 页面数据 ----------
async function fillForm(): Promise<void> {
roleOptions.value = await ManagedUserAPI.roleList();
if (isCreateMode.value) {
return;
}
const current = await EventAPI.detail(routeId.value);
Object.assign(formModel, {
id: current.id || "",
@@ -516,8 +534,8 @@ async function fillForm(): Promise<void> {
requirePhoto: current.requirePhoto ?? false,
limitType: current.limitType || "NONE",
limitValueText: current.limitValue ? String(current.limitValue) : "",
beginAtText: current.beginAt ? Time.formatDateTime(new Date(current.beginAt)) : "",
endAtText: current.endAt ? Time.formatDateTime(new Date(current.endAt)) : "",
beginAtText: current.beginAt ? Time.toDateTime(current.beginAt) : "",
endAtText: current.endAt ? Time.toDateTime(current.endAt) : "",
roleCodeList: [...(current.roleCodeList || [])]
});
colorDraft.value = formModel.color;
@@ -527,7 +545,7 @@ onMounted(async () => {
try {
await fillForm();
} catch {
showToast("登记事件加载失败", "error");
Toast.error("登记事件加载失败");
}
});
</script>
@@ -696,4 +714,3 @@ onMounted(async () => {
}
}
</style>