Files

687 lines
27 KiB
Vue

<script>
/**
* 创建工单:加载类型/部门及可选实例,上传附件后提交标题、富文本内容和关联实例;选项未就绪或附件仍上传时不提交。
*/
import "~/assets/styles/RichTextEditor.NkmgPT5m.css";
import "~/assets/styles/create.gX6gpD00.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { computed } from "vue";
import { reactive } from "vue";
import { createElementBlock } from "vue";
import { Fragment } from "vue";
import { createElementVNode } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { withModifiers } from "vue";
import { useRouter } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import button from "primevue/button";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
import message from "primevue/message";
import radiobutton from "primevue/radiobutton";
import inputtext from "primevue/inputtext";
import { n as product_name_n } from "~/lib/product-name.js";
import select from "primevue/select";
import RichTextEditor from "~/components/RichTextEditor.vue";
import TicketNotice from "~/components/TicketNotice.vue";
import chip from "primevue/chip";
import multiselect from "primevue/multiselect";
var le = {
class: `ticket-create`,
},
ue = {
class: `ticket-section`,
},
de = {
class: `section-fields`,
},
fe = {
key: 0,
role: `status`,
},
C = {
class: `type-options`,
},
w = {
class: `field`,
},
T = {
key: 2,
class: `field`,
},
E = {
class: `field`,
},
D = {
class: `host-opt`,
},
O = {
class: `host-opt-meta`,
},
k = {
class: `ticket-section`,
},
A = {
class: `section-fields`,
},
j = {
class: `field`,
},
M = {
class: `field`,
},
N = {
class: `upload-drop`,
for: `files`,
},
P = [`disabled`],
F = {
key: 0,
class: `files`,
},
I = {
class: `fname`,
},
L = [`aria-label`, `disabled`, `onClick`],
R = {
class: `actions`,
},
z = _plugin_vue_export_helper_t(
defineComponent({
__name: `create`,
setup(m) {
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: ``,
}),
W = ref(!1),
G = ref(``),
K = ref(!1),
q = ref(!1);
// 选择器优先显示主 IP,缺失时显示分配 IP。
function J(e) {
return e.dedicate_ip || e.assign_ip || ``;
}
// 实例候选文案组合 ID、名称与 IP,方便同名实例区分。
function pe(e) {
return [`#${e.id}`, e.name, J(e)].filter(Boolean).join(` / `);
}
let Y = computed(() => new Map(H.value.map((e) => [e.id, e])));
// 已选实例展示产品名与 ID,列表缺失时仍保留其编号。
function me(e) {
let t = Y.value.get(e);
return t
? `${product_name_n(t.product_name) || t.name} (#${t.id})`
: `#${e}`;
}
let X = ref([]),
Z = ref(!1);
// 逐个上传并要求 save_name,上传部分成功的附件仍保留,失败显示具体文件问题。
async function he(e) {
if (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) {
let t = await b.upload(e);
if (!t?.save_name)
throw Error(`附件「${e.name}」上传失败,请重新选择文件`);
X.value.push({
save_name: t.save_name,
name: e.name,
});
}
} catch (e) {
G.value = e instanceof Error ? e.message : `上传失败`;
} finally {
((Z.value = !1), (t.value = ``));
}
}
}
// 提交期间锁定附件列表,其余时间可移除本地待提交项。
function ge(e) {
W.value || X.value.splice(e, 1);
}
// 类型/部门加载成功后再开放提交;实例可选列表分页读取,最多 100 页,实例查询失败不阻止无关联工单创建。
async function Q() {
if (!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`),
]);
((B.value = e.list ?? []), (V.value = t.list ?? []));
try {
let e = [],
t = 1,
n = 0;
do {
let r = await b.get(`/host`, {
page: t,
limit: 200,
});
(e.push(...(r.list ?? [])),
(n = Number(r.count ?? e.length)),
(t += 1));
} while (e.length < n && t <= 100);
H.value = e;
} catch {
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) {
G.value =
e instanceof Error ? e.message : `加载工单选项失败,请重试`;
} finally {
K.value = !1;
}
}
}
// 标题和内容非空且选项就绪后创建工单,只提交附件保存标识,成功进入返回 ID 的详情。
async function $() {
if (!(W.value || Z.value || K.value || !q.value)) {
if (((G.value = ``), !U.title.trim())) {
G.value = `请输入工单标题`;
return;
}
if (!U.content.trim()) {
G.value = `请输入工单内容`;
return;
}
W.value = !0;
try {
let e = await b.post(`/ticket`, {
title: U.title.trim(),
content: U.content.trim(),
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),
});
z.push(`/tickets/${e.id}`);
} catch (e) {
G.value = e instanceof Error ? e.message : `提交失败`;
} finally {
W.value = !1;
}
}
}
return (
onMounted(Q),
// 类型/部门选项就绪且附件上传完成后才可创建;富文本编辑器与本地附件草稿共同组成提交内容。
(n, i) => {
let s = TicketNotice,
c = button,
m = radiobutton,
_ = inputtext,
y = select,
b = chip,
z = multiselect,
J = RichTextEditor,
Y = message,
_e = NuxtLink;
return (
openBlock(),
createElementBlock(`div`, le, [
createVNode(s),
(i[18] ||= createElementVNode(`h2`, null, `创建工单`, -1)),
createElementVNode(
`form`,
{
onSubmit: withModifiers($, [`prevent`]),
},
[
createElementVNode(`section`, ue, [
(i[9] ||= createElementVNode(
`aside`,
null,
[
createElementVNode(`h3`, null, `基本信息`),
createElementVNode(
`p`,
null,
`请提供一些基本信息,这有助于我们快速定位您的问题并将其转交给正确的团队处理。`,
),
],
-1,
)),
createElementVNode(`div`, de, [
unref(K)
? (openBlock(),
createElementBlock(`div`, fe, `正在加载工单选项…`))
: unref(q)
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(c, {
key: 1,
label: `重新加载工单选项`,
severity: `secondary`,
outlined: ``,
onClick: Q,
})),
createElementVNode(`fieldset`, C, [
(i[5] ||= createElementVNode(
`legend`,
null,
`工单类型`,
-1,
)),
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(B),
(t) => (
openBlock(),
createElementBlock(
`label`,
{
key: t.id,
},
[
createVNode(
m,
{
modelValue: unref(U).ticket_type_id,
"onUpdate:modelValue": (i[0] ||= (e) =>
(unref(U).ticket_type_id = e)),
"input-id": `ticket-type-${t.id}`,
value: t.id,
disabled: !unref(q),
},
null,
8,
[
`modelValue`,
`input-id`,
`value`,
`disabled`,
],
),
createElementVNode(
`span`,
null,
toDisplayString(t.name),
1,
),
],
)
),
),
128,
)),
]),
createElementVNode(`div`, w, [
(i[6] ||= createElementVNode(
`label`,
{
for: `title`,
},
`工单标题`,
-1,
)),
createVNode(
_,
{
id: `title`,
modelValue: unref(U).title,
"onUpdate:modelValue": (i[1] ||= (e) =>
(unref(U).title = e)),
placeholder: `输入工单标题`,
maxlength: `100`,
fluid: ``,
},
null,
8,
[`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`,
{
for: `ticket-hosts`,
},
`关联产品`,
-1,
)),
createVNode(
z,
{
"input-id": `ticket-hosts`,
modelValue: unref(U).host_ids,
"onUpdate:modelValue": (i[3] ||= (e) =>
(unref(U).host_ids = e)),
options: unref(H),
optionLabel: `name`,
optionValue: `id`,
filter: ``,
filterFields: [
`product_name`,
`name`,
`dedicate_ip`,
`assign_ip`,
`id`,
],
filterPlaceholder: `输入关键字`,
placeholder: `选择关联产品`,
display: `chip`,
fluid: ``,
},
{
option: withCtx(({ option: t }) => [
createElementVNode(`div`, D, [
createElementVNode(
`span`,
null,
toDisplayString(
unref(product_name_n)(t.product_name) ||
t.name,
),
1,
),
createElementVNode(
`span`,
O,
toDisplayString(pe(t)),
1,
),
]),
]),
chip: withCtx(
({ value: e, removeCallback: t }) => [
createVNode(
b,
{
label: me(e),
removable: ``,
onRemove: (n) => t(n, e),
},
null,
8,
[`label`, `onRemove`],
),
],
),
_: 1,
},
8,
[`modelValue`, `options`],
),
]),
]),
]),
createElementVNode(`section`, k, [
(i[17] ||= createElementVNode(
`aside`,
null,
[
createElementVNode(`h3`, null, `详细信息`),
createElementVNode(`p`, null, [
createTextVNode(
`请尽可能详细地描述您的问题,包括您已经尝试过的解决方案。`,
),
createElementVNode(`br`),
createTextVNode(
`如果您有相关的文件或图片,请上传它们,这将有助于我们更快地诊断问题并为您提供解决方案。`,
),
]),
],
-1,
)),
createElementVNode(`div`, A, [
createElementVNode(`div`, j, [
(i[10] ||= createElementVNode(
`label`,
{
for: `content`,
},
`工单内容`,
-1,
)),
createVNode(
J,
{
id: `content`,
modelValue: unref(U).content,
"onUpdate:modelValue": (i[4] ||= (e) =>
(unref(U).content = e)),
label: `工单内容`,
placeholder: `请描述问题、复现步骤和报错信息`,
disabled: unref(W),
},
null,
8,
[`modelValue`, `disabled`],
),
]),
createElementVNode(`div`, M, [
(i[15] ||= createElementVNode(
`label`,
{
for: `files`,
},
`附件`,
-1,
)),
createElementVNode(`label`, N, [
(i[11] ||= createElementVNode(
`i`,
{
class: `pi pi-upload`,
},
null,
-1,
)),
(i[12] ||= createElementVNode(
`span`,
null,
[
createElementVNode(`b`, null, `点击此处`),
createTextVNode(`上传相关文件`),
],
-1,
)),
createElementVNode(
`small`,
null,
toDisplayString(
unref(Z)
? `正在上传,请稍候…`
: `支持多选;上传完成后再提交工单`,
),
1,
),
createElementVNode(
`input`,
{
id: `files`,
type: `file`,
multiple: ``,
disabled: unref(Z) || unref(W),
onChange: he,
},
null,
40,
P,
),
]),
unref(X).length
? (openBlock(),
createElementBlock(`ul`, F, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(X),
(t, n) => (
openBlock(),
createElementBlock(
`li`,
{
key: t.save_name,
},
[
(i[14] ||= createElementVNode(
`i`,
{
class: `pi pi-paperclip`,
},
null,
-1,
)),
createElementVNode(
`span`,
I,
toDisplayString(t.name),
1,
),
createElementVNode(
`button`,
{
type: `button`,
class: `rm`,
"aria-label": `移除附件 ${t.name}`,
disabled: unref(W),
onClick: (e) => ge(n),
},
[
...(i[13] ||= [
createElementVNode(
`i`,
{
class: `pi pi-times`,
},
null,
-1,
),
]),
],
8,
L,
),
],
)
),
),
128,
)),
]))
: createCommentVNode(``, !0),
]),
unref(G)
? (openBlock(),
createBlock(
Y,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(G)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, R, [
createVNode(
_e,
{
to: `/tickets`,
class: `cancel-link`,
},
{
default: withCtx(() => [
...(i[16] ||= [createTextVNode(`取消`, -1)]),
]),
_: 1,
},
),
createVNode(
c,
{
type: `submit`,
label: `创建工单`,
loading: unref(W),
disabled: unref(Z) || unref(K) || !unref(q),
},
null,
8,
[`loading`, `disabled`],
),
]),
]),
]),
],
32,
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-710c7c49`]],
);
export {};
export default z;
</script>