588 lines
20 KiB
Vue
588 lines
20 KiB
Vue
<script>
|
|
/**
|
|
* 新闻资讯:分类与关键词分页查询,文章可独立打开或嵌入资源中心;正文加载和列表状态分开维护。
|
|
*/
|
|
import "~/assets/styles/news.CbIzdrjt.css";
|
|
definePageMeta({
|
|
scrollToTop: !0,
|
|
alias: [`/news/article`],
|
|
});
|
|
import { toDisplayString } from "vue";
|
|
import { renderList } from "vue";
|
|
import { onMounted } from "vue";
|
|
import { onBeforeUnmount } from "vue";
|
|
import { watch } from "vue";
|
|
import { createBlock } from "vue";
|
|
import { ref } from "vue";
|
|
import { withCtx } from "vue";
|
|
import { createCommentVNode } 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 { withKeys } from "vue";
|
|
import { useRoute } from "#app";
|
|
import { useRouter } from "#app";
|
|
import { NuxtLink } from "#components";
|
|
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 paginator from "primevue/paginator";
|
|
import ListSegments from "~/components/ListSegments.vue";
|
|
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 { n as sanitize_n } from "~/lib/sanitize.js";
|
|
import { t as sanitize_t } from "~/lib/sanitize.js";
|
|
import { t as composables_t } from "~/lib/composables-2.js";
|
|
import { t as content_route_t } from "~/lib/content-route.js";
|
|
var fe = {
|
|
class: `list-page`,
|
|
},
|
|
pe = {
|
|
key: 0,
|
|
class: `list-head`,
|
|
},
|
|
me = {
|
|
class: `list-toolbar`,
|
|
},
|
|
he = {
|
|
class: `list-toolbar-right`,
|
|
},
|
|
ge = {
|
|
class: `list-search`,
|
|
},
|
|
_e = {
|
|
key: 2,
|
|
class: `news-list`,
|
|
},
|
|
ve = {
|
|
key: 3,
|
|
class: `news-list`,
|
|
},
|
|
ye = [`src`],
|
|
be = {
|
|
class: `news-main`,
|
|
},
|
|
D = {
|
|
class: `news-title`,
|
|
},
|
|
O = {
|
|
class: `news-excerpt`,
|
|
},
|
|
k = {
|
|
class: `news-meta`,
|
|
},
|
|
A = {
|
|
key: 5,
|
|
class: `list-foot`,
|
|
},
|
|
j = {
|
|
key: 0,
|
|
},
|
|
M = {
|
|
key: 2,
|
|
class: `news-detail`,
|
|
},
|
|
N = {
|
|
class: `news-detail-time`,
|
|
},
|
|
xe = [`innerHTML`],
|
|
P = _plugin_vue_export_helper_t(
|
|
defineComponent({
|
|
__name: `news`,
|
|
props: {
|
|
embedded: {
|
|
type: Boolean,
|
|
},
|
|
},
|
|
setup(h) {
|
|
composables_t({
|
|
scrollToTop: !0,
|
|
alias: `/news/article`,
|
|
});
|
|
let x = h,
|
|
P = useRoute(),
|
|
Se = useRouter(),
|
|
F = useApi(),
|
|
I = ref([]),
|
|
L = ref(``),
|
|
R = ref(``),
|
|
{
|
|
list: z,
|
|
total: B,
|
|
loading: Ce,
|
|
error: V,
|
|
page: we,
|
|
limit: H,
|
|
onPage: Te,
|
|
reload: U,
|
|
} = useListPage_t(`/news`, {
|
|
limit: 10,
|
|
params: () => ({
|
|
addon_idcsmart_news_type_id: L.value,
|
|
keywords: R.value,
|
|
}),
|
|
});
|
|
// 新闻分类加载失败不阻止列表使用,保留空分类选项。
|
|
async function Ee() {
|
|
try {
|
|
let e = await F.get(`/news/type`);
|
|
I.value = e.list ?? [];
|
|
} catch {
|
|
I.value = [];
|
|
}
|
|
}
|
|
// 选择分类后应用其 ID 并重新查询列表。
|
|
function De(e) {
|
|
((L.value = e === `` || e === void 0 ? `` : Number(e)), U());
|
|
}
|
|
// 摘要先经过公共清理函数,再移除标签、尾部截断实体和多余空格;不能把摘要当可信 HTML。
|
|
function Oe(e) {
|
|
return sanitize_t(e || ``)
|
|
.replace(/<[^>]*>/g, ``)
|
|
.replace(/<[^>]*$/, ``)
|
|
.replace(/&(?:#(?:x[\da-f]*|\d*)|[a-z]+)(?=(?:…|\.{3})?\s*$)/gi, ``)
|
|
.replace(/ /g, ` `)
|
|
.replace(/\s+/g, ` `)
|
|
.trim();
|
|
}
|
|
let W = ref(!1),
|
|
G = ref(!1),
|
|
K = ref(null),
|
|
q = ref(``),
|
|
J = ref(null),
|
|
Y = 0,
|
|
X = !1;
|
|
// 根据嵌入模式生成资源中心或独立新闻文章地址。
|
|
function ke(e) {
|
|
return x?.embedded
|
|
? `/resources?section=news&id=${e}`
|
|
: `/news/article?id=${e}`;
|
|
}
|
|
// 解析文章路由并校验 ID;缺失或无效时取消旧详情请求并清空正文。
|
|
async function Z() {
|
|
if (X) return;
|
|
let e = P.query.id !== void 0 || P.path === `/news/article`,
|
|
t = content_route_t(P.query.id);
|
|
if (!e || !t) {
|
|
(Y++,
|
|
(W.value = e),
|
|
(G.value = !1),
|
|
(K.value = null),
|
|
(J.value = null),
|
|
(q.value = e ? `新闻编号无效,请从列表重新选择` : ``));
|
|
return;
|
|
}
|
|
await $({
|
|
id: t,
|
|
title: `新闻详情`,
|
|
create_time: 0,
|
|
});
|
|
}
|
|
// 关闭详情时废弃旧请求,并移除 URL 中的文章 ID,保留其余筛选查询。
|
|
function Q() {
|
|
if (
|
|
((W.value = !1),
|
|
Y++,
|
|
(G.value = !1),
|
|
P.query.id !== void 0 || P.path === `/news/article`)
|
|
) {
|
|
let e = {
|
|
...P.query,
|
|
};
|
|
(delete e.id,
|
|
Se.replace({
|
|
path: x?.embedded ? `/resources` : `/news`,
|
|
query: e,
|
|
}));
|
|
}
|
|
}
|
|
// 按文章 ID 获取正文,确认新闻对象存在;只有最新请求且未卸载时才写入弹窗。
|
|
async function $(e) {
|
|
let t = ++Y;
|
|
((W.value = !0),
|
|
(G.value = !0),
|
|
(J.value = e),
|
|
(q.value = ``),
|
|
(K.value = null));
|
|
try {
|
|
let n = await F.get(`/news/${e.id}`);
|
|
if (X || t !== Y) return;
|
|
if (!n.news) throw Error(`新闻不存在或已删除`);
|
|
K.value = n.news;
|
|
} catch (e) {
|
|
!X &&
|
|
t === Y &&
|
|
(q.value =
|
|
e instanceof Error ? e.message : `正文加载失败,请重试`);
|
|
} finally {
|
|
!X && t === Y && (G.value = !1);
|
|
}
|
|
}
|
|
return (
|
|
onMounted(async () => {
|
|
await Promise.all([Ee(), Z()]);
|
|
}),
|
|
watch(
|
|
() => P.query.id,
|
|
() => {
|
|
Z();
|
|
},
|
|
),
|
|
onBeforeUnmount(() => {
|
|
((X = !0), Y++);
|
|
}),
|
|
// 列表摘要为纯文本,详情 HTML 经公共清理工具处理后展示;正文失败不覆盖列表查询状态。
|
|
(n, r) => {
|
|
let i = ListSegments,
|
|
o = inputtext,
|
|
h = button,
|
|
v = message,
|
|
y = skeleton,
|
|
b = tag,
|
|
x = NuxtLink,
|
|
C = ListEmpty,
|
|
T = paginator,
|
|
E = dialog;
|
|
return (
|
|
openBlock(),
|
|
createElementBlock(`div`, fe, [
|
|
n.$props.embedded
|
|
? createCommentVNode(``, !0)
|
|
: (openBlock(),
|
|
createElementBlock(`div`, pe, [
|
|
...(r[3] ||= [
|
|
createElementVNode(`h2`, null, `新闻中心`, -1),
|
|
]),
|
|
])),
|
|
createElementVNode(`div`, me, [
|
|
createVNode(
|
|
i,
|
|
{
|
|
options: [
|
|
{
|
|
label: `全部`,
|
|
value: ``,
|
|
},
|
|
...unref(I).map((e) => ({
|
|
label: `${e.name}(${e.news_num})`,
|
|
value: e.id,
|
|
})),
|
|
],
|
|
"model-value": unref(L),
|
|
"onUpdate:modelValue": De,
|
|
},
|
|
null,
|
|
8,
|
|
[`options`, `model-value`],
|
|
),
|
|
createElementVNode(`div`, he, [
|
|
createElementVNode(`span`, ge, [
|
|
createVNode(
|
|
o,
|
|
{
|
|
modelValue: unref(R),
|
|
"onUpdate:modelValue": (r[0] ||= (e) =>
|
|
isRef(R) ? (R.value = e) : null),
|
|
placeholder: `搜索标题`,
|
|
onKeyup: withKeys(unref(U), [`enter`]),
|
|
},
|
|
null,
|
|
8,
|
|
[`modelValue`, `onKeyup`],
|
|
),
|
|
]),
|
|
createVNode(
|
|
h,
|
|
{
|
|
label: `查询`,
|
|
size: `small`,
|
|
severity: `secondary`,
|
|
outlined: ``,
|
|
onClick: unref(U),
|
|
},
|
|
null,
|
|
8,
|
|
[`onClick`],
|
|
),
|
|
]),
|
|
]),
|
|
unref(V)
|
|
? (openBlock(),
|
|
createBlock(
|
|
v,
|
|
{
|
|
key: 1,
|
|
severity: `error`,
|
|
closable: !1,
|
|
},
|
|
{
|
|
default: withCtx(() => [
|
|
createTextVNode(toDisplayString(unref(V)), 1),
|
|
]),
|
|
_: 1,
|
|
},
|
|
))
|
|
: createCommentVNode(``, !0),
|
|
unref(Ce) && !unref(z).length
|
|
? (openBlock(),
|
|
createElementBlock(`div`, _e, [
|
|
(openBlock(),
|
|
createElementBlock(
|
|
Fragment,
|
|
null,
|
|
renderList(4, (e) =>
|
|
createElementVNode(
|
|
`div`,
|
|
{
|
|
key: e,
|
|
class: `news-card`,
|
|
},
|
|
[
|
|
createVNode(y, {
|
|
height: `72px`,
|
|
}),
|
|
],
|
|
),
|
|
),
|
|
64,
|
|
)),
|
|
]))
|
|
: unref(z).length
|
|
? (openBlock(),
|
|
createElementBlock(`div`, ve, [
|
|
(openBlock(!0),
|
|
createElementBlock(
|
|
Fragment,
|
|
null,
|
|
renderList(
|
|
unref(z),
|
|
(t) => (
|
|
openBlock(),
|
|
createBlock(
|
|
x,
|
|
{
|
|
key: t.id,
|
|
to: ke(t.id),
|
|
class: `news-card`,
|
|
},
|
|
{
|
|
default: withCtx(() => [
|
|
t.img
|
|
? (openBlock(),
|
|
createElementBlock(
|
|
`img`,
|
|
{
|
|
key: 0,
|
|
src: t.img,
|
|
class: `news-img`,
|
|
alt: ``,
|
|
},
|
|
null,
|
|
8,
|
|
ye,
|
|
))
|
|
: createCommentVNode(``, !0),
|
|
createElementVNode(`div`, be, [
|
|
createElementVNode(
|
|
`div`,
|
|
D,
|
|
toDisplayString(t.title),
|
|
1,
|
|
),
|
|
createElementVNode(
|
|
`div`,
|
|
O,
|
|
toDisplayString(Oe(t.excerpt)),
|
|
1,
|
|
),
|
|
createElementVNode(`div`, k, [
|
|
t.type
|
|
? (openBlock(),
|
|
createBlock(
|
|
b,
|
|
{
|
|
key: 0,
|
|
value: t.type,
|
|
severity: `secondary`,
|
|
},
|
|
null,
|
|
8,
|
|
[`value`],
|
|
))
|
|
: createCommentVNode(``, !0),
|
|
createElementVNode(
|
|
`span`,
|
|
null,
|
|
toDisplayString(
|
|
(`formatTime` in n
|
|
? n.formatTime
|
|
: unref(dict_r))(t.create_time),
|
|
),
|
|
1,
|
|
),
|
|
]),
|
|
]),
|
|
]),
|
|
_: 2,
|
|
},
|
|
1032,
|
|
[`to`],
|
|
)
|
|
),
|
|
),
|
|
128,
|
|
)),
|
|
]))
|
|
: (openBlock(),
|
|
createBlock(C, {
|
|
key: 4,
|
|
icon: `pi pi-megaphone`,
|
|
title: `暂无新闻`,
|
|
description: `站点发布的新闻与公告会显示在这里`,
|
|
})),
|
|
unref(B) > 0
|
|
? (openBlock(),
|
|
createElementBlock(`div`, A, [
|
|
(r[4] ||= createElementVNode(
|
|
`div`,
|
|
{
|
|
class: `list-bulk`,
|
|
},
|
|
null,
|
|
-1,
|
|
)),
|
|
createVNode(
|
|
T,
|
|
{
|
|
rows: unref(H),
|
|
totalRecords: unref(B),
|
|
first: (unref(we) - 1) * unref(H),
|
|
rowsPerPageOptions: [10, 20, 50],
|
|
onPage: unref(Te),
|
|
},
|
|
null,
|
|
8,
|
|
[`rows`, `totalRecords`, `first`, `onPage`],
|
|
),
|
|
]))
|
|
: createCommentVNode(``, !0),
|
|
createVNode(
|
|
E,
|
|
{
|
|
visible: unref(W),
|
|
header: unref(K)?.title || unref(J)?.title || `新闻详情`,
|
|
modal: ``,
|
|
draggable: !1,
|
|
style: {
|
|
width: `760px`,
|
|
},
|
|
"onUpdate:visible": (r[2] ||= (e) => {
|
|
e || Q();
|
|
}),
|
|
},
|
|
{
|
|
footer: withCtx(() => [
|
|
unref(q) && unref(J)
|
|
? (openBlock(),
|
|
createBlock(h, {
|
|
key: 0,
|
|
label: `重新加载正文`,
|
|
icon: `pi pi-refresh`,
|
|
severity: `secondary`,
|
|
onClick: (r[1] ||= (e) => $(unref(J))),
|
|
}))
|
|
: createCommentVNode(``, !0),
|
|
createVNode(h, {
|
|
label: `关闭`,
|
|
severity: `secondary`,
|
|
text: ``,
|
|
onClick: Q,
|
|
}),
|
|
]),
|
|
default: withCtx(() => [
|
|
unref(G)
|
|
? (openBlock(),
|
|
createElementBlock(`div`, j, [
|
|
createVNode(y, {
|
|
height: `220px`,
|
|
}),
|
|
]))
|
|
: unref(q)
|
|
? (openBlock(),
|
|
createBlock(
|
|
v,
|
|
{
|
|
key: 1,
|
|
severity: `error`,
|
|
closable: !1,
|
|
},
|
|
{
|
|
default: withCtx(() => [
|
|
createTextVNode(toDisplayString(unref(q)), 1),
|
|
]),
|
|
_: 1,
|
|
},
|
|
))
|
|
: unref(K)
|
|
? (openBlock(),
|
|
createElementBlock(`div`, M, [
|
|
createElementVNode(
|
|
`div`,
|
|
N,
|
|
toDisplayString(
|
|
(`formatTime` in n
|
|
? n.formatTime
|
|
: unref(dict_r))(unref(K).create_time),
|
|
),
|
|
1,
|
|
),
|
|
createElementVNode(
|
|
`div`,
|
|
{
|
|
class: `news-detail-html`,
|
|
innerHTML: (`sanitizeHtml` in n
|
|
? n.sanitizeHtml
|
|
: unref(sanitize_n))(
|
|
(`decodeEntities` in n
|
|
? n.decodeEntities
|
|
: unref(sanitize_t))(
|
|
unref(K).content || ``,
|
|
),
|
|
),
|
|
},
|
|
null,
|
|
8,
|
|
xe,
|
|
),
|
|
]))
|
|
: createCommentVNode(``, !0),
|
|
]),
|
|
_: 1,
|
|
},
|
|
8,
|
|
[`visible`, `header`],
|
|
),
|
|
])
|
|
);
|
|
}
|
|
);
|
|
},
|
|
}),
|
|
[[`__scopeId`, `data-v-0f61b69d`]],
|
|
);
|
|
export {};
|
|
export default P;
|
|
</script>
|