687 lines
27 KiB
Vue
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>
|