634 lines
22 KiB
Vue
634 lines
22 KiB
Vue
<script>
|
||
/**
|
||
* 工单列表:状态/类型/关键词分页查询;筛选字典和统计独立请求,部分失败不会把已成功的选项全部丢弃。
|
||
*/
|
||
import "~/assets/styles/tickets.Dz8JrDhQ.css";
|
||
import { toDisplayString } 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 { isRef } from "vue";
|
||
import { createElementBlock } 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 { normalizeStyle } from "vue";
|
||
import { withKeys } from "vue";
|
||
import { useRoute } 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 inputtext from "primevue/inputtext";
|
||
import { r as dict_r } from "~/lib/dict.js";
|
||
import datatable from "primevue/datatable";
|
||
import paginator from "primevue/paginator";
|
||
import select from "primevue/select";
|
||
import column from "primevue/column";
|
||
import ListEmpty from "~/components/ListEmpty.vue";
|
||
import { t as useListPage_t } from "~/lib/useListPage.js";
|
||
var ie = {
|
||
class: `list-page`,
|
||
},
|
||
D = {
|
||
class: `list-head`,
|
||
},
|
||
O = {
|
||
class: `list-head-actions`,
|
||
},
|
||
ae = {
|
||
class: `list-toolbar`,
|
||
},
|
||
oe = {
|
||
class: `list-toolbar-right`,
|
||
},
|
||
k = {
|
||
class: `list-table`,
|
||
},
|
||
A = {
|
||
key: 3,
|
||
class: `list-foot`,
|
||
},
|
||
j = _plugin_vue_export_helper_t(
|
||
defineComponent({
|
||
__name: `index`,
|
||
setup(f) {
|
||
let v = useApi(),
|
||
j = useRoute(),
|
||
M = ref([]),
|
||
N = ref([]),
|
||
P = ref({}),
|
||
F = ref(``),
|
||
I = ref(!1),
|
||
L = ref(``),
|
||
R = ref(!1),
|
||
z = ref(``),
|
||
B = ref(``),
|
||
V = Number(j.query.status),
|
||
H = ref(V > 0 ? V : null),
|
||
U = ref(null),
|
||
{
|
||
list: W,
|
||
total: G,
|
||
loading: K,
|
||
error: q,
|
||
page: J,
|
||
limit: Y,
|
||
onPage: se,
|
||
reload: X,
|
||
} = useListPage_t(`/ticket`, {
|
||
limit: 10,
|
||
params: () => ({
|
||
keywords: B.value,
|
||
status: H.value ? [H.value] : [],
|
||
ticket_type_id: U.value ?? ``,
|
||
}),
|
||
});
|
||
// 状态和类型使用 allSettled 分别处理,失败项单独汇总提示。
|
||
async function Z() {
|
||
if (!I.value) {
|
||
((I.value = !0), (F.value = ``));
|
||
try {
|
||
let [e, t] = await Promise.allSettled([
|
||
v.get(`/ticket/status`),
|
||
v.get(`/ticket/type`),
|
||
]);
|
||
(e.status === `fulfilled` && (M.value = e.value.list ?? []),
|
||
t.status === `fulfilled` && (N.value = t.value.list ?? []));
|
||
let n = [e, t].flatMap((e, t) =>
|
||
e.status === `rejected`
|
||
? [
|
||
`${t === 0 ? `工单状态` : `工单类型`}加载失败:${e.reason instanceof Error ? e.reason.message : `请稍后重试`}`,
|
||
]
|
||
: [],
|
||
);
|
||
F.value = n.join(`;`);
|
||
} finally {
|
||
I.value = !1;
|
||
}
|
||
}
|
||
}
|
||
// 工单统计独立加载,异常不清空工单主列表。
|
||
async function Q() {
|
||
if (!R.value) {
|
||
((R.value = !0), (L.value = ``));
|
||
try {
|
||
P.value = await v.get(`/ticket/statistic`);
|
||
} catch (e) {
|
||
L.value = e instanceof Error ? e.message : `工单统计加载失败`;
|
||
} finally {
|
||
R.value = !1;
|
||
}
|
||
}
|
||
}
|
||
computed(() =>
|
||
M.value
|
||
.filter((e) => P.value[e.id] !== void 0)
|
||
.map((e) => ({
|
||
...e,
|
||
count: P.value[e.id] ?? 0,
|
||
})),
|
||
);
|
||
let ce = computed(() => [
|
||
{
|
||
label: `全部状态`,
|
||
value: null,
|
||
},
|
||
...M.value.map((e) => ({
|
||
label: e.name,
|
||
value: e.id,
|
||
})),
|
||
]),
|
||
le = computed(() => [
|
||
{
|
||
label: `全部类型`,
|
||
value: null,
|
||
},
|
||
...N.value.map((e) => ({
|
||
label: e.name,
|
||
value: e.id,
|
||
})),
|
||
]),
|
||
$ = computed(() => !!B.value || !!H.value || !!U.value);
|
||
// 确认搜索后应用关键词并交由公共分页工具重载。
|
||
function ue() {
|
||
((B.value = z.value.trim()), X());
|
||
}
|
||
// 清除关键词、状态与工单类型,恢复全部工单。
|
||
function de() {
|
||
((z.value = ``),
|
||
(B.value = ``),
|
||
(H.value = null),
|
||
(U.value = null),
|
||
X());
|
||
}
|
||
return (
|
||
onMounted(() => {
|
||
(Z(), Q());
|
||
}),
|
||
// 筛选字典、统计提示与工单主列表分开展示,失败选项提供重试,不用空列表掩盖请求错误。
|
||
(t, r) => {
|
||
let o = message,
|
||
f = button,
|
||
g = NuxtLink,
|
||
_ = inputtext,
|
||
v = select,
|
||
E = ListEmpty,
|
||
j = column,
|
||
M = datatable,
|
||
N = paginator;
|
||
return (
|
||
openBlock(),
|
||
createElementBlock(`div`, ie, [
|
||
createVNode(
|
||
o,
|
||
{
|
||
severity: `info`,
|
||
closable: !1,
|
||
class: `support-notice`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
...(r[3] ||= [
|
||
createElementVNode(
|
||
`div`,
|
||
null,
|
||
[
|
||
createTextVNode(
|
||
`尊敬的客户您好:为了更好地为您服务,请详细描述您遇到的问题,并提供相关截图或报错信息。`,
|
||
),
|
||
createElementVNode(`br`),
|
||
createTextVNode(
|
||
`请勿在工单中发送账户密码等敏感信息。我们会尽快为您处理。`,
|
||
),
|
||
],
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createElementVNode(`div`, D, [
|
||
(r[4] ||= createElementVNode(`h2`, null, `工单`, -1)),
|
||
createElementVNode(`div`, O, [
|
||
createVNode(
|
||
g,
|
||
{
|
||
to: `/tickets/create`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createVNode(f, {
|
||
as: `span`,
|
||
label: `创建工单`,
|
||
icon: `pi pi-plus`,
|
||
}),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
]),
|
||
unref(L)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 0,
|
||
severity: `warn`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
` 工单统计暂不可用:` +
|
||
toDisplayString(unref(L)) +
|
||
` `,
|
||
1,
|
||
),
|
||
createVNode(
|
||
f,
|
||
{
|
||
label: `重试统计`,
|
||
size: `small`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
loading: unref(R),
|
||
onClick: Q,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
unref(F)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 1,
|
||
severity: `warn`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(F)) + ` `, 1),
|
||
createVNode(
|
||
f,
|
||
{
|
||
label: `重试筛选选项`,
|
||
size: `small`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
loading: unref(I),
|
||
onClick: Z,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
createElementVNode(`div`, ae, [
|
||
createElementVNode(`div`, oe, [
|
||
createVNode(
|
||
_,
|
||
{
|
||
modelValue: unref(z),
|
||
"onUpdate:modelValue": (r[0] ||= (e) =>
|
||
isRef(z) ? (z.value = e) : null),
|
||
class: `list-search`,
|
||
placeholder: `输入关键字`,
|
||
onKeyup: withKeys(ue, [`enter`]),
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
createVNode(
|
||
v,
|
||
{
|
||
modelValue: unref(U),
|
||
"onUpdate:modelValue": (r[1] ||= (e) =>
|
||
isRef(U) ? (U.value = e) : null),
|
||
class: `list-select`,
|
||
options: unref(le),
|
||
optionLabel: `label`,
|
||
optionValue: `value`,
|
||
onChange: unref(X),
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`, `options`, `onChange`],
|
||
),
|
||
createVNode(
|
||
v,
|
||
{
|
||
modelValue: unref(H),
|
||
"onUpdate:modelValue": (r[2] ||= (e) =>
|
||
isRef(H) ? (H.value = e) : null),
|
||
class: `list-select`,
|
||
options: unref(ce),
|
||
optionLabel: `label`,
|
||
optionValue: `value`,
|
||
onChange: unref(X),
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`, `options`, `onChange`],
|
||
),
|
||
createVNode(f, {
|
||
label: `清除筛选`,
|
||
icon: `pi pi-filter-slash`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
size: `small`,
|
||
onClick: de,
|
||
}),
|
||
]),
|
||
]),
|
||
createElementVNode(`div`, k, [
|
||
createVNode(
|
||
M,
|
||
{
|
||
value: unref(W),
|
||
loading: unref(K),
|
||
lazy: ``,
|
||
rows: unref(Y),
|
||
totalRecords: unref(G),
|
||
first: (unref(J) - 1) * unref(Y),
|
||
dataKey: `id`,
|
||
},
|
||
{
|
||
empty: withCtx(() => [
|
||
createVNode(
|
||
E,
|
||
{
|
||
icon: `pi pi-comments`,
|
||
title: unref(K) ? `加载中…` : `还没有工单`,
|
||
description: unref($)
|
||
? `换个筛选条件试试`
|
||
: `遇到问题可以提交工单联系我们`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
unref($)
|
||
? createCommentVNode(``, !0)
|
||
: (openBlock(),
|
||
createBlock(
|
||
g,
|
||
{
|
||
key: 0,
|
||
to: `/tickets/create`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createVNode(f, {
|
||
as: `span`,
|
||
label: `创建工单`,
|
||
icon: `pi pi-plus`,
|
||
size: `small`,
|
||
}),
|
||
]),
|
||
_: 1,
|
||
},
|
||
)),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`title`, `description`],
|
||
),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
j,
|
||
{
|
||
header: `ID`,
|
||
style: {
|
||
width: `120px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createVNode(
|
||
g,
|
||
{
|
||
to: `/tickets/${t.id}`,
|
||
class: `col-id`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
`#` + toDisplayString(t.ticket_num),
|
||
1,
|
||
),
|
||
]),
|
||
_: 2,
|
||
},
|
||
1032,
|
||
[`to`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
j,
|
||
{
|
||
header: `标题`,
|
||
style: {
|
||
"min-width": `220px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createVNode(
|
||
g,
|
||
{
|
||
to: `/tickets/${t.id}`,
|
||
class: `title-link`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(t.title),
|
||
1,
|
||
),
|
||
]),
|
||
_: 2,
|
||
},
|
||
1032,
|
||
[`to`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
j,
|
||
{
|
||
header: `工单类型`,
|
||
style: {
|
||
width: `130px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(t.name || `—`),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
j,
|
||
{
|
||
header: `提交时间`,
|
||
style: {
|
||
"min-width": `160px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: n }) => [
|
||
createTextVNode(
|
||
toDisplayString(
|
||
(`formatTime` in t
|
||
? t.formatTime
|
||
: unref(dict_r))(n.post_time),
|
||
),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
j,
|
||
{
|
||
header: `最近回复`,
|
||
style: {
|
||
"min-width": `160px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: n }) => [
|
||
createTextVNode(
|
||
toDisplayString(
|
||
n.last_reply_time
|
||
? (`formatTime` in t
|
||
? t.formatTime
|
||
: unref(dict_r))(n.last_reply_time)
|
||
: `—`,
|
||
),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
j,
|
||
{
|
||
header: `状态`,
|
||
style: {
|
||
width: `110px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`span`,
|
||
{
|
||
class: `status-tag`,
|
||
style: normalizeStyle({
|
||
color: `var(--p-text-color)`,
|
||
borderColor: t.color,
|
||
}),
|
||
},
|
||
toDisplayString(t.status),
|
||
5,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`value`, `loading`, `rows`, `totalRecords`, `first`],
|
||
),
|
||
]),
|
||
unref(q)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 2,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(q)), 1),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
unref(G) > 0
|
||
? (openBlock(),
|
||
createElementBlock(`div`, A, [
|
||
(r[5] ||= createElementVNode(
|
||
`div`,
|
||
{
|
||
class: `list-bulk`,
|
||
},
|
||
null,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
N,
|
||
{
|
||
rows: unref(Y),
|
||
totalRecords: unref(G),
|
||
first: (unref(J) - 1) * unref(Y),
|
||
rowsPerPageOptions: [10, 20, 50],
|
||
onPage: unref(se),
|
||
},
|
||
null,
|
||
8,
|
||
[`rows`, `totalRecords`, `first`, `onPage`],
|
||
),
|
||
]))
|
||
: createCommentVNode(``, !0),
|
||
])
|
||
);
|
||
}
|
||
);
|
||
},
|
||
}),
|
||
[[`__scopeId`, `data-v-1ff3e04d`]],
|
||
);
|
||
export {};
|
||
export default j;
|
||
</script>
|