修复:将页面异步副作用绑定到当前归属
This commit is contained in:
1 parent
b3a66d97c8
commit
eca3aa8fe5
6 files changed
+623
-90
No files matched your search
@@ -9,6 +9,7 @@ import {
|
||||
reactive,
|
||||
ref,
|
||||
useId,
|
||||
watch,
|
||||
} from "vue";
|
||||
import Button from "openvue/button";
|
||||
import Column from "openvue/column";
|
||||
@@ -19,11 +20,12 @@ import Message from "openvue/message";
|
||||
import Paginator from "openvue/paginator";
|
||||
import Textarea from "openvue/textarea";
|
||||
import ListEmpty from "~/components/ListEmpty.vue";
|
||||
import { useApi } from "~/lib/auth.js";
|
||||
import { useApi, useAuth } from "~/lib/auth.js";
|
||||
import { n as loadCompleteList } from "~/lib/complete-list.js";
|
||||
import "~/assets/styles/ssh-keys.WWTOf2zG.css";
|
||||
|
||||
const api = useApi();
|
||||
const session = useAuth().prepareLogin();
|
||||
const fieldId = useId();
|
||||
const draftSearch = ref("");
|
||||
const appliedSearch = ref("");
|
||||
@@ -43,6 +45,16 @@ const copiedKeyId = ref(null);
|
||||
let copyResetTimer;
|
||||
let requestGeneration = 0;
|
||||
let disposed = false;
|
||||
let editorGeneration = 0;
|
||||
const isCurrent = () => !disposed && session.isCurrent();
|
||||
watch(
|
||||
editorVisible,
|
||||
() => {
|
||||
editorGeneration++;
|
||||
saving.value = false;
|
||||
},
|
||||
{ flush: "sync" },
|
||||
);
|
||||
|
||||
const firstRecord = computed(() => (currentPage.value - 1) * pageSize.value);
|
||||
const visibleRows = computed(() =>
|
||||
@@ -53,7 +65,7 @@ const visibleRows = computed(() =>
|
||||
|
||||
// 仅允许当前加载代次且组件尚未卸载时修改列表反馈。
|
||||
function isCurrentLoad(generation) {
|
||||
return !disposed && generation === requestGeneration;
|
||||
return isCurrent() && generation === requestGeneration;
|
||||
}
|
||||
|
||||
// 删除或搜索后夹紧页码;返回是否变化,供服务端分页决定是否重读修正后的页。
|
||||
@@ -67,7 +79,7 @@ function clampPage() {
|
||||
|
||||
// 非搜索模式请求当前服务端页,删除末页最后一条时先修正页码再重读。
|
||||
async function loadKeys() {
|
||||
if (disposed) return;
|
||||
if (!isCurrent()) return;
|
||||
if (appliedSearch.value) return loadSearchResults();
|
||||
const generation = ++requestGeneration;
|
||||
searchLoading.value = false;
|
||||
@@ -174,7 +186,7 @@ function openEditDialog(key) {
|
||||
|
||||
// 校验名称非空、字符数上限和公钥非空;按编辑对象选择创建/更新,成功后刷新当前模式列表。
|
||||
async function saveKey() {
|
||||
if (saving.value || disposed) return;
|
||||
if (saving.value || !isCurrent()) return;
|
||||
actionError.value = "";
|
||||
if (!keyForm.name.trim()) {
|
||||
actionError.value = "请输入密钥名称";
|
||||
@@ -189,6 +201,8 @@ async function saveKey() {
|
||||
return;
|
||||
}
|
||||
saving.value = true;
|
||||
const generation = editorGeneration;
|
||||
const ownsDraft = () => isCurrent() && generation === editorGeneration;
|
||||
try {
|
||||
if (editingKey.value) {
|
||||
await api.put("/ssh_key/" + editingKey.value.id, {
|
||||
@@ -198,25 +212,25 @@ async function saveKey() {
|
||||
} else {
|
||||
await api.post("/ssh_key", { ...keyForm });
|
||||
}
|
||||
if (disposed) return;
|
||||
editorVisible.value = false;
|
||||
if (!isCurrent()) return;
|
||||
if (ownsDraft()) editorVisible.value = false;
|
||||
await loadKeys();
|
||||
} catch (error) {
|
||||
if (!disposed)
|
||||
if (ownsDraft())
|
||||
actionError.value = error instanceof Error ? error.message : "保存失败";
|
||||
} finally {
|
||||
if (!disposed) saving.value = false;
|
||||
if (ownsDraft()) saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 删除后复用加载逻辑;末页恢复由 loadKeys/clampPage 统一处理。
|
||||
async function deleteKey(key) {
|
||||
if (disposed) return;
|
||||
if (!isCurrent()) return;
|
||||
try {
|
||||
await api.del("/ssh_key/" + key.id);
|
||||
await loadKeys();
|
||||
if (isCurrent()) await loadKeys();
|
||||
} catch (error) {
|
||||
if (!disposed)
|
||||
if (isCurrent())
|
||||
actionError.value = error instanceof Error ? error.message : "删除失败";
|
||||
}
|
||||
}
|
||||
@@ -225,7 +239,7 @@ async function deleteKey(key) {
|
||||
async function copyPublicKey(key) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(key.public_key);
|
||||
if (disposed) return;
|
||||
if (!isCurrent()) return;
|
||||
clearTimeout(copyResetTimer);
|
||||
copiedKeyId.value = key.id;
|
||||
copyResetTimer = setTimeout(() => (copiedKeyId.value = null), 2000);
|
||||
|
||||
Reference in new issue
Block a user