Compare commits
6 Commits
b4c36923ea
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 98d7981115 | |||
| 2b2a02fe88 | |||
| 007d380757 | |||
| 79a78da82c | |||
| cfcd378c08 | |||
| 24f9c2f995 |
@@ -1,6 +1,6 @@
|
||||
# TakeoutSaaS C-Side Mini Program (Taro)
|
||||
# TakeoutSaaS.C-Side-Mini-Program-V1
|
||||
|
||||
基于官方 `Taro CLI` 初始化的微信小程序前端,当前使用 `Taro 4 + Vue3 + Pinia + NutUI Taro + Vite`。
|
||||
基于官方 `Taro CLI` 初始化的面向消费者的微信小程序前端,当前使用 `Taro 4 + Vue3 + Pinia + NutUI Taro + Vite`。
|
||||
|
||||
## 当前状态
|
||||
- 已切换到 `Taro + Vue3 + NutUI` 方案。
|
||||
@@ -36,6 +36,7 @@ pnpm lint
|
||||
## 目录结构
|
||||
- `src/pages`:按页面域拆分,每个页面保持 `index.vue + composables + styles`
|
||||
- `src/components`:复用组件目录,统一使用 `src/components/<kebab-name>/index.vue`
|
||||
- `mini/custom-tab-bar`:微信小程序原生 `custom-tab-bar` 源文件,会在构建时复制到 `dist/custom-tab-bar`
|
||||
- `src/stores`:`app`、`cart`
|
||||
- `src/services`:请求封装与 Mini API 领域接口
|
||||
- `src/shared`:类型、常量、mock 数据、格式化函数
|
||||
|
||||
@@ -35,7 +35,12 @@ export default defineConfig<'vite'>(async (merge) => {
|
||||
__TENANT_CODE__: JSON.stringify(tenantCode)
|
||||
},
|
||||
copy: {
|
||||
patterns: [],
|
||||
patterns: [
|
||||
{
|
||||
from: 'mini/custom-tab-bar',
|
||||
to: 'dist/custom-tab-bar'
|
||||
}
|
||||
],
|
||||
options: {}
|
||||
},
|
||||
framework: 'vue3',
|
||||
|
||||
49
mini/custom-tab-bar/index.js
Normal file
@@ -0,0 +1,49 @@
|
||||
Component({
|
||||
data: {
|
||||
selected: 0,
|
||||
list: [
|
||||
{
|
||||
pagePath: 'pages/home/index',
|
||||
text: '首页',
|
||||
iconPath: '../assets/tabbar/home.png',
|
||||
selectedIconPath: '../assets/tabbar/home-active.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/menu/index',
|
||||
text: '点餐',
|
||||
iconPath: '../assets/tabbar/menu.png',
|
||||
selectedIconPath: '../assets/tabbar/menu-active.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/orders/index',
|
||||
text: '订单',
|
||||
iconPath: '../assets/tabbar/orders.png',
|
||||
selectedIconPath: '../assets/tabbar/orders-active.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/profile/index',
|
||||
text: '我的',
|
||||
iconPath: '../assets/tabbar/profile.png',
|
||||
selectedIconPath: '../assets/tabbar/profile-active.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
methods: {
|
||||
setCurrent(index) {
|
||||
this.setData({ selected: index })
|
||||
},
|
||||
switchTab(event) {
|
||||
const index = Number(event.currentTarget.dataset.index)
|
||||
const currentItem = this.data.list[this.data.selected]
|
||||
const targetItem = this.data.list[index]
|
||||
const currentPath = currentItem ? currentItem.pagePath : ''
|
||||
|
||||
if (!targetItem || currentPath === targetItem.pagePath) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setData({ selected: index })
|
||||
wx.switchTab({ url: `/${targetItem.pagePath}` })
|
||||
}
|
||||
}
|
||||
})
|
||||
3
mini/custom-tab-bar/index.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
20
mini/custom-tab-bar/index.wxml
Normal file
@@ -0,0 +1,20 @@
|
||||
<view class="custom-tab-bar">
|
||||
<view
|
||||
wx:for="{{list}}"
|
||||
wx:key="pagePath"
|
||||
class="custom-tab-bar__item"
|
||||
data-index="{{index}}"
|
||||
bindtap="switchTab"
|
||||
>
|
||||
<image
|
||||
class="custom-tab-bar__icon"
|
||||
src="{{selected === index ? item.selectedIconPath : item.iconPath}}"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text
|
||||
class="custom-tab-bar__label {{selected === index ? 'custom-tab-bar__label--active' : ''}}"
|
||||
>
|
||||
{{item.text}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
42
mini/custom-tab-bar/index.wxss
Normal file
@@ -0,0 +1,42 @@
|
||||
.custom-tab-bar {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-top: 14px;
|
||||
padding-bottom: calc(6px + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(6px + env(safe-area-inset-bottom));
|
||||
min-height: 72px;
|
||||
background: #ffffff;
|
||||
border-top: 1rpx solid #e5e7eb;
|
||||
box-shadow: 0 -4px 18px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.custom-tab-bar__item {
|
||||
flex: 1;
|
||||
min-height: 44px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.custom-tab-bar__icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
|
||||
.custom-tab-bar__label {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.custom-tab-bar__label--active {
|
||||
color: #16a34a;
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -1,39 +1,39 @@
|
||||
{
|
||||
"miniprogramRoot": "dist/",
|
||||
"projectname": "TakeoutSaaS.C-Side-Mini-Program-Taro",
|
||||
"description": "Takeout SaaS C-end WeChat mini program (Taro Vue3)",
|
||||
"appid": "wx30f91e6afe79f405",
|
||||
"setting": {
|
||||
"urlCheck": true,
|
||||
"es6": false,
|
||||
"enhance": false,
|
||||
"compileHotReLoad": false,
|
||||
"postcss": false,
|
||||
"minified": false,
|
||||
"compileWorklet": false,
|
||||
"uglifyFileName": false,
|
||||
"uploadWithSourceMap": true,
|
||||
"packNpmManually": false,
|
||||
"packNpmRelationList": [],
|
||||
"minifyWXSS": true,
|
||||
"minifyWXML": true,
|
||||
"localPlugins": false,
|
||||
"disableUseStrict": false,
|
||||
"useCompilerPlugins": false,
|
||||
"condition": false,
|
||||
"swc": false,
|
||||
"disableSWC": true,
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
}
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"simulatorPluginLibVersion": {},
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"editorSetting": {}
|
||||
"miniprogramRoot": "dist/",
|
||||
"projectname": "TakeoutSaaS.C-Side-Mini-Program-Taro",
|
||||
"description": "Takeout SaaS C-end WeChat mini program (Taro Vue3)",
|
||||
"appid": "wx30f91e6afe79f405",
|
||||
"setting": {
|
||||
"urlCheck": true,
|
||||
"es6": true,
|
||||
"enhance": true,
|
||||
"compileHotReLoad": false,
|
||||
"postcss": false,
|
||||
"minified": false,
|
||||
"compileWorklet": false,
|
||||
"uglifyFileName": false,
|
||||
"uploadWithSourceMap": true,
|
||||
"packNpmManually": false,
|
||||
"packNpmRelationList": [],
|
||||
"minifyWXSS": true,
|
||||
"minifyWXML": true,
|
||||
"localPlugins": false,
|
||||
"disableUseStrict": false,
|
||||
"useCompilerPlugins": false,
|
||||
"condition": false,
|
||||
"swc": false,
|
||||
"disableSWC": true,
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
}
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"simulatorPluginLibVersion": {},
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"editorSetting": {}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export default defineAppConfig({
|
||||
backgroundColor: '#F8FAFC'
|
||||
},
|
||||
tabBar: {
|
||||
custom: true,
|
||||
color: '#64748b',
|
||||
selectedColor: '#16a34a',
|
||||
backgroundColor: '#ffffff',
|
||||
@@ -25,19 +26,27 @@ export default defineAppConfig({
|
||||
list: [
|
||||
{
|
||||
pagePath: 'pages/home/index',
|
||||
text: '首页'
|
||||
text: '首页',
|
||||
iconPath: 'assets/tabbar/home.png',
|
||||
selectedIconPath: 'assets/tabbar/home-active.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/menu/index',
|
||||
text: '点餐'
|
||||
text: '点餐',
|
||||
iconPath: 'assets/tabbar/menu.png',
|
||||
selectedIconPath: 'assets/tabbar/menu-active.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/orders/index',
|
||||
text: '订单'
|
||||
text: '订单',
|
||||
iconPath: 'assets/tabbar/orders.png',
|
||||
selectedIconPath: 'assets/tabbar/orders-active.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/profile/index',
|
||||
text: '我的'
|
||||
text: '我的',
|
||||
iconPath: 'assets/tabbar/profile.png',
|
||||
selectedIconPath: 'assets/tabbar/profile-active.png'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
src/assets/tabbar/home-active.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/tabbar/home.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/tabbar/menu-active.png
Normal file
|
After Width: | Height: | Size: 875 B |
BIN
src/assets/tabbar/menu.png
Normal file
|
After Width: | Height: | Size: 987 B |
BIN
src/assets/tabbar/orders-active.png
Normal file
|
After Width: | Height: | Size: 776 B |
BIN
src/assets/tabbar/orders.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/tabbar/profile-active.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/tabbar/profile.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
@@ -4,34 +4,55 @@ import { pinia, useAppStore, useCartStore } from '@/stores'
|
||||
import {
|
||||
FulfillmentScenes,
|
||||
demoHotProducts,
|
||||
type FulfillmentScene,
|
||||
type MiniProductCard
|
||||
} from '@/shared'
|
||||
import { openRoute } from '@/utils/router'
|
||||
|
||||
const categoryCards = [
|
||||
{ key: 'recommend', icon: '⭐', label: '推荐', toneClass: 'home-page__cat-icon--orange' },
|
||||
{ key: 'meal', icon: '🍚', label: '主食', toneClass: 'home-page__cat-icon--green-soft' },
|
||||
{ key: 'snack', icon: '🍜', label: '小吃', toneClass: 'home-page__cat-icon--yellow', badge: 'HOT' },
|
||||
{ key: 'drink', icon: '🧋', label: '饮品', toneClass: 'home-page__cat-icon--green-light' },
|
||||
{ key: 'set', icon: '📦', label: '套餐', toneClass: 'home-page__cat-icon--mint' },
|
||||
{ key: 'dessert', icon: '🍰', label: '甜品', toneClass: 'home-page__cat-icon--amber' }
|
||||
] as const
|
||||
export interface HomeCategoryItem {
|
||||
key: string
|
||||
char: string
|
||||
label: string
|
||||
tone: string
|
||||
badge?: string
|
||||
}
|
||||
|
||||
const trustItems = [
|
||||
{ key: 'fresh', icon: '⚡', label: '现炒现做' },
|
||||
{ key: 'fast', icon: '🕐', label: '30分钟送达' },
|
||||
{ key: 'pickup', icon: '🛍', label: '自提更快' },
|
||||
{ key: 'quality', icon: '🛡', label: '品质保证' }
|
||||
] as const
|
||||
export interface HomeTrustItem {
|
||||
key: string
|
||||
label: string
|
||||
}
|
||||
|
||||
const categoryCards: HomeCategoryItem[] = [
|
||||
{ key: 'recommend', char: '荐', label: '推荐', tone: 'warm' },
|
||||
{ key: 'staple', char: '饭', label: '主食', tone: 'green' },
|
||||
{ key: 'snack', char: '食', label: '小吃', tone: 'amber', badge: '热卖' },
|
||||
{ key: 'drink', char: '饮', label: '饮品', tone: 'teal' },
|
||||
{ key: 'combo', char: '套', label: '套餐', tone: 'blue' },
|
||||
{ key: 'dessert', char: '甜', label: '甜品', tone: 'rose' }
|
||||
]
|
||||
|
||||
const trustItems: HomeTrustItem[] = [
|
||||
{ key: 'fresh', label: '现炒现做' },
|
||||
{ key: 'fast', label: '30分钟送达' },
|
||||
{ key: 'pickup', label: '自提免等' },
|
||||
{ key: 'quality', label: '品质保障' }
|
||||
]
|
||||
|
||||
export function useHomePage () {
|
||||
const appStore = useAppStore(pinia)
|
||||
const cartStore = useCartStore(pinia)
|
||||
|
||||
const currentStore = computed(() => appStore.currentStore)
|
||||
const cartCount = computed(() => cartStore.itemCount)
|
||||
const currentScene = computed(() => appStore.scene)
|
||||
const sceneOptions = computed(() => appStore.sceneOptions)
|
||||
const isDineIn = computed(() => appStore.scene === FulfillmentScenes.DineIn)
|
||||
const recommendedProducts = ref<MiniProductCard[]>(demoHotProducts)
|
||||
|
||||
function switchScene (scene: string) {
|
||||
appStore.setScene(scene as FulfillmentScene)
|
||||
}
|
||||
|
||||
async function refreshPage () {
|
||||
await appStore.initBootstrap()
|
||||
await appStore.initStores()
|
||||
@@ -52,11 +73,14 @@ export function useHomePage () {
|
||||
return {
|
||||
cartCount,
|
||||
categoryCards,
|
||||
currentScene,
|
||||
currentStore,
|
||||
goMenu,
|
||||
goStoreSelect,
|
||||
isDineIn,
|
||||
recommendedProducts,
|
||||
sceneOptions,
|
||||
switchScene,
|
||||
trustItems
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '首页'
|
||||
navigationBarTitleText: '首页',
|
||||
usingComponents: {}
|
||||
})
|
||||
|
||||
@@ -1,112 +1,145 @@
|
||||
<template>
|
||||
<view class="home-page">
|
||||
<view class="hp">
|
||||
<!-- Store Card -->
|
||||
<view class="home-page__store-card">
|
||||
<view class="home-page__store-icon-wrap">
|
||||
<text class="home-page__store-icon-emoji">📍</text>
|
||||
</view>
|
||||
<view class="home-page__store-text">
|
||||
<view class="home-page__store-name">
|
||||
<text class="home-page__store-name-text">{{ currentStore.name }}</text>
|
||||
<view class="home-page__store-status">
|
||||
<view class="home-page__store-status-dot" />
|
||||
<view class="hp__store" @click="goStoreSelect">
|
||||
<view class="hp__store-info">
|
||||
<view class="hp__store-row">
|
||||
<text class="hp__store-name">{{ currentStore.name }}</text>
|
||||
<view class="hp__store-status">
|
||||
<view class="hp__store-dot" />
|
||||
<text>营业中</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="home-page__store-addr">{{ currentStore.address }}</text>
|
||||
<text class="hp__store-addr">{{ currentStore.address }}</text>
|
||||
</view>
|
||||
<view class="home-page__store-switch" @click="goStoreSelect">
|
||||
<text>切换</text>
|
||||
<text class="home-page__chevron">›</text>
|
||||
<view class="hp__store-switch">
|
||||
<text>切换门店</text>
|
||||
<text class="hp__store-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Dine-in Scan Button -->
|
||||
<view v-if="isDineIn" class="home-page__scan-tab" @click="goMenu">
|
||||
<text>🔲</text>
|
||||
<text>堂食扫码点餐</text>
|
||||
<view class="hp__scan" @click="goMenu">
|
||||
<view class="hp__scan-ico">
|
||||
<IconScan size="18" color="#fff" />
|
||||
</view>
|
||||
<text class="hp__scan-text">堂食扫码点餐</text>
|
||||
</view>
|
||||
|
||||
<!-- Search Bar -->
|
||||
<view class="home-page__search" @click="goMenu">
|
||||
<text class="home-page__search-icon">🔍</text>
|
||||
<text class="home-page__search-placeholder">搜索菜品、套餐、饮品</text>
|
||||
<view class="hp__search" @click="goMenu">
|
||||
<view class="hp__search-ico">
|
||||
<IconSearch size="14" color="#9CA3AF" />
|
||||
</view>
|
||||
<text class="hp__search-ph">搜索菜品、套餐、饮品</text>
|
||||
</view>
|
||||
|
||||
<!-- Banner -->
|
||||
<view class="home-page__banner" @click="goMenu">
|
||||
<view class="home-page__banner-content">
|
||||
<view class="home-page__banner-tag">
|
||||
<text>✓ 新客专享</text>
|
||||
<view class="hp__banner" @click="goMenu">
|
||||
<view class="hp__banner-body">
|
||||
<view class="hp__banner-label">
|
||||
<text>新客专享</text>
|
||||
</view>
|
||||
<text class="home-page__banner-title">首单立减</text>
|
||||
<view class="home-page__banner-amount">
|
||||
<text class="home-page__banner-unit">¥</text>
|
||||
<text>12</text>
|
||||
<text class="hp__banner-title">首单立减</text>
|
||||
<view class="hp__banner-price">
|
||||
<text class="hp__banner-sym">¥</text>
|
||||
<text class="hp__banner-val">12</text>
|
||||
</view>
|
||||
<text class="home-page__banner-desc">全场满38再减8 · 限时三天</text>
|
||||
<view class="home-page__banner-cta">
|
||||
<text>立即领取 →</text>
|
||||
<text class="hp__banner-note">全场满 38 再减 8 · 限时三天</text>
|
||||
<view class="hp__banner-btn">
|
||||
<text>立即领取</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="home-page__banner-ring-outer" />
|
||||
<view class="home-page__banner-ring" />
|
||||
<image
|
||||
class="home-page__banner-food-img"
|
||||
src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?w=240&h=240&fit=crop&auto=format&q=80"
|
||||
class="hp__banner-img"
|
||||
src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?w=280&h=280&fit=crop&q=80"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="hp__banner-ring" />
|
||||
</view>
|
||||
|
||||
<!-- Categories -->
|
||||
<view class="home-page__categories">
|
||||
<view class="hp__cats">
|
||||
<view
|
||||
v-for="category in categoryCards"
|
||||
:key="category.key"
|
||||
class="home-page__cat-item"
|
||||
v-for="c in categoryCards"
|
||||
:key="c.key"
|
||||
class="hp__cat"
|
||||
@click="goMenu"
|
||||
>
|
||||
<view class="home-page__cat-icon" :class="category.toneClass">
|
||||
<text>{{ category.icon }}</text>
|
||||
<text v-if="category.badge" class="home-page__cat-badge">{{ category.badge }}</text>
|
||||
<view class="hp__cat-ico" :class="`hp__cat-ico--${c.tone}`">
|
||||
<text>{{ c.char }}</text>
|
||||
<text v-if="c.badge" class="hp__cat-badge">{{ c.badge }}</text>
|
||||
</view>
|
||||
<text class="home-page__cat-label">{{ category.label }}</text>
|
||||
<text class="hp__cat-name">{{ c.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Section Header -->
|
||||
<view class="home-page__section-head">
|
||||
<text class="home-page__section-title">热门推荐</text>
|
||||
<view class="home-page__section-more" @click="goMenu">
|
||||
<text>查看全部 ›</text>
|
||||
<!-- Promo Strip -->
|
||||
<view class="hp__promo" @click="goMenu">
|
||||
<view class="hp__promo-tag">
|
||||
<text>限时</text>
|
||||
</view>
|
||||
<text class="hp__promo-text">人气双人餐 超值特惠 ¥49 起</text>
|
||||
<text class="hp__promo-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<!-- Section Header: Hot Products -->
|
||||
<view class="hp__hd">
|
||||
<text class="hp__hd-title">热门推荐</text>
|
||||
<view class="hp__hd-more" @click="goMenu">
|
||||
<text>查看全部</text>
|
||||
<text class="hp__hd-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Product List -->
|
||||
<view class="home-page__product-list">
|
||||
<ProductCard
|
||||
v-for="product in recommendedProducts"
|
||||
:key="product.id"
|
||||
:product="product"
|
||||
@select="goMenu"
|
||||
@action="goMenu"
|
||||
/>
|
||||
<!-- Product Grid -->
|
||||
<view class="hp__grid">
|
||||
<view
|
||||
v-for="p in recommendedProducts"
|
||||
:key="p.id"
|
||||
class="hp__card"
|
||||
@click="goMenu"
|
||||
>
|
||||
<view class="hp__card-cover">
|
||||
<image class="hp__card-img" :src="p.coverImageUrl" mode="aspectFill" />
|
||||
<text v-if="p.tagTexts?.length" class="hp__card-tag">{{ p.tagTexts[0] }}</text>
|
||||
</view>
|
||||
<view class="hp__card-body">
|
||||
<text class="hp__card-name">{{ p.name }}</text>
|
||||
<text class="hp__card-desc">{{ p.description }}</text>
|
||||
<text class="hp__card-sales">{{ p.salesText }}</text>
|
||||
<view class="hp__card-foot">
|
||||
<view class="hp__card-prices">
|
||||
<text class="hp__card-sym">¥</text>
|
||||
<text class="hp__card-num">{{ p.price }}</text>
|
||||
<text v-if="p.originalPriceText" class="hp__card-old">¥{{ p.originalPriceText }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="hp__card-add"
|
||||
:class="{ 'hp__card-add--pill': p.hasOptions }"
|
||||
@click.stop="goMenu"
|
||||
>
|
||||
<text>{{ p.hasOptions ? '选规格' : '+' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Trust Section -->
|
||||
<view class="home-page__trust">
|
||||
<view class="home-page__trust-grid">
|
||||
<view v-for="item in trustItems" :key="item.key" class="home-page__trust-item">
|
||||
<view class="home-page__trust-icon"><text>{{ item.icon }}</text></view>
|
||||
<text class="home-page__trust-label">{{ item.label }}</text>
|
||||
</view>
|
||||
<!-- Trust Strip -->
|
||||
<view class="hp__trust">
|
||||
<view v-for="t in trustItems" :key="t.key" class="hp__trust-pill">
|
||||
<view class="hp__trust-dot" />
|
||||
<text>{{ t.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Floating Cart FAB -->
|
||||
<view v-if="cartCount > 0" class="home-page__fab" @click="goMenu">
|
||||
<text class="home-page__fab-icon">🛒</text>
|
||||
<view class="home-page__fab-badge">
|
||||
<view v-if="cartCount > 0" class="hp__fab" @click="goMenu">
|
||||
<view class="hp__fab-ico">
|
||||
<IconCart size="20" color="#fff" />
|
||||
</view>
|
||||
<view class="hp__fab-badge">
|
||||
<text>{{ cartCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -114,16 +147,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ProductCard from '@/components/product-card/index.vue'
|
||||
import { Search as IconSearch, Cart2 as IconCart, Scan2 as IconScan } from '@nutui/icons-vue-taro'
|
||||
import { useTabBarSelection } from '@/utils/useTabBarSelection'
|
||||
import { useHomePage } from './composables/useHomePage'
|
||||
|
||||
useTabBarSelection(0)
|
||||
|
||||
const {
|
||||
cartCount,
|
||||
categoryCards,
|
||||
currentStore,
|
||||
goMenu,
|
||||
goStoreSelect,
|
||||
isDineIn,
|
||||
recommendedProducts,
|
||||
trustItems
|
||||
} = useHomePage()
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
.home-page__banner {
|
||||
border-radius: 24px;
|
||||
@use '../../../styles/variables' as *;
|
||||
|
||||
.hp__banner {
|
||||
border-radius: $r-xl;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
min-height: 160px;
|
||||
background: linear-gradient(135deg, #14532D 0%, #166534 35%, #15803D 70%, #1A7A42 100%);
|
||||
min-height: 156px;
|
||||
background: linear-gradient(135deg, #14532D 0%, #166534 40%, #15803D 100%);
|
||||
padding: 22px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 16px rgba(22, 101, 52, 0.25), 0 8px 32px rgba(22, 101, 52, 0.12);
|
||||
}
|
||||
|
||||
.home-page__banner-content {
|
||||
.hp__banner-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 60%;
|
||||
z-index: 2;
|
||||
max-width: 58%;
|
||||
}
|
||||
|
||||
.home-page__banner-tag {
|
||||
.hp__banner-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 3px 10px;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 999px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
width: fit-content;
|
||||
@@ -33,89 +32,79 @@
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.home-page__banner-title {
|
||||
.hp__banner-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.home-page__banner-amount {
|
||||
.hp__banner-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 2px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.hp__banner-sym {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.hp__banner-val {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
line-height: 1.1;
|
||||
margin: 4px 0 2px;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.home-page__banner-unit {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.home-page__banner-desc {
|
||||
.hp__banner-note {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
margin-top: 6px;
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.home-page__banner-cta {
|
||||
.hp__banner-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
height: 32px;
|
||||
padding: 0 18px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
height: 30px;
|
||||
padding: 0 16px;
|
||||
background: rgba(255, 255, 255, 0.93);
|
||||
color: #14532D;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
border-radius: 999px;
|
||||
width: fit-content;
|
||||
margin-top: 14px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.home-page__banner-food-img {
|
||||
.hp__banner-img {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
width: 116px;
|
||||
height: 116px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||
border: 3px solid rgba(255, 255, 255, 0.12);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
|
||||
z-index: 1;
|
||||
background: linear-gradient(135deg, rgba(34, 197, 94, 0.2), rgba(22, 163, 74, 0.2));
|
||||
}
|
||||
|
||||
.home-page__banner-ring {
|
||||
.hp__banner-ring {
|
||||
position: absolute;
|
||||
right: -2px;
|
||||
right: 2px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 144px;
|
||||
height: 144px;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.08);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.home-page__banner-ring-outer {
|
||||
position: absolute;
|
||||
right: -14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 168px;
|
||||
height: 168px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
@@ -1,73 +1,57 @@
|
||||
@use '../../../styles/variables' as *;
|
||||
|
||||
.home-page {
|
||||
// ─── Page Shell ───
|
||||
.hp {
|
||||
min-height: 100vh;
|
||||
padding: 14px 16px 24px;
|
||||
padding: 12px 16px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
gap: 16px;
|
||||
background: $bg;
|
||||
}
|
||||
|
||||
.home-page__store-card {
|
||||
// ─── Store Card ───
|
||||
.hp__store {
|
||||
background: $card;
|
||||
border-radius: $r-lg;
|
||||
padding: 14px 16px;
|
||||
padding: 16px;
|
||||
box-shadow: $shadow-sm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.home-page__store-icon-wrap {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: $r-sm;
|
||||
background: $primary-light;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-page__store-icon-emoji {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.home-page__store-text {
|
||||
.hp__store-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.home-page__store-name {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: $text-1;
|
||||
.hp__store-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.home-page__store-name-text {
|
||||
.hp__store-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: $text-1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.home-page__store-status {
|
||||
.hp__store-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
gap: 4px;
|
||||
font-size: 11px;
|
||||
color: $primary;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-page__store-status-dot {
|
||||
.hp__store-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
@@ -75,87 +59,170 @@
|
||||
animation: pulse-dot 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.home-page__store-addr {
|
||||
.hp__store-addr {
|
||||
font-size: 12px;
|
||||
color: $text-3;
|
||||
margin-top: 3px;
|
||||
white-space: nowrap;
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.home-page__store-switch {
|
||||
font-size: 13px;
|
||||
color: $primary;
|
||||
font-weight: 500;
|
||||
.hp__store-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
font-size: 13px;
|
||||
color: $primary;
|
||||
font-weight: 500;
|
||||
padding: 8px 0;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-page__chevron {
|
||||
font-size: 16px;
|
||||
.hp__store-arrow {
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.home-page__scan-tab {
|
||||
// ─── Scene Tabs ───
|
||||
.hp__tabs {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
background: #EEF2F6;
|
||||
border-radius: $r-full;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.hp__tab {
|
||||
flex: 1;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
height: 44px;
|
||||
border-radius: $r-sm;
|
||||
background: $primary;
|
||||
color: #fff;
|
||||
border-radius: $r-full;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2px 10px rgba(22, 163, 74, 0.35);
|
||||
font-weight: 500;
|
||||
color: $text-3;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
.home-page__search {
|
||||
.hp__tab--on {
|
||||
background: $primary;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2px 8px rgba(22, 163, 74, 0.3);
|
||||
}
|
||||
|
||||
// ─── Dine-in Scan Button ───
|
||||
.hp__scan {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
height: 52px;
|
||||
border-radius: $r-md;
|
||||
background: $primary;
|
||||
box-shadow: 0 2px 12px rgba(22, 163, 74, 0.3);
|
||||
}
|
||||
|
||||
.hp__scan-ico {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hp__scan-text {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
// ─── Search Bar ───
|
||||
.hp__search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #F1F5F9;
|
||||
border-radius: $r-md;
|
||||
border-radius: $r-sm;
|
||||
padding: 0 14px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.home-page__search-icon {
|
||||
font-size: 14px;
|
||||
.hp__search-ico {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-page__search-placeholder {
|
||||
.hp__search-ph {
|
||||
font-size: 14px;
|
||||
color: $text-4;
|
||||
}
|
||||
|
||||
.home-page__section-head {
|
||||
// ─── Section Header ───
|
||||
.hp__hd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 2px 0;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.home-page__section-title {
|
||||
.hp__hd-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: $text-1;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.home-page__section-more {
|
||||
font-size: 13px;
|
||||
color: $text-4;
|
||||
.hp__hd-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
font-size: 13px;
|
||||
color: $text-4;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.home-page__product-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
.hp__hd-arrow {
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
// ─── Promo Strip ───
|
||||
.hp__promo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #FFFBEB;
|
||||
border-radius: $r-sm;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid #FEF3C7;
|
||||
}
|
||||
|
||||
.hp__promo-tag {
|
||||
background: $accent;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
padding: 2px 7px;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hp__promo-text {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: #92400E;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hp__promo-arrow {
|
||||
font-size: 18px;
|
||||
color: $accent;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
@use '../../../styles/variables' as *;
|
||||
|
||||
.home-page__categories {
|
||||
.hp__cats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 2px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.home-page__cat-item {
|
||||
.hp__cat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 2px 8px;
|
||||
border-radius: $r-sm;
|
||||
padding: 4px 0 6px;
|
||||
}
|
||||
|
||||
.home-page__cat-icon {
|
||||
.hp__cat-ico {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 16px;
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
font-size: 18px;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
|
||||
&--orange { background: #FFF4ED; }
|
||||
&--green-soft { background: #F0FDF4; }
|
||||
&--yellow { background: #FFFBEB; }
|
||||
&--green-light { background: #ECFDF5; }
|
||||
&--mint { background: #F0FDF4; }
|
||||
&--amber { background: #FFF7ED; }
|
||||
&--warm { background: #FFF4ED; color: #EA580C; }
|
||||
&--green { background: #F0FDF4; color: #16A34A; }
|
||||
&--amber { background: #FFFBEB; color: #D97706; }
|
||||
&--teal { background: #F0FDFA; color: #0D9488; }
|
||||
&--blue { background: #EFF6FF; color: #2563EB; }
|
||||
&--rose { background: #FFF1F2; color: #E11D48; }
|
||||
}
|
||||
|
||||
.home-page__cat-label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: $text-2;
|
||||
}
|
||||
|
||||
.home-page__cat-badge {
|
||||
.hp__cat-badge {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -6px;
|
||||
top: -4px;
|
||||
right: -8px;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
background: $red;
|
||||
color: #fff;
|
||||
padding: 1px 5px;
|
||||
border-radius: 999px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.hp__cat-name {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: $text-2;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@use '../../../styles/variables' as *;
|
||||
|
||||
.home-page__fab {
|
||||
.hp__fab {
|
||||
position: fixed;
|
||||
bottom: 92px;
|
||||
right: 16px;
|
||||
@@ -8,30 +8,33 @@
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
|
||||
background: $primary;
|
||||
box-shadow: 0 4px 16px rgba(22, 163, 74, 0.35);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.home-page__fab-icon {
|
||||
font-size: 20px;
|
||||
.hp__fab-ico {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.home-page__fab-badge {
|
||||
.hp__fab-badge {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: -2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 9px;
|
||||
background: $red;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 4px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@forward './base.scss';
|
||||
@forward './banner.scss';
|
||||
@forward './category.scss';
|
||||
@forward './product.scss';
|
||||
@forward './trust.scss';
|
||||
@forward './fab.scss';
|
||||
|
||||
137
src/pages/home/styles/product.scss
Normal file
@@ -0,0 +1,137 @@
|
||||
@use '../../../styles/variables' as *;
|
||||
|
||||
.hp__grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.hp__card {
|
||||
width: calc(50% - 6px);
|
||||
background: $card;
|
||||
border-radius: $r-lg;
|
||||
box-shadow: $shadow-sm;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hp__card-cover {
|
||||
width: 100%;
|
||||
height: 136px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hp__card-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: $border;
|
||||
}
|
||||
|
||||
.hp__card-tag {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
padding: 2px 8px;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
background: rgba(22, 163, 74, 0.88);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.hp__card-body {
|
||||
padding: 10px 12px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hp__card-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $text-1;
|
||||
line-height: 1.4;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hp__card-desc {
|
||||
font-size: 11px;
|
||||
color: $text-3;
|
||||
margin-top: 3px;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hp__card-sales {
|
||||
font-size: 10px;
|
||||
color: $text-4;
|
||||
margin-top: 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hp__card-foot {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-top: auto;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.hp__card-prices {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 1px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.hp__card-sym {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: $primary-dark;
|
||||
}
|
||||
|
||||
.hp__card-num {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: $primary-dark;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.hp__card-old {
|
||||
font-size: 10px;
|
||||
color: $text-4;
|
||||
text-decoration: line-through;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.hp__card-add {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: $primary;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
&--pill {
|
||||
width: auto;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,28 @@
|
||||
@use '../../../styles/variables' as *;
|
||||
|
||||
.home-page__trust {
|
||||
background: $card;
|
||||
border-radius: $r-lg;
|
||||
padding: 16px 12px;
|
||||
box-shadow: $shadow-xs;
|
||||
}
|
||||
|
||||
.home-page__trust-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.home-page__trust-item {
|
||||
.hp__trust {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.hp__trust-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 4px;
|
||||
}
|
||||
|
||||
.home-page__trust-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 11px;
|
||||
padding: 6px 12px;
|
||||
background: $primary-lighter;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
color: $primary-dark;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.home-page__trust-label {
|
||||
font-size: 11px;
|
||||
color: $text-2;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
.hp__trust-dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: $primary;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '点餐'
|
||||
navigationBarTitleText: '点餐',
|
||||
usingComponents: {}
|
||||
})
|
||||
|
||||
@@ -126,8 +126,11 @@
|
||||
import ProductCard from '@/components/product-card/index.vue'
|
||||
import CartDrawer from '@/components/cart-drawer/index.vue'
|
||||
import SpecPopup from '@/components/spec-popup/index.vue'
|
||||
import { useTabBarSelection } from '@/utils/useTabBarSelection'
|
||||
import { useMenuPage } from './composables/useMenuPage'
|
||||
|
||||
useTabBarSelection(1)
|
||||
|
||||
const {
|
||||
activeDetail,
|
||||
appStore,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '订单'
|
||||
navigationBarTitleText: '订单',
|
||||
usingComponents: {}
|
||||
})
|
||||
|
||||
@@ -61,14 +61,18 @@
|
||||
<NutButton type="primary" block @click="goMenu">去点餐</NutButton>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button as NutButton, Empty, Price, Tag } from '@nutui/nutui-taro'
|
||||
import PageHero from '@/components/page-hero/index.vue'
|
||||
import { useTabBarSelection } from '@/utils/useTabBarSelection'
|
||||
import { useOrdersPage } from './composables/useOrdersPage'
|
||||
|
||||
useTabBarSelection(2)
|
||||
|
||||
const {
|
||||
activeTab,
|
||||
goMenu,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '我的'
|
||||
navigationBarTitleText: '我的',
|
||||
usingComponents: {}
|
||||
})
|
||||
|
||||
@@ -44,14 +44,18 @@
|
||||
<NutButton block plain @click="goDineIn">编辑桌号</NutButton>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button as NutButton } from '@nutui/nutui-taro'
|
||||
import PageHero from '@/components/page-hero/index.vue'
|
||||
import { useTabBarSelection } from '@/utils/useTabBarSelection'
|
||||
import { useProfilePage } from './composables/useProfilePage'
|
||||
|
||||
useTabBarSelection(3)
|
||||
|
||||
const {
|
||||
customerStore,
|
||||
draftName,
|
||||
|
||||
@@ -8,13 +8,13 @@ const productMap: Record<Id, MiniProductCard> = {
|
||||
id: '2000000000000000001',
|
||||
name: '招牌鸡腿饭',
|
||||
description: '招牌酱汁鸡腿搭配时蔬与米饭',
|
||||
coverImageUrl: 'https://dummyimage.com/160x160/f3f4f6/111827&text=Chicken',
|
||||
coverImageUrl: 'https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=400&h=400&fit=crop&q=80',
|
||||
price: 22,
|
||||
priceText: formatPrice(22),
|
||||
originalPrice: 26,
|
||||
originalPriceText: formatPrice(26),
|
||||
salesText: '月售 268',
|
||||
tagTexts: ['招牌', '热销'],
|
||||
tagTexts: ['招牌'],
|
||||
soldOut: false,
|
||||
hasOptions: false
|
||||
},
|
||||
@@ -22,13 +22,13 @@ const productMap: Record<Id, MiniProductCard> = {
|
||||
id: '2000000000000000002',
|
||||
name: '黑椒牛柳饭',
|
||||
description: '黑椒风味牛柳,适合工作日快餐',
|
||||
coverImageUrl: 'https://dummyimage.com/160x160/e5e7eb/111827&text=Beef',
|
||||
coverImageUrl: 'https://images.unsplash.com/photo-1544025162-d76694265947?w=400&h=400&fit=crop&q=80',
|
||||
price: 29,
|
||||
priceText: formatPrice(29),
|
||||
originalPrice: 33,
|
||||
originalPriceText: formatPrice(33),
|
||||
salesText: '月售 186',
|
||||
tagTexts: ['牛肉', '推荐'],
|
||||
tagTexts: ['推荐'],
|
||||
soldOut: false,
|
||||
hasOptions: false
|
||||
},
|
||||
@@ -36,7 +36,7 @@ const productMap: Record<Id, MiniProductCard> = {
|
||||
id: '2000000000000000003',
|
||||
name: '藤椒鸡丝沙拉',
|
||||
description: '轻食组合,适合晚餐与健身场景',
|
||||
coverImageUrl: 'https://dummyimage.com/160x160/dbeafe/111827&text=Salad',
|
||||
coverImageUrl: 'https://images.unsplash.com/photo-1512621776951-a57141f2eefd?w=400&h=400&fit=crop&q=80',
|
||||
price: 18,
|
||||
priceText: formatPrice(18),
|
||||
salesText: '月售 92',
|
||||
@@ -48,7 +48,7 @@ const productMap: Record<Id, MiniProductCard> = {
|
||||
id: '2000000000000000004',
|
||||
name: '芝士薯球',
|
||||
description: '外酥里糯的轻食小吃',
|
||||
coverImageUrl: 'https://dummyimage.com/160x160/fef3c7/111827&text=Snack',
|
||||
coverImageUrl: 'https://images.unsplash.com/photo-1555939594-58d7cb561ad1?w=400&h=400&fit=crop&q=80',
|
||||
price: 12,
|
||||
priceText: formatPrice(12),
|
||||
salesText: '月售 143',
|
||||
@@ -60,7 +60,7 @@ const productMap: Record<Id, MiniProductCard> = {
|
||||
id: '2000000000000000005',
|
||||
name: '茉莉轻乳茶',
|
||||
description: '清爽不腻,适合套餐搭配',
|
||||
coverImageUrl: 'https://dummyimage.com/160x160/ecfccb/111827&text=Tea',
|
||||
coverImageUrl: 'https://images.unsplash.com/photo-1558857563-b371033873b8?w=400&h=400&fit=crop&q=80',
|
||||
price: 10,
|
||||
priceText: formatPrice(10),
|
||||
salesText: '月售 310',
|
||||
@@ -72,11 +72,11 @@ const productMap: Record<Id, MiniProductCard> = {
|
||||
id: '2000000000000000006',
|
||||
name: '冰美式',
|
||||
description: '办公场景高频搭配饮品',
|
||||
coverImageUrl: 'https://dummyimage.com/160x160/e0f2fe/111827&text=Coffee',
|
||||
coverImageUrl: 'https://images.unsplash.com/photo-1509042239860-f550ce710b93?w=400&h=400&fit=crop&q=80',
|
||||
price: 14,
|
||||
priceText: formatPrice(14),
|
||||
salesText: '月售 201',
|
||||
tagTexts: ['办公必备'],
|
||||
tagTexts: ['热卖'],
|
||||
soldOut: false,
|
||||
hasOptions: false
|
||||
}
|
||||
@@ -150,6 +150,3 @@ export function buildMockPriceEstimate (payload: PriceEstimatePayload): PriceEst
|
||||
payableAmountText: formatPrice(payableAmount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
35
src/utils/useTabBarSelection.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import Taro, { useDidShow } from '@tarojs/taro'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
type CustomTabBarInstance = {
|
||||
setCurrent: (index: number) => void
|
||||
}
|
||||
|
||||
function syncTabBarSelection(index: number, retries = 6) {
|
||||
const page = Taro.getCurrentInstance().page
|
||||
|
||||
if (!page) {
|
||||
return
|
||||
}
|
||||
|
||||
const tabBar = Taro.getTabBar<CustomTabBarInstance>(page)
|
||||
|
||||
if (tabBar?.setCurrent) {
|
||||
tabBar.setCurrent(index)
|
||||
return
|
||||
}
|
||||
|
||||
if (retries > 0) {
|
||||
setTimeout(() => syncTabBarSelection(index, retries - 1), 16)
|
||||
}
|
||||
}
|
||||
|
||||
export function useTabBarSelection(index: number) {
|
||||
onMounted(() => {
|
||||
syncTabBarSelection(index)
|
||||
})
|
||||
|
||||
useDidShow(() => {
|
||||
syncTabBarSelection(index)
|
||||
})
|
||||
}
|
||||