Files
NixMsg/web/src/views/SettingsView.vue
T

195 lines
6.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";
import type { FormInst, FormRules } from "naive-ui";
import {
NAlert,
NButton,
NDescriptions,
NDescriptionsItem,
NForm,
NFormItem,
NInput,
NSpace,
NSpin,
NTabPane,
NTabs,
NText,
} from "naive-ui";
import PageHeader from "@/components/PageHeader.vue";
import HelpTip from "@/components/HelpTip.vue";
import LoadFailed from "@/components/LoadFailed.vue";
import { changePassword, getSettings, type RuntimeSettings } from "@/api/admin";
import { ApiError } from "@/api/http";
import { message } from "@/utils/notify";
import { limitsLabel } from "@/utils/labels";
const loading = ref(true);
const loadError = ref("");
const settings = ref<RuntimeSettings | null>(null);
const pwd = reactive({ old_password: "", new_password: "", confirm: "" });
const pwdLoading = ref(false);
const pwdError = ref("");
const formRef = ref<FormInst | null>(null);
const rules: FormRules = {
new_password: [
{
validator(_, value: string) {
if (Array.from(value || "").length < 12) {
return new Error("新密码至少 12 个字符");
}
return true;
},
trigger: ["blur", "input"],
},
],
confirm: [
{
validator() {
if (pwd.new_password !== pwd.confirm) {
return new Error("两次输入的新密码不一致");
}
return true;
},
trigger: ["blur", "input"],
},
],
};
async function load() {
loading.value = true;
loadError.value = "";
try {
settings.value = await getSettings();
} catch (e) {
loadError.value = e instanceof Error ? e.message : "加载失败";
} finally {
loading.value = false;
}
}
onMounted(() => {
void load();
});
async function submitPassword() {
pwdError.value = "";
try {
await formRef.value?.validate();
} catch {
return;
}
pwdLoading.value = true;
try {
await changePassword(pwd.old_password, pwd.new_password);
message.success("管理员密码已修改");
pwd.old_password = "";
pwd.new_password = "";
pwd.confirm = "";
} catch (e) {
pwdError.value = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "修改失败";
} finally {
pwdLoading.value = false;
}
}
</script>
<template>
<div class="page">
<PageHeader title="系统" help="管理员密码在此修改;运行参数只读,修改请改配置文件后重启。" />
<div class="page-body">
<n-tabs type="line" animated>
<n-tab-pane name="password" tab="管理员密码">
<n-alert v-if="pwdError" type="error" style="margin-bottom: 12px; max-width: 480px" :title="pwdError" />
<n-form
ref="formRef"
:model="pwd"
:rules="rules"
label-placement="left"
label-width="100"
style="max-width: 480px"
>
<n-form-item label="原密码" path="old_password">
<n-input v-model:value="pwd.old_password" type="password" show-password-on="click" />
</n-form-item>
<n-form-item path="new_password">
<template #label>
新密码
<HelpTip>按字符数计至少 12 位。后端不检查 nst_ 前缀(那是端登录密码规则)。</HelpTip>
</template>
<n-input v-model:value="pwd.new_password" type="password" show-password-on="click" />
</n-form-item>
<n-form-item label="确认新密码" path="confirm">
<n-input v-model:value="pwd.confirm" type="password" show-password-on="click" />
</n-form-item>
<n-space>
<n-button type="primary" :loading="pwdLoading" @click="submitPassword">修改密码</n-button>
</n-space>
</n-form>
</n-tab-pane>
<n-tab-pane name="settings" tab="运行参数(只读)">
<n-spin :show="loading">
<LoadFailed v-if="loadError && !settings" :description="loadError" @retry="load" />
<template v-else-if="settings">
<n-descriptions label-placement="left" :column="2" bordered size="small" style="margin-bottom: 16px">
<n-descriptions-item>
<template #label>端监听地址 <HelpTip>listen</HelpTip></template>
{{ settings.listen }}
</n-descriptions-item>
<n-descriptions-item>
<template #label>后台监听地址 <HelpTip>admin_listen</HelpTip></template>
{{ settings.admin_listen || "(空,与端共用)" }}
</n-descriptions-item>
<n-descriptions-item>
<template #label>会话闲置天数 <HelpTip>session_idle_days</HelpTip></template>
{{ settings.session_idle_days }}
</n-descriptions-item>
<n-descriptions-item>
<template #label>记录保留天数 <HelpTip>record_retention_days</HelpTip></template>
{{ settings.record_retention_days }}
</n-descriptions-item>
<n-descriptions-item>
<template #label>防重小时数 <HelpTip>idempotency_hours</HelpTip></template>
{{ settings.idempotency_hours }}
</n-descriptions-item>
<n-descriptions-item>
<template #label>回执保留天数 <HelpTip>receipt_retention_days</HelpTip></template>
{{ settings.receipt_retention_days }}
</n-descriptions-item>
<n-descriptions-item>
<template #label>SQLite 同步模式 <HelpTip>sqlite_synchronous</HelpTip></template>
{{ settings.sqlite_synchronous }}
</n-descriptions-item>
</n-descriptions>
<n-text strong style="display: block; margin-bottom: 8px">限额</n-text>
<n-descriptions label-placement="left" :column="2" bordered size="small">
<n-descriptions-item v-for="(v, k) in settings.limits" :key="k">
<template #label>
{{ limitsLabel[String(k)] || k }}
<HelpTip>{{ k }}</HelpTip>
</template>
{{ v }}
</n-descriptions-item>
</n-descriptions>
</template>
</n-spin>
</n-tab-pane>
</n-tabs>
</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>