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

1633 lines
67 KiB
Vue

<script>
/**
* 商品目录:两级分类、搜索、分页和配置摘要;摘要缓存按账户令牌与 API origin 分区,切换会话后清理,不能跨账号复用。
*/
import "~/assets/styles/ProductGroupLabel.B1kWpFA-.css";
import "~/assets/styles/ProductDescription.Ctb7jDuO.css";
import "~/assets/styles/ProductConfigurationSummary.BqZns1LB.css";
import "~/assets/styles/ProductCategoryNotice.CwaR3eXN.css";
import "~/assets/styles/products.CrccEo2m.css";
import { i as rolldown_runtime_i } from "~/lib/rolldown-runtime.js";
import { useRuntimeConfig } from "#app";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { normalizeClass } from "vue";
import { watch } from "vue";
import { onScopeDispose } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { computed } from "vue";
import { effectScope } 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 { useRouter } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import { getCrypto } from "~/utils/crypto.js";
import { useAuth } 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 { n as product_name_n } from "~/lib/product-name.js";
import { t as product_name_t } from "~/lib/product-name.js";
import paginator from "primevue/paginator";
import ListEmpty from "~/components/ListEmpty.vue";
import skeleton from "primevue/skeleton";
import ProductCategoryNotice from "~/components/ProductCategoryNotice.vue";
import { n as ProductConfigurationSummary_n } from "~/components/ProductConfigurationSummary.vue";
import ProductConfigurationSummary from "~/components/ProductConfigurationSummary.vue";
import ProductDescription from "~/components/ProductDescription.vue";
import ProductGroupLabel from "~/components/ProductGroupLabel.vue";
import { productId } from "~/lib/product-id.js";
var j = rolldown_runtime_i(getCrypto(), 1),
M = 2592e6,
N = 100,
P = 512e3,
F = 3;
// 校验本地摘要缓存键的结构、字段长度和规范序列化,拒绝不符合当前格式的存储条目。
function I(e) {
if (typeof e != `string` || e.length > 4096) return !1;
try {
let t = JSON.parse(e);
return (
Array.isArray(t) &&
t.length === 3 &&
Number.isSafeInteger(t[0]) &&
t[0] > 0 &&
typeof t[1] == `string` &&
t[1].length <= 2048 &&
product_name_t(t[1]) &&
typeof t[2] == `string` &&
t[2].length <= 128 &&
JSON.stringify(t) === e
);
} catch {
return !1;
}
}
// 摘要仅接受有界、非空的字符串数组,不从本地存储信任任意结构。
function L(e) {
return (
Array.isArray(e) &&
e.length > 0 &&
e.length <= 5 &&
e.every(
(e) => typeof e == `string` && e.trim().length > 0 && e.length <= 1024,
)
);
}
// 管理摘要缓存、过期清理和同键在途请求复用;持久化容量有界,清空操作会使旧异步任务失效。
function R(e, t = Date.now, n) {
let r = new Map(),
i = new Map(),
a = 0,
o = (e) => JSON.stringify([e.id, e.name, e.module || e.res_module || ``]);
function s() {
try {
n?.storage.removeItem(n.key);
} catch {}
}
function c() {
if (n)
try {
let e = t(),
i = [...r]
.filter(
([t, n]) =>
I(t) && L(n.summary) && n.expiresAt > e && n.expiresAt <= e + M,
)
.map(([e, t]) => ({
key: e,
summary: t.summary,
expiresAt: t.expiresAt,
})),
a = JSON.stringify({
version: F,
entries: i,
});
for (; a.length > P && i.length;)
(i.shift(),
(a = JSON.stringify({
version: F,
entries: i,
})));
i.length ? n.storage.setItem(n.key, a) : s();
} catch {
s();
}
}
if (n)
try {
let e = n.storage.getItem(n.key);
if (e !== null) {
if (e.length > P) throw Error(`Summary cache is too large`);
let n = JSON.parse(e);
if (
!n ||
typeof n != `object` ||
!(`version` in n) ||
n.version !== F ||
!(`entries` in n) ||
!Array.isArray(n.entries)
)
throw Error(`Invalid summary cache`);
let i = t();
for (let e of n.entries)
if (!(
!e ||
typeof e != `object` ||
!I(e.key) ||
!L(e.summary) ||
typeof e.expiresAt != `number` ||
!Number.isFinite(e.expiresAt) ||
e.expiresAt <= i ||
e.expiresAt > i + M
))
for (
r.delete(e.key),
r.set(e.key, {
summary: [...e.summary],
expiresAt: e.expiresAt,
});
r.size > N;
)
r.delete(r.keys().next().value);
c();
}
} catch {
s();
}
function l(e) {
if (!product_name_t(e.name)) return;
let n = o(e),
i = r.get(n);
if (i) {
if (i.expiresAt <= t()) {
(r.delete(n), c());
return;
}
return (r.delete(n), r.set(n, i), c(), [...i.summary]);
}
}
async function u(n, s) {
if (!product_name_t(n.name)) return [];
let u = o(n);
if (s?.force) (r.delete(u), c());
else {
let e = l(n);
if (e) return e;
}
let d = i.get(u);
if (!d) {
let o = a,
s = {
id: n.id,
name: n.name,
module: n.module,
res_module: n.res_module,
};
((d = Promise.resolve()
.then(() => (o === a ? ProductConfigurationSummary_n(e, s) : []))
.then((e) => {
if (o !== a) return [];
if (e.length) {
for (
r.set(u, {
summary: [...e],
expiresAt: t() + M,
});
r.size > N;
)
r.delete(r.keys().next().value);
c();
}
return e;
})
.catch(() => [])
.finally(() => {
i.get(u) === d && i.delete(u);
})),
i.set(u, d));
}
return [...(await d)];
}
return {
load: u,
peek: l,
clear() {
(a++, r.clear(), i.clear(), s());
},
};
}
var z = new WeakMap();
// 为当前 Pinia 会话建立缓存管理;每次读取确认 localStorage 令牌一致,并按后端 origin 隔离数据。
function pe() {
let e = useAuth(),
n = useRuntimeConfig(),
r = useApi(),
i = z.get(e);
if (!i) {
((i = {
caches: new Map(),
version: 0,
}),
z.set(e, i));
let t = i;
effectScope(!0).run(() =>
watch(
() => e.token,
() => B(t),
{
flush: `sync`,
},
),
);
}
let a = i;
function s() {
if (localStorage.getItem(`jwt`) !== e.token) {
B(a);
return;
}
let t = String(n.public.apiBase ?? ``),
i = a.caches.get(t);
if (!i) {
let o = e.token,
s = a.version,
c = () =>
s === a.version &&
e.token === o &&
localStorage.getItem(`jwt`) === o &&
String(n.public.apiBase ?? ``) === t;
async function l(e) {
if (!c()) throw Error(`登录状态已变化`);
let t = await e();
if (!c()) throw Error(`登录状态已变化`);
return t;
}
((i = R(
{
get: (...e) => l(() => r.get(...e)),
getRaw: (...e) => l(() => r.getRaw(...e)),
},
Date.now,
{
storage: localStorage,
key: `console:product-summary:v3:${(0, j.default)(JSON.stringify([t, o])).toString()}`,
},
)),
a.caches.set(t, i));
}
return i;
}
return {
load: (e, t) => s()?.load(e, t) ?? Promise.resolve([]),
peek: (e) => s()?.peek(e),
clear: () => B(a),
};
}
// 提升缓存版本并清除各分区及持久化内容,使旧账号的在途摘要结果不能写回。
function B(e) {
e.version++;
for (let t of e.caches.values()) t.clear();
e.caches.clear();
}
var me = {
class: `catalog-page`,
},
he = {
class: `catalog-header`,
},
ge = [`aria-busy`],
_e = {
key: 1,
class: `category-error`,
role: `alert`,
},
ve = {
class: `category-options`,
role: `group`,
"aria-label": `一级产品分类`,
},
ye = [`aria-pressed`, `disabled`, `onClick`],
be = {
key: 0,
class: `category-options secondary-categories`,
role: `group`,
"aria-label": `二级产品分类`,
},
xe = [`aria-pressed`, `disabled`, `onClick`],
Se = {
key: 1,
class: `category-empty`,
},
Ce = {
key: 3,
class: `category-empty`,
},
we = [`aria-busy`],
Te = {
class: `catalog-toolbar`,
},
Ee = {
class: `search-input`,
},
De = {
class: `catalog-count`,
role: `status`,
"aria-live": `polite`,
},
Oe = {
key: 0,
class: `search-result`,
},
ke = {
key: 2,
class: `product-list`,
role: `status`,
"aria-label": `正在加载产品`,
},
Ae = [`aria-busy`, `inert`],
je = [`aria-busy`, `aria-label`],
Me = {
class: `product-heading`,
},
Ne = {
class: `product-purchase`,
},
Pe = {
class: `product-price`,
},
Fe = {
key: 0,
},
Ie = [`inert`, `aria-disabled`],
Le = {
class: `catalog-help`,
},
V = _plugin_vue_export_helper_t(
defineComponent({
__name: `index`,
setup(e) {
let t = useApi(),
o = useRoute(),
p = useRouter(),
y = pe(),
T = ref([]),
E = ref(0),
D = ref(!0),
A = ref(!1),
j = ref(``),
M = ref(1),
N = ref(12),
P = ref(``),
F = ref(``),
I = ref([]),
L = ref([]),
R = ref(null),
z = ref(null),
B = ref(!0),
V = ref(``),
H = computed(() =>
L.value.filter((e) => Number(e.parent_id) === R.value),
),
U = computed(() => {
let e = H.value.find((e) => Number(e.id) === z.value)?.description;
return typeof e == `string` ? e : ``;
}),
W = 0,
G = 0,
K = ``;
onScopeDispose(() => {
(W++, G++);
});
// 部分物理模块列表无可靠起价,需要后续从套餐配置补充,不直接把无效零价当报价。
function q(e) {
let t = Number(e.price);
return (
e.module === `jy_dcim` &&
e.pay_type !== `free` &&
(!Number.isFinite(t) || t <= 0)
);
}
// 列出有效父分类与子分类配对,供未指定分类时逐一寻找可展示商品。
function Re() {
return I.value.flatMap((e) =>
L.value
.filter((t) => Number(t.parent_id) === Number(e.id))
.map((t) => ({
first: Number(e.id),
second: Number(t.id),
})),
);
}
// 分类查询参数只接受正的安全整数,避免错误编号进入目录状态。
function J(e) {
if (typeof e != `string` || !/^[1-9]\d*$/.test(e)) return null;
let t = Number(e);
return Number.isSafeInteger(t) ? t : null;
}
// 依据实际分类树恢复查询选择,子分类必须属于有效父分类;无效组合不直接套用。
function ze() {
let e = J(o.query.g) ?? J(o.query.first_group_id),
t = J(o.query.sg) ?? J(o.query.second_group_id),
n = L.value.find((e) => Number(e.id) === t),
r = I.value.find((t) => Number(t.id) === e);
if (n) {
let e = Number(n.parent_id);
if (!r || Number(r.id) !== e) {
if (I.value.find((t) => Number(t.id) === e))
return {
first: e,
second: Number(n.id),
};
} else
return {
first: Number(r.id),
second: Number(n.id),
};
}
if (r) {
let e = L.value.find((e) => Number(e.parent_id) === Number(r.id));
return {
first: Number(r.id),
second: Number(e?.id) || null,
};
}
return null;
}
// 把分类选择规范为 g/sg 查询参数,去掉旧参数别名并保留其他查询。
function Y(e, t) {
let n = {
...o.query,
};
(delete n.first_group_id,
delete n.second_group_id,
e ? (n.g = String(e)) : delete n.g,
t ? (n.sg = String(t)) : delete n.sg,
(o.query.g !== n.g ||
o.query.sg !== n.sg ||
o.query.first_group_id !== void 0 ||
o.query.second_group_id !== void 0) &&
p.replace({
path: o.path,
query: n,
}));
}
// 未指定有效分类时依次查找非空分类;全部为空则回到首个分类展示真实空态。
async function Be(e) {
let t = Re(),
n = t[0];
if (!n) {
((R.value = Number(I.value[0]?.id) || null),
(z.value = null),
Y(R.value, z.value),
await Z());
return;
}
for (let n of t) {
if (e !== G) return;
((R.value = n.first), (z.value = n.second));
let t = await Z();
if (e !== G) return;
if (t !== `empty`) {
Y(R.value, z.value);
return;
}
}
((R.value = n.first),
(z.value = n.second),
Y(R.value, z.value),
(T.value = []),
(E.value = 0),
(A.value = !0),
(j.value = ``),
(D.value = !1));
}
// 重新加载两级分类后恢复有效选择与页码;分类已变更时重新选择,不沿用过期商品列表。
async function X() {
let e = ++G;
(W++, (B.value = !0), (D.value = !0), (V.value = ``), (j.value = ``));
let n = {
first: R.value,
second: z.value,
page: M.value,
};
try {
let [r, i] = await Promise.all([
t.get(`/product/group/first`),
t.get(`/product/group/second`),
]);
if (e !== G) return;
((I.value = r.list ?? []), (L.value = i.list ?? []));
let a = L.value.find(
(e) =>
Number(e.id) === n.second && Number(e.parent_id) === n.first,
),
o =
ze() ??
(a
? {
first: n.first,
second: n.second,
}
: null),
s = !!o;
((M.value =
o?.first === n.first && o?.second === n.second ? n.page : 1),
(B.value = !1),
s
? ((R.value = o.first),
(z.value = o.second),
Y(R.value, z.value),
await Z())
: await Be(e));
} catch (t) {
if (e !== G) return;
((V.value = t instanceof Error ? t.message : `加载产品分类失败`),
(D.value = !1));
} finally {
e === G && (B.value = !1);
}
}
// 切换一级分类时选择其首个子分类,重置分页并同步 URL。
function Ve(e) {
if (
!B.value &&
R.value !== e &&
I.value.some((t) => Number(t.id) === e)
)
return (
(R.value = e),
(z.value = Number(H.value[0]?.id) || null),
(M.value = 1),
Y(R.value, z.value),
Z()
);
}
// 只允许切换当前一级分类下存在的二级分类。
function He(e) {
if (
!B.value &&
z.value !== e &&
H.value.some((t) => Number(t.id) === e)
)
return ((z.value = e), (M.value = 1), Y(R.value, z.value), Z());
}
// 按当前分类/页码/搜索请求列表;仅同一查询复用现有摘要与起价,异步补全仍需匹配列表代次。
async function Z(e = !1) {
if (B.value || V.value) return;
let n = ++W;
if (!z.value) {
((T.value = []),
(E.value = 0),
(A.value = !0),
(K = ``),
(j.value = ``),
(D.value = !1));
return;
}
((D.value = !0), (j.value = ``));
try {
let r = {
id: z.value,
page: M.value,
limit: N.value,
};
F.value && (r.keywords = F.value);
let i = JSON.stringify(r),
a = K === i,
o = new Map(a ? T.value.map((e) => [e.id, e]) : []);
a || ((T.value = []), (E.value = 0), (A.value = !1));
let s = await t.get(`/product`, r);
if (n !== W) return;
((T.value = (s.list ?? [])
.filter((t) => productId(t.id) !== null)
.map((t) => ({
...t,
startingPrice: q(t)
? (o.get(t.id)?.startingPrice ?? null)
: void 0,
configurationSummary: product_name_t(t.name)
? e
? o.get(t.id)?.configurationSummary
: y.peek(t)
: [],
}))),
(E.value = Number(s.count) || 0),
(K = i),
(A.value = !0));
for (let t of T.value)
product_name_t(t.name) &&
(e || t.configurationSummary === void 0) &&
y
.load(t, {
force: e,
})
.catch(() => [])
.then((e) => {
n === W && (t.configurationSummary = e);
});
for (let e of T.value)
q(e) &&
t
.get(`/product/${e.id}/jy_dcim/order_page`)
.then((t) => {
if (n !== W) return;
let r = (t.package ?? [])
.map((e) => Number(e.price))
.filter((e) => Number.isFinite(e) && e > 0);
e.startingPrice = r.length ? Math.min(...r).toFixed(2) : ``;
})
.catch(() => {
n === W && (e.startingPrice = ``);
});
return T.value.length ? `loaded` : `empty`;
} catch (e) {
return n === W
? ((j.value = e instanceof Error ? e.message : `加载产品失败`),
`failed`)
: void 0;
} finally {
n === W && (D.value = !1);
}
}
// 搜索确认后应用关键词并回到第一页。
function Ue() {
return ((F.value = P.value.trim()), (M.value = 1), Z());
}
// 清空草稿关键词,再复用统一搜索提交逻辑。
function We() {
return ((P.value = ``), Ue());
}
// 加载中或当前查询有错误时不接受分页,防止叠加新的列表状态。
function Ge(e) {
if (!(D.value || B.value || j.value || V.value))
return ((M.value = e.page + 1), (N.value = e.rows), Z());
}
// 把已知计费周期转换为中文单位,未知英文枚举不直接作为用户文案。
function Ke(e) {
return e
? {
monthly: `月`,
quarterly: `季`,
semiannually: `半年`,
annually: `年`,
biennially: `两年`,
triennially: `三年`,
onetime: `次`,
}[e] || (/[\u4e00-\u9fff]/.test(e) ? e : ``)
: ``;
}
// 仅在库存控制已开启且数量明确不大于零时判定售罄。
function Q(e) {
return (
Number(e.stock_control) === 1 && e.qty != null && Number(e.qty) <= 0
);
}
// 不允许订购标记或未知模块会阻止进入配置流程,不能单靠列表有价格就开放下单。
function $(e) {
let t = e.module?.trim();
return (
e.order_allowed === !1 ||
(!!t &&
![
`mf_cloud`,
`remf_cloud`,
`mf_dcim`,
`remf_dcim`,
`idcsmart_common`,
`remf_finance`,
`remf_finance_dcim`,
`remf_finance_common`,
`kky_ecs`,
`jy_cloud`,
`jy_dcim`,
].includes(t))
);
}
// 区别免费、有效起价、补价中、缺货及配置后报价,不把未知金额显示成零元。
function qe(e) {
if (e.pay_type === `free`) return `免费`;
let t = Number(e.price);
return Number.isFinite(t) && t > 0
? `¥ ${t.toFixed(2)}`
: e.startingPrice === null
? `正在获取价格…`
: e.startingPrice
? `¥ ${e.startingPrice} 起`
: Q(e)
? `暂无价格`
: `配置后报价`;
}
// 详情链接保留当前有效分类选择,返回目录时可恢复上下文。
function Je(e) {
return {
path: `/products/${e}`,
query: {
...(R.value
? {
g: String(R.value),
}
: {}),
...(z.value
? {
sg: String(z.value),
}
: {}),
},
};
}
return (
onMounted(X),
// 分类、商品列表、起价/摘要及库存状态分别呈现;配置摘要是辅助信息,最终报价仍在配置器中确认。
(e, t) => {
let i = NuxtLink,
o = skeleton,
s = button,
l = ProductGroupLabel,
f = ProductCategoryNotice,
p = inputtext,
y = message,
S = ListEmpty,
C = ProductConfigurationSummary,
w = ProductDescription,
k = paginator;
return (
openBlock(),
createElementBlock(`div`, me, [
createElementVNode(`header`, he, [
(t[6] ||= createElementVNode(
`div`,
null,
[
createElementVNode(`h1`, null, `订购产品`),
createElementVNode(
`p`,
null,
`选择产品,按需配置计算、存储与网络资源。`,
),
],
-1,
)),
createVNode(
i,
{
to: `/cart`,
class: `cart-link`,
},
{
default: withCtx(() => [
...(t[5] ||= [
createElementVNode(
`i`,
{
class: `pi pi-shopping-cart`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(`购物车`, -1),
]),
]),
_: 1,
},
),
]),
createElementVNode(
`section`,
{
class: `catalog-categories`,
"aria-label": `产品分类`,
"aria-busy": unref(B),
},
[
(t[7] ||= createElementVNode(`h2`, null, `产品分类`, -1)),
unref(B) && !unref(I).length
? (openBlock(),
createBlock(o, {
key: 0,
height: `86px`,
}))
: createCommentVNode(``, !0),
unref(V)
? (openBlock(),
createElementBlock(`div`, _e, [
createElementVNode(
`span`,
null,
`产品分类加载失败:` + toDisplayString(unref(V)),
1,
),
createVNode(s, {
label: `重新加载分类`,
severity: `secondary`,
outlined: ``,
onClick: X,
}),
]))
: createCommentVNode(``, !0),
unref(I).length
? (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
createElementVNode(`div`, ve, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(I),
(e) => (
openBlock(),
createElementBlock(
`button`,
{
key: e.id,
type: `button`,
class: normalizeClass({
active: unref(R) === Number(e.id),
}),
"aria-pressed":
unref(R) === Number(e.id),
disabled: unref(B) || !!unref(V),
onClick: (t) => Ve(Number(e.id)),
},
[
createVNode(
l,
{
name: e.name,
},
null,
8,
[`name`],
),
],
10,
ye,
)
),
),
128,
)),
]),
unref(H).length
? (openBlock(),
createElementBlock(`div`, be, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(H),
(e) => (
openBlock(),
createElementBlock(
`button`,
{
key: e.id,
type: `button`,
class: normalizeClass({
active: unref(z) === Number(e.id),
}),
"aria-pressed":
unref(z) === Number(e.id),
disabled: unref(B) || !!unref(V),
onClick: (t) => He(Number(e.id)),
},
[
createVNode(
l,
{
name: e.name,
},
null,
8,
[`name`],
),
],
10,
xe,
)
),
),
128,
)),
]))
: (openBlock(),
createElementBlock(
`p`,
Se,
`该分类暂无可选产品。`,
)),
],
64,
))
: !unref(B) && !unref(V)
? (openBlock(),
createElementBlock(`p`, Ce, `暂无可选产品分类。`))
: createCommentVNode(``, !0),
],
8,
ge,
),
unref(U)
? (openBlock(),
createBlock(
f,
{
key: 0,
description: unref(U),
},
null,
8,
[`description`],
))
: createCommentVNode(``, !0),
createElementVNode(
`section`,
{
class: `catalog-panel`,
"aria-label": `可订购产品`,
"aria-busy": unref(D),
},
[
createElementVNode(`div`, Te, [
createElementVNode(
`form`,
{
class: `catalog-search`,
role: `search`,
onSubmit: withModifiers(Ue, [`prevent`]),
},
[
createElementVNode(`div`, Ee, [
(t[8] ||= createElementVNode(
`i`,
{
class: `pi pi-search`,
"aria-hidden": `true`,
},
null,
-1,
)),
createVNode(
p,
{
modelValue: unref(P),
"onUpdate:modelValue": (t[0] ||= (e) =>
isRef(P) ? (P.value = e) : null),
placeholder: `搜索产品名称或配置`,
"aria-label": `搜索产品名称或配置`,
},
null,
8,
[`modelValue`],
),
]),
createVNode(
s,
{
type: `submit`,
label: `搜索`,
disabled: unref(D) || !!unref(V) || !unref(z),
},
null,
8,
[`disabled`],
),
],
32,
),
createElementVNode(`div`, De, [
createElementVNode(
`span`,
null,
toDisplayString(
unref(D)
? `正在加载产品…`
: `共 ${unref(E)} 款产品`,
),
1,
),
createVNode(
s,
{
icon: `pi pi-refresh`,
"aria-label": `刷新产品`,
title: `刷新产品`,
text: ``,
severity: `secondary`,
disabled: unref(D),
onClick: (t[1] ||= (e) =>
unref(V) || !unref(z) ? X() : Z(!0)),
},
null,
8,
[`disabled`],
),
]),
]),
unref(F)
? (openBlock(),
createElementBlock(`div`, Oe, [
createElementVNode(
`span`,
null,
`“` + toDisplayString(unref(F)) + `” 的搜索结果`,
1,
),
createElementVNode(
`button`,
{
type: `button`,
onClick: We,
},
[
...(t[9] ||= [
createTextVNode(`清除搜索`, -1),
createElementVNode(
`i`,
{
class: `pi pi-times`,
"aria-hidden": `true`,
},
null,
-1,
),
]),
],
),
]))
: createCommentVNode(``, !0),
(t[13] ||= createElementVNode(
`div`,
{
class: `product-columns`,
"aria-hidden": `true`,
},
[
createElementVNode(`span`, null, `产品`),
createElementVNode(`span`, null, `产品介绍`),
createElementVNode(`span`, null, `价格与订购`),
],
-1,
)),
(unref(j) || unref(V)) && unref(A)
? (openBlock(),
createBlock(
y,
{
key: 1,
severity: `error`,
closable: !1,
role: `alert`,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(j) || unref(V)),
1,
),
createVNode(
s,
{
label: `重新加载`,
severity: `secondary`,
text: ``,
disabled: unref(D) || unref(B),
onClick: (t[2] ||= (e) =>
unref(V) ? X() : Z()),
},
null,
8,
[`disabled`],
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(D) && !unref(A)
? (openBlock(),
createElementBlock(`div`, ke, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(N),
(e) => (
openBlock(),
createElementBlock(
`div`,
{
key: e,
class: `product-row product-skeleton`,
},
[
createVNode(o, {
height: `22px`,
width: `70%`,
}),
createVNode(o, {
height: `42px`,
}),
createVNode(o, {
height: `38px`,
}),
],
)
),
),
128,
)),
]))
: unref(V) && !unref(A)
? (openBlock(),
createBlock(S, {
key: 3,
icon: `pi pi-exclamation-circle`,
title: `暂时无法展示产品`,
description: `重新加载上方产品分类后查看可订购产品。`,
}))
: unref(j) && !unref(A)
? (openBlock(),
createBlock(
S,
{
key: 4,
icon: `pi pi-exclamation-circle`,
title: `产品加载失败`,
description: unref(j),
},
{
default: withCtx(() => [
createVNode(s, {
label: `重新加载`,
severity: `secondary`,
outlined: ``,
onClick: (t[3] ||= (e) => Z()),
}),
]),
_: 1,
},
8,
[`description`],
))
: unref(T).length
? (openBlock(),
createElementBlock(
`div`,
{
key: 5,
class: `product-list`,
"aria-busy": unref(D),
inert:
unref(D) || unref(B) || unref(j) || unref(V)
? !0
: void 0,
},
[
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(T),
(e) => (
openBlock(),
createElementBlock(
`article`,
{
key: e.id,
class: normalizeClass([
`product-row`,
{
unavailable: Q(e) || $(e),
"product-skeleton":
e.configurationSummary ===
void 0,
},
]),
"aria-busy":
e.configurationSummary === void 0,
"aria-label":
e.configurationSummary === void 0
? `正在加载产品配置`
: void 0,
},
[
e.configurationSummary === void 0
? (openBlock(),
createElementBlock(
Fragment,
{
key: 0,
},
[
createVNode(o, {
height: `64px`,
width: `85%`,
}),
createVNode(o, {
height: `42px`,
}),
createVNode(o, {
height: `38px`,
}),
],
64,
))
: (openBlock(),
createElementBlock(
Fragment,
{
key: 1,
},
[
createElementVNode(
`div`,
Me,
[
(t[10] ||=
createElementVNode(
`span`,
{
class: `product-icon`,
},
[
createElementVNode(
`i`,
{
class: `pi pi-server`,
"aria-hidden": `true`,
},
),
],
-1,
)),
createElementVNode(
`div`,
null,
[
createElementVNode(
`h2`,
null,
toDisplayString(
unref(
product_name_n,
)(e.name),
),
1,
),
createVNode(
C,
{
product: e,
summary:
e.configurationSummary,
},
null,
8,
[
`product`,
`summary`,
],
),
createElementVNode(
`span`,
{
class:
normalizeClass(
[
`product-status`,
{
"in-stock":
!Q(
e,
) &&
!$(e),
},
],
),
},
[
createElementVNode(
`i`,
{
class:
normalizeClass(
Q(e) ||
$(e)
? `pi pi-minus-circle`
: `pi pi-check-circle`,
),
"aria-hidden": `true`,
},
null,
2,
),
createTextVNode(
toDisplayString(
$(e)
? `暂未上线`
: Q(e)
? `暂时缺货`
: `可订购`,
),
1,
),
],
2,
),
],
),
],
),
createVNode(
w,
{
description:
e.description,
"product-name": e.name,
compact: ``,
},
null,
8,
[
`description`,
`product-name`,
],
),
createElementVNode(
`div`,
Ne,
[
createElementVNode(
`div`,
Pe,
[
createElementVNode(
`strong`,
null,
toDisplayString(
qe(e),
),
1,
),
e.pay_type !==
`free` &&
Number(e.price) >
0 &&
Ke(e.cycle)
? (openBlock(),
createElementBlock(
`span`,
Fe,
` / ` +
toDisplayString(
Ke(
e.cycle,
),
) +
`起`,
1,
))
: createCommentVNode(
``,
!0,
),
],
),
$(e) || Q(e)
? (openBlock(),
createBlock(
s,
{
key: 0,
label: $(e)
? `暂未上线`
: `暂时缺货`,
severity: `secondary`,
outlined: ``,
disabled: ``,
},
null,
8,
[`label`],
))
: (openBlock(),
createBlock(
i,
{
key: 1,
to: Je(e.id),
class: `configure-link`,
"aria-disabled":
unref(D) ||
unref(B) ||
!!unref(j) ||
!!unref(V),
tabindex:
unref(D) ||
unref(B) ||
unref(j) ||
unref(V)
? -1
: void 0,
"aria-label": `配置 ${unref(product_name_n)(e.name)}`,
},
{
default:
withCtx(
() => [
...(t[11] ||=
[
createTextVNode(
`选择配置`,
-1,
),
createElementVNode(
`i`,
{
class: `pi pi-arrow-right`,
"aria-hidden": `true`,
},
null,
-1,
),
]),
],
),
_: 1,
},
8,
[
`to`,
`aria-disabled`,
`tabindex`,
`aria-label`,
],
)),
],
),
],
64,
)),
],
10,
je,
)
),
),
128,
)),
],
8,
Ae,
))
: (openBlock(),
createBlock(
S,
{
key: 6,
icon: `pi pi-box`,
title: unref(F)
? `未找到匹配的产品`
: `暂无可订购产品`,
description: unref(F)
? `尝试其他名称或配置关键字,或切换产品分类。`
: `当前分类没有可供订购的产品,请切换分类或稍后刷新查看。`,
},
{
default: withCtx(() => [
unref(F)
? (openBlock(),
createBlock(s, {
key: 0,
label: `清除搜索`,
severity: `secondary`,
outlined: ``,
onClick: We,
}))
: (openBlock(),
createBlock(s, {
key: 1,
label: `刷新产品`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: (t[4] ||= (e) =>
unref(z) ? Z() : X()),
})),
]),
_: 1,
},
8,
[`title`, `description`],
)),
unref(T).length
? (openBlock(),
createElementBlock(
`footer`,
{
key: 7,
class: `catalog-footer`,
inert:
unref(D) || unref(B) || unref(j) || unref(V)
? !0
: void 0,
"aria-disabled":
unref(D) || unref(B) || !!unref(j) || !!unref(V),
},
[
(t[12] ||= createElementVNode(
`p`,
null,
`展示价格为起价,具体费用以所选配置的报价为准。`,
-1,
)),
unref(E) > unref(N)
? (openBlock(),
createBlock(
k,
{
key: 0,
rows: unref(N),
"total-records": unref(E),
first: (unref(M) - 1) * unref(N),
"rows-per-page-options": [12, 24, 48],
onPage: Ge,
},
null,
8,
[`rows`, `total-records`, `first`],
))
: createCommentVNode(``, !0),
],
8,
Ie,
))
: createCommentVNode(``, !0),
],
8,
we,
),
createElementVNode(`p`, Le, [
(t[15] ||= createTextVNode(`需要选型建议?`, -1)),
createVNode(
i,
{
to: `/tickets/create`,
},
{
default: withCtx(() => [
...(t[14] ||= [
createTextVNode(`联系技术支持`, -1),
createElementVNode(
`i`,
{
class: `pi pi-arrow-up-right`,
"aria-hidden": `true`,
},
null,
-1,
),
]),
]),
_: 1,
},
),
]),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-a6c63d86`]],
);
export {};
export default V;
</script>