update registration event editor

This commit is contained in:
Timi
2026-08-02 00:20:13 +08:00
parent 11c48bf00c
commit 35dff786b1
10 changed files with 376 additions and 215 deletions
+188 -22
View File
@@ -8,10 +8,17 @@
<t-textarea v-model.trim="formModel.description" class="textarea" placeholder="事件说明" autosize borderless />
</t-form-item>
<t-form-item label="颜色">
<label class="color-picker">
<input v-model="formModel.color" type="color">
<span v-text="formModel.color" />
</label>
<t-button
size="extra-small"
variant="outline"
class="color-trigger"
@click="openColorPicker"
>
<div class="content">
<span class="swatch" :style="{ background: formModel.color }" />
<span v-text="formModel.color" />
</div>
</t-button>
</t-form-item>
<t-form-item label="状态">
<t-radio-group v-model="formModel.status" class="inline-options" variant="default-filled">
@@ -19,10 +26,10 @@
<t-radio :block="false" borderless value="INACTIVE">无效</t-radio>
</t-radio-group>
</t-form-item>
<t-form-item label="启用">
<t-form-item label="启用" content-align="right">
<t-switch v-model="formModel.enabled" />
</t-form-item>
<t-form-item label="拍照">
<t-form-item label="需要拍照" content-align="right">
<t-switch v-model="formModel.requirePhoto" />
</t-form-item>
<t-form-item label="排序">
@@ -62,12 +69,50 @@
</t-checkbox>
</t-checkbox-group>
</section>
<t-button v-can="savePermissionCode" class="save" theme="primary" @click="saveRegistrationEvent">保存</t-button>
<div class="bottom-actions">
<t-button
v-if="!isCreateMode"
v-can="deletePermissionCode"
class="delete bg-white"
theme="danger"
variant="outline"
:loading="removing"
:disabled="saving || removing"
@click="confirmRemoveRegistrationEvent"
>
删除事件
</t-button>
<t-button
v-can="savePermissionCode"
class="save"
theme="primary"
:loading="saving"
:disabled="removing"
@click="saveRegistrationEvent"
>
保存
</t-button>
</div>
</div>
<t-popup v-model="colorPickerVisible" placement="bottom">
<div class="color-popup">
<div class="header">
<t-button variant="text" size="small" @click="cancelColorPicker">取消</t-button>
<span class="title" v-text="'颜色选择'" />
<t-button theme="primary" variant="text" size="small" @click="confirmColorPicker">确定</t-button>
</div>
<t-color-picker
v-model="colorDraft"
format="HEX8"
type="multiple"
:swatch-colors="colorSwatchList"
/>
</div>
</t-popup>
</template>
<script setup lang="ts">
import { Toast } from "tdesign-mobile-vue";
import { DialogPlugin, Toast } from "tdesign-mobile-vue";
import type { Role } from "timi-web";
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
import GaoUserAPI from "@/api/GaoUserAPI";
@@ -88,11 +133,30 @@ const router = useRouter();
const routeId = computed(() => String(route.params.id || ""));
const isCreateMode = computed(() => !routeId.value);
const savePermissionCode = computed(() => isCreateMode.value ? gaoPermissionCodes.registrationEvent.create : gaoPermissionCodes.registrationEvent.update);
const deletePermissionCode = gaoPermissionCodes.registrationEvent.delete;
const pageAccess = computed(() => ({
value: [gaoPermissionCodes.registrationEvent.read, gaoPermissionCodes.user.read, savePermissionCode.value],
value: isCreateMode.value
? [gaoPermissionCodes.registrationEvent.read, gaoPermissionCodes.user.read, savePermissionCode.value]
: [gaoPermissionCodes.registrationEvent.read, gaoPermissionCodes.user.read, { value: [savePermissionCode.value, deletePermissionCode], mode: "or" as const }],
mode: "and" as const
}));
const roleOptions = ref<Role[]>([]);
const colorPickerVisible = ref(false);
const colorDraft = ref("#2f88ff");
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" },
@@ -116,6 +180,21 @@ const formModel = reactive({
});
const limitValuePlaceholder = computed(() => formModel.limitType === "INTERVAL_HOUR" ? "请输入间隔小时数" : "请输入累计上限次数");
function openColorPicker(): void {
colorDraft.value = formModel.color;
colorPickerVisible.value = true;
}
function cancelColorPicker(): void {
colorPickerVisible.value = false;
colorDraft.value = formModel.color;
}
function confirmColorPicker(): void {
formModel.color = colorDraft.value;
colorPickerVisible.value = false;
}
function isValueLimitType(value: RegistrationLimitType): boolean {
return value === "INTERVAL_HOUR" || value === "TOTAL_LIMIT";
}
@@ -162,6 +241,7 @@ async function saveRegistrationEvent(): Promise<void> {
return;
}
saving.value = true;
try {
const payload = buildPayload();
if (isCreateMode.value) {
@@ -173,6 +253,47 @@ async function saveRegistrationEvent(): Promise<void> {
await router.replace("/management/registration-events");
} catch {
showToast("登记事件保存失败", "error");
} finally {
saving.value = false;
}
}
async function confirmRemoveRegistrationEvent(): Promise<void> {
const id = formModel.id || routeId.value;
if (!id || removing.value) {
return;
}
const eventName = formModel.name.trim() || "未命名事件";
const dialog = DialogPlugin.confirm({
title: "删除登记事件",
content: `确定删除登记事件 ${eventName} 吗?删除后无法恢复。`,
cancelBtn: "取消",
confirmBtn: "删除",
onCancel() {
dialog.destroy();
},
async onConfirm() {
dialog.destroy();
await removeRegistrationEvent(id);
}
});
}
async function removeRegistrationEvent(id: string): Promise<void> {
if (removing.value) {
return;
}
removing.value = true;
try {
await RegistrationEventAPI.remove(id);
showToast("登记事件已删除");
await router.replace("/management/registration-events");
} catch {
showToast("登记事件删除失败", "error");
} finally {
removing.value = false;
}
}
@@ -204,6 +325,7 @@ async function fillForm(): Promise<void> {
sortText: String(current.sort ?? 0),
roleCodeList: [...(current.roleCodeList || [])]
});
colorDraft.value = formModel.color;
}
onMounted(async () => {
@@ -227,22 +349,30 @@ onMounted(async () => {
width: 100%;
}
.color-picker {
.color-trigger {
--td-button-extra-small-padding-horizontal: 2px;
gap: .75rem;
display: flex;
align-items: center;
padding-right: .875rem;
justify-content: flex-start;
input {
padding: 0;
width: 2rem;
height: 2rem;
border: 0;
background: transparent;
}
.content {
gap: .5rem;
display: flex;
span {
color: #52607a;
font-size: .875rem;
.swatch {
width: 1.5rem;
height: 1.5rem;
display: block;
border-radius: .25rem;
}
span {
color: #52607a;
font-size: .875rem;
}
}
}
@@ -289,21 +419,57 @@ onMounted(async () => {
}
}
.save {
.bottom-actions {
gap: .75rem;
left: 1rem;
right: 1rem;
display: flex;
bottom: calc(.75rem + env(safe-area-inset-bottom, 0));
position: fixed;
.delete {
flex: 1;
}
.save {
flex: 2;
}
}
}
.color-popup {
padding: .75rem 0 1.25rem;
background: #fff;
border-radius: 1rem 1rem 0 0;
.header {
display: flex;
padding: 0 1rem;
align-items: center;
justify-content: space-between;
span {
font-size: 1rem;
font-weight: 700;
}
}
}
:global(html.theme-dark) {
.registration-event-edit {
.color-picker span,
.color-trigger span,
.roles .title .hint {
color: #94a3b8;
}
.color-trigger .swatch {
box-shadow: inset 0 0 0 1px rgba(148, 163, 184, .24);
}
.color-popup {
background: #0f172a;
}
.roles .group .item {
color: #e2e8f0;
background: rgba(15, 23, 42, .72);