页面顺序
index页面(第一页面)
顶部日期及总收入总支出对应代码
<view class="calendar-content" id="calendar-content">
<view class="bg" />
<view class=" date-title">
<!-- 从底部弹起的滚动选择器。时期选择器 -->
<picker
class="date-picker"
mode="date"
@change="onPickerChange"
fields="month"
:value="pickerDate"
:end="pickerEnd"
>
<view class="now-date x-c">
<text>{{ pickerDateText.year }}年{{ pickerDateText.month }}月</text><!-- 顶部显示日期,与112行相关 -->
<i class="iconfont icon-bottom icon-down" />
</view>
</picker>
<view class="statement">
<view class="statement-text">
<text class="statement-text-title">总收入</text>
<view class="statement-text-total">
<text class="statement-text-total-char">¥</text>
<text>{{ indexStat.income }}</text>
</view>
</view>
<view class="statement-text">
<text class="statement-text-title">总支出</text>
<view class="statement-text-total">
<text class="statement-text-total-char">¥</text>
<text>{{ indexStat.expend }}</text>
</view>
</view>
</view>
</view>
日历部分
mb-ba-calender属于自定义组件,定义于src\components\base\mbill-calendar.vue
<view class="calendar">
<mb-ba-calendar
ref="calendar"
:expand="expand"
:tags="bill.indexTags"
:date="calendarDate"
@change="onDayChange"
@changemonth="onMonthChange"
@sizechange="onSizeChange"
/>
</view>
</view>
帐单列表部分
<mb-b-day-group>属于自定义组件
<scroll-view
class="statement-item"
:style="{
height: scrollHeight + 'px',
}"
scroll-y="true"
:refresher-enabled="true"
:refresher-triggered="triggered"
lower-threshold="50"
@scrolltolower="onLowerBottom"
@refresherpulling="onPulling"
@refresherrefresh="onRefresh"
@refresherrestore="onRestore"
@refresherabort="onAbort"
>
<mb-b-day-group :groups="indexBills" />
<mb-ba-login-hint v-if="!hasLogin" />
<mb-ba-empty v-if="!indexBills || (indexBills.length <= 0 && hasLogin)" />
<uni-load-more status="no-more" v-if="hasLogin && indexBills.length>0"></uni-load-more>
</scroll-view>
<mb-b-day-list-popup
:height=70
:show="popShow"
:date="popDate"
@change="onBillsOnDayPopup"
/>
<uni-fab ref="fab" :pattern="pattern" @fabClick="fabClick" horizontal='right' vertical='bottom'/>
- `<calendar-content>` 是日历的容器,包含了背景、日期选择器、总收入和总支出等元素。
- `<calendar>` 是日历组件的容器。 - `<calendar-expand>` 用于展开和收缩日历。
- `<scroll-view>` 是账单列表的滚动视图。
- `<mb-b-day-group>` 是账单分组组件。
- `<mb-ba-login-hint>` 和 `<mb-ba-empty>` 用于显示登录提示和空数据提示。
- `<uni-load-more>` 是加载更多组件。
- `<mb-b-day-list-popup>` 是账单列表弹窗组件。
- `<uni-fab>` 是浮动操作按钮组件。
脚本部分
脚本区基本内容说明链接:页面 | uni-app官网
代码部分带有功能注释
<script setup>
import { ref, onMounted, provide, getCurrentInstance, reactive, watch, computed } from 'vue';
import { onReachBottom, onShow, onLoad } from '@dcloudio/uni-app';
import * as datetime from "@/common/utils/datetime";
import { store, mutations } from '@/uni_modules/uni-id-pages/common/store.js'
import { storeToRefs } from 'pinia';
import { useBillStore } from "@/store/bill";
const bill = useBillStore();
//const { indexStat, indexBills } = bill;// 数据不是响应式的
const { indexStat, indexBills } = storeToRefs(bill);
// console.log(indexStat, indexBills);
const { proxy, ctx } = getCurrentInstance();
let db = uniCloud.database();
const now = new Date();
const calendarDate = reactive({
year: now.getFullYear(),
month: now.getMonth() + 1,
})
let hasLogin = computed(()=>{
return store.hasLogin
})
const pickerDate = ref(datetime.getCurDate());
const pickerDateText=reactive({
year:now.getFullYear(),
month:now.getMonth() + 1
})
const pickerEnd = datetime.getCurDate()
// const status = ref('loading')
const expand = ref(true)
const popShow = ref(false)
const popDate = ref({})
const dateTitleHeight = 62
const tabbarHeight = getApp().globalData.tabbarHeight
const expandHeight = ref(25)
const scrollHeight = ref(0)
const state=1
const triggered = ref(false)
const freshing = ref(false)
const pattern = {
buttonColor: '#ff9f18',
iconColor: 'ghostwhite'
}
let indexTotal = 0
let pH = 0
let scrollMaxHeight = 0
let indexPage = {
page:1,
size:50
}
let loading = false
onLoad( async () => {
// console.log('onLoad')
//console.log('1:', account.defUser, account.token );//isLogin defUser
//console.log(getCurrentPages()[getCurrentPages().length-1].route)
getFixedHeight();
triggered.value = true;
// initData();
// const res = await getDataList();
// console.log(res);
})
onShow(() => {
// console.log('onShow')
initData();
// status.value = 'more'
// console.log(getCurrentPages()[getCurrentPages().length-1].$page);
})
const fabClick = () =>{
// if (!hasLogin) {
// proxy.$tip.toast("暂未登录,请先登录!");
// return;
// }
uni.navigateTo({
url:'../bill/edit'
})
}
const getDataList = () => {
return new Promise((resolve, reject) => {
db.collection('mbill_category').get().then((res)=>{
resolve(res.result.data)
});
})
}
const completedEditCallback = () =>{
console.log('completedEditCallback');
}
// 初始化数据
const initData = () => {
// console.log('初始化数据')
return new Promise((resolve, reject) => {
// console.log(pickerDate.value);
if(hasLogin.value){
indexPage.page = 1;
bill.getIndexTotalStat(pickerDate.value).then((res)=>{
// if (indexPage.page * indexPage.size >= indexStat.value.count){
// status.value = 'no-more'
// } else{
// status.value = 'more'
// }
});
bill.getIndexBillTags(pickerDate.value);
getMonthBills(true)
.then((res) => resolve(res))
.catch((err) => reject(err));
}
triggered.value = false;
});
}
//#region 接口请求
// 初始化、切换月份重新加载账单
const getMonthBills = (init = false) => {
// console.log(indexPage.page , indexStat.value.count);
if (loading) return;
loading = true;
return bill.getIndexBills({
date: pickerDate.value,
page: indexPage,
isInit: init,
})
.then((res) => {
// console.log(indexBills.value);
indexTotal = res;
})
.finally(() => {
loading = false;
// if (indexPage.page * indexPage.size >= indexStat.value.count){
// status.value = 'no-more'
// } else{
// status.value = 'more'
// }
});
}
//#endregion
//#region 组件初始化
// 获取固定高度值
const getFixedHeight = () =>{
let that = this;
uni.getSystemInfo({
success(res) {
pH = res.windowHeight - 5; // 多出的5px,不让底部触底
},
});
}
// 动态计算scroll view 高度
const getDynamicHeight = (h) => {
// console.log(h);
if (h) {
// 10 为日历原本有上下为5的边距
scrollHeight.value = pH - tabbarHeight - h - expandHeight.value - dateTitleHeight;
scrollMaxHeight = scrollHeight.value;
return;
}
let query = uni.createSelectorQuery();
query.select("#calendar-content").fields({ size: true });
query.exec((data) => {
// console.log(data);
let elHeight = data[0].height;
scrollHeight.value = pH - tabbarHeight - elHeight - expandHeight.value;
scrollMaxHeight = scrollHeight.value;
// 30 为时间时间选择栏的上下为15的边距
// console.log("初始化高度", scrollMaxHeight);
});
}
// 日历月份变更
const onMonthChange = (date) =>{
// console.log('onMonthChange');
// console.log("date", date);
pickerDateText.month = date.month;
pickerDateText.year = date.year;
pickerDate.value = `${date.year}-${date.month}`;
initData();
}
// 选中具体日期时弹窗展示账单
const onDayChange =(e) =>{
// console.log('onDayChange');
if (!hasLogin.value) {
proxy.$tip.toast("暂未登录,请先登录!");
return;
}
//console.log("选中日期:", e);
popDate.value = e;
popShow.value = true;
}
// 选择日期
const onPickerChange =({ detail }) =>{
// console.log('1:', calendarDate);
let d = new Date(detail.value);
let select = datetime.getCurDateObj(d);
// console.log(select.year, pickerDateText.year);
if (
select.year == pickerDateText.year &&
select.month == pickerDateText.month
)
return;
calendarDate.year = select.year;
calendarDate.month = select.month;
pickerDate.value = datetime.getCurDate(d);
// console.log(datetime.getCurDate(d));
pickerDateText.year = calendarDate.year;
pickerDateText.month = calendarDate.month;
// console.log('2:', calendarDate);
initData();
}
// 弹窗状态改变触发
const onBillsOnDayPopup =(e) =>{
//this.setTabBarShow(!e.show);
if(!e.show)
wx.showTabBar();
popShow.value = e.show;
}
// 日历size发生变更
const onSizeChange =(h) =>{
//console.log("日历尺寸", h);
getDynamicHeight(h);
}
// 展开、收缩scroll-view
const onExpandView =() =>{
let minHeight = dateTitleHeight;
// console.log(minHeight);
if (expand.value) {
scrollHeight.value =
pH - minHeight - tabbarHeight - expandHeight.value;
expand.value = false;
} else {
scrollHeight.value = scrollMaxHeight;
expand.value = true;
}
}
// 自定义下拉刷新控件被下拉
const onPulling =(e) => {
// console.log("onpulling", e);
if (e.detail.deltaY < 0) return; // 防止上滑页面也触发下拉
triggered.value = true;
}
// 自定义下拉刷新被触发
const onRefresh = async () => {
if (freshing.value) return;
freshing.value = true;
// console.log("下拉刷新数据");
initData().finally((res) => {
triggered.value = false;
freshing.value = false;
});
}
// 自定义下拉刷新被复位
const onRestore =() =>{
triggered.value = "restore"; // 需要重置
}
// 自定义下拉刷新被中止
const onAbort =() =>{}
// scroll触底事件
const onLowerBottom = () =>{
// console.log("触底加载");
// if (indexPage.page * indexPage.size >= indexTotal) return;
// console.log("加载...");
indexPage.page += 1;
getMonthBills();
}
//#endregion
</script>
- `pickerDate` 和 `pickerDateText` 用于存储日期选择器的值和文本。
- `pickerEnd` 是日期选择器的结束日期。
- `expand` 用于控制日历的展开和收缩状态。
- `popShow` 和 `popDate` 用于控制账单列表弹窗的显示和日期。
- `dateTitleHeight`、`tabbarHeight` 和 `expandHeight` 是一些固定高度的值。
- `scrollHeight` 用于动态计算滚动视图的高度。
- `state` 是一个状态变量。
- `triggered` 和 `freshing` 用于控制下拉刷新的状态。
- `pattern` 是浮动操作按钮的样式。
- `indexTotal` 是账单总数。
- `pH` 是屏幕高度。
- `loading` 用于控制数据加载状态。
- `indexPage` 是页码和每页大小。
- `getDataList` 是获取数据列表的方法。
- `completedEditCallback` 是编辑完成后的回调函数。
- `initData` 是初始化数据的方法。
- `getMonthBills` 是获取月账单数据的方法。
- `getFixedHeight` 是获取固定高度的方法。
- `getDynamicHeight` 是动态计算滚动视图高度的方法。
- `onMonthChange` 是日历月份变更的回调函数。
- `onDayChange` 是选中日期时的回调函数。
- `onPickerChange` 是日期选择器变更的回调函数。
- `onBillsOnDayPopup` 是账单列表弹窗状态变更的回调函数。
- `onSizeChange` 是日历尺寸变更的回调函数。
- `onExpandView` 是展开和收缩滚动视图的方法。
- `onPulling`、`onRefresh`、`onRestore` 和 `onAbort` 是下拉刷新的回调函数。 - `onLowerBottom` 是滚动视图触底的回调函数。
style样式区
<style lang="scss" scoped>
.test {
font-size: 240rpx;
color: $primary-color;
}
.calendar-content {
.bg {
z-index: -1;
position: absolute;
top: 0;
background-color: $primary-color;
width: 100%;
height: 210rpx;
border-radius: 0 0 30rpx 30rpx;
}
.date-title {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 30rpx;
align-items: center;
height: 64rpx;
.date-picker {
.now-date {
font-size: 30rpx;
font-weight: bold;
align-items: baseline;
.icon-down {
font-size: 20rpx;
margin-left: 10rpx;
}
}
}
.statement {
display: flex;
}
.statement-text {
margin-left: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
&-title {
font-size: 30rpx;
}
&-total {
font-size: 35rpx;
font-weight: bold;
&-char {
font-size: 28rpx;
}
}
}
}
.calendar {
margin: 0 20rpx;
}
}
.calendar-expand {
width: 100%;
text-align: center;
}
.statement-item {
// background: white;
padding-top: 36rpx;
border-radius: 30rpx 30rpx 0 0;
// padding-bottom: 36rpx;
// margin-bottom: 36rpx;
// background-color: red;
}
.popup-box {
.asset {
height: 700rpx;
}
}
</style>