feat: Add "Task List" and "Settings" pages with initial navigation setup

- Created `pages/list` for managing and displaying to-do tasks.
- Created `pages/settings` as a placeholder for application settings.
- Updated `pages/index` for a refreshed layout with navigation to "Tasks" and "Settings".
- Integrated custom tab bar across all pages for seamless transitions.
This commit is contained in:
grtsinry43 2025-06-09 13:30:02 +08:00
parent 060615a3d9
commit 5c3d956de9
Signed by: grtsinry43
GPG Key ID: F3305FB3A978C934
15 changed files with 445 additions and 151 deletions

View File

@ -1,8 +1,24 @@
{
"pages": [
"pages/index/index",
"pages/list/list",
"pages/settings/settings",
"pages/logs/logs"
],
"tabBar": {
"custom": true,
"list": [
{
"pagePath": "pages/index/index"
},
{
"pagePath": "pages/list/list"
},
{
"pagePath": "pages/settings/settings"
}
]
},
"componentFramework": "glass-easel",
"lazyCodeLoading": "requiredComponents"
}

View File

@ -1,9 +1,14 @@
{
"usingComponents": {
"t-button": "tdesign-miniprogram/button/button",
"t-navbar": "tdesign-miniprogram/navbar/navbar",
"t-input": "tdesign-miniprogram/input/input",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
"t-checkbox": "tdesign-miniprogram/checkbox/checkbox",
"t-icon": "tdesign-miniprogram/icon/icon",
"t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar",
"t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item",
"t-navbar": "tdesign-miniprogram/navbar/navbar"
"t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item"
},
"navigationStyle": "custom"
}

View File

