Files
console-dev/app/components/GlobalSearch.vue
T

408 lines
16 KiB
Vue

<script>
// 全局搜索同时展示实例与商品结果,负责输入防抖、过期请求失效和结果跳转。
import "~/assets/styles/GlobalSearch.BQqObidy.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { useId } from "vue";
import { onUnmounted } from "vue";
import { ref } from "vue";
import { createCommentVNode } from "vue";
import { computed } from "vue";
import { isRef } from "vue";
import { createElementBlock } from "vue";
import { Fragment } from "vue";
import { withDirectives } from "vue";
import { createElementVNode } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import { vModelText } from "vue";
import { withModifiers } from "vue";
import { withKeys } from "vue";
import { useRouter } from "#app";
import { useApi } from "~/lib/auth.js";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
import { n as product_name_n } from "~/lib/product-name.js";
var w = {
class: `gs-input`,
},
T = [`id`, `aria-controls`],
E = [`aria-busy`],
D = {
key: 0,
class: `gs-state`,
role: `status`,
},
O = {
key: 1,
class: `gs-state gs-error`,
role: `alert`,
},
k = {
key: 0,
class: `gs-group`,
},
A = [`onClick`],
j = {
class: `gs-item-name`,
},
ee = {
class: `gs-item-sub`,
},
te = {
key: 1,
class: `gs-group`,
},
ne = [`onClick`],
M = {
class: `gs-item-name`,
},
N = {
class: `gs-item-sub`,
},
P = {
key: 2,
class: `gs-state`,
role: `status`,
},
F = Object.assign(
_plugin_vue_export_helper_t(
defineComponent({
__name: `GlobalSearch`,
setup(h) {
let S = useApi(),
F = useRouter(),
I = ref(``),
L = ref(!1),
R = ref(!1),
z = ref(``),
B = ref(null),
V = ref(null),
H = ref(null),
U = `global-search-${useId()}`,
W = null,
G = 0,
K = !1;
// 取消待执行的防抖任务并递增代次,已发出的旧请求也不能更新当前结果。
function q() {
(W !== null && clearTimeout(W),
(W = null),
(G += 1),
(R.value = !1));
}
function J(e = !1) {
(q(), e && H.value?.focus(), (L.value = !1));
}
// 搜索使用去空白关键词;只有当前代次且组件未卸载时才写入结果或错误。
async function Y() {
if ((q(), K)) return;
let e = G,
t = I.value.trim();
if (((z.value = ``), (B.value = null), !t)) {
L.value = !1;
return;
}
((R.value = !0), (L.value = !0));
try {
let n = await S.get(`/global_search`, {
keywords: t,
});
if (e !== G || K) return;
B.value = n;
} catch (t) {
if (e !== G || K) return;
((B.value = null),
(z.value =
t instanceof Error ? t.message : `搜索失败,请重试`));
} finally {
e === G && !K && (R.value = !1);
}
}
// 每次输入先关闭旧结果,停顿 300ms 后再查询,避免逐字符请求。
function X() {
(J(),
(B.value = null),
(z.value = ``),
I.value.trim() && (W = setTimeout(Y, 300)));
}
let Z = computed(() => B.value?.hosts ?? []),
Q = computed(() => B.value?.products ?? []),
re = computed(
() =>
!R.value &&
B.value != null &&
!Z.value.length &&
!Q.value.length,
);
function ie(e) {
(J(),
(I.value = ``),
(B.value = null),
F.push(`/servers/server?id=${e}`));
}
function ae(e) {
(J(), (I.value = ``), (B.value = null), F.push(`/products/${e}`));
}
function $(e) {
V.value && !V.value.contains(e.target) && J();
}
return (
onMounted(() => document.addEventListener(`click`, $)),
onUnmounted(() => {
((K = !0), document.removeEventListener(`click`, $), q());
}),
(n, r) => (
openBlock(),
createElementBlock(
`div`,
{
ref_key: `box`,
ref: V,
class: `gs`,
onKeydown: (r[2] ||= withKeys(
withModifiers((e) => J(!0), [`stop`, `prevent`]),
[`esc`],
)),
},
[
createElementVNode(`span`, w, [
(r[3] ||= createElementVNode(
`i`,
{
class: `pi pi-search`,
"aria-hidden": `true`,
},
null,
-1,
)),
withDirectives(
createElementVNode(
`input`,
{
id: `${U}-input`,
name: `keywords`,
ref_key: `input`,
ref: H,
"onUpdate:modelValue": (r[0] ||= (e) =>
isRef(I) ? (I.value = e) : null),
type: `search`,
"aria-label": `搜索产品标识或商品名`,
"aria-controls": unref(L) ? U : void 0,
autocomplete: `off`,
placeholder: `搜索产品标识 / 商品名`,
onInput: X,
onKeyup: withKeys(Y, [`enter`]),
onFocus: (r[1] ||= (e) =>
unref(I).trim() &&
(unref(B) || unref(z)) &&
(L.value = !0)),
},
null,
40,
T,
),
[[vModelText, unref(I)]],
),
]),
unref(L)
? (openBlock(),
createElementBlock(
`div`,
{
key: 0,
id: U,
class: `gs-panel`,
role: `region`,
"aria-label": `搜索结果`,
"aria-busy": unref(R),
},
[
unref(R)
? (openBlock(),
createElementBlock(`div`, D, [
...(r[4] ||= [
createElementVNode(
`i`,
{
class: `pi pi-spin pi-spinner`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(` 搜索中…`, -1),
]),
]))
: unref(z)
? (openBlock(),
createElementBlock(`div`, O, [
createElementVNode(
`span`,
null,
toDisplayString(unref(z)),
1,
),
createElementVNode(
`button`,
{
type: `button`,
class: `gs-retry`,
onClick: Y,
},
`重试搜索`,
),
]))
: (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
unref(Z).length
? (openBlock(),
createElementBlock(`div`, k, [
(r[5] ||= createElementVNode(
`div`,
{
class: `gs-group-title`,
},
`产品`,
-1,
)),
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(Z),
(t) => (
openBlock(),
createElementBlock(
`button`,
{
key: t.id,
type: `button`,
class: `gs-item`,
onClick: (e) => ie(t.id),
},
[
createElementVNode(
`span`,
j,
toDisplayString(
unref(product_name_n)(
t.product_name,
),
),
1,
),
createElementVNode(
`span`,
ee,
`#` +
toDisplayString(t.id) +
` · ` +
toDisplayString(t.name),
1,
),
],
8,
A,
)
),
),
128,
)),
]))
: createCommentVNode(``, !0),
unref(Q).length
? (openBlock(),
createElementBlock(`div`, te, [
(r[6] ||= createElementVNode(
`div`,
{
class: `gs-group-title`,
},
`商品`,
-1,
)),
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(Q),
(t) => (
openBlock(),
createElementBlock(
`button`,
{
key: t.id,
type: `button`,
class: `gs-item`,
onClick: (e) => ae(t.id),
},
[
createElementVNode(
`span`,
M,
toDisplayString(
unref(product_name_n)(
t.name,
),
),
1,
),
createElementVNode(
`span`,
N,
`#` +
toDisplayString(t.id),
1,
),
],
8,
ne,
)
),
),
128,
)),
]))
: createCommentVNode(``, !0),
unref(re)
? (openBlock(),
createElementBlock(
`div`,
P,
`没有匹配的结果`,
))
: createCommentVNode(``, !0),
],
64,
)),
],
8,
E,
))
: createCommentVNode(``, !0),
],
544,
)
)
);
},
}),
[[`__scopeId`, `data-v-bc4d898a`]],
),
{
__name: `GlobalSearch`,
},
);
export {};
export default F;
</script>