html部分
<template>
<view class="pages-main">
<!-- 标题栏 -->
<!-- #ifndef MP-TOUTIAO -->
<view class="" :style="'height:'+barHeight +'px;'"></view>
<!-- #endif -->
<!-- #ifdef MP-TOUTIAO -->
<view class="" :style="'height:'+(barHeight-statusBarHeight) +'px;'"></view>
<!-- #endif -->
<view class="nabbar-bar">
<!-- //手机状态栏 -->
<!-- #ifndef MP-TOUTIAO -->
<view class="status-bar" :style="{'height': statusBarHeight + 'px'}"></view>
<!-- #endif -->
<!-- //导航栏 -->
<view class="nabbar-box flex-cent" :style="{'height': (barHeight-statusBarHeight) + 'px', }">
<!-- #ifdef MP-WEIXIN || MP-KUAISHOU -->
<image class="left-icon" src="@/static/image/icon-xiangzuo.png" mode="widthFix" @click="onGoBack"></image>
<!-- #endif -->
<view class="nav-tabs flex-cent">
<view v-for="(item, index) in titleList" :key="index" class="nav_item flex-cent"
@click="onNavSwitch(item.value)" :id="index==0? 'navTitle': ''" >
<view class="nav_title">{{ item.title }}</view>
<view class="task_tabs_line_wrapper">
<view class="task_tabs_line"
:style="{width: 100 / titleList.length + '%',transform: `translateX(${isNavActive * 100}%);`}">
<view class="inside_line"></view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="body-main">
<!-- #ifndef MP-TOUTIAO -->
<swiper class="list_swiper" @change="changeSwiper" :current="isNavActive" :scroll-anchoring="true"
:style="'height:calc(100vh - '+barHeight+'px)'">
<!-- #endif -->
<!-- #ifdef MP-TOUTIAO -->
<swiper class="list_swiper" @change="changeSwiper" :current="isNavActive" :scroll-anchoring="true"
:style="'height:calc(100vh - '+(barHeight-statusBarHeight)+'px)'">
<!-- #endif -->
<swiper-item class="list_swiper_item" :key="0" >
<scroll-view class="scroll-view" :scroll-y="true" :scroll-with-animation="true"
:scroll-top="scrollTop" :scroll-into-view="isScrollShow? 'book'+selevtType: ''">
<!-- :style="{height: conHeight + 'px'}" -->
<view class="list-box" v-for="(item,index) in expenditureList" :key="index"
:id="'book'+item._id">
<view class="title-box">{{item.name}}</view>
<view class="list-main flex-fs-left">
<view class="list-item " v-for="(item1,index1) in item.children" :key="index1"
:class="selectCategory._id==item1._id?'is-acitve': ''" @click="onSelectClass(item1)"
:id="selectCategory._id==item1._id?'activeItem': ''">
<view class="head-box flex-cent">
<image :src="item1.iconUrl | onFiltersImg" mode="widthFix"></image>
</view>
<view class="item-title">{{item1.name}}</view>
</view>
</view>
</view>
<view class="height-foot" v-if="isOpenCalculator"></view>
</scroll-view>
</swiper-item>
<swiper-item class="list_swiper_item" :key="1" >
<view class="list-box">
<view class="list-main flex-fs-left">
<view class="list-item" v-for="(item1,index1) in incomeList" :key="index1"
:class="selectCategory._id==item1._id?'is-acitve': ''" @click="onSelectClass(item1)">
<view class="head-box flex-cent">
<image :src="item1.iconUrl | onFiltersImg" mode="widthFix"></image>
</view>
<view class="item-title">{{item1.name}}</view>
</view>
</view>
</view>
<view class="height-foot" v-if="isOpenCalculator"></view>
</swiper-item>
</swiper>
</view>
<calculator :isOpen="isOpenCalculator" :selectCategory="selectCategory" :formModel="formModel"></calculator>
<!-- 新手引导 -->
<block v-if="showGuide && !functionGuidance">
<guide :show="showGuide" :width="cWidth" :height="cHeight" :left="cLeft" :right="cRight" :top="cTop"
:showMessage='cShowMsg' :currentIndex="currentIndex" :noticeArray="noticeArray" pageName="记账本分类页"
@onNextBtn="clicktoNext" @operatingButton="onWriteAccount" @closeClick="onCloseClick"></guide>
</block>
<block v-if="isFeedbackShow">
<feedbackPopup :isOpen="isFeedbackShow" @close="onCloseFeedback" pageName="记账本分类页"></feedbackPopup>
</block>
</view>
</template>
js部分
<script>
import { accAdd, accSub, accMul, accDiv } from '@/utils/calculation.js'
import calculator from '../components/calculator.vue'
import guide from '@/components/guide.vue'
import { mapGetters, mapMutations } from "vuex";
import { bookkeepingCategoryList } from '@/api/bookApi.js'
import feedbackPopup from '../components/feedback-popup.vue'
const globalData = getApp().globalData
const defaultImageUrl = globalData.defaultImageUrl
const imageUrlPrefix = globalData.imageUrlPrefix
export default {
filters: {
onFiltersImg(val) {
if (val) {
const photoArray = val.split(',')
if (photoArray[0].indexOf('http') == -1) {
return imageUrlPrefix+photoArray[0]
} else {
return photoArray[0]
}
} else {
return defaultImageUrl
}
},
},
data() {
return {
statusBarHeight: 20,
isNavActive: 0,
barHeight: 44,
formModel: {},
bookinclasslist: [],
bookoutclasslist: [],
bookclasslist: [],
currentClass: {},
// 标题列表
titleList: [{
title: '支出', // 标题名
value: 0, // 标题编号
count: 0
}, {
title: '收入',
value: 1,
count: 0
}],
isOpenCalculator: false,
showGuide: false, //引导是否显示
cShowTitls: '',
cShowMsg: '', // 展示的解释语
cWidth: '',
cHeight: '',
cLeft: '',
cTop: '',
currentIndex: 0,
noticeArray: [{
showID: "navTitle", // 对应的id
showMessage: `选择您要记录的是收入还是收入`, // 对应的解释文本
type: "bottom", // 解释框的气泡类型
isbtn: true,
arrowShow: true,
seeTop: '200',
},{
showID: "activeItem", // 对应的id
showMessage: `点击您此次支出的分类`, // 对应的解释文本
type: "bottom", // 解释框的气泡类型
isbtn: false,
arrowShow: true,
}],
incomeList: [], // 收入类别列表
expenditureList: [], // 支出类别列表
selectCategory: {
_id: '',
name: '',
type: 2,
},
scrollTop: 0,
selevtType: '',
isScrollShow: false,
isFeedbackShow: false,
}
},
components: {
calculator,
guide,
feedbackPopup,
},
onLoad(options) {
//获取手机系统的信息(在这主要是获取状态栏和胶囊的高度)
let {statusBarHeight,system} = uni.getSystemInfoSync()
this.statusBarHeight = statusBarHeight
this.barHeight = statusBarHeight + (system.indexOf('iOS') > -1 ? 44 : 48)
if (this.bookCategoryList.length>0) {
this.incomeList = this.bookCategoryList[1].children || []
this.expenditureList = this.bookCategoryList[0].children || []
this.guideInit()
} else {
this.getCategoryList()
}
if (options && options.model) {
let model = decodeURIComponent(options.model)
let mode = JSON.parse(model)
let category = {}
if (mode.type==2) {
this.isNavActive = 0
this.expenditureList.some(e=>{
category = e.children.find(i=> i._id == mode.categoryId)
return category
})
} else {
this.isNavActive = 1
category = this.incomeList.find(i=> i._id == mode.categoryId)
}
this.selectCategory = category
this.formModel={
Money: mode.amount,
ConsumptionTime: mode.ytd,
id: mode._id
}
setTimeout(o=> {
this.onWriteAccount()
},100)
}
},
computed: {
...mapGetters(['hasAuth', 'loginUserKey','bookCategoryList','functionGuidance','bookPageEnterNum']),
},
methods: {
getCategoryList() {
bookkeepingCategoryList({key: this.loginUserKey.key}).then(data=> {
this.incomeList = data[1].children || []
this.expenditureList = data[0].children || []
this.$store.commit('BOOK_CATEGORY_LIST',data||[])
this.guideInit()
})
},
onGoBack() {
if (this.bookPageEnterNum==2) {
this.isFeedbackShow = true
} else {
uni.navigateBack()
}
},
onCloseFeedback(val) {
this.isFeedbackShow = false
if (val==2) {
uni.navigateBack()
}
},
onNavSwitch(val) {
this.isNavActive = val
if (JSON.stringify(this.formModel) =='{}') {
this.$set(this,'isOpenCalculator',false)
}
},
// 左右滑动切换标题的回调
changeSwiper(e) {
let val = e.target.current
this.onNavSwitch(val)
},
onSelectClass(val) {
this.selectCategory = val
this.isScrollShow = true
this.selevtType = val.parentId
this.$set(this,'isOpenCalculator',true)
this.showGuide = false;
// 埋点
// this.$uma.trackEvent('BookkeepingCategaryClick',{page_name: '记账本分类页',user_id: this.loginUserKey._id|| '',event_time: Date.parse(new Date())})
},
guideInit() {
if(!this.functionGuidance && this.expenditureList.length>0) {
this.selectCategory = this.expenditureList[0].children[0]
this.$nextTick(()=> {
if (this.currentIndex >= this.noticeArray.length) {
this.showGuide = false;
return;
}
this.showGuide = true;
this.cShowMsg = this.noticeArray[this.currentIndex].showMessage;
var idS = '#' + this.noticeArray[this.currentIndex].showID;
console.log(idS)
//根据布局信息显示引导框位置
const query = uni.createSelectorQuery().in(this);
query.select(idS).boundingClientRect(data => {
console.log("得到布局位置信息" + JSON.stringify(data));
this.cWidth = data.width;
// #ifndef MP-KUAISHOU
this.cHeight = data.height;
this.cTop = data.top;
// #endif
// #ifdef MP-KUAISHOU
if (data.height<= 46) {
this.cHeight = data.height * 2;
this.cTop = data.top + 20;
} else {
this.cHeight = data.height;
this.cTop = data.top;
}
// #endif
console.log(data)
console.log('----------------')
this.cLeft = data.left;
}).exec();
});
}
},
onWriteAccount() {
this.onSelectClass(this.selectCategory)
},
clicktoNext(val) {
if (this.currentIndex >= this.noticeArray.length) {
this.showGuide = false;
return;
}
this.noticeArray[this.currentIndex].zindex = 0;
this.cShowMsg = '';
this.currentIndex++;
if (this.currentIndex >= this.noticeArray.length) {
this.showGuide = false;
return;
}
this.cShowMsg = this.noticeArray[this.currentIndex].showMessage;
var idS = '#' + this.noticeArray[this.currentIndex].showID;
console.log(idS)
const query = uni.createSelectorQuery().in(this);
query.select(idS).boundingClientRect(data => {
console.log("得到布局位置信息" + JSON.stringify(data));
this.cWidth = data.width;
this.cHeight = data.height;
this.cLeft = data.left;
this.cTop = data.top;
}).exec();
},
onCloseClick() {
this.showGuide = false;
},
},
onBackPress(e) {
console.log(e)
console.log('监听页面的返回事件')
},
watch: {
loginUserKey() {},
bookCategoryList() {},
functionGuidance() {},
bookPageEnterNum() {},
}
}
</script>
css部分
<style lang="less" scoped>
.pages-main {
.nabbar-bar {
width: 100%;
z-index: 100;
position: fixed;
top: 0;
left: 0;
right: 0;
background: #fff;
z-index: 100;
.status-bar {
width: 100%;
height: var(--status-bar-height);
}
.nabbar-box {
width: 100%;
padding: 0 25rpx;
z-index: 996;
align-items: center;
padding-right: 90rpx;
.left-icon {
font-size: 36rpx;
color: #000;
position: absolute;
left: 16rpx;
width: 40rpx;
height: 40rpx;
z-index: 2;
}
}
.nav-tabs {
position: relative;
height: 100%;
width: 100%;
padding: 0 100rpx;
}
.nav_item {
width: 50%;
font-size: 36rpx;
font-family: PingFang SC;
font-weight: 700;
color: #4F4B4E;
height: 100%;
align-items: center;
flex-direction: column;
.task_tabs_line_wrapper {
position: absolute;
bottom: 0rpx;
left: 0;
height: 4rpx;
width: 100%;
padding: 0 100rpx;
.task_tabs_line {
height: 4rpx;
transition: all 0.2s ease;
.inside_line {
width: 30rpx;
margin: 0 auto;
height: 4rpx;
background: #F08500;
border-radius: 2rpx;
}
}
}
}
}
.list_swiper {
height: calc(100vh - 160rpx);
}
.list_swiper_item {
width: 100%;
height: 100%;
padding: 16rpx 0;
// overflow-y: scroll;
.scroll-view {
width: 100%;
height: 100%;
overflow-y: scroll;
}
}
.height-foot {
height: 590rpx;
}
.list-box {
width: 100%;
background: #FFFFFF;
border-radius: 18rpx;
padding: 36rpx 40rpx 0;
margin-bottom: 12rpx;
}
.body-main {
padding: 0 16rpx;
.title-box {
font-size: 30rpx;
font-family: PingFang SC;
font-weight: 700;
color: #4F4B4E;
line-height: 42rpx;
}
.list-main {
flex-wrap: wrap;
padding-top: 38rpx;
}
.list-item {
width: 25%;
margin-bottom: 46rpx;
.head-box {
width: 100rpx;
height: 100rpx;
background: #F6F6F6;
margin: 0 auto;
border-radius: 50%;
overflow: hidden;
image,img {
width: 58rpx;
height: 58rpx;
background: #F6F6F6;
}
}
&.is-acitve {
.head-box {
background: #FDF2E5;
image,img {
background-color: #FDF2E5;
}
}
}
}
.item-title {
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 400;
color: #4F4B4E;
text-align: center;
line-height: 40rpx;
padding-top: 18rpx;
}
}
}
</style>
calculator.vue
<template>
<view>
<block v-if="isOpen">
<view class="input-calculator" >
<view class="input-box flex-sb-cent">
<!-- <view class="padding-sm margin-xsradius ">
<view class="margin-xsradius" :class="selectCategory.IconName" :style="'background-color:'+selectCategory.BgColor"></view>
<text class="text-grey">{{selectCategory.Name}}</text>
</view> -->
<view class="padding-sm ">
<view class="text-black text-xxl contner">{{model.Money||'0.00'}}</view>
</view>
</view>
<!-- <view class="flex-sb-cent input" :class="InputBottom>0?'foot':''" :style="[{bottom:InputBottom+'px'}]">
<view class="action">
<text class=" text-grey">备注</text>
</view>
<input class="solid-bottom" :adjust-position="false" :focus="false" maxlength="300" v-model="model.Remark"
cursor-spacing="10" @focus="InputFocus" @blur="InputBlur"></input>
</view> -->
<view class="flex-sb-cent cu-list col-4">
<view class="cu-item flex-cent border" @click="modifyNum('number','1')">
<text class="text-black">1</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','2')">
<text class="text-black">2</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','3')">
<text class="text-black">3</text>
</view>
<view class="cu-item flex-cent border time" @click="onDateOpen">
<block v-if="model.ConsumptionTime == today">
<image class="image-time" src="../static/icon-time.png" mode="widthFix"></image>
今天
</block>
<text class="text-black" v-else>{{model.ConsumptionTime}}</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','4')">
<text class="text-black">4</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','5')">
<text class="text-black">5</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','6')">
<text class="text-black">6</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('operator','+')">
<text class="text-black">+</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','7')">
<text class="text-black">7</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','8')">
<text class="text-black">8</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','9')">
<text class="text-black">9</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('operator','-')">
<text class="text-black">-</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('string','.')">
<text class="text-black">.</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('number','0')">
<text class="text-black">0</text>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('cut')">
<image class="del-img" src="../static/icon-del.png" mode="widthFix"></image>
</view>
<view class="cu-item flex-cent border" @click="modifyNum('equal')"
style="background-color: #FF951A;" v-if="state.currentOperator">
<text class="text-black">=</text>
</view>
<view class="cu-item flex-cent border " style="background-color: #FF951A;"
id="cuItemSubmit" @click="onSubmit" v-if="!state.currentOperator">
<text class="text-bold">完成</text>
</view>
</view>
</view>
<pickerDate :isOpen="isPickerShow" @onDayClick="onDayClick" @close="onClosePick"></pickerDate>
<!-- 新手引导 -->
<block v-if="showGuide && !functionGuidance">
<guide :show="showGuide" :width="cWidth" :height="cHeight" :left="cLeft" :right="cRight" :top="cTop"
:showMessage='cShowMsg' :currentIndex="currentIndex" :noticeArray="noticeArray" pageName="记账本分类页"
@onNextBtn="clicktoNext" @operatingButton="onWriteAccount" @closeClick="onCloseClick"></guide>
</block>
</block>
</view>
</template>
<script>
import pickerDate from './picker-date.vue'
import { accAdd,accSub,accDiv,accMul } from '@/utils/calculation.js'
import guide from '@/components/guide.vue'
import { saveOrUpdateBookkeeping } from '@/api/bookApi.js'
import { mapGetters, mapMutations } from "vuex";
export default {
props: {
isOpen: {
default: false,
type: Boolean
},
selectCategory: {
default: {},
type: Object
},
formModel: {
default: {},
type: Object
}
},
data() {
return {
model: {
Money: '',
// Remark: '',
ConsumptionTime: '',
},
today: this.getDate(),
InputBottom: 0,
isPickerShow: false,
/*
*currentNumber: 用于跟踪当前输入的数字或操作数。当用户按下数字按钮时,这个变量将被更新以包含新的数字字符。
* 例如,当用户按下数字键 "1"、"2"、"3" 时,currentNumber 将依次变为 "1"、"12"、"123"。
currentOperator: 用于跟踪当前选定的操作符,例如加法 "+"、减法 "-"、乘法 "*"、除法 "/"。
* 如果用户按下运算符按钮,这个变量将被设置为相应的运算符。
previousValue: 用于存储上一个操作的结果,以便进行下一个操作时使用。
* 例如,如果用户按下 "1 + 2",previousValue 将存储 "1",然后在按下 "=" 后,将 "previousValue + currentNumber" 计算为最终结果。
isDecimal: 用于跟踪当前数字是否包含小数点。当用户按下小数点按钮时,这个标志将设置为 true,以确保小数点只能出现一次。
* */
state: {
currentNumber: '',
currentOperator: '',
previousValue: null,
isDecimal: false,
},
showGuide: false, //引导是否显示
cShowTitls: '',
cShowMsg: '', // 展示的解释语
cWidth: '',
cHeight: '',
cLeft: '',
cTop: '',
currentIndex: 0,
noticeArray: [{
showID: "cuItemSubmit", // 对应的id
showMessage: `记账可以养成更好地消费习惯,快来开启您的记账生活吧!`, // 对应的解释文本
type: "top", // 解释框的气泡类型
isbtn: false,
arrowShow: true, // 指示箭头是否显示
}],
}
},
components: {
pickerDate,
guide
},
created() {
},
computed: {
...mapGetters(['hasAuth', 'loginUserKey','functionGuidance']),
},
mounted() {
if (JSON.stringify(this.formModel) != '{}') {
this.model = this.formModel
this.state.currentNumber = this.formModel.Money || ''
} else {
this.model.ConsumptionTime = this.getDate()
}
},
methods: {
onDateOpen() {
this.$set(this,'isPickerShow',true)
},
onDayClick(time) {
this.model.ConsumptionTime = time
this.$set(this,'isPickerShow',false)
},
onClosePick() {
this.$set(this,'isPickerShow',false)
},
InputFocus(e) {
this.InputBottom = e.detail.height
},
InputBlur(e) {
this.InputBottom = 0
},
bindDateChange(e) {
this.model.ConsumptionTime = e.target.value
},
getDate(type) {
const date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
if (type === 'start') {
year = year - 60;
} else if (type === 'end') {
year = year + 2;
}
month = month > 9 ? month : '0' + month;;
day = day > 9 ? day : '0' + day;
return `${year}/${month}/${day}`;
},
modifyNum(type, value) {
if (type === 'number') {
if (this.model.Money.length>=23) return //
if (this.state.isDecimal) {
const decimalCount = this.state.currentNumber.split('.')[1];
if (decimalCount && decimalCount.length >= 2) {
return;
}
if(this.state.currentNumber.length>=11) return
this.state.currentNumber += value;
this.model.Money += value;
} else {
if(this.state.currentNumber.length>=8) return
if (this.state.currentNumber == '0') {
if (this.state.currentOperator !== '') {
if (value == '0') {
this.state.currentNumber = this.state.currentNumber;
this.model.Money = this.model.Money
} else {
this.state.currentNumber += value;
this.model.Money = this.model.Money.toString().slice(0, -1); // 删除最后一个数
this.model.Money += value; // 重新添加
}
} else {
this.state.currentNumber = value;
this.model.Money = value
}
} else if (this.state.currentNumber==''&& value=='0'){
this.state.isDecimal = true
this.state.currentNumber = '0.';
this.model.Money += '0.'
} else {
this.state.currentNumber += value;
this.model.Money += value;
}
}
} else if (type === 'operator') {
if (this.state.currentNumber !== '') {
if (this.state.currentOperator !== '') {
// 如果之前已经有运算符,先计算上一步的结果
this.calculate();
}
this.state.previousValue = parseFloat(this.state.currentNumber);
this.state.currentNumber = '';
this.state.isDecimal = false;
this.model.Money += `${value}`; // 添加运算符到表达式
this.state.currentOperator = value;
} else if (this.model.Money.length>=1){
this.model.Money = this.model.Money.toString().slice(0, -1); // 删除最后一个数
this.model.Money += `${value}`; // 添加运算符到表达式
this.state.currentOperator = value;
}
} else if (type === 'equal') {
if (this.state.currentOperator && this.state.currentNumber !== '') {
this.model.Money += `${this.state.currentNumber}`; // 显示完整表达式
this.calculate(); // 计算结果
this.state.currentOperator = '';
} else if (this.state.currentOperator && this.state.currentNumber == '' ){
this.cut();
}
} else if (type === 'string') {
if (this.model.Money.length>=23) return
if (this.state.currentNumber == '') {
this.state.currentNumber = '0';
}
if (value === '.' && this.state.isDecimal) {
return; // 如果已经输入小数点,不再输入
}
this.state.isDecimal = value === '.';
this.state.currentNumber += value;
if (this.state.currentOperator !== '') {
const currentCount = this.model.Money.split(this.state.currentOperator)[1];
console.log(currentCount)
console.log('currentCount0000000000000000000')
if (currentCount) {
this.model.Money += value
} else {
this.model.Money += `${this.state.currentNumber}`; // 显示完整表达式
}
} else {
this.model.Money = this.state.currentNumber; // 更新表达式
}
} else if (type === 'clear') {
this.clear();
} else if (type === 'cut') {
this.cut();
}
},
calculate() {
const num1 = this.state.previousValue;
const num2 = parseFloat(this.state.currentNumber);
let result = 0;
switch (this.state.currentOperator) {
case '+':
// num1 + num2
result = accAdd(num1, num2);
break;
case '-':
// num1 - num2
result = accSub(num1, num2);
break;
case '*':
// num1 * num2
result = accMul(num1, num2);
break;
case '/':
// num1 / num2
result = accDiv(num1, num2);
break;
}
this.state.currentNumber = result.toString();
this.state.isDecimal = this.state.currentNumber.includes('.');
this.model.Money = result;
},
clear() {
this.state.currentNumber = '';
this.state.currentOperator = '';
this.state.previousValue = null;
this.state.isDecimal = false;
this.model.Money = this.state.currentNumber;
},
cut() {
if (this.state.currentNumber !== '' || this.state.currentOperator !== '') {
// 删除最后一个字符,无论是数字还是运算符
const lastChar = this.model.Money.toString().slice(-1);
this.model.Money = this.model.Money.toString().slice(0, -1);
//解决输入一个运算符后,删除之后,不能再输入运算符
if (lastChar === '+' || lastChar === '-' || lastChar === '*') {
this.state.currentOperator = '';
this.state.currentNumber = this.model.Money
} else if (this.state.currentNumber !== '') {
// 如果最后一个字符是数字,则更新当前数字
this.state.currentNumber = this.state.currentNumber.slice(0, -1);
}
} else if (this.state.previousValue !== null) {
// 如果以上条件都不满足,则删除前一个值的最后一个字符
const previousValueString = this.state.previousValue.toString();
if (previousValueString.length > 1) {
this.state.previousValue = parseFloat(previousValueString.slice(0, -1));
} else {
this.state.previousValue = null;
}
}
//解决输入小数点之后,删了小数点,之后不能再输入小数点问题
this.state.isDecimal = this.state.currentNumber.includes('.')
//避免当输入的算式清空后,还在以上个运算结果为初始值继续运算
if (this.model.Money === '') {
this.state.currentNumber = '';
this.state.currentOperator = '';
this.state.previousValue = null;
}
},
Deltext(e) { //删除
if (this.model.Money.length > 1) {
this.model.Money = this.model.Money.substr(0, this.model.Money.length - 1)
if (this.model.Money === '-' || this.model.Money === "+") {
this.model.Money = "0"
}
} else {
this.model.Money = "0"
}
},
onSubmit(e) {
if (!this.selectCategory._id) return this.$util.Tips({title: '请选择分类!'})
if (!this.model.Money || this.model.Money == 0) return this.$util.Tips({title: '请输入金额!'})
if (this.model.Money.length>8) return this.$util.Tips({title: '金额过大!'})
let parme = {
amount: Math.abs(this.model.Money),
date: this.model.ConsumptionTime,
categoryId: this.selectCategory._id,
categoryIcon: this.selectCategory.iconUrl,
categoryName: this.selectCategory.name,
type: this.selectCategory.type,
userType: this.loginUserKey.type,
key: this.loginUserKey.key,
deviceType: 3,
remark: '',
// id: 0, // 存在及更新
}
if (this.model.id) {
parme.id = this.model.id
}
saveOrUpdateBookkeeping(parme).then(data=> {
this.$util.Tips({title: '保存成功!'})
if (this.functionGuidance) {
uni.navigateBack()
} else {
let pages = getCurrentPages();//获取页面栈
let prevPage = pages[pages.length - 2]; //上一个页面
//直接调用上一个页面的setData()方法,把数据存到上一个页面中去
prevPage.data.source = 2
uni.navigateBack()
}
// 埋点
// this.$uma.trackEvent('BookkeepingFinishClick',{page_name: '记账本分类页',user_id: this.loginUserKey._id|| '',event_time: Date.parse(new Date())})
})
},
guideInit() {
if(!this.functionGuidance && this.hasAuth) {
this.model.Money = 10
this.$nextTick(()=> {
if (this.currentIndex >= this.noticeArray.length) {
this.showGuide = false;
return;
}
this.showGuide = true;
this.cShowMsg = this.noticeArray[this.currentIndex].showMessage;
var idS = '#' + this.noticeArray[this.currentIndex].showID;
console.log(idS)
//根据布局信息显示引导框位置
const query = uni.createSelectorQuery().in(this);
query.select(idS).boundingClientRect(data => {
console.log("得到布局位置信息" + JSON.stringify(data));
this.cWidth = data.width;
this.cHeight = data.height;
this.cLeft = data.left;
this.cTop = data.top;
}).exec();
});
}
},
onWriteAccount() {
this.onSubmit()
this.showGuide = false;
},
clicktoNext(val) {
if (this.currentIndex >= this.noticeArray.length) {
this.showGuide = false;
return;
}
this.noticeArray[this.currentIndex].zindex = 0;
this.cShowMsg = '';
this.currentIndex++;
if (this.currentIndex >= this.noticeArray.length) {
this.showGuide = false;
return;
}
this.cShowMsg = this.noticeArray[this.currentIndex].showMessage;
var idS = '#' + this.noticeArray[this.currentIndex].showID;
console.log(idS)
const query = uni.createSelectorQuery().in(this);
query.select(idS).boundingClientRect(data => {
console.log("得到布局位置信息" + JSON.stringify(data));
this.cWidth = data.width;
this.cHeight = data.height;
this.cLeft = data.left;
this.cTop = data.top;
}).exec();
},
onCloseClick() {
this.showGuide = false;
},
},
watch: {
isOpen() {
this.guideInit()
},
formModel() {
if (JSON.stringify(this.formModel) != '{}') {
this.model = this.formModel
} else {
this.model.ConsumptionTime = this.getDate()
}
},
functionGuidance(){},
}
}
</script>
<style lang="less" scoped>
// @import '../css/main.css';
.input-calculator {
position: fixed;
bottom: var(--window-bottom);
width: 100%;
background: #fff;
box-shadow: 0rpx 0rpx 2rpx 3rpx rgba(229,229,229,0.5);
.input-box {
height: 118rpx;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 700;
color: #4F4B4E;
line-height: 118rpx;
padding: 0 46rpx;
}
.cu-list {
flex-wrap: wrap;
}
.cu-item {
width: 25%;
text-align: center;
height: 118rpx;
line-height: 118rpx;
overflow: hidden;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 700;
color: #4F4B4E;
letter-spacing: 0;
&.time {
font-size: 26rpx;
}
&:active {
background: #F6F6F6;
}
}
.image-time {
width: 36rpx;
height: 36rpx;
margin-right: 7rpx;
}
.del-img {
width: 46rpx;
height: 46rpx;
}
.border {
border-top: 1rpx solid #E0E0E0;
border-right: 1rpx solid #E0E0E0;
&:nth-child(4n) {
border-right: none;
}
}
}
.submit:active {
background-color: #FF951A !important;
}
</style>
./picker-date.vue
<template>
<view class="">
<view class="batch-num" :class="isOpen ? 'on' : ''">
<view class="batch-body">
<view class="title">
日期选择
<image class="close-img" @click="onCancel(0)" src="@/static/image/close.png" mode="widthFix"></image>
</view>
<!-- 日期选择器 -->
<view class="calendar-wrapper">
<!-- 选择月份 -->
<view class="header" v-if="headerBar">
<view class="iconfont iconarrow-left-bold pre" @click="changeMonth('pre')">
<image src="../static/icon-left.png" mode="widthFix"></image>
</view>
<view class="">{{y+'年'+formatNum(m)+'月'}}</view>
<view class="iconfont iconarrow-left-bold-copy next" @click="changeMonth('next')">
<image src="../static/icon-right.png" mode="widthFix"></image>
</view>
</view>
<!-- 星期栏 -->
<view class="week">
<view class="week-day" v-for="(item, index) in weekDay" :key="index">{{ item }}</view>
</view>
<!-- 日历数字 -->
<view class="content" :style="{ height: height }">
<view :style="{ top: positionTop + 'rpx' }" class="days">
<view class="item" v-for="(item,index) in dates" :key="index">
<view class="day" @click="selectOne(item, $event)" :class="{
choose: getActday(item),//选中的日期
chooseMarkDay:getChoose(item),
nolm: !item.isCurM,//不在本月的日
today: isToday(item.year, item.month, item.date),//当日日期
isWorkDay: isWorkDay(item.year, item.month, item.date)//周一至周五
}">
{{Number(item.date)}}
</view>
</view>
</view>
</view>
</view>
<view class="slect-time">{{choose}}</view>
<view class="sub-btn" @click="onSubmitDeta">确定</view>
</view>
</view>
<view class="mask" v-show="isOpen" @click="onCancel(0)"></view>
</view>
</template>
<script>
export default {
name: 'pickerDate',
props: {
isOpen: {
type: Boolean,
default: false
},
// 星期几为第一天(0为星期日)
weekstart: {
type: Number,
default: 0
},
// 标记的日期
markDays: {
type: Array,
default: () => {
return [];
}
},
//是否展示月份切换按钮
headerBar: {
type: Boolean,
default: true
},
//过去日期是否不可点击
disabledAfter: {
type: Boolean,
default: false
},
//接收用户选择的参数
actDay: {
type: Array,
default: [],
},
//接受已经被选择的参数
chooseDay: {
type: Array,
default: [],
}
},
data() {
return {
weektext: ['日', '一', '二', '三', '四', '五', '六'],
y: new Date().getFullYear(), // 年
m: new Date().getMonth() + 1, // 月
d: new Date().getDate(), //日
dates: [], // 当前月的日期数据
positionTop: 0,
choose: this.getToday().date,
chooseArr: [],
}
},
created() {
this.dates = this.monthDay(this.y, this.m);
console.log('')
},
mounted() {
// this.choose = this.getToday().date;
},
computed: {
// 顶部星期栏
weekDay() {
return this.weektext.slice(this.weekstart).concat(this.weektext.slice(0, this.weekstart));
},
height() {
return (this.dates.length / 7) * 80 + 'rpx';
},
},
methods: {
onCancel() {
this.$emit('close')
console.log('eeeeeeeeeeeeeeeee')
},
//已被投标的日期
getChoose(val) {
let day = val.year+'/'+val.month+'/'+val.date
for (let i = 0; i < this.chooseDay.length; i++) {
if (day == this.chooseDay[i]) {
return true;
}
}
},
//用户选择的日期
getActday(val) {
let day = val.year+'/'+val.month+'/'+val.date
if (this.choose == day) {
return true
} else {
return false
}
},
formatNum(num) {
let res = Number(num);
return res < 10 ? '0' + res : res;
},
getToday() {
let date = new Date();
let y = date.getFullYear();
let m = date.getMonth();
let d = date.getDate();
let week = new Date().getDay();
let weekText = ['日', '一', '二', '三', '四', '五', '六'];
let formatWeek = '星期' + weekText[week];
let today = {
date: y + '/' + this.formatNum(m + 1) + '/' + this.formatNum(d),
week: formatWeek
};
return today;
},
// 获取当前月份数据
monthDay(y, month) {
let dates = [];
let m = Number(month);
let firstDayOfMonth = new Date(y, m - 1, 1).getDay(); // 当月第一天星期几
let lastDateOfMonth = new Date(y, m, 0).getDate(); // 当月最后一天
let lastDayOfLastMonth = new Date(y, m - 2, 0).getDate(); // 上一月的最后一天
let weekstart = this.weekstart == 7 ? 0 : this.weekstart;
let startDay = (() => {
// 周初有几天是上个月的
if (firstDayOfMonth == weekstart) {
return 0;
} else if (firstDayOfMonth > weekstart) {
return firstDayOfMonth - weekstart;
} else {
return 7 - weekstart + firstDayOfMonth;
}
})();
let endDay = 7 - ((startDay + lastDateOfMonth) % 7); // 结束还有几天是下个月的
for (let i = 1; i <= startDay; i++) {
dates.push({
date: this.formatNum(lastDayOfLastMonth - startDay + i),
day: weekstart + i - 1 || 7,
month: m - 1 >= 0 ? this.formatNum(m - 1) : 12,
year: m - 1 >= 0 ? y : y - 1
});
}
for (let j = 1; j <= lastDateOfMonth; j++) {
dates.push({
date: this.formatNum(j),
day: (j % 7) + firstDayOfMonth - 1 || 7,
month: this.formatNum(m),
year: y,
isCurM: true, //是否当前月份
});
}
for (let k = 1; k <= endDay; k++) {
dates.push({
date: this.formatNum(k),
day: (lastDateOfMonth + startDay + weekstart + k - 1) % 7 || 7,
month: m + 1 <= 11 ? this.formatNum(m + 1) : 0,
year: m + 1 <= 11 ? y : y + 1
});
}
// console.log(dates); //日期
return dates;
},
isWorkDay(y, m, d) {
//是否工作日
let ymd = y+'/'+m+'/'+d // `${y}/${m}/${d}`;
let formatDY = new Date(ymd.replace(/-/g, '/'));
let week = formatDY.getDay();
if (week == 0 || week == 6) {
return false;
} else {
return true;
}
},
isFutureDay(y, m, d) {
//是否未来日期
let ymd = y+'/'+m+'/'+d //`${y}/${m}/${d}`;
let formatDY = new Date(ymd.replace(/-/g, '/'));
let showTime = formatDY.getTime();
let curTime = new Date().getTime();
if (showTime > curTime) {
return true;
} else {
return false;
}
},
// 标记日期
isMarkDay(y, m, d) {
let flag = false;
for (let i = 0; i < this.markDays.length; i++) {
let dy = y+'/'+m+'/'+d // `${y}-${m}-${d}`;
if (this.markDays[i] == dy) {
flag = true;
break;
}
}
return flag;
},
isToday(y, m, d) {
let checkD = y + '/' + m + '/' + d;
let today = this.getToday().date;
if (checkD == today) {
return true;
} else {
return false;
}
},
// 点击回调
selectOne(i, event) {
let date = i.year+'/'+i.month+'/'+i.date
let selectD = new Date(date).getTime();
let curTime = new Date().getTime();
// let week = new Date(date).getDay();
// let weekText = ['日', '一', '二', '三', '四', '五', '六'];
// let formatWeek = '星期' + weekText[week];
// let response = {
// date: date,
// week: formatWeek
// };
if (!i.isCurM) {
console.log('不在当前月范围内');
return false;
}
if (selectD < curTime) {
if (this.disabledAfter) {
console.log('过去日期不可选');
return false;
} else {
this.choose = date;
}
} else {
this.choose = date;
this.chooseArr.push(date)
}
// this.response = response
},
onSubmitDeta() {
this.$emit('onDayClick', this.choose);
},
//改变年月
changYearMonth(y, m) {
this.dates = this.monthDay(y, m);
this.y = y;
this.m = m;
},
changeMonth(type) {
if (type == 'pre') {
if (this.m + 1 == 2) {
this.m = 12;
this.y = this.y - 1;
} else {
this.m = this.m - 1;
}
} else {
if (this.m + 1 == 13) {
this.m = 1;
this.y = this.y + 1;
} else {
this.m = this.m + 1;
}
}
this.dates = this.monthDay(this.y, this.m);
}
}
}
</script>
<style lang="less" scoped>
.batch-num {
width: 100%;
// height: 624rpx;
// background-color: #fff;
position: fixed;
left: 0;
bottom: 0;
z-index: 1000;
transform: translate3d(0, 100%, 0);
transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
overflow: hidden;
&.on {
transform: translate3d(0, 0, 0);
}
.batch-body {
// padding: 20rpx;
padding: 35rpx 0 30rpx;
background: #fff;
border-radius: 18rpx 18rpx 0 0;
.title {
font-size: 30rpx;
font-family: PingFang SC;
color: #858585;
line-height: 42rpx;
font-weight: 700;
text-align: center;
padding-bottom: 35rpx;
position: relative;
}
.close-img {
position: absolute;
right: 20rpx;
width: 58rpx;
height: 58rpx;
}
}
.calendar-wrapper {
color: #6f6d6d;
font-size: 28rpx;
text-align: center;
background-color: #fff;
padding-bottom: 10rpx;
border-radius: 20rpx;
.header {
display: flex;
align-items: center;
justify-content: center;
height: 88rpx;
font-weight: bold;
font-size: 28rpx;
font-family: PingFang SC;
color: #4F4B4E;
.pre,
.next {
image,img {
width: 24rpx;
height: 24rpx;
}
}
.pre {
margin-right: 30rpx;
}
.next {
margin-left: 30rpx;
}
}
.week {
display: flex;
align-items: center;
height: 80rpx;
line-height: 80rpx;
background: #FFFFFF;
box-shadow: 0rpx 4rpx 6rpx 0rpx rgba(201,201,201,0.5);
border-radius: 18rpx 18rpx 0rpx 0rpx;
view {
flex: 1;
}
}
.content {
position: relative;
overflow: hidden;
transition: height 0.4s ease;
.days {
transition: top 0.3s;
display: flex;
align-items: center;
flex-wrap: wrap;
position: relative;
.item {
position: relative;
display: block;
height: 80rpx;
line-height: 80rpx;
width: calc(100% / 7);
.day {
display: inline-block;
vertical-align: middle;
font-size: 26rpx;
font-family: PingFang SC;
font-weight: 400;
color: #4F4B4E;
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
overflow: hidden;
border-radius: 50%;
&.choose {
background-color: #FF951A;
color: #fff;
}
&.chooseMarkDay {
//已被投标的日期
color: #FF951A;
}
&.nolm {
color: #fff;
opacity: 0;
}
}
.isWorkDay {
color: #25272a;
}
.notSigned {
font-style: normal;
width: 8rpx;
height: 8rpx;
background: #fa7268;
border-radius: 10rpx;
position: absolute;
left: 50%;
bottom: 0;
pointer-events: none;
}
.today {
color: #FF951A;
}
.workDay {
font-style: normal;
width: 8rpx;
height: 8rpx;
background: #4d7df9;
border-radius: 10rpx;
position: absolute;
left: 50%;
bottom: 0;
pointer-events: none;
}
.markDays {
font-style: normal;
position: absolute;
top: 2rpx;
right: 11rpx;
pointer-events: none;
font-size: 80rpx;
color: rgba(160, 234, 193, 0.5);
}
}
}
}
}
.cancle-btn {
width: 100%;
height: 97rpx;
background: #FFFFFF;
text-align: center;
line-height: 97rpx;
font-family: PingFang SC;
font-size: 34rpx;
font-weight: 500;
color: #858585;
}
.slect-time {
text-align: center;
font-size: 26rpx;
font-family: PingFang SC;
font-weight: 400;
color: #858585;
line-height: 37rpx;
}
.sub-btn {
width: 560rpx;
height: 96rpx;
background: #FF951A;
border-radius: 14rpx;
font-size: 34rpx;
font-family: PingFang SC;
font-weight: 700;
color: #FFFFFF;
text-align: center;
line-height: 96rpx;
margin: 0 auto;
margin-top: 10rpx;
}
}
// 分享
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.7;
background-color: rgba(0, 0, 0, 0.7);
z-index: 999 !important;
}
</style>
calculation.js
export function accAdd(arg1, arg2) {
var r1, r2, m, c;
try {
r1 = arg1.toString().split(".")[1].length;
}
catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
}
catch (e) {
r2 = 0;
}
c = Math.abs(r1 - r2);
m = Math.pow(10, Math.max(r1, r2));
if (c > 0) {
var cm = Math.pow(10, c);
if (r1 > r2) {
arg1 = Number(arg1.toString().replace(".", ""));
arg2 = Number(arg2.toString().replace(".", "")) * cm;
} else {
arg1 = Number(arg1.toString().replace(".", "")) * cm;
arg2 = Number(arg2.toString().replace(".", ""));
}
} else {
arg1 = Number(arg1.toString().replace(".", ""));
arg2 = Number(arg2.toString().replace(".", ""));
}
return (arg1 + arg2) / m;
}
export function accSub(arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split(".")[1].length;
}
catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
}
catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
export function accMul(arg1, arg2) {
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
try {
m += s1.split(".")[1].length;
}
catch (e) {
}
try {
m += s2.split(".")[1].length;
}
catch (e) {
}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
export function accDiv(arg1, arg2) {
var t1 = 0, t2 = 0, r1, r2;
try {
t1 = arg1.toString().split(".")[1].length;
}
catch (e) {
}
try {
t2 = arg2.toString().split(".")[1].length;
}
catch (e) {
}
r1 = Number(arg1.toString().replace(".", ""));
r2 = Number(arg2.toString().replace(".", ""));
return (r1 / r2) * Math.pow(10, t2 - t1);
}