修复工单内容格式与财务页面接口契约

This commit is contained in:
cieanyau committed 2026-11-07 08:12:28 +08:00
1 parent 90989f8f8a
commit d330ad01cc
8 files changed
+551 -149

No files matched your search

+62 -74
View File
@@ -4,7 +4,6 @@
*/
import { definePageMeta } from "#imports";
import "~/assets/styles/TicketAttachments.DtbJGLkE.css";
import "~/assets/styles/RichTextEditor.NkmgPT5m.css";
import "~/assets/styles/_id_.C-0--Lx-.css";
definePageMeta({
key: (e) => e.fullPath,
@@ -19,7 +18,6 @@ import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { computed } from "vue";
import { isRef } from "vue";
import { createElementBlock } from "vue";
import { Fragment } from "vue";
import { createElementVNode } from "vue";
@@ -38,7 +36,7 @@ import message from "openvue/message";
import { r as dict_r } from "~/lib/dict.js";
import skeleton from "openvue/skeleton";
import { n as sanitize_n } from "~/lib/sanitize.js";
import RichTextEditor from "~/components/RichTextEditor.vue";
import textarea from "openvue/textarea";
import { n as ticketAttachments_n } from "~/lib/ticketAttachments.js";
import TicketAttachments from "~/components/TicketAttachments.vue";
import TicketNotice from "~/components/TicketNotice.vue";
@@ -84,12 +82,10 @@ var le = {
k = {
class: `time`,
},
A = [`innerHTML`],
j = {
key: 0,
class: `message-quote`,
},
M = [`innerHTML`],
N = {
class: `reply-box`,
},
@@ -98,7 +94,6 @@ var le = {
class: `reply-quote`,
role: `status`,
},
F = [`innerHTML`],
I = {
class: `reply-attachments`,
},
@@ -173,6 +168,7 @@ var le = {
{
id: 0,
content: e.content,
content_format: e.content_format,
attachment: e.attachment || [],
create_time: e.create_time,
type: `Client`,
@@ -235,13 +231,26 @@ var le = {
function Ce(e) {
e.id > 0 && canOperate.value && (Y.value = e.id);
}
// 引用已删除时明确提示;否则优先用服务端引用快照,再从当前对话寻找原文。
// 正文和引用共用格式边界:纯文本不经过 HTML 解码,历史富文本继续净化。
function contentProps(item) {
return item?.content_format === `text`
? {
textContent: String(item.content ?? ``),
style: { whiteSpace: `pre-wrap`, overflowWrap: `anywhere` },
}
: { innerHTML: sanitize_n(item?.content) };
}
// 引用快照保留服务端格式;删除提示或缺失提示明确使用纯文本。
function we(e) {
return Number(e.quote_info?.is_deleted) === 1
? `该回复已删除`
: e.quote_info?.content ||
Z.value.find((t) => t.id === e.quote_reply_id)?.content ||
`原回复 #${e.quote_reply_id}(内容暂不可用)`;
if (Number(e.quote_info?.is_deleted) === 1)
return { content: `该回复已删除`, content_format: `text` };
if (typeof e.quote_info?.content === `string`) return e.quote_info;
return (
Z.value.find((t) => t.id === e.quote_reply_id) || {
content: `原回复 #${e.quote_reply_id}(内容暂不可用)`,
content_format: `text`,
}
);
}
// 引用发送者优先来自引用快照,缺失时回查当前对话。
function Te(e) {
@@ -273,7 +282,7 @@ var le = {
G.value = !0;
try {
await z.post(`/ticket/${B}/reply`, {
content: W.value.trim(),
content: W.value,
attachment: q.value.map((e) => e.save_name),
quote_reply_id: Y.value,
});
@@ -320,7 +329,7 @@ var le = {
}
return (
onMounted(X),
// 对话按时间展示并使用公共 HTML 清理与附件解析;引用删除有独立文案,只读工单不开放回复编辑。
// 对话遵循服务端内容格式;客户回复使用纯文本,只读工单不开放编辑。
(n, a) => {
let c = TicketNotice,
m = NuxtLink,
@@ -328,7 +337,7 @@ var le = {
v = message,
y = button,
S = TicketAttachments,
C = RichTextEditor;
C = textarea;
return (
openBlock(),
createElementBlock(`div`, le, [
@@ -513,13 +522,10 @@ var le = {
`div`,
{
class: `message-body`,
innerHTML: (`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(t.content),
...contentProps(t),
},
null,
8,
A,
16,
),
t.quote_reply_id
? (openBlock(),
@@ -535,16 +541,10 @@ var le = {
createElementVNode(
`div`,
{
innerHTML:
(`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(
we(t),
),
...contentProps(we(t)),
},
null,
8,
M,
16,
),
createVNode(
S,
@@ -608,15 +608,10 @@ var le = {
createElementVNode(
`div`,
{
innerHTML: (`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(
unref($).content,
),
...contentProps(unref($)),
},
null,
8,
F,
16,
),
]),
createVNode(y, {
@@ -635,50 +630,43 @@ var le = {
{
modelValue: unref(W),
"onUpdate:modelValue": (a[1] ||= (e) =>
isRef(W) ? (W.value = e) : null),
label: `回复内容`,
"min-height": 140,
(W.value = e)),
"aria-label": `回复内容`,
rows: 6,
autoResize: true,
fluid: true,
placeholder: `输入回复内容…`,
disabled: !unref(canOperate),
},
{
"toolbar-end": withCtx(() => [
createElementVNode(`label`, L, [
(a[3] ||= createElementVNode(
`i`,
{
class: `pi pi-paperclip`,
},
null,
-1,
)),
(a[4] ||= createTextVNode(
`添加附件`,
-1,
)),
createElementVNode(
`input`,
{
id: `reply-files`,
type: `file`,
multiple: ``,
disabled:
unref(G) ||
unref(J) ||
!unref(canOperate),
onChange: xe,
},
null,
40,
_e,
),
]),
]),
_: 1,
disabled: !unref(canOperate) || unref(G),
},
null,
8,
[`modelValue`, `disabled`],
),
createElementVNode(`label`, L, [
(a[3] ||= createElementVNode(
`i`,
{ class: `pi pi-paperclip` },
null,
-1,
)),
(a[4] ||= createTextVNode(`添加附件`, -1)),
createElementVNode(
`input`,
{
id: `reply-files`,
type: `file`,
multiple: ``,
disabled:
unref(G) ||
unref(J) ||
!unref(canOperate),
onChange: xe,
},
null,
40,
_e,
),
]),
createElementVNode(`div`, I, [
unref(J)
? (openBlock(),
+13 -56
View File
@@ -1,8 +1,7 @@
<script>
/**
* 创建工单:加载类型/部门及可选实例,上传附件后提交标题、富文本内容和关联实例;选项未就绪或附件仍上传时不提交。
* 创建工单:加载实际部门及可选实例,上传附件后提交标题、纯文本内容和关联实例;选项未就绪或附件仍上传时不提交。
*/
import "~/assets/styles/RichTextEditor.NkmgPT5m.css";
import "~/assets/styles/create.gX6gpD00.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -32,8 +31,7 @@ import message from "openvue/message";
import radiobutton from "openvue/radiobutton";
import inputtext from "openvue/inputtext";
import { n as product_name_n } from "~/lib/product-name.js";
import select from "openvue/select";
import RichTextEditor from "~/components/RichTextEditor.vue";
import textarea from "openvue/textarea";
import TicketNotice from "~/components/TicketNotice.vue";
import chip from "openvue/chip";
import multiselect from "openvue/multiselect";
@@ -56,10 +54,6 @@ var le = {
w = {
class: `field`,
},
T = {
key: 2,
class: `field`,
},
E = {
class: `field`,
},
@@ -110,12 +104,10 @@ var le = {
let b = useApi(),
z = useRouter(),
B = ref([]),
V = ref([]),
H = ref([]),
U = reactive({
title: ``,
ticket_type_id: null,
admin_role_id: null,
host_ids: [],
content: ``,
}),
@@ -172,17 +164,14 @@ var le = {
function ge(e) {
W.value || X.value.splice(e, 1);
}
// 类型/部门加载成功后再开放提交;实例可选列表分页读取,最多 100 页,实例查询失败不阻止无关联工单创建。
// 后端以 ticket_type_id 决定部门;实例查询失败不阻止无关联工单创建。
async function Q() {
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`),
]);
let e = await b.get(`/ticket/type`);
if (!isCurrent()) return;
((B.value = e.list ?? []), (V.value = t.list ?? []));
B.value = e.list ?? [];
try {
let e = [],
t = 1,
@@ -203,9 +192,7 @@ var le = {
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));
((U.ticket_type_id = B.value[0]?.id ?? null), (q.value = !0));
} catch (e) {
if (!isCurrent()) return;
G.value =
@@ -230,9 +217,8 @@ var le = {
try {
let e = await b.post(`/ticket`, {
title: U.title.trim(),
content: U.content.trim(),
content: U.content,
ticket_type_id: U.ticket_type_id,
admin_role_id: U.admin_role_id,
host_ids: U.host_ids,
attachment: X.value.map((e) => e.save_name),
});
@@ -248,16 +234,15 @@ var le = {
}
return (
onMounted(Q),
// 类型/部门选项就绪且附件上传完成后才可创建;富文本编辑器与本地附件草稿共同组成提交内容。
// 部门选项就绪且附件上传完成后才可创建;正文按客户纯文本契约原样提交。
(n, i) => {
let s = TicketNotice,
c = button,
m = radiobutton,
_ = inputtext,
y = select,
b = chip,
z = multiselect,
J = RichTextEditor,
J = textarea,
Y = message,
_e = NuxtLink;
return (
@@ -303,7 +288,7 @@ var le = {
(i[5] ||= createElementVNode(
`legend`,
null,
`工单类型`,
`工单部门`,
-1,
)),
(openBlock(!0),
@@ -377,36 +362,6 @@ var le = {
[`modelValue`],
),
]),
unref(V).length > 1
? (openBlock(),
createElementBlock(`div`, T, [
(i[7] ||= createElementVNode(
`label`,
{
for: `department`,
},
`服务部门`,
-1,
)),
createVNode(
y,
{
"input-id": `department`,
modelValue: unref(U).admin_role_id,
"onUpdate:modelValue": (i[2] ||= (e) =>
(unref(U).admin_role_id = e)),
options: unref(V),
optionLabel: `name`,
optionValue: `id`,
disabled: !unref(q),
fluid: ``,
},
null,
8,
[`modelValue`, `options`, `disabled`],
),
]))
: createCommentVNode(``, !0),
createElementVNode(`div`, E, [
(i[8] ||= createElementVNode(
`label`,
@@ -517,7 +472,9 @@ var le = {
modelValue: unref(U).content,
"onUpdate:modelValue": (i[4] ||= (e) =>
(unref(U).content = e)),
label: `工单内容`,
rows: 8,
autoResize: true,
fluid: true,
placeholder: `请描述问题、复现步骤和报错信息`,
disabled: unref(W),
},
-1
View File
@@ -4,7 +4,6 @@
*/
import { definePageMeta } from "#imports";
import "~/assets/styles/TicketAttachments.DtbJGLkE.css";
import "~/assets/styles/RichTextEditor.NkmgPT5m.css";
import "~/assets/styles/_id_.C-0--Lx-.css";
definePageMeta({
key: (route) => route.fullPath,