Files
console-dev/app/pages/messages/index.vue
T

916 lines
33 KiB
Vue

<script>
/**
* 站内消息:分页筛选、批量已读/删除与详情弹窗;读状态变化同时刷新公共邮箱计数,详情请求用序号隔离快速切换。
*/
import "~/assets/styles/NotificationPreferences.DcH8Noxd.css";
import "~/assets/styles/messages.asRdYcVu.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { normalizeClass } 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 { 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 { useRoute } from "#app";
import { useApi } from "~/lib/auth.js";
import dialog from "primevue/dialog";
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 iconfield from "primevue/iconfield";
import inputicon from "primevue/inputicon";
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";
import tag from "primevue/tag";
import skeleton from "primevue/skeleton";
import NotificationPreferences from "~/components/NotificationPreferences.vue";
import { n as sanitize_n } from "~/lib/sanitize.js";
import { t as useMailbox_t } from "~/lib/useMailbox.js";
import { t as composables_t } from "~/lib/composables-2.js";
var ge = {
class: `list-page`,
},
_e = {
class: `list-head`,
},
ve = {
class: `list-head-actions`,
},
ye = {
class: `message-panel`,
},
be = {
class: `list-toolbar`,
},
xe = {
class: `selection-toolbar`,
},
Se = {
class: `selection-summary`,
},
Ce = {
class: `bulk-actions`,
},
we = {
key: 1,
class: `message-error`,
},
Te = {
key: 2,
class: `list-table`,
},
Ee = [`onClick`],
De = {
key: 0,
class: `mail-dot`,
},
Oe = {
key: 3,
class: `list-foot`,
},
ke = {
key: 0,
class: `detail-state`,
},
w = {
key: 1,
class: `detail-state`,
},
Ae = {
class: `detail-meta`,
},
je = [`innerHTML`],
Me = {
class: `detail-nav`,
},
Ne = {
class: `confirm-text`,
},
T = _plugin_vue_export_helper_t(
defineComponent({
__name: `index`,
setup(p) {
composables_t({
key: (e) => e.fullPath,
});
let y = useApi(),
T = useRoute(),
{ types: E, refresh: D } = useMailbox_t(),
O = ref(null),
k = ref(``),
A = ref(``),
j = ref(``),
{
list: M,
total: N,
loading: P,
error: F,
page: I,
limit: L,
onPage: Pe,
reload: R,
} = useListPage_t(`/client_care/mail/list`, {
limit: 10,
params: () => ({
orderby: `id`,
sort: `desc`,
read: O.value ?? ``,
type: k.value,
keywords: j.value,
}),
}),
z = ref([]),
B = ref(!1),
V = computed(() => O.value !== null || !!k.value || !!j.value),
Fe = [
{
label: `全部消息`,
value: null,
},
{
label: `未读`,
value: 0,
},
{
label: `已读`,
value: 1,
},
],
Ie = computed(() => [
{
label: `全部类型`,
value: ``,
},
...E.value.map((e) => ({
label: e.name_lang || e.name,
value: e.name,
})),
]);
// 类型显示优先使用服务端本地化名称,未知类型保留原标识。
function H(e) {
return E.value.find((t) => t.name === e)?.name_lang || e || `—`;
}
// 提交后才应用搜索词,避免输入每个字符都发请求。
function Le() {
((j.value = A.value.trim()), R());
}
// 清空阅读状态、类型与关键词,重新加载消息列表。
function Re() {
((A.value = ``),
(j.value = ``),
(O.value = null),
(k.value = ``),
R());
}
// 将指定 ID 标为已读,完成后清除选择并刷新列表与全局未读计数。
async function ze(e) {
if (!(!e.length || B.value || G.value)) {
((B.value = !0), (U.value = ``));
try {
(await y.put(`/client_care/mail/read`, {
id: e,
}),
(z.value = []),
await R(),
await D());
} catch (e) {
U.value = e instanceof Error ? e.message : `操作失败`;
} finally {
B.value = !1;
}
}
}
// 使用 all 标记请求服务端全部已读,不局限于当前可见页。
async function Be() {
if (!(B.value || G.value)) {
((B.value = !0), (U.value = ``));
try {
(await y.put(`/client_care/mail/read`, {
all: 1,
}),
(z.value = []),
await R(),
await D());
} catch (e) {
U.value = e instanceof Error ? e.message : `操作失败`;
} finally {
B.value = !1;
}
}
}
let U = ref(``),
W = ref(!1),
G = ref(!1);
// 删除前先打开确认弹窗,读状态更新和删除期间不重复触发。
function Ve() {
!z.value.length ||
B.value ||
G.value ||
((U.value = ``), (W.value = !0));
}
// 仅提交选中消息的 ID;成功后同步列表与未读计数。
async function He() {
if (!z.value.length || G.value || B.value) return;
let e = z.value.map((e) => e.id);
((G.value = !0), (U.value = ``));
try {
(await y.del(`/client_care/mail`, {
id: e,
}),
(W.value = !1),
(z.value = []),
await R(),
await D());
} catch (e) {
((W.value = !1),
(U.value = e instanceof Error ? e.message : `删除失败`));
} finally {
G.value = !1;
}
}
let K = ref(!1),
q = ref(null),
J = ref(!1),
Y = ref(``),
X = ref(null),
Z = 0;
// 详情打开时清除旧正文并递增请求序号;加载成功后尝试标读,标读失败不覆盖已加载的正文。
async function Q(e) {
let t = ++Z;
((X.value = e),
(K.value = !0),
(J.value = !0),
(Y.value = ``),
(q.value = null));
try {
let n = await y.get(`/client_care/mail/${e}`);
if (t !== Z) return;
if (!n?.mail?.id) {
Y.value = `消息不存在或已删除`;
return;
}
((q.value = n.mail), (J.value = !1));
let r = M.value.find((t) => t.id === e);
if (!r || r.read === 0)
try {
(await y.put(`/client_care/mail/read`, {
id: [e],
}),
r && (r.read = 1),
await D());
} catch {}
} catch (e) {
t === Z && (Y.value = e instanceof Error ? e.message : `加载失败`);
} finally {
t === Z && (J.value = !1);
}
}
return (
onMounted(async () => {
await D();
let e = Number(T.query.id);
e && Q(e);
}),
(t, i) => {
let s = button,
p = NotificationPreferences,
_ = inputicon,
v = inputtext,
y = iconfield,
x = select,
S = message,
C = column,
T = ListEmpty,
E = datatable,
D = paginator,
j = skeleton,
Z = tag,
$ = dialog;
return (
openBlock(),
createElementBlock(`div`, ge, [
createElementVNode(`div`, _e, [
(i[12] ||= createElementVNode(
`div`,
{
class: `page-heading`,
},
[
createElementVNode(`h2`, null, `消息`),
createElementVNode(
`p`,
null,
`查看产品、订单与账户的重要通知`,
),
],
-1,
)),
createElementVNode(`div`, ve, [
createVNode(
s,
{
label: `全部已读`,
icon: `pi pi-check`,
size: `small`,
severity: `secondary`,
outlined: ``,
disabled: !unref(N) || unref(P) || unref(G),
loading: unref(B),
onClick: Be,
},
null,
8,
[`disabled`, `loading`],
),
]),
]),
createVNode(p),
createElementVNode(`section`, ye, [
createElementVNode(`div`, be, [
createElementVNode(
`form`,
{
class: `list-toolbar-right`,
onSubmit: withModifiers(Le, [`prevent`]),
},
[
createVNode(
y,
{
class: `search-field`,
},
{
default: withCtx(() => [
createVNode(_, {
class: `pi pi-search`,
}),
createVNode(
v,
{
modelValue: unref(A),
"onUpdate:modelValue": (i[0] ||= (e) =>
isRef(A) ? (A.value = e) : null),
class: `list-search`,
placeholder: `搜索消息标题`,
"aria-label": `搜索消息标题`,
},
null,
8,
[`modelValue`],
),
]),
_: 1,
},
),
createVNode(
x,
{
modelValue: unref(O),
"onUpdate:modelValue": (i[1] ||= (e) =>
isRef(O) ? (O.value = e) : null),
class: `list-select`,
options: Fe,
optionLabel: `label`,
optionValue: `value`,
"aria-label": `阅读状态`,
onChange: unref(R),
},
null,
8,
[`modelValue`, `onChange`],
),
createVNode(
x,
{
modelValue: unref(k),
"onUpdate:modelValue": (i[2] ||= (e) =>
isRef(k) ? (k.value = e) : null),
class: `list-select`,
options: unref(Ie),
optionLabel: `label`,
optionValue: `value`,
"aria-label": `消息类型`,
onChange: unref(R),
},
null,
8,
[`modelValue`, `options`, `onChange`],
),
createVNode(s, {
type: `submit`,
label: `搜索`,
severity: `secondary`,
outlined: ``,
}),
unref(V)
? (openBlock(),
createBlock(s, {
key: 0,
label: `清除筛选`,
icon: `pi pi-filter-slash`,
severity: `secondary`,
text: ``,
onClick: Re,
}))
: createCommentVNode(``, !0),
],
32,
),
]),
createElementVNode(`div`, xe, [
createElementVNode(
`span`,
Se,
toDisplayString(
unref(z).length
? `已选择 ${unref(z).length} 条消息`
: `共 ${unref(N)} 条消息`,
),
1,
),
createElementVNode(`div`, Ce, [
createVNode(
s,
{
label: `标记已读`,
icon: `pi pi-envelope-open`,
size: `small`,
severity: `secondary`,
text: ``,
disabled: !unref(z).length || unref(B) || unref(G),
onClick: (i[3] ||= (e) =>
ze(unref(z).map((e) => e.id))),
},
null,
8,
[`disabled`],
),
createVNode(
s,
{
label: `删除`,
icon: `pi pi-trash`,
size: `small`,
severity: `danger`,
text: ``,
disabled: !unref(z).length || unref(B) || unref(G),
onClick: Ve,
},
null,
8,
[`disabled`],
),
]),
]),
unref(U)
? (openBlock(),
createBlock(
S,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(U)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(F)
? (openBlock(),
createElementBlock(`div`, we, [
createVNode(
S,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(F)), 1),
]),
_: 1,
},
),
createVNode(
s,
{
label: `重新加载消息`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: unref(R),
},
null,
8,
[`onClick`],
),
]))
: (openBlock(),
createElementBlock(`div`, Te, [
createVNode(
E,
{
selection: unref(z),
"onUpdate:selection": (i[4] ||= (e) =>
isRef(z) ? (z.value = e) : null),
value: unref(M),
loading: unref(P),
lazy: ``,
scrollable: ``,
rows: unref(L),
totalRecords: unref(N),
first: (unref(I) - 1) * unref(L),
dataKey: `id`,
},
{
empty: withCtx(() => [
createVNode(
T,
{
icon: `pi pi-envelope`,
title: unref(P)
? `加载中…`
: unref(V)
? `没有符合条件的消息`
: `还没有消息`,
description: unref(V)
? `换个筛选条件试试`
: `订单、产品、财务等重要通知会出现在这里`,
},
null,
8,
[`title`, `description`],
),
]),
default: withCtx(() => [
createVNode(C, {
selectionMode: `multiple`,
style: {
width: `3rem`,
},
}),
createVNode(
C,
{
header: `标题`,
style: {
"min-width": `260px`,
},
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass([
`mail-title`,
{
unread: t.read === 0,
},
]),
onClick: (e) => Q(t.id),
},
[
t.read === 0
? (openBlock(),
createElementBlock(`span`, De))
: createCommentVNode(``, !0),
createTextVNode(
` ` + toDisplayString(t.title),
1,
),
],
10,
Ee,
),
]),
_: 1,
},
),
createVNode(
C,
{
header: `类型`,
style: {
width: `150px`,
},
},
{
body: withCtx(({ data: t }) => [
createTextVNode(
toDisplayString(H(t.type)),
1,
),
]),
_: 1,
},
),
createVNode(
C,
{
header: `接收时间`,
style: {
"min-width": `160px`,
},
},
{
body: withCtx(({ data: n }) => [
createTextVNode(
toDisplayString(
(`formatTime` in t
? t.formatTime
: unref(dict_r))(n.create_time),
),
1,
),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[
`selection`,
`value`,
`loading`,
`rows`,
`totalRecords`,
`first`,
],
),
])),
unref(N) > 0
? (openBlock(),
createElementBlock(`div`, Oe, [
(i[13] ||= createElementVNode(
`div`,
{
class: `list-bulk`,
},
null,
-1,
)),
createVNode(
D,
{
rows: unref(L),
totalRecords: unref(N),
first: (unref(I) - 1) * unref(L),
rowsPerPageOptions: [10, 20, 50],
onPage: unref(Pe),
},
null,
8,
[`rows`, `totalRecords`, `first`, `onPage`],
),
]))
: createCommentVNode(``, !0),
]),
createVNode(
$,
{
visible: unref(K),
"onUpdate:visible": (i[9] ||= (e) =>
isRef(K) ? (K.value = e) : null),
header: unref(q)?.title || `消息详情`,
modal: ``,
style: {
width: `680px`,
},
draggable: !1,
},
{
footer: withCtx(() => [
createElementVNode(`div`, Me, [
createVNode(
s,
{
label: `上一条`,
icon: `pi pi-angle-left`,
size: `small`,
severity: `secondary`,
text: ``,
disabled: !unref(q)?.prev?.id,
onClick: (i[6] ||= (e) =>
unref(q)?.prev?.id && Q(unref(q).prev.id)),
},
null,
8,
[`disabled`],
),
createVNode(
s,
{
label: `下一条`,
icon: `pi pi-angle-right`,
iconPos: `right`,
size: `small`,
severity: `secondary`,
text: ``,
disabled: !unref(q)?.next?.id,
onClick: (i[7] ||= (e) =>
unref(q)?.next?.id && Q(unref(q).next.id)),
},
null,
8,
[`disabled`],
),
]),
createVNode(s, {
label: `关闭`,
severity: `secondary`,
text: ``,
onClick: (i[8] ||= (e) => (K.value = !1)),
}),
]),
default: withCtx(() => [
unref(J)
? (openBlock(),
createElementBlock(`div`, ke, [
createVNode(j, {
height: `18px`,
width: `45%`,
}),
createVNode(j, {
height: `150px`,
}),
]))
: unref(Y)
? (openBlock(),
createElementBlock(`div`, w, [
createVNode(
S,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(Y)),
1,
),
]),
_: 1,
},
),
createVNode(s, {
label: `重新加载消息`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: (i[5] ||= (e) =>
unref(X) && Q(unref(X))),
}),
]))
: unref(q)
? (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
createElementVNode(`div`, Ae, [
createVNode(
Z,
{
value: H(unref(q).type),
severity: `secondary`,
},
null,
8,
[`value`],
),
createElementVNode(
`span`,
null,
toDisplayString(
(`formatTime` in t
? t.formatTime
: unref(dict_r))(
unref(q).create_time,
),
),
1,
),
]),
createElementVNode(
`div`,
{
class: `detail-body`,
innerHTML: (`sanitizeHtml` in t
? t.sanitizeHtml
: unref(sanitize_n))(unref(q).content),
},
null,
8,
je,
),
],
64,
))
: createCommentVNode(``, !0),
]),
_: 1,
},
8,
[`visible`, `header`],
),
createVNode(
$,
{
visible: unref(W),
"onUpdate:visible": (i[11] ||= (e) =>
isRef(W) ? (W.value = e) : null),
header: `删除消息`,
modal: ``,
closable: !unref(G),
closeOnEscape: !unref(G),
draggable: !1,
style: {
width: `420px`,
},
},
{
footer: withCtx(() => [
createVNode(
s,
{
label: `取消`,
severity: `secondary`,
text: ``,
disabled: unref(G),
onClick: (i[10] ||= (e) => (W.value = !1)),
},
null,
8,
[`disabled`],
),
createVNode(
s,
{
label: `确认删除`,
severity: `danger`,
loading: unref(G),
onClick: He,
},
null,
8,
[`loading`],
),
]),
default: withCtx(() => [
createElementVNode(
`p`,
Ne,
`确认删除选中的 ` +
toDisplayString(unref(z).length) +
` 条消息?删除后不可恢复。`,
1,
),
]),
_: 1,
},
8,
[`visible`, `closable`, `closeOnEscape`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-e9dbafec`]],
);
export {};
export default T;
</script>