@ -1,54 +1,23 @@
// index.ts
// 获取应用实例
const app = getApp<IAppOption>()
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
// pages/index/index.ts
Component({
data: {
motto: 'Hello World',
userInfo: {
avatarUrl: defaultAvatarUrl,
nickName: '',
},
hasUserInfo: false,
canIUseGetUserProfile: wx.canIUse('getUserProfile'),
canIUseNicknameComp: wx.canIUse('input.type.nickname'),
activeTab: 'home', // For tab bar
},
methods: {
// 事件处理函数
bindViewTap() {
wx.navigateTo({
url: '../logs/logs',
})
onTabChange(e: any) {
const targetPage = e.detail.value;
if (targetPage === 'list') {
wx.switchTab({ url: '/pages/list/list' });
} else if (targetPage === 'settings') {
wx.switchTab({ url: '/pages/settings/settings' });
}
// No need to navigate if already on 'home'
},
onChooseAvatar(e: any) {
const { avatarUrl } = e.detail
const { nickName } = this.data.userInfo
this.setData({
"userInfo.avatarUrl": avatarUrl,
hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
onInputChange(e: any) {
const nickName = e.detail.value
const { avatarUrl } = this.data.userInfo
this.setData({
"userInfo.nickName": nickName,
hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
getUserProfile() {
// 推荐使用wx.getUserProfile获取用户信息开发者每次通过该接口获取用户个人信息均需用户确认开发者妥善保管用户快速填写的头像昵称避免重复弹窗
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
console.log(res)
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
goToTasks() {
wx.switchTab({ url: '/pages/list/list' });
},
goToSettings() {
wx.switchTab({ url: '/pages/settings/settings' });
}
},
})
});

View File

@ -1,56 +1,29 @@
<!--index.wxml-->
<view class="block">
<t-navbar
t-class-placeholder="t-navbar-placeholder"
t-class-content="t-navbar-content"
title="主页"
t-class-title="nav-title"
/>
</view>
<!--pages/index/index.wxml-->
<view class="container">
<t-navbar title="首页" />
<scroll-view class="scrollarea" scroll-y type="list">
<view class="container">
<view class="userinfo">
<t-button theme="primary">按钮</t-button>
<block wx:if="{{canIUseNicknameComp && !hasUserInfo}}">
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{userInfo.avatarUrl}}"></image>
</button>
<view class="nickname-wrapper">
<text class="nickname-label">昵称</text>
<input type="nickname" class="nickname-input" placeholder="请输入昵称" bind:change="onInputChange" />
</view>
</block>
<block wx:elif="{{!hasUserInfo}}">
<button wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile"> 获取头像昵称 </button>
<view wx:else> 请使用2.10.4及以上版本基础库 </view>
</block>
<block wx:else>
<image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</block>
</view>
<view class="usermotto">
<text class="user-motto">{{motto}}</text>
</view>
<view class="hero-section">
<image class="hero-image" src="/assets/images/dashboard-art.png" mode="aspectFit" />
<text class="hero-title">欢迎回来!</text>
<text class="hero-subtitle">今天有什么计划?让我们开始吧。</text>
</view>
</scroll-view>
<!-- 文本 + 图标 + 徽标 -->
<view class="wrapper">
<t-tab-bar t-class="t-tab-bar" defaultValue="label1" split="{{false}}">
<t-tab-bar-item badge-props="{{ {count: 16} }}" ariaLabel="首页有16条消息" value="label1" icon="home">
首页
</t-tab-bar-item>
<t-tab-bar-item badge-props="{{ { dot: true } }}" ariaLabel="应用,有新的消息" value="label2" icon="app">
应用
</t-tab-bar-item>
<t-tab-bar-item badge-props="{{ {count: 'New'} }}" ariaLabel="聊天New" value="label3" icon="chat">
聊天
</t-tab-bar-item>
<t-tab-bar-item badge-props="{{ {count: '···'} }}" ariaLabel="我的,有很多消息" value="label4" icon="user">
我的
</t-tab-bar-item>
<view class="quick-actions">
<t-button theme="primary" size="large" block bind:tap="goToTasks" class="action-button">
<t-icon name="bulletpoint" slot="icon" />
查看我的任务
</t-button>
<t-button theme="default" size="large" block bind:tap="goToSettings" class="action-button">
<t-icon name="setting" slot="icon" />
打开设置
</t-button>
</view>
<view class="footer-space"></view>
<t-tab-bar value="{{activeTab}}" bind:change="onTabChange" t-class="custom-tab-bar">
<t-tab-bar-item value="home" icon="home" aria-label="首页">首页</t-tab-bar-item>
<t-tab-bar-item value="list" icon="bulletpoint" aria-label="任务">任务</t-tab-bar-item>
<t-tab-bar-item value="settings" icon="setting" aria-label="设置">设置</t-tab-bar-item>
</t-tab-bar>
</view>

View File

@ -1,62 +1,83 @@
/**index.wxss**/
page {
height: 100vh;
display: flex;
flex-direction: column;
}
.scrollarea {
flex: 1;
overflow-y: hidden;
height: 100vh;
background-color: #f8f9fa; /* Lighter, cleaner background */
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
.userinfo {
.container {
display: flex;
flex-direction: column;
flex: 1;
padding: 20rpx;
box-sizing: border-box;
padding-bottom: 120rpx; /* Add padding for the fixed tab bar */
}
.t-navbar {
margin-bottom: 20rpx;
}
.hero-section {
display: flex;
flex-direction: column;
align-items: center;
color: #aaa;
width: 80%;
text-align: center;
padding: 60rpx 20rpx;
background-color: #ffffff;
border-radius: 24rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.05);
margin-bottom: 40rpx;
}
.userinfo-avatar {
overflow: hidden;
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
.hero-image {
width: 300rpx; /* Adjust as needed */
height: 200rpx; /* Adjust as needed */
margin-bottom: 30rpx;
}
.usermotto {
margin-top: 200px;
.hero-title {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.avatar-wrapper {
padding: 0;
width: 56px !important;
border-radius: 8px;
margin-top: 40px;
margin-bottom: 40px;
.hero-subtitle {
font-size: 28rpx;
color: #666;
line-height: 1.6;
}
.avatar {
display: block;
width: 56px;
height: 56px;
}
.nickname-wrapper {
.quick-actions {
display: flex;
width: 100%;
padding: 16px;
box-sizing: border-box;
border-top: .5px solid rgba(0, 0, 0, 0.1);
border-bottom: .5px solid rgba(0, 0, 0, 0.1);
color: black;
flex-direction: column;
gap: 20rpx; /* Adds space between buttons */
margin-bottom: 40rpx;
}
.nickname-label {
width: 105px;
.action-button .t-button {
width: 100%; /* Make button take full width if block attribute isn't enough */
}
.nickname-input {
flex: 1;
.action-button .t-icon {
margin-right: 10rpx;
}
.footer-space {
height: 100rpx; /* Space at the bottom before tab bar, if content is scrollable */
}
.custom-tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000; /* Ensure it's on top */
background-color: #ffffff; /* Ensure tab bar has a background */
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
}
/** Removed old userinfo and motto styles as per the change description **/

View File

@ -0,0 +1,16 @@
{
"usingComponents": {
"t-button": "tdesign-miniprogram/button/button",
"t-navbar": "tdesign-miniprogram/navbar/navbar",
"t-input": "tdesign-miniprogram/input/input",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
"t-checkbox": "tdesign-miniprogram/checkbox/checkbox",
"t-icon": "tdesign-miniprogram/icon/icon",
"t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar",
"t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item"
},
"navigationStyle": "custom",
"navigationBarTitleText": "任务列表"
}

View File

@ -0,0 +1,62 @@
// pages/list/list.ts
interface ITodo {
text: string;
completed: boolean;
}
Component({
data: {
newTodoText: '',
todos: [] as ITodo[],
activeTab: 'list', // For tab bar
},
methods: {
onNewTodoInput(e: any) {
this.setData({
newTodoText: e.detail.value,
});
},
addTodo() {
const { newTodoText, todos } = this.data;
if (newTodoText.trim() === '') {
return;
}
const newTodos = [...todos, { text: newTodoText, completed: false }];
this.setData({
todos: newTodos,
newTodoText: '',
});
},
toggleTodo(e: any) {
const index = e.currentTarget.dataset.index;
const { todos } = this.data;
const newTodos = todos.map((todo, i) => {
if (i === index) {
return { ...todo, completed: !todo.completed };
}
return todo;
});
this.setData({
todos: newTodos,
});
},
removeTodo(e: any) {
const index = e.currentTarget.dataset.index;
const { todos } = this.data;
const newTodos = todos.filter((_, i) => i !== index);
this.setData({
todos: newTodos,
});
},
onTabChange(e: any) {
const targetPage = e.detail.value;
if (targetPage === 'home') {
wx.switchTab({ url: '/pages/index/index' });
} else if (targetPage === 'settings') {
wx.switchTab({ url: '/pages/settings/settings' });
}
// No need to navigate if already on 'list'
},
},
});

View File

@ -0,0 +1,47 @@
<!--pages/list/list.wxml-->
<view class="container">
<t-navbar title="我的任务" />
<view class="add-todo-section">
<t-input
t-class="add-todo-input"
placeholder="写下你的任务..."
value="{{newTodoText}}"
bind:change="onNewTodoInput"
bind:enter="addTodo"
/>
<t-button t-class="add-todo-button" theme="primary" icon="add" bind:tap="addTodo" />
</view>
<scroll-view class="todo-list-scroll" scroll-y type="list">
<t-cell-group t-class="todo-list">
<block wx:if="{{todos.length === 0}}">
<view class="empty-state">
<t-icon name="check-circle" size="xl" />
<text>太棒了!没有待办事项</text>
</view>
</block>
<block wx:else>
<t-cell wx:for="{{todos}}" wx:key="index" t-class-left="todo-item-left">
<view class="todo-item-content">
<t-checkbox
checked="{{item.completed}}"
bind:change="toggleTodo"
data-index="{{index}}"
icon="{{ item.completed ? ['check-circle-filled', 'circle'] : ['circle', 'check-circle-filled'] }}"
/>
<text class="{{item.completed ? 'todo-text completed' : 'todo-text'}}">{{item.text}}</text>
</view>
<t-icon name="delete" slot="right-icon" color="#e54d42" bind:tap="removeTodo" data-index="{{index}}" />
</t-cell>
</block>
</t-cell-group>
</scroll-view>
<t-tab-bar value="{{activeTab}}" bind:change="onTabChange" t-class="custom-tab-bar">
<t-tab-bar-item value="home" icon="home" aria-label="首页">首页</t-tab-bar-item>
<t-tab-bar-item value="list" icon="bulletpoint" aria-label="任务">任务</t-tab-bar-item>
<t-tab-bar-item value="settings" icon="setting" aria-label="设置">设置</t-tab-bar-item>
</t-tab-bar>
</view>

View File

@ -0,0 +1,115 @@
/* pages/list/list.wxss */
page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f4f4f4;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
.container {
display: flex;
flex-direction: column;
flex: 1;
padding: 20rpx;
box-sizing: border-box;
padding-bottom: 120rpx; /* Space for tab bar */
}
.add-todo-section {
display: flex;
align-items: center;
margin-bottom: 30rpx;
background-color: #fff;
border-radius: 16rpx;
padding: 10rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.add-todo-input {
flex: 1;
margin-right: 20rpx;
}
.add-todo-input .t-input__control {
font-size: 30rpx;
}
.add-todo-button {
min-width: auto;
}
.todo-list-scroll {
flex: 1;
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
overflow-y: auto;
}
.todo-item-left {
display: flex;
align-items: center;
width: 100%;
}
.todo-item-content {
display: flex;
align-items: center;
flex-grow: 1;
}
.todo-text {
margin-left: 20rpx;
font-size: 30rpx;
color: #333;
flex-grow: 1;
word-break: break-all;
}
.todo-text.completed {
text-decoration: line-through;
color: #aaa;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
text-align: center;
}
.empty-state .t-icon {
margin-bottom: 20rpx;
color: #0052d9;
}
.empty-state text {
font-size: 28rpx;
}
.t-navbar {
margin-bottom: 20rpx;
}
.todo-list .t-cell {
padding-top: 24rpx;
padding-bottom: 24rpx;
}
.todo-list .t-cell .t-icon[name="delete"] {
font-size: 40rpx;
padding-left: 20rpx;
}
.custom-tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000; /* Ensure it's on top */
}

View File

@ -0,0 +1,11 @@
{
"usingComponents": {
"t-navbar": "tdesign-miniprogram/navbar/navbar",
"t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar",
"t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item",
"t-icon": "tdesign-miniprogram/icon/icon"
},
"navigationStyle": "custom",
"navigationBarTitleText": "设置"
}

View File

@ -0,0 +1,18 @@
// pages/settings/settings.ts
Component({
data: {
activeTab: 'settings', // For tab bar
},
methods: {
onTabChange(e: any) {
const targetPage = e.detail.value;
if (targetPage === 'home') {
wx.switchTab({ url: '/pages/index/index' });
} else if (targetPage === 'list') {
wx.switchTab({ url: '/pages/list/list' });
}
// No need to navigate if already on 'settings'
},
},
});

View File

@ -0,0 +1,14 @@
<!--pages/settings/settings.wxml-->
<view class="container">
<t-navbar title="设置" />
<view class="content">
<t-icon name="setting" size="xl" />
<text>设置页面,敬请期待!</text>
</view>
<t-tab-bar value="{{activeTab}}" bind:change="onTabChange" t-class="custom-tab-bar">
<t-tab-bar-item value="home" icon="home" aria-label="首页">首页</t-tab-bar-item>
<t-tab-bar-item value="list" icon="bulletpoint" aria-label="任务">任务</t-tab-bar-item>
<t-tab-bar-item value="settings" icon="setting" aria-label="设置">设置</t-tab-bar-item>
</t-tab-bar>
</view>

View File

@ -0,0 +1,39 @@
/* pages/settings/settings.wxss */
page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f4f4f4;
}
.container {
display: flex;
flex-direction: column;
flex: 1;
padding-bottom: 120rpx; /* Space for tab bar */
}
.content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40rpx;
text-align: center;
color: #555;
}
.content .t-icon {
margin-bottom: 20rpx;
color: #0052d9;
}
.custom-tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
}

View File

@ -8,8 +8,5 @@
"license": "",
"dependencies": {
"tdesign-miniprogram": "^1.9.4"
},
"devDependencies": {
"miniprogram-api-typings": "^2.8.3-1"
}
}

9
pnpm-lock.yaml generated
View File

@ -11,21 +11,12 @@ importers:
tdesign-miniprogram:
specifier: ^1.9.4
version: 1.9.4
devDependencies:
miniprogram-api-typings:
specifier: ^2.8.3-1
version: 2.12.0
packages:
miniprogram-api-typings@2.12.0:
resolution: {integrity: sha512-ibvbqeslVFur0IAvTxLMvsbtvVcMo6gwvOnj0YZHV7aeDLu091VQRrETT2QuiG9P6aZWRcxeNGJChRKVPCp9VQ==}
tdesign-miniprogram@1.9.4:
resolution: {integrity: sha512-dc/V53tTmmrHGzrXclBK56h06nq3vo7GQgBTgiVtlAU5Mp/R/a50axswUKf7W3311rKb8Cuj3F3c45kmkBVPRQ==}
snapshots:
miniprogram-api-typings@2.12.0: {}
tdesign-miniprogram@1.9.4: {}