底部导航
This commit is contained in:
@@ -127,3 +127,29 @@ This project uses `@antmjs/vantui` v3.7. The official docs: https://antmjs.githu
|
|||||||
- Use `@tarojs/components` for: `View`, `Text`, `ScrollView`, `Image` (native, simpler)
|
- Use `@tarojs/components` for: `View`, `Text`, `ScrollView`, `Image` (native, simpler)
|
||||||
- Use `@antmjs/vantui` for: styled UI components (Search, Swiper, Tabs, Grid, Button, etc.)
|
- Use `@antmjs/vantui` for: styled UI components (Search, Swiper, Tabs, Grid, Button, etc.)
|
||||||
- VantUI component props extend Taro's `ViewProps`, so `onClick`/`onTap` and style props are always available
|
- VantUI component props extend Taro's `ViewProps`, so `onClick`/`onTap` and style props are always available
|
||||||
|
|
||||||
|
## Custom TabBar (`src/custom-tab-bar/`)
|
||||||
|
|
||||||
|
This project uses Taro's **custom tabBar** mechanism (`tabBar.custom: true` in `app.config.ts`). Key rules:
|
||||||
|
|
||||||
|
### Component location
|
||||||
|
- The custom tabBar component MUST be at `src/custom-tab-bar/index.tsx` — this is a Taro convention, not configurable
|
||||||
|
- Taro automatically renders this component on every tab page; do NOT import `<CustomTabBar />` in individual pages
|
||||||
|
|
||||||
|
### Do NOT use CoverView
|
||||||
|
- `CoverView` / `CoverImage` have known bugs on real devices: invisible, click events don't bubble
|
||||||
|
- Use regular `<View>` with `position: fixed; bottom: 0; z-index: 999` instead
|
||||||
|
|
||||||
|
### Active tab state
|
||||||
|
- Taro creates a **new instance** of the tabBar component on each page, so `useState` resets on every tab switch
|
||||||
|
- Derive the active tab from `Taro.getCurrentPages()` at render time — NOT from component state
|
||||||
|
- Pattern: `const route = Taro.getCurrentPages()[last]?.route; const activeKey = TAB_LIST.find(t => route.includes(t.key))`
|
||||||
|
|
||||||
|
### Tab switching
|
||||||
|
- Use `Taro.switchTab({ url: pagePath })` — this matches native tabBar behavior
|
||||||
|
- The `pagePath` format must match the `tabBar.list[].pagePath` in `app.config.ts`
|
||||||
|
|
||||||
|
### Center "publish" button
|
||||||
|
- The publish button is a UI-only element not in `tabBar.list`
|
||||||
|
- It triggers a VantUI `<Popup>` rendered as a sibling of the tabBar, NOT nested inside it
|
||||||
|
- The popup state can use `useState` since it's ephemeral and not shared across tabs
|
||||||
|
|||||||
+29
-3
@@ -1,13 +1,39 @@
|
|||||||
export default defineAppConfig({
|
export default defineAppConfig({
|
||||||
pages: [
|
pages: [
|
||||||
'pages/index/index',
|
'pages/index/index',
|
||||||
|
'pages/tasks/index',
|
||||||
|
'pages/messages/index',
|
||||||
|
'pages/profile/index',
|
||||||
],
|
],
|
||||||
window: {
|
window: {
|
||||||
backgroundTextStyle: 'light',
|
backgroundTextStyle: 'light',
|
||||||
navigationBarBackgroundColor: '#fff',
|
navigationBarBackgroundColor: '#fff',
|
||||||
navigationBarTitleText: 'WeChat',
|
navigationBarTitleText: 'WeChat',
|
||||||
navigationBarTextStyle: 'black'
|
navigationBarTextStyle: 'black',
|
||||||
|
},
|
||||||
|
tabBar: {
|
||||||
|
custom: true,
|
||||||
|
color: '#969799',
|
||||||
|
selectedColor: '#1989fa',
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
pagePath: 'pages/index/index',
|
||||||
|
text: '首页',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'pages/tasks/index',
|
||||||
|
text: '校园任务',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'pages/messages/index',
|
||||||
|
text: '消息',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'pages/profile/index',
|
||||||
|
text: '我的',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
animation: false,
|
animation: false,
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
.publish-popup {
|
||||||
|
padding: 32px 24px 0;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 24px 24px 0 0;
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
|
||||||
|
.popup-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-subtitle {
|
||||||
|
font-size: 26px;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.publish-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 28px 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background: #f7f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-right: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.card-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1a1a1a;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-desc {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-cancel {
|
||||||
|
padding: 20px 0 40px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.cancel-text {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 32px;
|
||||||
|
color: #969799;
|
||||||
|
padding: 12px 48px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import { Popup } from '@antmjs/vantui'
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
interface PublishOption {
|
||||||
|
id: string
|
||||||
|
label: string
|
||||||
|
icon: string
|
||||||
|
desc: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const PUBLISH_OPTIONS: PublishOption[] = [
|
||||||
|
{ id: 'article', label: '发布文章', icon: '📝', desc: '分享知识与见解' },
|
||||||
|
{ id: 'goods', label: '发布二手', icon: '🛍️', desc: '闲置物品交易' },
|
||||||
|
{ id: 'express', label: '快递代取', icon: '📦', desc: '发布代取任务' },
|
||||||
|
{ id: 'activity', label: '校园活动', icon: '🎉', desc: '组织校园活动' },
|
||||||
|
{ id: 'help', label: '求助信息', icon: '🆘', desc: '发布求助需求' },
|
||||||
|
]
|
||||||
|
|
||||||
|
interface PublishPopupProps {
|
||||||
|
visible: boolean
|
||||||
|
onClose: () => void
|
||||||
|
onSelect: (option: PublishOption) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PublishPopup({ visible, onClose, onSelect }: PublishPopupProps) {
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
visible={visible}
|
||||||
|
position='bottom'
|
||||||
|
round
|
||||||
|
onClose={onClose}
|
||||||
|
closeable
|
||||||
|
customStyle={{ background: 'transparent' }}
|
||||||
|
>
|
||||||
|
<View className='publish-popup'>
|
||||||
|
<View className='popup-header'>
|
||||||
|
<Text className='popup-title'>发布</Text>
|
||||||
|
<Text className='popup-subtitle'>选择你要发布的内容类型</Text>
|
||||||
|
</View>
|
||||||
|
<View className='popup-grid'>
|
||||||
|
{PUBLISH_OPTIONS.map((option) => (
|
||||||
|
<View
|
||||||
|
key={option.id}
|
||||||
|
className='publish-card'
|
||||||
|
onClick={() => onSelect(option)}
|
||||||
|
>
|
||||||
|
<Text className='card-icon'>{option.icon}</Text>
|
||||||
|
<View className='card-info'>
|
||||||
|
<Text className='card-label'>{option.label}</Text>
|
||||||
|
<Text className='card-desc'>{option.desc}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
<View className='popup-cancel' onClick={onClose}>
|
||||||
|
<Text className='cancel-text'>取消</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Popup>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { PublishOption }
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
"component": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
.custom-tab-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-around;
|
||||||
|
height: 100px;
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
-webkit-backdrop-filter: blur(20px);
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
z-index: 999;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.tab-icon {
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-label {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #969799;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active .tab-label {
|
||||||
|
color: #1989fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中间发布按钮 */
|
||||||
|
.tab-publish {
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding-top: 0;
|
||||||
|
|
||||||
|
.publish-btn {
|
||||||
|
width: 88px;
|
||||||
|
height: 88px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #1989fa 0%, #07c160 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 8px 24px rgba(25, 137, 250, 0.4);
|
||||||
|
margin-top: -30px;
|
||||||
|
|
||||||
|
.publish-icon {
|
||||||
|
font-size: 44px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.publish-label {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import { useState, useCallback } from 'react'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import PublishPopup from '@/components/PublishPopup'
|
||||||
|
import type { PublishOption } from '@/components/PublishPopup'
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
/** Tab 项配置(顺序与 app.config.ts tabBar.list 一致) */
|
||||||
|
interface TabItem {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
icon: string
|
||||||
|
pagePath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const TAB_LIST: TabItem[] = [
|
||||||
|
{ key: 'index', label: '首页', icon: '🏠', pagePath: '/pages/index/index' },
|
||||||
|
{ key: 'tasks', label: '校园任务', icon: '📋', pagePath: '/pages/tasks/index' },
|
||||||
|
{ key: 'messages', label: '消息', icon: '💬', pagePath: '/pages/messages/index' },
|
||||||
|
{ key: 'profile', label: '我的', icon: '👤', pagePath: '/pages/profile/index' },
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据当前页面路由推导激活的 tab。
|
||||||
|
* 不使用 useState —— Taro 在每次切换 tab 时都会重建 custom-tab-bar 实例,
|
||||||
|
* 因此直接从 getCurrentPages() 实时计算即可保证正确性。
|
||||||
|
*/
|
||||||
|
function useActiveKey(): string {
|
||||||
|
const pages = Taro.getCurrentPages()
|
||||||
|
if (pages.length > 0) {
|
||||||
|
const route = pages[pages.length - 1].route || ''
|
||||||
|
for (const tab of TAB_LIST) {
|
||||||
|
if (route.includes(tab.key)) return tab.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'index'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取底部安全距离 */
|
||||||
|
function useSafeBottom(): number {
|
||||||
|
try {
|
||||||
|
const info = Taro.getSystemInfoSync()
|
||||||
|
if (info.safeArea) {
|
||||||
|
return info.safeArea.bottom
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CustomTabBar() {
|
||||||
|
const activeKey = useActiveKey()
|
||||||
|
const safeBottom = useSafeBottom()
|
||||||
|
const [publishVisible, setPublishVisible] = useState(false)
|
||||||
|
|
||||||
|
/** 切换 Tab —— 使用 Taro.switchTab 与原生 tabBar 行为一致 */
|
||||||
|
const handleTabClick = useCallback((tab: TabItem) => {
|
||||||
|
if (tab.key === activeKey) return
|
||||||
|
Taro.switchTab({ url: tab.pagePath })
|
||||||
|
}, [activeKey])
|
||||||
|
|
||||||
|
/** 发布弹窗 */
|
||||||
|
const handlePublish = () => setPublishVisible(true)
|
||||||
|
const handlePublishClose = () => setPublishVisible(false)
|
||||||
|
const handlePublishSelect = (option: PublishOption) => {
|
||||||
|
setPublishVisible(false)
|
||||||
|
Taro.showToast({ title: `${option.label}即将上线`, icon: 'none' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 4 个 tab 拆分为 [0,1] + 发布按钮 + [2,3]
|
||||||
|
const leftTabs = TAB_LIST.slice(0, 2)
|
||||||
|
const rightTabs = TAB_LIST.slice(2)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
className='custom-tab-bar'
|
||||||
|
style={{ paddingBottom: `${safeBottom}px` }}
|
||||||
|
>
|
||||||
|
{leftTabs.map((tab) => (
|
||||||
|
<View
|
||||||
|
key={tab.key}
|
||||||
|
className={`tab-item ${activeKey === tab.key ? 'active' : ''}`}
|
||||||
|
onClick={() => handleTabClick(tab)}
|
||||||
|
>
|
||||||
|
<Text className='tab-icon'>{tab.icon}</Text>
|
||||||
|
<Text className='tab-label'>{tab.label}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* 中间发布按钮 —— 不属于 tabBar list,纯 UI 元素 */}
|
||||||
|
<View className='tab-item tab-publish' onClick={handlePublish}>
|
||||||
|
<View className='publish-btn'>
|
||||||
|
<Text className='publish-icon'>+</Text>
|
||||||
|
</View>
|
||||||
|
<Text className='tab-label publish-label'>发布</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{rightTabs.map((tab) => (
|
||||||
|
<View
|
||||||
|
key={tab.key}
|
||||||
|
className={`tab-item ${activeKey === tab.key ? 'active' : ''}`}
|
||||||
|
onClick={() => handleTabClick(tab)}
|
||||||
|
>
|
||||||
|
<Text className='tab-icon'>{tab.icon}</Text>
|
||||||
|
<Text className='tab-label'>{tab.label}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 发布弹窗 —— 使用 VantUI Popup,独立于 tabBar 渲染 */}
|
||||||
|
<PublishPopup
|
||||||
|
visible={publishVisible}
|
||||||
|
onClose={handlePublishClose}
|
||||||
|
onSelect={handlePublishSelect}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { Search, Swiper, SwiperItem, Grid, GridItem, Tabs, Tab, Image as VantIma
|
|||||||
import ArticleCard from '@/components/ArticleCard'
|
import ArticleCard from '@/components/ArticleCard'
|
||||||
import { banners, navItems, articles } from '@/mock/articles'
|
import { banners, navItems, articles } from '@/mock/articles'
|
||||||
import type { ArticleCategory } from '@/types/article'
|
import type { ArticleCategory } from '@/types/article'
|
||||||
|
import CustomTabBar from "@/custom-tab-bar";
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
|
||||||
/** 获取状态栏和导航栏高度 */
|
/** 获取状态栏和导航栏高度 */
|
||||||
@@ -163,6 +164,8 @@ export default function Index() {
|
|||||||
{/* 底部安全距离 */}
|
{/* 底部安全距离 */}
|
||||||
<View className='bottom-safe' />
|
<View className='bottom-safe' />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
<CustomTabBar />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '消息',
|
||||||
|
navigationStyle: 'custom',
|
||||||
|
})
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.messages-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
|
||||||
|
.page-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 300px;
|
||||||
|
|
||||||
|
.placeholder-icon {
|
||||||
|
font-size: 96px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-text {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #323233;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-desc {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import CustomTabBar from "@/custom-tab-bar"
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
export default function Messages() {
|
||||||
|
return (
|
||||||
|
<View className='messages-page'>
|
||||||
|
<View className='page-placeholder'>
|
||||||
|
<Text className='placeholder-icon'>💬</Text>
|
||||||
|
<Text className='placeholder-text'>消息</Text>
|
||||||
|
<Text className='placeholder-desc'>即将上线,敬请期待</Text>
|
||||||
|
</View>
|
||||||
|
<CustomTabBar />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '我的',
|
||||||
|
navigationStyle: 'custom',
|
||||||
|
})
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.profile-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
|
||||||
|
.page-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 300px;
|
||||||
|
|
||||||
|
.placeholder-icon {
|
||||||
|
font-size: 96px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-text {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #323233;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-desc {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import CustomTabBar from "@/custom-tab-bar"
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
export default function Profile() {
|
||||||
|
return (
|
||||||
|
<View className='profile-page'>
|
||||||
|
<View className='page-placeholder'>
|
||||||
|
<Text className='placeholder-icon'>👤</Text>
|
||||||
|
<Text className='placeholder-text'>我的</Text>
|
||||||
|
<Text className='placeholder-desc'>即将上线,敬请期待</Text>
|
||||||
|
</View>
|
||||||
|
<CustomTabBar />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '校园任务',
|
||||||
|
navigationStyle: 'custom',
|
||||||
|
"usingComponents": {}
|
||||||
|
})
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.tasks-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
|
||||||
|
.page-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 300px;
|
||||||
|
|
||||||
|
.placeholder-icon {
|
||||||
|
font-size: 96px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-text {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #323233;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-desc {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import CustomTabBar from "@/custom-tab-bar"
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
export default function Tasks() {
|
||||||
|
return (
|
||||||
|
<View className='tasks-page'>
|
||||||
|
<View className='page-placeholder'>
|
||||||
|
<Text className='placeholder-icon'>📋</Text>
|
||||||
|
<Text className='placeholder-text'>校园任务</Text>
|
||||||
|
<Text className='placeholder-desc'>即将上线,敬请期待</Text>
|
||||||
|
</View>
|
||||||
|
<CustomTabBar />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user