新增全局 YjyfTabBar 组件,替换各页面自定义导航,实现一致的 tab 切换逻辑与图标样式 新增全局设计令牌与 SCSS 变量系统,统一色彩、间距、字阶、圆角与投影规范 新增 POST 请求防并发锁,避免重复提交 重构多个页面,添加骨架屏、空状态引导、点击反馈类,修复硬编码样式与假数据 删除废弃的后台入口文件与 nginx 配置,清理冗余代码
216 行
7.3 KiB
Vue
216 行
7.3 KiB
Vue
<template>
|
||
<view class="yjyf-tabbar-container">
|
||
<view class="tabbar">
|
||
<!-- 1. 首页 -->
|
||
<view class="tab" :class="{ on: current === 0 }" @tap="switchTab(0)">
|
||
<image class="tab-icon" :src="current === 0 ? ICONS.homeActive : ICONS.home" mode="aspectFit"></image>
|
||
<text class="tab-txt">首页</text>
|
||
</view>
|
||
|
||
<!-- 2. 订单 -->
|
||
<view class="tab" :class="{ on: current === 1 }" @tap="switchTab(1)">
|
||
<image class="tab-icon" :src="current === 1 ? ICONS.orderActive : ICONS.order" mode="aspectFit"></image>
|
||
<text class="tab-txt">订单</text>
|
||
</view>
|
||
|
||
<!-- 3. 中间高光 FAB:立即下单 -->
|
||
<view class="tab tab-center">
|
||
<view class="fab-wrapper" @tap="onOrder">
|
||
<view class="fab clickable-hover">
|
||
<image class="fab-icon" :src="ICONS.plus" mode="aspectFit"></image>
|
||
</view>
|
||
<text class="fab-label">立即下单</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 4. 消息 -->
|
||
<view class="tab" :class="{ on: current === 2 }" @tap="switchTab(2)">
|
||
<image class="tab-icon" :src="current === 2 ? ICONS.msgActive : ICONS.msg" mode="aspectFit"></image>
|
||
<text class="tab-txt">消息</text>
|
||
</view>
|
||
|
||
<!-- 5. 我的 -->
|
||
<view class="tab" :class="{ on: current === 3 }" @tap="switchTab(3)">
|
||
<image class="tab-icon" :src="current === 3 ? ICONS.mineActive : ICONS.mine" mode="aspectFit"></image>
|
||
<text class="tab-txt">我的</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部防遮挡安全垫高 -->
|
||
<view class="tabbar-placeholder"></view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { isLogin, toLogin } from '@/utils/request.js'
|
||
|
||
const props = defineProps({
|
||
current: {
|
||
type: Number,
|
||
default: 0
|
||
}
|
||
})
|
||
|
||
// 现代细腻矢量 SVG 图标集(双色品牌橙高质感)
|
||
const ICONS = {
|
||
// 首页
|
||
home: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238E9297' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M3 9.5L12 3l9 6.5V20a1 1 0 0 1-1 1h-5v-6a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v6H4a1 1 0 0 1-1-1V9.5z'/></svg>",
|
||
homeActive: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%23FFEADB' d='M4 10.2L12 4.3l8 5.9V19a1 1 0 0 1-1 1h-4v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5H5a1 1 0 0 1-1-1v-8.8z'/><path fill='%23F0710D' d='M12 3a1 1 0 0 1 .6.2l9 6.5a1 1 0 0 1-1.2 1.6L12 5.2 3.6 11.3a1 1 0 0 1-1.2-1.6l9-6.5A1 1 0 0 1 12 3zm-2 12h4v5h-4v-5z'/></svg>",
|
||
|
||
// 订单
|
||
order: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238E9297' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/><polyline points='14 2 14 8 20 8'/><line x1='16' y1='13' x2='8' y2='13'/><line x1='16' y1='17' x2='8' y2='17'/><line x1='10' y1='9' x2='8' y2='9'/></svg>",
|
||
orderActive: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%23FFEADB' d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/><path fill='%23F0710D' d='M14 2v6h6M8 13h8v2H8zm0 4h8v2H8zm0-8h3v2H8z'/></svg>",
|
||
|
||
// 中间加号
|
||
plus: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FFFFFF' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'><line x1='12' y1='5' x2='12' y2='19'/><line x1='5' y1='12' x2='19' y2='12'/></svg>",
|
||
|
||
// 消息
|
||
msg: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238E9297' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z'/></svg>",
|
||
msgActive: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%23FFEADB' d='M20.5 11.5a8.5 8.5 0 0 1-12.3 7.6L3 21l1.9-5.2A8.5 8.5 0 1 1 20.5 11.5z'/><circle fill='%23F0710D' cx='8.5' cy='11.5' r='1.5'/><circle fill='%23F0710D' cx='12' cy='11.5' r='1.5'/><circle fill='%23F0710D' cx='15.5' cy='11.5' r='1.5'/></svg>",
|
||
|
||
// 我的
|
||
mine: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238E9297' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/><circle cx='12' cy='7' r='4'/></svg>",
|
||
mineActive: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%23FFEADB' d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/><circle fill='%23FFEADB' cx='12' cy='7' r='4'/><path fill='%23F0710D' d='M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0 2c-3.3 0-8 1.7-8 5v2h16v-2c0-3.3-4.7-5-8-5z'/></svg>"
|
||
}
|
||
|
||
const TAB_ROUTES = [
|
||
'/pages/index/index',
|
||
'/pages/order/list',
|
||
'/pages/message/message',
|
||
'/pages/mine/mine'
|
||
]
|
||
|
||
function needLogin() {
|
||
if (isLogin()) return true
|
||
toLogin()
|
||
return false
|
||
}
|
||
|
||
function onOrder() {
|
||
if (!needLogin()) return
|
||
uni.navigateTo({ url: '/pages/order/quick' })
|
||
}
|
||
|
||
function switchTab(index) {
|
||
if (props.current === index) return
|
||
if (index > 0 && !needLogin()) return
|
||
|
||
const targetUrl = TAB_ROUTES[index]
|
||
if (targetUrl) {
|
||
uni.reLaunch({ url: targetUrl })
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.yjyf-tabbar-container {
|
||
position: relative;
|
||
z-index: 99;
|
||
}
|
||
|
||
.tabbar {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
height: 104rpx;
|
||
background: rgba(255, 255, 255, 0.98);
|
||
backdrop-filter: blur(20px);
|
||
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
|
||
display: flex;
|
||
align-items: center;
|
||
padding-bottom: constant(safe-area-inset-bottom);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||
z-index: 100;
|
||
}
|
||
|
||
.tab {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.tab-icon {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.tab-txt {
|
||
margin-top: 4rpx;
|
||
font-size: 20rpx;
|
||
color: #8E9297;
|
||
font-weight: 500;
|
||
letter-spacing: 0.5rpx;
|
||
transition: color 0.2s ease;
|
||
}
|
||
|
||
.tab.on .tab-txt {
|
||
color: #F0710D;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.tab.on .tab-icon {
|
||
transform: scale(1.06);
|
||
}
|
||
|
||
/* 中间 FAB:精致小圆纽 */
|
||
.tab-center {
|
||
position: relative;
|
||
overflow: visible;
|
||
}
|
||
|
||
.fab-wrapper {
|
||
position: relative;
|
||
top: -26rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.fab {
|
||
width: 88rpx;
|
||
height: 88rpx;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #FFA93F 0%, #F0710D 100%);
|
||
box-shadow: 0 6rpx 18rpx rgba(240, 113, 13, 0.32);
|
||
border: 4rpx solid #FFFFFF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||
}
|
||
|
||
.fab:active {
|
||
transform: scale(0.92);
|
||
box-shadow: 0 2rpx 10rpx rgba(240, 113, 13, 0.2);
|
||
}
|
||
|
||
.fab-icon {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
}
|
||
|
||
.fab-label {
|
||
margin-top: 6rpx;
|
||
font-size: 20rpx;
|
||
color: #F0710D;
|
||
font-weight: 600;
|
||
letter-spacing: 0.5rpx;
|
||
}
|
||
|
||
/* 页面垫高高度 */
|
||
.tabbar-placeholder {
|
||
width: 100%;
|
||
height: 120rpx;
|
||
padding-bottom: constant(safe-area-inset-bottom);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
</style>
|