修复:将页面异步副作用绑定到当前归属

This commit is contained in:
cieanyau committed 2026-11-07 04:00:10 +08:00
1 parent b3a66d97c8
commit eca3aa8fe5
6 files changed
+623 -90

No files matched your search

+45 -24
View File
@@ -12,6 +12,7 @@ definePageMeta({
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { onScopeDispose } from "vue";
import { normalizeClass } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
@@ -30,7 +31,7 @@ import { openBlock } from "vue";
import { normalizeStyle } from "vue";
import { useRoute } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import { useApi, useAuth } from "~/lib/auth.js";
import button from "openvue/button";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
import message from "openvue/message";
@@ -124,6 +125,12 @@ var le = {
defineComponent({
__name: `[id]`,
setup(m) {
const session = useAuth().prepareLogin();
let disposed = false;
const isCurrent = () => !disposed && session.isCurrent();
onScopeDispose(() => {
disposed = true;
});
let y = useRoute(),
z = useApi(),
B = String(y.params.id ?? y.query.id ?? ``);
@@ -143,17 +150,20 @@ var le = {
const canOperate = computed(() => V.value?.can_operate === 1);
// 读取工单并确认实际存在;后续回复/催单/关闭完成也复用此入口同步服务端状态。
async function X() {
if (!isCurrent()) return;
try {
let e = await z.get(`/ticket/${B}`);
if (!isCurrent()) return;
if (!e.ticket || !e.ticket.id) {
U.value = `工单不存在或已删除`;
return;
}
V.value = e.ticket;
} catch (e) {
U.value = e instanceof Error ? e.message : `加载失败`;
if (isCurrent())
U.value = e instanceof Error ? e.message : `加载失败`;
} finally {
H.value = !1;
if (isCurrent()) H.value = !1;
}
}
let Z = computed(() => {
@@ -192,15 +202,16 @@ var le = {
);
// 工单允许操作时逐个上传并检查 save_name,成功部分保留在当前附件草稿。
async function xe(e) {
if (J.value || G.value || !canOperate.value) return;
if (!isCurrent() || J.value || G.value || !canOperate.value) return;
let t = e.target,
n = Array.from(t.files ?? []);
if (n.length) {
((J.value = !0), (U.value = ``));
try {
for (let e of n) {
if (!isCurrent() || !canOperate.value) return;
let t = await z.upload(e);
if (!canOperate.value) return;
if (!isCurrent() || !canOperate.value) return;
if (!t?.save_name)
throw Error(`附件「${e.name}」上传失败,请重新选择文件`);
q.value.push({
@@ -209,9 +220,10 @@ var le = {
});
}
} catch (e) {
U.value = e instanceof Error ? e.message : `附件上传失败`;
if (isCurrent())
U.value = e instanceof Error ? e.message : `附件上传失败`;
} finally {
((J.value = !1), (t.value = ``));
if (isCurrent()) ((J.value = !1), (t.value = ``));
}
}
}
@@ -254,47 +266,56 @@ var le = {
}
// 回复要求内容非空、附件已上传且服务端允许操作;成功后清空草稿与引用并重读工单。
async function De() {
if (!(G.value || !W.value.trim() || J.value || !canOperate.value)) {
if (
isCurrent() &&
!(G.value || !W.value.trim() || J.value || !canOperate.value)
) {
G.value = !0;
try {
(await z.post(`/ticket/${B}/reply`, {
await z.post(`/ticket/${B}/reply`, {
content: W.value.trim(),
attachment: q.value.map((e) => e.save_name),
quote_reply_id: Y.value,
}),
(W.value = ``),
(q.value = []),
(Y.value = 0),
await X());
});
if (!isCurrent()) return;
W.value = ``;
q.value = [];
Y.value = 0;
await X();
} catch (e) {
U.value = e instanceof Error ? e.message : `回复失败`;
if (isCurrent())
U.value = e instanceof Error ? e.message : `回复失败`;
} finally {
G.value = !1;
if (isCurrent()) G.value = !1;
}
}
}
// 催单后重新读取工单,结果与可继续操作状态以服务端为准。
async function Oe() {
if (!canOperate.value) return;
if (!isCurrent() || !canOperate.value) return;
K.value = !0;
try {
(await z.put(`/ticket/${B}/urge`), await X());
await z.put(`/ticket/${B}/urge`);
if (isCurrent()) await X();
} catch (e) {
U.value = e instanceof Error ? e.message : `催单失败`;
if (isCurrent())
U.value = e instanceof Error ? e.message : `催单失败`;
} finally {
K.value = !1;
if (isCurrent()) K.value = !1;
}
}
// 关闭工单完成后重读详情,由服务端权限控制回复与上传入口。
async function ke() {
if (!canOperate.value) return;
if (!isCurrent() || !canOperate.value) return;
K.value = !0;
try {
(await z.put(`/ticket/${B}/close`), await X());
await z.put(`/ticket/${B}/close`);
if (isCurrent()) await X();
} catch (e) {
U.value = e instanceof Error ? e.message : `关闭失败`;
if (isCurrent())
U.value = e instanceof Error ? e.message : `关闭失败`;
} finally {
K.value = !1;
if (isCurrent()) K.value = !1;
}
}
return (
+26 -9
View File
@@ -7,6 +7,7 @@ import "~/assets/styles/create.gX6gpD00.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { onScopeDispose } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
@@ -24,7 +25,7 @@ import { openBlock } from "vue";
import { withModifiers } from "vue";
import { useRouter } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import { useApi, useAuth } from "~/lib/auth.js";
import button from "openvue/button";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
import message from "openvue/message";
@@ -100,6 +101,12 @@ var le = {
defineComponent({
__name: `create`,
setup(m) {
const session = useAuth().prepareLogin();
let disposed = false;
const isCurrent = () => !disposed && session.isCurrent();
onScopeDispose(() => {
disposed = true;
});
let b = useApi(),
z = useRouter(),
B = ref([]),
@@ -136,14 +143,16 @@ var le = {
Z = ref(!1);
// 逐个上传并要求 save_name,上传部分成功的附件仍保留,失败显示具体文件问题。
async function he(e) {
if (Z.value || W.value) return;
if (!isCurrent() || Z.value || W.value) return;
let t = e.target,
n = Array.from(t.files ?? []);
if (n.length) {
((Z.value = !0), (G.value = ``));
try {
for (let e of n) {
if (!isCurrent()) return;
let t = await b.upload(e);
if (!isCurrent()) return;
if (!t?.save_name)
throw Error(`附件「${e.name}」上传失败,请重新选择文件`);
X.value.push({
@@ -152,9 +161,10 @@ var le = {
});
}
} catch (e) {
G.value = e instanceof Error ? e.message : `上传失败`;
if (isCurrent())
G.value = e instanceof Error ? e.message : `上传失败`;
} finally {
((Z.value = !1), (t.value = ``));
if (isCurrent()) ((Z.value = !1), (t.value = ``));
}
}
}
@@ -164,45 +174,50 @@ var le = {
}
// 类型/部门加载成功后再开放提交;实例可选列表分页读取,最多 100 页,实例查询失败不阻止无关联工单创建。
async function Q() {
if (!K.value) {
if (isCurrent() && !K.value) {
((K.value = !0), (q.value = !1), (G.value = ``));
try {
let [e, t] = await Promise.all([
b.get(`/ticket/type`),
b.get(`/ticket/department`),
]);
if (!isCurrent()) return;
((B.value = e.list ?? []), (V.value = t.list ?? []));
try {
let e = [],
t = 1,
n = 0;
do {
if (!isCurrent()) return;
let r = await b.get(`/host`, {
page: t,
limit: 200,
});
if (!isCurrent()) return;
(e.push(...(r.list ?? [])),
(n = Number(r.count ?? e.length)),
(t += 1));
} while (e.length < n && t <= 100);
H.value = e;
} catch {
if (!isCurrent()) return;
H.value = [];
}
((U.ticket_type_id = B.value[0]?.id ?? null),
(U.admin_role_id = V.value[0]?.id ?? null),
(q.value = !0));
} catch (e) {
if (!isCurrent()) return;
G.value =
e instanceof Error ? e.message : `加载工单选项失败,请重试`;
} finally {
K.value = !1;
if (isCurrent()) K.value = !1;
}
}
}
// 标题和内容非空且选项就绪后创建工单,只提交附件保存标识,成功进入返回 ID 的详情。
async function $() {
if (!(W.value || Z.value || K.value || !q.value)) {
if (isCurrent() && !(W.value || Z.value || K.value || !q.value)) {
if (((G.value = ``), !U.title.trim())) {
G.value = `请输入工单标题`;
return;
@@ -221,11 +236,13 @@ var le = {
host_ids: U.host_ids,
attachment: X.value.map((e) => e.save_name),
});
if (!isCurrent()) return;
z.push(`/tickets/${e.id}`);
} catch (e) {
G.value = e instanceof Error ? e.message : `提交失败`;
if (isCurrent())
G.value = e instanceof Error ? e.message : `提交失败`;
} finally {
W.value = !1;
if (isCurrent()) W.value = !1;
}
}
}