Files
vmall/apps/mall/pages/stores/index.vue
T
Chengdong Zhang 0d0e10b97b feat(ui): adopt Tailwind v4 design system and archive change
- Add tailwindcss v4 + @tailwindcss/vite to mall, shop-admin, admin
- Add @vmall/shared/theme.css tokens with html[data-accent] presets
- Add @vmall/ui kit (VBtn/VBadge/VField/VInput/VCard/VPanel/VTable/VPage,
  VAccentSwatch, useAccent) as a Nuxt module
- Convert all three apps to kit + utilities; delete ui.css/mall.css and
  every <style scoped>; consoles get accent presets, mall locked to red
- Fix VCard boolean prop default (padding) and PDP/store stale
  useAsyncData keys on param navigation
- Archive adopt-tailwind-design-system; new frontend-ui capability spec
2026-09-22 18:22:50 +08:00

40 lines
2.9 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 { t as pick } from "@vmall/shared";
const { locale, t } = useI18n();
const { $api } = useNuxtApp();
// Shared with the order surfaces, which resolve shop ids to names from it.
const { data: shops } = await useAsyncData("shops", () => $api.listShops(), { default: () => [] });
const stores = computed(() => shops.value);
const breadcrumb = computed(() => [
{ label: t("stores.breadcrumbHome"), to: "/" },
{ label: t("stores.directory") },
]);
</script>
<template>
<div class="min-h-screen bg-bg pb-16 font-sans text-text">
<UiBreadcrumb :items="breadcrumb" />
<section class="mx-auto w-full max-w-mall px-4">
<VCard :padded="false" class="overflow-hidden">
<header class="flex items-center justify-between border-b border-border bg-bg px-[18px] py-3.5"><h1 class="m-0 border-l-[3px] border-primary pl-2.5 text-base font-medium">{{ t("stores.directory") }}</h1></header>
<div v-if="stores.length" class="divide-y divide-border">
<article v-for="store in stores" :key="store.id" class="flex min-h-[126px] items-center gap-0 bg-surface px-[22px] py-5 hover:bg-bg max-sm:flex-wrap max-sm:gap-3">
<NuxtLink :to="`/stores/${store.slug}`" class="flex h-20 w-20 shrink-0 items-center justify-center border border-border bg-surface">
<img v-if="store.logo" class="h-[72px] w-[72px] object-contain" :src="store.logo" :alt="pick(store.name, locale)" loading="lazy" />
<span v-else class="flex h-full w-full items-center justify-center bg-bg text-[28px] font-bold uppercase text-muted" aria-hidden="true">{{ pick(store.name, locale).slice(0, 1) }}</span>
</NuxtLink>
<div class="min-w-0 flex-1 px-7 max-sm:w-[calc(100%-100px)] max-sm:px-0"><NuxtLink :to="`/stores/${store.slug}`" class="block text-base font-bold text-text no-underline hover:text-primary">{{ pick(store.name, locale) }}</NuxtLink><p v-if="store.company" class="mt-2 text-[13px] text-muted">{{ store.company }}</p><p v-if="store.region || store.address" class="mt-2 flex gap-6 text-xs text-muted max-sm:flex-col max-sm:gap-1"><span v-if="store.region">{{ t("stores.region") }}{{ store.region }}</span><span v-if="store.address">{{ t("stores.address") }}{{ pick(store.address, locale) }}</span></p></div>
<div class="w-[130px] text-center text-[13px] text-primary max-sm:ml-[100px] max-sm:w-auto">{{ t("stores.positiveRate") }}</div>
<div class="flex w-[145px] flex-col items-end gap-2.5 max-sm:ml-auto max-sm:w-auto"><NuxtLink :to="`/stores/${store.slug}`" class="inline-flex items-center rounded-md border border-primary bg-primary px-4 py-2 text-sm font-medium text-white no-underline hover:bg-primary-hover">{{ t("stores.visitStore") }}</NuxtLink></div>
</article>
</div>
<UiEmptyState v-else :text="t('common.empty')" />
</VCard>
</section>
</div>
</template>