feat(components): 新增分页、选择器和标签栏组件并优化插件页面

- 添加 pagination-plugin 组件,包含完整的分页功能和页面跳转选择
- 添加 select-plugin 组件,支持单选和多选模式的选择弹窗
- 添加 tab-bar-plugin 组件,提供底部导航栏功能
- 在 page-plugin 中集成 t-loading 组件替换原有的加载逻辑
- 移除页面中的登录状态监听逻辑和相关订阅功能
- 修改页面样式传递方式,将 style 属性改为 customStyle
- 更新组件依赖配置文件,添加新的组件引用
This commit is contained in:
zhengw
2026-01-12 16:29:53 +08:00
parent f0c68126dc
commit 0d58fc80f4
15 changed files with 365 additions and 25 deletions

View File

@@ -3,7 +3,8 @@
"usingComponents": {
"t-button": "tdesign-miniprogram/button/button",
"t-checkbox": "tdesign-miniprogram/checkbox/checkbox",
"t-divider": "tdesign-miniprogram/divider/divider",
"t-link": "tdesign-miniprogram/link/link"
"t-link": "tdesign-miniprogram/link/link",
"t-loading": "tdesign-miniprogram/loading/loading",
"no-auth-plugin": "/pages/components/no-auth-plugin/no-auth-plugin"
}
}
}

View File

@@ -1,6 +1,6 @@
import { base, defaultAvatarUrl } from "@/utils/config";
import { Subscribe } from "@/utils/subscribe";
import { getCurrentPageRoute, getStorage, setStorage } from "@/utils/util";
import { setStorage } from "@/utils/util";
// import { IStorage } from "@/utils/storage";
// const app = getApp();
@@ -12,22 +12,22 @@ Component({
isLogin: Boolean,
loading: Boolean,
isAuth: null,
style: null,
customStyle: null,
},
attached() {
this.setData({ isLogin: getStorage("isLogin") == 1 });
// attached() {
// this.setData({ isLogin: getStorage("isLogin") == 1 });
console.log("attached");
Subscribe.on("isLogin", getCurrentPageRoute(), () => {
this.setData({ isLogin: getStorage("isLogin") == 1 });
console.log("监听到 isLogin 变化:");
});
},
detached() {
console.log("detached");
Subscribe.off("isLogin", getCurrentPageRoute());
},
// console.log("attached");
// Subscribe.on("isLogin", getCurrentPageRoute(), () => {
// this.setData({ isLogin: getStorage("isLogin") == 1 });
// console.log("监听到 isLogin 变化:");
// });
// },
// detached() {
// console.log("detached");
// Subscribe.off("isLogin", getCurrentPageRoute());
// },
data: {
avatarUrl: defaultAvatarUrl,

View File

@@ -1,15 +1,15 @@
<block wx:if="{{isLogin}}">
<view wx:if="{{!isAuth}}" style="padding: 12px;padding-bottom: env(safe-area-inset-bottom);{{style ||''}}">
<view wx:if="{{isAuth}}" style="padding: 12px;padding-bottom: env(safe-area-inset-bottom);{{customStyle ||''}}">
<slot></slot>
</view>
<no-auth-plugin wx:else />
</block>
<block wx:else>
<!-- <loading_plugin wx:if="{{loading}}" /> -->
<view style="padding: 0 16px;height: 100vh;justify-content: center;display: flex;flex-direction: column;">
<t-loading wx:if="{{loading}}" theme="spinner" fullscreen text="加载中..." layout="vertical" />
<view wx:else style="padding: 0 16px;height: 100vh;justify-content: center;display: flex;flex-direction: column;">
<view style="display: flex;justify-content: center;">
<view style="width: 80px;height: 80px;overflow: hidden;border-radius: 80px;">
<image style="width: 100%;height: 100%;object-fit: cover;" src="{{avatarUrl}}" mode="cover"></image>
<image style="width: 100%;height: 100%;object-fit: cover;" src="{{avatarUrl}}" mode="cover" />
</view>
</view>
<view style="padding: 20px 0">
@@ -27,11 +27,12 @@
<t-checkbox borderless style="padding: 0;margin: 1em 0;" value="{{isAgree}}" bind:change="changeAgreementCheck">
我已阅读并同意
</t-checkbox>
<!-- <t-divider /> -->
<t-link theme="primary" bindtap="navAgreement" data-type="user" style="margin-bottom: 8px;">
《{{appletName}}平台用户服务协议》</t-link>
《{{appletName}}平台用户服务协议》
</t-link>
<t-link theme="primary" bindtap="navAgreement" data-type="privacy" style="margin-bottom: 8px;">
《{{appletName}}隐私权政策》</t-link>
《{{appletName}}隐私权政策》
</t-link>
</view>
</block>

View File

@@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"t-picker": "tdesign-miniprogram/picker/picker",
"t-picker-item": "tdesign-miniprogram/picker-item/picker-item"
}
}

