diff --git a/frontend/src/views/Dashboard/Dashboard.test.js b/frontend/src/views/Dashboard/Dashboard.test.js index 86918f4..fb08034 100644 --- a/frontend/src/views/Dashboard/Dashboard.test.js +++ b/frontend/src/views/Dashboard/Dashboard.test.js @@ -433,3 +433,24 @@ test("serverModal.compilesAndPreservesDeploymentFields", async () => { assert.ok(html.includes("nginx\n-s\nreload")); } }); + +test("changePassword.loadsScopedBaseStylesAndCompiles", async () => { + const { readFile } = await import("node:fs/promises"); + const { parse, compileScript, compileTemplate } = await import("@vue/compiler-sfc"); + const source = await readFile(new URL("../components/ChangePassword/ChangePassword.vue", import.meta.url), "utf8"); + const { descriptor, errors } = parse(source); + assert.deepEqual(errors, []); + assert.ok(compileScript(descriptor, { id: "change-password" })); + const compiled = compileTemplate({ + source: descriptor.template.content, + filename: "ChangePassword.vue", + id: "change-password", + }); + assert.deepEqual(compiled.errors, []); + assert.ok(descriptor.styles.some(style => style.scoped && style.src === "../../Dashboard/Dashboard.css")); + assert.ok(descriptor.styles.some(style => style.scoped && style.src === "./ChangePassword.css")); + assert.match(source, //); + const base = await readFile(new URL("./Dashboard.css", import.meta.url), "utf8"); + assert.match(base, /\.modal-backdrop\s*\{[^}]*position:\s*fixed;/); + assert.match(base, /\.field\s*\{/); +}); diff --git a/frontend/src/views/components/ChangePassword/ChangePassword.css b/frontend/src/views/components/ChangePassword/ChangePassword.css index 55aa454..be456da 100644 --- a/frontend/src/views/components/ChangePassword/ChangePassword.css +++ b/frontend/src/views/components/ChangePassword/ChangePassword.css @@ -4,7 +4,7 @@ .change-password-form { display: grid; - gap: 16px; + gap: 0; } .change-password-form small { @@ -39,3 +39,13 @@ width: min(100%, calc(100vw - 28px)); } } + +.change-password-modal .field > span { + display: flex; + align-items: center; + gap: 6px; +} + +.change-password-modal .modal-actions { + padding-top: 12px; +} diff --git a/frontend/src/views/components/ChangePassword/ChangePassword.js b/frontend/src/views/components/ChangePassword/ChangePassword.js new file mode 100644 index 0000000..a43c910 --- /dev/null +++ b/frontend/src/views/components/ChangePassword/ChangePassword.js @@ -0,0 +1,85 @@ +import { onBeforeUnmount, ref } from "vue"; + +export function useChangePassword() { + const visible = ref(false); + const submitting = ref(false); + const success = ref(false); + const error = ref(""); + const notice = ref(""); + const form = ref({ + currentPassword: "", + newPassword: "", + confirmPassword: "", + }); + let redirectTimer = 0; + + function open() { + visible.value = true; + success.value = false; + error.value = ""; + notice.value = ""; + } + + function close() { + if (submitting.value || success.value) { + return; + } + visible.value = false; + error.value = ""; + notice.value = ""; + form.value = { + currentPassword: "", + newPassword: "", + confirmPassword: "", + }; + } + + async function submit() { + if (submitting.value) { + return; + } + error.value = ""; + notice.value = ""; + if (form.value.newPassword !== form.value.confirmPassword) { + error.value = "两次输入的新密码不一致。"; + return; + } + submitting.value = true; + try { + const response = await fetch("/api/v1/auth/password", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify({ + current_password: form.value.currentPassword, + new_password: form.value.newPassword, + confirm_password: form.value.confirmPassword, + }), + }); + const result = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(result.error || "修改密码失败,请稍后重试。"); + } + success.value = true; + notice.value = result.message || "密码修改成功,请重新登录。"; + redirectTimer = window.setTimeout(() => { + window.location.href = "/login"; + }, 1600); + } catch (submitError) { + error.value = submitError.message || "修改密码失败,请稍后重试。"; + } finally { + submitting.value = false; + } + } + + onBeforeUnmount(() => { + if (redirectTimer) { + window.clearTimeout(redirectTimer); + } + }); + + return { visible, submitting, success, error, notice, form, open, close, submit }; +} diff --git a/frontend/src/views/components/ChangePassword/ChangePassword.vue b/frontend/src/views/components/ChangePassword/ChangePassword.vue index 108ccc1..e1fafdf 100644 --- a/frontend/src/views/components/ChangePassword/ChangePassword.vue +++ b/frontend/src/views/components/ChangePassword/ChangePassword.vue @@ -1,85 +1,18 @@ - + +