127 lines
3.7 KiB
Vue
127 lines
3.7 KiB
Vue
<script setup lang="ts">
|
||
import { computed, onMounted, ref } from "vue";
|
||
import { NAlert, NButton, NForm, NFormItem, NInput, NSpace, NSpin, NSwitch, NText } from "naive-ui";
|
||
import PageHeader from "@/components/PageHeader.vue";
|
||
import HelpTip from "@/components/HelpTip.vue";
|
||
import { getRegistration, updateRegistration, type RegistrationSettings } from "@/api/admin";
|
||
import { formatLocalMs } from "@/utils/time";
|
||
import { message } from "@/utils/notify";
|
||
|
||
const loading = ref(true);
|
||
const saving = ref(false);
|
||
const data = ref<RegistrationSettings | null>(null);
|
||
const codeDraft = ref("");
|
||
|
||
const hasSavedCode = computed(() => (data.value?.code ?? "").length >= 8);
|
||
const switchDisabled = computed(() => !hasSavedCode.value && !data.value?.enabled);
|
||
|
||
async function load() {
|
||
loading.value = true;
|
||
try {
|
||
data.value = await getRegistration();
|
||
codeDraft.value = data.value.code;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
void load();
|
||
});
|
||
|
||
async function onToggle(enabled: boolean) {
|
||
if (!data.value) return;
|
||
if (enabled && !hasSavedCode.value) {
|
||
message.error("请先保存或生成安全码");
|
||
return;
|
||
}
|
||
saving.value = true;
|
||
try {
|
||
data.value = await updateRegistration({ enabled });
|
||
message.success(enabled ? "已开启自助注册" : "已关闭自助注册");
|
||
} finally {
|
||
saving.value = false;
|
||
}
|
||
}
|
||
|
||
async function saveCode() {
|
||
saving.value = true;
|
||
try {
|
||
data.value = await updateRegistration({ code: codeDraft.value });
|
||
codeDraft.value = data.value.code;
|
||
message.success("安全码已更新");
|
||
} finally {
|
||
saving.value = false;
|
||
}
|
||
}
|
||
|
||
async function generateCode() {
|
||
saving.value = true;
|
||
try {
|
||
data.value = await updateRegistration({ generate: true });
|
||
codeDraft.value = data.value.code;
|
||
message.success("已生成新安全码");
|
||
} finally {
|
||
saving.value = false;
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="page">
|
||
<PageHeader title="注册设置" help="关闭时任何自助注册失败。安全码 8–64 字符,可手填或生成,后台可查看明文。" />
|
||
<div class="page-body">
|
||
<n-spin :show="loading">
|
||
<n-form v-if="data" label-placement="left" label-width="100" :show-feedback="false" style="max-width: 560px">
|
||
<n-alert
|
||
v-if="!hasSavedCode"
|
||
type="warning"
|
||
title="请先保存或生成安全码"
|
||
data-testid="reg-code-missing"
|
||
style="margin-bottom: 12px"
|
||
/>
|
||
<n-form-item>
|
||
<template #label>
|
||
开放注册
|
||
<HelpTip>开启后需同时设置安全码,客户端注册时携带。</HelpTip>
|
||
</template>
|
||
<n-switch
|
||
:value="data.enabled"
|
||
:loading="saving"
|
||
:disabled="switchDisabled"
|
||
data-testid="reg-enabled"
|
||
@update:value="onToggle"
|
||
/>
|
||
</n-form-item>
|
||
<n-form-item label="安全码">
|
||
<n-input v-model:value="codeDraft" placeholder="8–64 字符" data-testid="reg-code" />
|
||
</n-form-item>
|
||
<n-form-item label="更新时间">
|
||
<n-text>{{ formatLocalMs(data.updated_at_ms) }}</n-text>
|
||
</n-form-item>
|
||
<n-space>
|
||
<n-button type="primary" :loading="saving" data-testid="reg-save-code" @click="saveCode">
|
||
保存安全码
|
||
</n-button>
|
||
<n-button :loading="saving" data-testid="reg-generate-code" @click="generateCode">生成安全码</n-button>
|
||
</n-space>
|
||
</n-form>
|
||
</n-spin>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
min-height: 0;
|
||
}
|
||
.page-body {
|
||
flex: 1;
|
||
overflow: auto;
|
||
padding: 16px;
|
||
}
|
||
</style>
|