View File

@@ -0,0 +1,102 @@
import { getDataSet, toNumber } from "@/utils/util";
Component({
options: { multipleSlots: true },
/**
* 组件的属性列表
*/
properties: {
curr_page: {
type: Number,
value: 1,
observer() {
this.page();
},
},
total: {
type: Number,
value: 0,
observer() {
this.page();
},
},
page_count: {
type: Number,
value: 20,
observer() {
this.page();
},
},
},
/**
* 组件的初始数据
*/
data: {
pages: [] as number[],
totalPage: 0,
open: false,
pickerPages: [] as any[],
},
/**
* 组件的方法列表
*/
methods: {
page() {
const totalPage = Math.ceil(
toNumber(this.data.total) / toNumber(this.data.page_count)
);
const showPageNum = 3;
// 计算显示的页码范围
let startPage = Math.max(
1,
toNumber(this.data.curr_page) - Math.floor(showPageNum / 2)
);
let endPage = Math.min(totalPage, startPage + showPageNum - 1);
// 调整起始页码,确保显示的页码数量不超过最大值
if (endPage - startPage + 1 < showPageNum) {
startPage = Math.max(1, endPage - showPageNum + 1);
}
// 生成页码数组
const pages = [];
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
this.setData({ pages, totalPage });
},
pageChange(e: any) {
let curr_page = getDataSet(e).page;
// if (curr_page == this.data.curr_page) {
// return;
// }
if (curr_page == "prev") {
curr_page = this.data.curr_page - 1;
} else if (curr_page == "next") {
curr_page = this.data.curr_page + 1;
}
this.triggerEvent("change", { curr_page });
},
pageSelect() {
this.setData({
open: true,
pickerPages: Array.from({ length: this.data.totalPage }, (_, i) => ({
value: i + 1,
label: `${i + 1}`,
})),
});
},
onConfirm(e: any) {
const curr_page = e.detail.value[0];
// if (curr_page == this.data.curr_page) {
// return;
// }
this.triggerEvent("change", { curr_page: curr_page });
},
},
});

View File

@@ -0,0 +1,42 @@
<view class="box">
<view>共{{total}}条</view>
<t-button
size="small"
disabled="{{curr_page == 1}}"
bindtap="pageChange"
data-page="prev"
icon="chevron-left"
style="padding-left: 8px;padding-right: 8px"
/>
<block wx:for="{{pages}}" wx:key="index">
<t-button
size="small"
theme="{{curr_page == item ? 'primary': ''}}"
variant="{{curr_page == item ? 'outline': ''}}"
bindtap="pageChange"
data-page="{{item}}"
>{{item}}
</t-button>
</block>
<t-button
size="small"
disabled="{{curr_page == totalPage}}"
bindtap="pageChange"
data-page="next"
icon="chevron-right"
style="padding-left: 8px;padding-right: 8px"
/>
<t-button size="small" bindtap="pageSelect" style="padding-left: 8px;padding-right: 8px">跳转
</t-button>
<t-picker
title="选择页码"
visible="{{open}}"
value="{{[curr_page]}}"
cancelBtn="取消"
confirmBtn="确认"
bind:confirm="onConfirm"
>
<t-picker-item options="{{pickerPages}}" />
</t-picker>
</view>

