小程序页面优化(门店)
This commit is contained in:
@@ -0,0 +1,159 @@
|
|||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { View, Text, ScrollView } from '@tarojs/components';
|
||||||
|
|
||||||
|
export type StoreCategoryNode = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
children?: StoreCategoryNode[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CategorySelection = {
|
||||||
|
parentId: string;
|
||||||
|
parentName: string;
|
||||||
|
childId: string;
|
||||||
|
childName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EMPTY_CATEGORY: CategorySelection = {
|
||||||
|
parentId: '',
|
||||||
|
parentName: '',
|
||||||
|
childId: '',
|
||||||
|
childName: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function formatCategoryLabel(sel: CategorySelection): string {
|
||||||
|
if (sel.childName) return sel.childName;
|
||||||
|
if (sel.parentName) return sel.parentName;
|
||||||
|
return '全部分类';
|
||||||
|
}
|
||||||
|
|
||||||
|
type CategoryPickerProps = {
|
||||||
|
open: boolean;
|
||||||
|
tree: StoreCategoryNode[];
|
||||||
|
value: CategorySelection;
|
||||||
|
onClose: () => void;
|
||||||
|
onConfirm: (next: CategorySelection) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TabKey = 'parent' | 'child';
|
||||||
|
|
||||||
|
export default function CategoryPicker({
|
||||||
|
open,
|
||||||
|
tree,
|
||||||
|
value,
|
||||||
|
onClose,
|
||||||
|
onConfirm,
|
||||||
|
}: CategoryPickerProps) {
|
||||||
|
const [draft, setDraft] = useState<CategorySelection>(value);
|
||||||
|
const [activeTab, setActiveTab] = useState<TabKey>('parent');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
setDraft(value);
|
||||||
|
setActiveTab(value.parentId ? 'child' : 'parent');
|
||||||
|
}, [open, value]);
|
||||||
|
|
||||||
|
const children = useMemo(() => {
|
||||||
|
const parent = tree.find((n) => n.id === draft.parentId);
|
||||||
|
return parent?.children ?? [];
|
||||||
|
}, [tree, draft.parentId]);
|
||||||
|
|
||||||
|
if (!open) return null;
|
||||||
|
|
||||||
|
function selectParent(node: StoreCategoryNode | null) {
|
||||||
|
if (!node) {
|
||||||
|
setDraft(EMPTY_CATEGORY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDraft({
|
||||||
|
parentId: node.id,
|
||||||
|
parentName: node.name,
|
||||||
|
childId: '',
|
||||||
|
childName: '',
|
||||||
|
});
|
||||||
|
setActiveTab('child');
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectChild(node: StoreCategoryNode | null) {
|
||||||
|
if (!node) {
|
||||||
|
setDraft((prev) => ({ ...prev, childId: '', childName: '' }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDraft((prev) => ({
|
||||||
|
...prev,
|
||||||
|
childId: node.id,
|
||||||
|
childName: node.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleConfirm() {
|
||||||
|
onConfirm(draft);
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="region-picker-overlay" onClick={onClose}>
|
||||||
|
<View className="region-picker-sheet" onClick={(e) => e.stopPropagation()}>
|
||||||
|
<View className="region-picker-toolbar">
|
||||||
|
<View className="region-picker-tabs">
|
||||||
|
<Text
|
||||||
|
className={`region-picker-tab${activeTab === 'parent' ? ' active' : ''}`}
|
||||||
|
onClick={() => setActiveTab('parent')}
|
||||||
|
>
|
||||||
|
{draft.parentName || '大类'}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
className={`region-picker-tab${activeTab === 'child' ? ' active' : ''}${!draft.parentId ? ' disabled' : ''}`}
|
||||||
|
onClick={() => draft.parentId && setActiveTab('child')}
|
||||||
|
>
|
||||||
|
{draft.childName || '细类'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Text className="region-picker-confirm ready" onClick={handleConfirm}>
|
||||||
|
确定
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<ScrollView className="region-picker-list" scrollY showScrollbar={false}>
|
||||||
|
{activeTab === 'parent' ? (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
className={`region-picker-option${!draft.parentId ? ' selected' : ''} region-picker-option--all`}
|
||||||
|
onClick={() => selectParent(null)}
|
||||||
|
>
|
||||||
|
<Text>全部分类</Text>
|
||||||
|
</View>
|
||||||
|
{tree.map((item) => (
|
||||||
|
<View
|
||||||
|
key={item.id}
|
||||||
|
className={`region-picker-option${draft.parentId === item.id ? ' selected' : ''}`}
|
||||||
|
onClick={() => selectParent(item)}
|
||||||
|
>
|
||||||
|
<Text>{item.name}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
className={`region-picker-option${!draft.childId ? ' selected' : ''} region-picker-option--all`}
|
||||||
|
onClick={() => selectChild(null)}
|
||||||
|
>
|
||||||
|
<Text>全部细类</Text>
|
||||||
|
</View>
|
||||||
|
{children.map((item) => (
|
||||||
|
<View
|
||||||
|
key={item.id}
|
||||||
|
className={`region-picker-option${draft.childId === item.id ? ' selected' : ''}`}
|
||||||
|
onClick={() => selectChild(item)}
|
||||||
|
>
|
||||||
|
<Text>{item.name}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { View, Text, Image, Input } from '@tarojs/components';
|
import { View, Text, Image, Input } from '@tarojs/components';
|
||||||
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
|
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
|
||||||
import PageShell from '../../components/PageShell';
|
import PageShell from '../../components/PageShell';
|
||||||
import TabMainHeader from '../../components/TabMainHeader';
|
import TabMainHeader from '../../components/TabMainHeader';
|
||||||
import RegionPicker from '../../components/RegionPicker';
|
import RegionPicker from '../../components/RegionPicker';
|
||||||
|
import CategoryPicker, {
|
||||||
|
EMPTY_CATEGORY,
|
||||||
|
formatCategoryLabel,
|
||||||
|
type CategorySelection,
|
||||||
|
type StoreCategoryNode,
|
||||||
|
} from '../../components/CategoryPicker';
|
||||||
import {
|
import {
|
||||||
DEFAULT_REGION,
|
DEFAULT_REGION,
|
||||||
formatRegionLabel,
|
formatRegionLabel,
|
||||||
@@ -26,20 +32,36 @@ type Store = {
|
|||||||
openTime?: string | null;
|
openTime?: string | null;
|
||||||
closeTime?: string | null;
|
closeTime?: string | null;
|
||||||
status?: string;
|
status?: string;
|
||||||
|
categoryId?: string | null;
|
||||||
|
category?: { id?: string; name?: string; parentId?: string | null } | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CATEGORY_TABS = ['全部', '火锅', '地方菜', '高端餐饮', '烧烤烤肉', '西餐'] as const;
|
|
||||||
const MOCK_DISTANCES = ['800m', '1.2km', '3.5km', '1.5km', '2.0km'];
|
const MOCK_DISTANCES = ['800m', '1.2km', '3.5km', '1.5km', '2.0km'];
|
||||||
|
|
||||||
export default function StoresPage() {
|
export default function StoresPage() {
|
||||||
const [stores, setStores] = useState<Store[]>([]);
|
const [stores, setStores] = useState<Store[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [categoryTab, setCategoryTab] = useState<string>('全部');
|
const [keywordInput, setKeywordInput] = useState('');
|
||||||
const [keyword, setKeyword] = useState('');
|
const [keyword, setKeyword] = useState('');
|
||||||
const [region, setRegion] = useState<RegionSelection>(DEFAULT_REGION);
|
const [region, setRegion] = useState<RegionSelection>(DEFAULT_REGION);
|
||||||
const [regionOpen, setRegionOpen] = useState(false);
|
const [regionOpen, setRegionOpen] = useState(false);
|
||||||
|
const [category, setCategory] = useState<CategorySelection>(EMPTY_CATEGORY);
|
||||||
|
const [categoryOpen, setCategoryOpen] = useState(false);
|
||||||
|
const [categoryTree, setCategoryTree] = useState<StoreCategoryNode[]>([]);
|
||||||
const [cityCode, setCityCode] = useState<string>(FALLBACK_CITY_CODE);
|
const [cityCode, setCityCode] = useState<string>(FALLBACK_CITY_CODE);
|
||||||
const regionLabel = formatRegionLabel(region);
|
const regionLabel = formatRegionLabel(region);
|
||||||
|
const categoryLabel = formatCategoryLabel(category);
|
||||||
|
|
||||||
|
const childIdsByParent = useMemo(() => {
|
||||||
|
const map = new Map<string, string[]>();
|
||||||
|
for (const root of categoryTree) {
|
||||||
|
map.set(
|
||||||
|
root.id,
|
||||||
|
(root.children ?? []).map((c) => c.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [categoryTree]);
|
||||||
|
|
||||||
useDidShow(() => {
|
useDidShow(() => {
|
||||||
syncTabBarSelected(1);
|
syncTabBarSelected(1);
|
||||||
@@ -49,6 +71,12 @@ export default function StoresPage() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void request<StoreCategoryNode[]>('/store-categories')
|
||||||
|
.then((tree) => setCategoryTree(Array.isArray(tree) ? tree : []))
|
||||||
|
.catch(() => setCategoryTree([]));
|
||||||
|
}, []);
|
||||||
|
|
||||||
const loadStores = useCallback(() => {
|
const loadStores = useCallback(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const path = cityCode ? `/stores?cityCode=${encodeURIComponent(cityCode)}` : '/stores';
|
const path = cityCode ? `/stores?cityCode=${encodeURIComponent(cityCode)}` : '/stores';
|
||||||
@@ -82,13 +110,37 @@ export default function StoresPage() {
|
|||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function matchesCategory(store: Store): boolean {
|
||||||
|
if (!category.parentId) return true;
|
||||||
|
const storeCatId = String(store.categoryId || store.category?.id || '');
|
||||||
|
const storeParentId = String(store.category?.parentId || '');
|
||||||
|
if (category.childId) {
|
||||||
|
return storeCatId === category.childId;
|
||||||
|
}
|
||||||
|
if (storeParentId && storeParentId === category.parentId) return true;
|
||||||
|
const siblings = childIdsByParent.get(category.parentId) ?? [];
|
||||||
|
return siblings.includes(storeCatId);
|
||||||
|
}
|
||||||
|
|
||||||
const filtered = stores.filter((s) => {
|
const filtered = stores.filter((s) => {
|
||||||
if (!matchesRegionFilter(s, region)) return false;
|
if (!matchesRegionFilter(s, region)) return false;
|
||||||
|
if (!matchesCategory(s)) return false;
|
||||||
if (!keyword.trim()) return true;
|
if (!keyword.trim()) return true;
|
||||||
const q = keyword.trim();
|
const q = keyword.trim();
|
||||||
return s.name.includes(q) || (s.address ?? '').includes(q) || (s.district ?? '').includes(q);
|
return s.name.includes(q) || (s.address ?? '').includes(q) || (s.district ?? '').includes(q);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function applySearch() {
|
||||||
|
setKeyword(keywordInput.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetFilters() {
|
||||||
|
setKeywordInput('');
|
||||||
|
setKeyword('');
|
||||||
|
setCategory(EMPTY_CATEGORY);
|
||||||
|
setRegion(DEFAULT_REGION);
|
||||||
|
}
|
||||||
|
|
||||||
function formatHours(store: Store) {
|
function formatHours(store: Store) {
|
||||||
if (store.openTime && store.closeTime) {
|
if (store.openTime && store.closeTime) {
|
||||||
return `营业时间: ${store.openTime}-${store.closeTime}`;
|
return `营业时间: ${store.openTime}-${store.closeTime}`;
|
||||||
@@ -99,29 +151,35 @@ export default function StoresPage() {
|
|||||||
return (
|
return (
|
||||||
<PageShell variant="tab" className="store-page no-tab-header">
|
<PageShell variant="tab" className="store-page no-tab-header">
|
||||||
<TabMainHeader title="门店" />
|
<TabMainHeader title="门店" />
|
||||||
<View className="store-toolbar">
|
|
||||||
<View className="store-location" onClick={() => setRegionOpen(true)}>
|
|
||||||
<View className="store-location-pin" />
|
|
||||||
<Text className="store-location-text">{regionLabel} ▾</Text>
|
|
||||||
</View>
|
|
||||||
<Input
|
|
||||||
className="store-search"
|
|
||||||
placeholder="搜索门店"
|
|
||||||
value={keyword}
|
|
||||||
onInput={(e) => setKeyword(e.detail.value)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="store-category-tabs">
|
<View className="store-filter">
|
||||||
{CATEGORY_TABS.map((tab) => (
|
<View className="store-search-row">
|
||||||
<Text
|
<Input
|
||||||
key={tab}
|
className="store-search-input"
|
||||||
className={`store-category-tab${categoryTab === tab ? ' store-category-tab--active' : ''}`}
|
placeholder="搜索门店名称/地址"
|
||||||
onClick={() => setCategoryTab(tab)}
|
value={keywordInput}
|
||||||
>
|
confirmType="search"
|
||||||
{tab}
|
onInput={(e) => setKeywordInput(e.detail.value)}
|
||||||
|
onConfirm={applySearch}
|
||||||
|
/>
|
||||||
|
<View className="store-search-btn" onClick={applySearch} aria-label="搜索">
|
||||||
|
<View className="store-search-icon" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="store-filter-row">
|
||||||
|
<View className="store-filter-chip" onClick={() => setRegionOpen(true)}>
|
||||||
|
<Text className="store-filter-chip-text">{regionLabel}</Text>
|
||||||
|
<Text className="store-filter-chip-arrow">▾</Text>
|
||||||
|
</View>
|
||||||
|
<View className="store-filter-chip" onClick={() => setCategoryOpen(true)}>
|
||||||
|
<Text className="store-filter-chip-text">{categoryLabel}</Text>
|
||||||
|
<Text className="store-filter-chip-arrow">▾</Text>
|
||||||
|
</View>
|
||||||
|
<Text className="store-filter-reset" onClick={resetFilters}>
|
||||||
|
重置
|
||||||
</Text>
|
</Text>
|
||||||
))}
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className="store-list">
|
<View className="store-list">
|
||||||
@@ -171,6 +229,13 @@ export default function StoresPage() {
|
|||||||
onClose={() => setRegionOpen(false)}
|
onClose={() => setRegionOpen(false)}
|
||||||
onConfirm={(next) => setRegion(next)}
|
onConfirm={(next) => setRegion(next)}
|
||||||
/>
|
/>
|
||||||
|
<CategoryPicker
|
||||||
|
open={categoryOpen}
|
||||||
|
tree={categoryTree}
|
||||||
|
value={category}
|
||||||
|
onClose={() => setCategoryOpen(false)}
|
||||||
|
onConfirm={(next) => setCategory(next)}
|
||||||
|
/>
|
||||||
</PageShell>
|
</PageShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,56 +3,34 @@
|
|||||||
background: var(--color-background);
|
background: var(--color-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-toolbar {
|
.store-filter {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 0 var(--space-page) 12px;
|
padding: 0 var(--space-page) 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-location {
|
.store-search-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-shrink: 0;
|
gap: 8px;
|
||||||
max-width: 42%;
|
width: 100%;
|
||||||
color: var(--color-on-surface-variant);
|
box-sizing: border-box;
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-location-pin {
|
.store-search-input {
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--color-heritage-red);
|
|
||||||
margin-right: 6px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.store-location-text {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.store-search {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 12px;
|
padding: 0 14px;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
background: var(--color-surface-container-low);
|
background: var(--color-surface-container-low);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
color: var(--color-on-surface);
|
color: var(--color-on-surface);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-search input,
|
.store-search-input input,
|
||||||
.store-search .taro-input,
|
.store-search-input .taro-input,
|
||||||
.store-search .weui-input {
|
.store-search-input .weui-input {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
min-height: 0 !important;
|
min-height: 0 !important;
|
||||||
@@ -66,32 +44,92 @@
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-category-tabs {
|
.store-search-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--color-heritage-red);
|
||||||
|
color: #fff;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 12px var(--space-page);
|
align-items: center;
|
||||||
overflow-x: auto;
|
justify-content: center;
|
||||||
white-space: nowrap;
|
|
||||||
border-bottom: 1px solid rgba(226, 190, 188, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-category-tab {
|
.store-search-icon {
|
||||||
flex-shrink: 0;
|
position: relative;
|
||||||
margin-right: 24px;
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border: 2px solid currentColor;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-search-icon::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: -5px;
|
||||||
|
bottom: -4px;
|
||||||
|
width: 7px;
|
||||||
|
height: 2px;
|
||||||
|
background: currentColor;
|
||||||
|
border-radius: 1px;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-filter-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-filter-chip {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--color-surface-container-low);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-filter-chip-text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--color-on-surface-variant);
|
color: var(--color-on-surface);
|
||||||
padding-bottom: 6px;
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-category-tab--active {
|
.store-filter-chip-arrow {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--color-subtle-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-filter-reset {
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
color: var(--color-heritage-red);
|
color: var(--color-heritage-red);
|
||||||
border-bottom-color: var(--color-heritage-red);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-list {
|
.store-list {
|
||||||
padding: 16px var(--space-page);
|
padding: 4px var(--space-page) 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-card {
|
.store-card {
|
||||||
|
|||||||
@@ -24,6 +24,16 @@ export class PublicStoreController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Controller('store-categories')
|
||||||
|
export class PublicStoreCategoriesController {
|
||||||
|
constructor(private readonly categories: StoreCategoryService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
list() {
|
||||||
|
return this.categories.listTree({ includeDisabled: false, ensure: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Controller('partner/store-categories')
|
@Controller('partner/store-categories')
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
export class PartnerStoreCategoriesController {
|
export class PartnerStoreCategoriesController {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
PartnerReportController,
|
PartnerReportController,
|
||||||
PartnerStoreCategoriesController,
|
PartnerStoreCategoriesController,
|
||||||
PartnerStoreController,
|
PartnerStoreController,
|
||||||
|
PublicStoreCategoriesController,
|
||||||
PublicStoreController,
|
PublicStoreController,
|
||||||
ShopDashboardController,
|
ShopDashboardController,
|
||||||
ShopStoreController,
|
ShopStoreController,
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
imports: [IamModule, AnalyticsModule, CityScopeModule, forwardRef(() => RedeemModule)],
|
imports: [IamModule, AnalyticsModule, CityScopeModule, forwardRef(() => RedeemModule)],
|
||||||
controllers: [
|
controllers: [
|
||||||
PublicStoreController,
|
PublicStoreController,
|
||||||
|
PublicStoreCategoriesController,
|
||||||
PartnerStoreCategoriesController,
|
PartnerStoreCategoriesController,
|
||||||
PartnerStoreController,
|
PartnerStoreController,
|
||||||
PartnerDashboardController,
|
PartnerDashboardController,
|
||||||
|
|||||||
Reference in New Issue
Block a user