Files
workspace/code/link-site/link-nuxt/app/pages/directory.vue
T
2026-08-31 22:32:03 +08:00

143 lines
4.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { companies, regionOptions, routeOptions, serviceOptions } from '~/data/mock'
// USelect 的候选项不允许 value 为空字符串(Reka UI 会直接抛错导致页面水合失败),
// 因此「全部」使用 'all' 作为哨兵值,筛选时再转换为「不限制」。
const ANY = 'all'
const route = useRoute()
const keyword = ref(typeof route.query.q === 'string' ? route.query.q : '')
const region = ref<string>(ANY)
const routeFilter = ref<string>(ANY)
const serviceFilter = ref<string>(ANY)
const verifiedOnly = ref(false)
// 平台主导航的搜索框会带 q 跳转;目录页内再次搜索时组件复用,需同步 query
watch(() => route.query.q, (q) => {
if (typeof q === 'string') {
keyword.value = q
}
})
const toItems = (options: string[], label: string) => [
{ label: `全部${label}`, value: ANY },
...options.map(o => ({ label: o, value: o }))
]
const filtered = computed(() => {
const kw = keyword.value.trim().toLowerCase()
return companies.filter((c) => {
if (verifiedOnly.value && !c.verified) return false
if (region.value !== ANY && c.region !== region.value) return false
if (routeFilter.value !== ANY && !c.routes.includes(routeFilter.value)) return false
if (serviceFilter.value !== ANY && !c.services.includes(serviceFilter.value)) return false
if (kw && !(`${c.name}${c.slogan}${c.description}${c.city}`.toLowerCase().includes(kw))) return false
return true
})
})
function resetFilters() {
keyword.value = ''
region.value = ANY
routeFilter.value = ANY
serviceFilter.value = ANY
verifiedOnly.value = false
}
useSeoMeta({
title: '企业目录 - 链通物流',
description: '按航线、服务类型与地区筛选物流企业:海运整箱、拼箱、空运、铁路联运、报关报检、仓储与跨境电商物流服务商目录。'
})
</script>
<template>
<UPage>
<UPageHeader
headline="企业目录"
title="查找物流服务商"
description="按航线、服务类型、地区筛选,或直接搜索企业名称与主营关键词。"
/>
<UPageBody>
<UContainer>
<!-- 筛选器 -->
<div class="flex flex-col lg:flex-row gap-3 lg:items-center mb-6">
<UInput
v-model="keyword"
icon="i-lucide-search"
placeholder="搜索企业名称、主营关键词…"
class="flex-1"
size="lg"
/>
<div class="flex flex-wrap gap-3 items-center">
<USelect
v-model="region"
:items="toItems(regionOptions, '地区')"
icon="i-lucide-map-pin"
class="w-32"
/>
<USelect
v-model="routeFilter"
:items="toItems(routeOptions, '航线')"
icon="i-lucide-globe"
class="w-36"
/>
<USelect
v-model="serviceFilter"
:items="toItems(serviceOptions, '服务')"
icon="i-lucide-ship"
class="w-40"
/>
<UCheckbox
v-model="verifiedOnly"
label="仅认证企业"
icon="i-lucide-badge-check"
/>
</div>
</div>
<!-- 结果统计 -->
<p class="text-sm text-muted mb-4">
共找到 <span class="font-semibold text-default">{{ filtered.length }}</span> 家企业
</p>
<!-- 企业列表 -->
<div
v-if="filtered.length"
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
>
<CompanyCard
v-for="company in filtered"
:key="company.slug"
:company="company"
/>
</div>
<div
v-else
class="flex flex-col items-center gap-3 py-20 text-center"
>
<UIcon
name="i-lucide-search-x"
class="size-10 text-dimmed"
/>
<p class="text-toned">
没有符合条件的企业,试试放宽筛选条件
</p>
<UButton
label="重置筛选"
color="neutral"
variant="outline"
icon="i-lucide-rotate-ccw"
@click="resetFilters"
/>
</div>
<div class="h-12" />
</UContainer>
</UPageBody>
</UPage>
</template>