View File

@@ -0,0 +1,8 @@
.box {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
column-gap: 8px;
row-gap: 8px;
}

View File

@@ -0,0 +1,10 @@
{
"component": true,
"usingComponents": {
"popup-plugin": "/pages/components/popup-plugin/popup-plugin",
"t-radio-group": "tdesign-miniprogram/radio-group/radio-group",
"t-checkbox": "tdesign-miniprogram/checkbox/checkbox",
"t-checkbox-group": "tdesign-miniprogram/checkbox-group/checkbox-group"
}
}

View File

@@ -0,0 +1,64 @@
Component({
options: { multipleSlots: true },
/**
* 组件的属性列表
*/
properties: {
mode: {
type: String,
value: "single",
},
title: {
type: String,
value: "请选择",
},
options: {
type: Array,
value: [],
},
value: {
type: null,
value: [],
},
},
observers: {
"value,mode": function (v, m) {
if (m == "multiple") {
this.setData({ val: Array.isArray(v) ? [...v] : [] });
} else {
this.setData({ val: v });
}
},
},
/**
* 组件的初始数据
*/
data: {
visible: false,
val: [] as any[] | string | number,
},
/**
* 组件的方法列表
*/
methods: {
click() {
this.setData({ visible: true });
},
close() {
this.setData({ visible: false });
},
radioChange(e: any) {
this.setData({ val: e.detail.value });
},
checkboxChange(e: any) {
this.setData({ val: e.detail.value });
},
ok() {
this.setData({ visible: false });
this.triggerEvent("change", { value: this.data.val });
},
},
});

View File

@@ -0,0 +1,30 @@
<view style="display: contents" bindtap="click">
<slot/>
</view>
<popup-plugin visible="{{visible}}" bind:close="close" title="{{title}}">
<view>
<t-radio-group
wx:if="{{mode == 'single'}}"
bind:change="radioChange"
allow-uncheck
value="{{val}}"
options="{{options}}"
/>
<t-checkbox-group wx:else value="{{val}}" bind:change="checkboxChange">
<t-checkbox
wx:for="{{options}}"
icon="rectangle"
wx:key="index"
value="{{item.value}}"
>{{item.label}}
</t-checkbox>
</t-checkbox-group>
</view>
<view class="footer">
<t-button size="small" theme="primary" bindtap="ok">确定</t-button>
</view>
</popup-plugin>

View File

@@ -0,0 +1,10 @@
.footer {
display: flex;
align-items: center;
justify-content: center;
position: sticky;
bottom: 0;
background: #fff;
z-index: 1;
padding-top: 12px;
}

View File

@@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar",
"t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item"
}
}

View File

@@ -0,0 +1,43 @@
Component({
options: { multipleSlots: true },
/**
* 组件的属性列表
*/
properties: {
active: {
type: String,
value: "home",
},
},
/**
* 组件的初始数据
*/
data: {
list: [
{ value: "home", label: "首页", icon: "home" },
{ value: "scan", label: "扫码", icon: "scan" },
{ value: "my", label: "我的", icon: "user" },
],
pages: {
index: "/pages/index/index",
scan: "/pages/processEntry/processEntry",
my: "/pages/my/my",
} as Record<string, string>,
},
/**
* 组件的方法列表
*/
methods: {
onChange(e: any) {
if (e.detail.value == this.data.active) {
return;
}
wx.reLaunch({
url: this.data.pages[e.detail.value],
});
},
},
});

View File

@@ -0,0 +1,13 @@
<t-tab-bar
t-class="t-tab-bar"
value="{{active}}"
bindchange="onChange"
theme="normal"
fixed="{{true}}"
split="{{false}}"
>
<t-tab-bar-item wx:for="{{list}}" wx:key="value" value="{{item.value}}" icon="{{item.icon}}">
{{item.label}}
</t-tab-bar-item>
</t-tab-bar>