This commit is contained in:
@fawn-nine
2024-05-29 15:34:50 +08:00
parent bb53af8bde
commit 4386f615f2
79 changed files with 6942 additions and 4121 deletions

View File

@@ -1,682 +0,0 @@
<template>
<!--音频组件-->
<view>
<view class="bgfff">
<view>
<view class="audo-video">
<!-- 播放封面 -->
<view :class="['fengmianBox','defaultBg', succes ? 'playAnimate' : '']" :style="{ backgroundImage: `url(${fengImg})` }"></view>
<!--音频播放按钮处-->
<view class="audo-top">
<!-- 顺序播放 -->
<image v-if="orderPlayBtn" style="width:50rpx;height:50rpx;" src="/static/orderPlaya.png" mode="aspectFill" @click="orderPlay(false)"></image>
<image v-else style="width:50rpx;height:50rpx;" src="/static/orderPlay.png" mode="aspectFill" @click="orderPlay(true)"></image>
<!--上一首切换按钮-->
<image v-if="jian" @click="sig" src="/static/xys.png" style="width:50rpx;height:50rpx;transform:rotate(180deg)"
mode="aspectFill"></image>
<image v-else style="width:50rpx;height:50rpx;" @click="nosig" src="/static/sys.png" mode="aspectFill"></image>
<!--上一首切换按钮-->
<!--快退按钮-->
<!-- <image src="/static/kt.png" style="width:45rpx;height:45rpx;" mode="aspectFill" @click="kt()"></image> -->
<!--快退按钮-->
<!--播放按钮-->
<image :src="succes?'/static/bofang2.png':'/static/zt.png'" mode="aspectFill"
style="width:180rpx;height:180rpx;" @click="plays()"></image>
<!--播放按钮-->
<!--快进按钮-->
<!-- <image src="/static/kj.png" style="width:45rpx;height:45rpx;" mode="aspectFill" @click="kj()"></image> -->
<!--快进按钮-->
<!--下一首切换按钮-->
<image v-if="jia" @click="noxig" style="width:50rpx;height:50rpx;transform:rotate(180deg)" src="/static/sys.png"
mode="aspectFill"> </image>
<image v-else style="width:50rpx;height:50rpx;" src="/static/xys.png" @click="xig" mode="aspectFill"></image>
<!-- 播放目录 -->
<image src="/static/libIcon.png" style="width:45rpx;height:45rpx;" mode="aspectFill" @click="showLib()"></image>
</view>
<!--音频播放按钮处-->
<!--音频api处[视频代替音频-实现倍数功能]-->
<video id="myVideo" ref="myVideo" :src="recorPath" class="hidden" @timeupdate="timeupdate"
:autoplay="autoplays" @loadedmetadata="loadedmetadata" @ended="next" controls
style="width: 10rpx;height:10rpx;">
</video>
<!--音频api处[视频代替音频-实现倍数功能]-->
<view class="audo-a" style="margin:0 auto;">
<!--进度条-->
<view class="slider-box">
<text class="mm">{{timer}}</text>
<slider style="width: 370rpx;" @change="sliderChange" @changing="sliderChanging"
class="audio-slider" block-size="16" :min="0" :max="duration" :value="currentTime"
activeColor="#2fc348" @touchstart="lock= true" @touchend="lock = false" />
<text class="ss" v-if="overTimer!='NaN:NaN'">{{overTimer}}</text>
<text class="ss" v-else>00.00</text>
</view>
<!--进度条-->
</view>
</view>
</view>
</view>
<view class="h-100"></view>
<!--占位-->
</view>
<!--音频组件 Author:chenxin-->
</template>
<script>
/*
list -- 音频文件传入 不传无法播放/数组形式
Faskms -- 快进秒数 number 默认15秒
Slowms -- 快退秒数 number 默认15秒
autoNext -- 是否自动播放下一首
autoplays -- 进入页面是否自动播放 - 默认false
slideYes -- 滑动进度条时是否开启播放 - 默认false
switAud -- 切换上下音频是否开启播放 - 默认false
BsNav -- 倍数数据传入/数组形式
按钮图片未自定义,如想改动请在组件内部修改,页面头部已注释 -- 逻辑根据自己需求改
目前只测试 微信小程序和H5和APP -- 其他平台未知
Author:chenxin 交流vx:cxalq8-24
*/
import {
mapState,mapMutations
} from 'vuex';
export default {
name: "cx-audio-play",
props: {
list: { //音频数据
Type: Array,
default:[]
},
fengImg:{
// 封面img
type:String,
default:''
},
Faskms: { //快进秒数
Type: Number,
default: 15,
},
jia:{
Type: Boolean,
default: false,
},
// orderPlayBtn:{
// // 按顺序播放
// Type: Boolean,
// default: false
// },
Slowms: { //快退秒数
Type: Number,
default: 15,
},
autoNext: {
Type: Boolean,
default: false,
},
autoplays: { //是否开启自动播放
Type: Boolean,
default: true,
},
slideYes: { //滑动进度条 - 是否开启播放
Type: Boolean,
default: false,
},
switAud: {
Type: Boolean, //切换上下音频 - 是否开启播放
default: false,
},
jian: { //减-切换图标
type:Boolean,
default:false
},
cctime:{ // 初始化播放位置(秒)
type:Object,
default:{}
},
BsNav: { //倍数-传入 0.5/0.8/1.0/1.25/1.5/2.0
Type: Array,
default: () => [{
id: 0.5,
bs: '0.5',
}, {
id: 0.8,
bs: '0.8'
}, {
id: 1.0,
bs: '1.0'
},{
id: 1.25,
bs: '1.25'
},{
id: 1.5,
bs: '1.5'
},{
id: 2.0,
bs: '2.0'
}
],
},
},
data() {
return {
orderPlayBtn:false,
shows: false, //倍数弹框
show: true, //倍数弹框动画默认开启
// jia: true, //加-切换图标
succes: true, //播放按钮
bsid: '', //倍数默认显示第一个
bsindex: 2, //倍数默认显示第一个
num: 0,
current: 0, //当前选中的索引
recorPath: '', //音频播放地址
lock: false, // 锁
currentTime: 0, //当前进度
duration: 1, // 总进度
videoContext: null,
loading: true, //锁 加载
audioMannager:null ,// 背景音乐
saveInterVal:null,
}
},
onReady() {},
onShow() {
},
onBackPress() {
},
mounted() {
// console.log(this.cctime,'cctime') // 默认播放的位置(秒),第一次加载
this.videoContext = uni.getBackgroundAudioManager()
// this.videoContext = uni.createVideoContext('myVideo', this)
//默认播放第一个 -- 按钮展示
if (this.list.length != 0) {
this.loading = true
this.videoContext.title = '暂无'
this.videoContext.singer = '暂无'
this.videoContext.coverImgUrl = this.fengImg
this.videoContext.src = this.list[0].recorPath
this.playloading()
// if (this.list.length > 1) { //音频文件大于1 -- 下一个切换默认显示
// // this.jia = false
// if (this.autoplays) {
// this.succes = true
// }
// }
}
this.videoContext.onEnded(() => {
this.next()
//this.setUserInfo({playFlag:false});
//console.log(this.userInfo.playFlag,'playFlag')
}) // 播放结束加载下一首
this.videoContext.onError((err) => {
console.log(err)
this.setUserInfo({playFlag:true});
this.next()
}) // 播放错误加载下一首
this.videoContext.onCanplay(() =>{
// this.setUserInfo({playFlag:true});
// console.log(this.userInfo.playFlag,'playFlag')
this.duration = this.videoContext.duration
// this.loading = false
uni.hideLoading()
this.succes = true
if(this.cctime.flag == 'init'){
// 页面初始化时
this.currentTime = this.cctime.time
// 跳转到指定的秒数上
this.videoContext.seek(this.currentTime)
console.log('获取到初始值,进行时间跳转')
}
})
this.videoContext.onPlay(() => {
this.playSeconds = Math.ceil(this.currentTime) // 秒数取整
})
this.videoContext.onTimeUpdate((res) => {
//console.log(this.videoContext.currentTime,'onTimeUpdate');
this.currentTime = Math.max(0, this.videoContext.currentTime)
// console.log(this.currentTime,'当前秒数');
// var ss = Math.floor(this.playSeconds / 60)
// console.log(ss, this.playSeconds)
// if(ss > this.playSeconds){
// this.playSeconds = ss
// console.log(ss)
// }
})
},
updated() {
},
onLoad() {
},
onHide() { //监听页面离开 - 销毁音频
// this.videoContext.stop();
console.log('onHide')
},
onUnload() { //监听页面卸载 - 销毁音频
// this.videoContext.stop();
console.log('onUnload')
},
destroyed() {
// if(this.videoContext){
// this.videoContext.stop();
// }
console.log('destroyed')
},
computed: {
...mapState(['userInfo']),
timer() {
return calcTimer(this.currentTime)
},
overTimer() {
return calcTimer(this.duration)
}
},
watch: {
},
methods: {
...mapMutations(['setUserInfo']),
// 存储听书进度
saveListenRate(){
console.log('存储听书进度')
this.$emit('saveRate',this.currentTime)
},
// 顺序播放
orderPlay(val){
// console.log(val)
if(val){
uni.showToast({
title:'开启顺序播放',
icon:'none'
})
this.orderPlayBtn = true
}else{
uni.showToast({
title:'关闭顺序播放',
icon:'none'
})
this.orderPlayBtn = false
}
},
// 显示播放列表
showLib(){
this.$emit('showLib',true)
},
plays() { //播放暂停
if (!this.list || this.list.length == 0) {
uni.showToast({
title: '暂无音频数据~',
icon: "none"
})
// console.log('暂无音频数据.~')
return;
}
// this.playloading()
this.succes = !this.succes
/// console.log('修改后',this.succes)
if (this.succes) {
console.log('去播放',this.succes)
// this.setUserInfo({playFlag:true});
this.videoContext.play()
} else {
// this.succes = false
console.log('去暂停',this.succes )
// this.setUserInfo({playFlag:false});
uni.hideLoading()
//#ifdef H5
this.videoContext.pause()
this.saveListenRate()
//#endif
//#ifndef H5
this.videoContext.pause()
this.saveListenRate()
//#endif
}
},
// beishu() { //倍速弹框
// this.shows = !this.shows
// },
// 倍速
// setRate(index, item) {
// this.bsid = item.id
// this.bsindex = index
// this.shows = false
// //#ifdef H5
// this.videoContext.playbackRate(2)
// //#endif
// //#ifndef H5
// this.videoContext.playbackRate(2)
// //#endif
// },
// 更新进度条
timeupdate(event) {
if (this.lock) return; // 锁
var currentTime, duration;
if (event.detail.detail) {
currentTime = event.detail.detail.currentTime
duration = event.detail.detail.duration
} else {
currentTime = event.detail.currentTime
duration = event.detail.duration
}
this.currentTime = currentTime
this.duration = duration
},
// 拖动进度条
sliderChange(data) {
//此处滑动进度条--开始播放
if (this.slideYes && !this.succes) {
//#ifdef H5
this.videoContext.play()
//#endif
//#ifndef H5
this.videoContext.play()
//#endif
this.succes = true
}
//#ifdef H5
this.videoContext.seek(data.detail.value) //获取秒数
//#endif
//#ifndef H5
this.videoContext.seek(data.detail.value) //获取秒数
//#endif
},
//拖动中
sliderChanging(data) {
if (data.detail.value == 0) {
this.succes = false
//#ifdef H5
this.videoContext.pause()
//#endif
//#ifndef H5
this.videoContext.pause()
//#endif
}
this.currentTime = data.detail.value
},
// 视频加载完成
loadedmetadata(data) {
this.duration = data.detail.duration
},
sig() { //上一首
this.$emit('playPrev',true)
// if (!this.list || this.list.length == 0) {
// console.log('暂无音频数据~')
// return;
// }
// this.num -= 1
// console.log(this.num,'this.num')
// if (this.num < this.list.length) {
// this.loading = true
// this.playloading() //加载框
// }
// if (this.num + 1 < this.list.length && this.num + 1 != 1) { //点击上一首小于音频数据总长度
// // this.jia = false // 下按钮-亮且可点击
// // this.jian = false // 上按钮-亮且可点击
// } else {
// // this.jian = true // 上按钮-灰且阻止
// // this.jia = false // 下按钮-亮且可点击
// }
// this.recorPath = this.list[this.num].recorPath
// if (this.switAud) { //切换时是否默认开启播放
// this.succes = true
// setTimeout(() => {
// //#ifdef H5
// this.$refs.myVideo.play()
// //#endif
// //#ifndef H5
// this.videoContext.play()
// //#endif
// }, 100)
// } else {
// this.succes = false
// }
},
xig() { //下一首
this.$emit("xiayishou");
// if (!this.list || this.list.length == 0) {
// console.log('暂无音频数据~')
// return;
// }
// this.num += 1
// if (this.num < this.list.length) {
// this.loading = true
// this.playloading() //加载框
// }
// if (this.num + 1 < this.list.length) { //点击下一首小于音频数据总长度
// this.jia = false // 下按钮-亮且可点击
// this.jian = false // 上按钮-亮且可点击
// } else { //大于总长度
// this.jia = true //下按钮 - 灰且阻止
// this.jian = false //上按钮 - 亮可点击
// }
// this.recorPath = this.list[this.num].recorPath
// if (this.switAud) { //切换时是否默认开启播放
// this.succes = true
// setTimeout(() => {
// //#ifdef H5
// this.$refs.myVideo.play()
// //#endif
// //#ifndef H5
// this.videoContext.play()
// //#endif
// }, 100)
// } else {
// this.succes = false
// }
},
nosig() {
uni.showToast({
title: '到头了~',
icon: "none"
})
},
noxig() {
uni.showToast({
title: '没有更多了~',
icon: "none"
})
},
next(data) { //监听音频结束
// console.log('开始播放下一首')
this.succes = false
/*音频结束--是否自动播放下一首*/
//if(this.orderPlayBtn){
setTimeout(()=>{
this.$emit("xiayishou");
},100)
// }else{
// console.log('音频结束-------')
// return
// }
// if (!this.autoNext) {
// return
// }
// if (this.num + 1 < this.list.length) {
// this.succes = true
// this.num += 1
// this.recorPath = this.list[this.num].recorPath
// setTimeout(() => {
// //#ifdef H5
// this.$refs.myVideo.play()
// //#endif
// //#ifndef H5
// this.videoContext.play()
// //#endif
// }, 100)
// } else {
// //this.jia = true //下按钮 - 灰且阻止
// //this.jian = false //上按钮 - 亮可点击
// }
},
playloading() { //加载框--封
if (this.loading) {
uni.showLoading({
title: "音频缓存中..."
})
this.loading = false
}
this.succes = false
// setTimeout(() => {
// uni.hideLoading()
// }, 1600)
},
},
watch: {}
}
//时间换算
function calcTimer(timer) {
if (timer === 0 || typeof timer !== 'number') {
return '00:00'
}
let mm = Math.floor(timer / 60)
let ss = Math.floor(timer % 60)
if (mm < 10) {
mm = '0' + mm
}
if (ss < 10) {
ss = '0' + ss
}
return mm + ':' + ss
}
</script>
<style lang="scss" scoped>
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.playAnimate{
-webkit-transform: rotate(360deg);
animation: rotation 6s linear infinite;
-moz-animation: rotation 6s linear infinite;
-webkit-animation: rotation 6s linear infinite;
-o-animation: rotation 6s linear infinite;
}
.fengmianBox{text-align: center; width: 400rpx; height: 400rpx; position: relative; border-radius: 400rpx; margin: 0 auto; margin-bottom: 140rpx; background-size: contain;}
.fengmianBox:after{
content: ''; display: inline-block; left:0; top:0; z-index: 0;width: 400rpx; height: 400rpx; position: absolute; border-radius: 400rpx; box-shadow: rgba(177, 235, 202, 0.8) 0px 0px 29px 0px;
}
.fengmianBox.defaultBg{background-image: url('@/static/icon/home_icon_0.png');}
page {
background-color: #F6F6F8;
}
/* #video {
width: 100%;
} */
.audo-video {
padding-bottom: 20rpx;
color: #999;
}
.slider-box {
display: flex;
align-items: center;
justify-content: center;
font-size: 27rpx;
color: #999; margin: 0 auto;
}
button {
display: inline-block;
width: 100rpx;
background-color: #fff;
font-size: 24rpx;
color: #000;
padding: 0;
}
.hidden {
position: fixed;
top: 0;
left: -10rpx;
z-index: -1;
width: 1rpx;
height: 1rpx;
}
.audo-top {
padding: 20rpx 40rpx;
display: flex;
justify-content: space-around;
align-items: center;
image {
width: 45rpx;
height: 45rpx;
}
}
.audo-a {
display: flex;
justify-content: space-between;
align-items: center;
width: 750rpx;
position: relative;
z-index: 9; margin: 0 auto;
}
.beishu {
position: relative;
width: 100rpx;
padding-top: 5rpx;
padding-bottom: 5rpx;
text-align: center;
border-radius: 25rpx;
font-size: 28rpx;
}
.absolute {
position: absolute;
.beishu-a {
width: 200rpx;
border-radius: 20rpx;
text-align: center;
line-height: 90rpx;
background: #fff;
.title {
pdding-left: 30rpx;
}
}
}
</style>

View File

@@ -1,360 +0,0 @@
<template>
<!--音频组件-->
<view>
<view class="bgfff">
<view>
<view class="audo-video">
<!-- 播放封面 -->
<view :class="['fengmianBox','defaultBg', userInfo.playFlag ? 'playAnimate' : '']" :style="{ backgroundImage: `url(${fengImg})` }"></view>
<!-- <view v-else :class="['fengmianBox','defaultBg', userInfo.playFlag ? 'playAnimate' : '']" style="{ border:1px solid red;backgroundImage: url('@/static/icon/fengziIcon.jpg') }">显示我</view> -->
<!--音频播放按钮处-->
<view class="audo-top">
<!-- 顺序播放 -->
<!-- <image v-if="orderPlayBtn" style="width:50rpx;height:50rpx;" src="/static/orderPlaya.png" mode="aspectFill" @click="orderPlay(false)"></image>
<image v-else style="width:50rpx;height:50rpx;" src="/static/orderPlay.png" mode="aspectFill" @click="orderPlay(true)"></image>
<!--上一首切换按钮-->
<!-- <image v-if="jian" @click="sig" src="/static/xys.png" style="width:50rpx;height:50rpx;transform:rotate(180deg)"
mode="aspectFill"></image>
<image v-else style="width:50rpx;height:50rpx;" @click="nosig" src="/static/sys.png" mode="aspectFill"></image>-->
<image class="prevMusic" @click="prevMusic" src="/static/xys.png" style="width:90rpx;height:90rpx;transform:rotate(180deg)"
mode="aspectFill"></image>
<!--上一首切换按钮-->
<!--播放按钮-->
<image :src="userInfo.playFlag ?'/static/bofang2.png':'/static/zt.png'" mode="aspectFill"
style="width:180rpx;height:180rpx;" @click="plays()"></image>
<!--播放按钮-->
<!--下一首切换按钮-->
<!-- <image v-if="jia" @click="noxig" style="width:50rpx;height:50rpx;transform:rotate(180deg)" src="/static/sys.png"
mode="aspectFill"> </image>
<image v-else style="width:50rpx;height:50rpx;" src="/static/xys.png" @click="xig" mode="aspectFill"></image> -->
<image class="nextMusic" style="width:90rpx;height:90rpx;" src="/static/xys.png" @click="nextMusic" mode="aspectFill"></image>
<!-- 播放目录 -->
<image src="/static/libIcon.png" style="width:45rpx;height:45rpx;" mode="aspectFill" @click="showLib()"></image>
</view>
<!--音频播放按钮处-->
<view class="audo-a" style="margin:0 auto;">
<!--进度条-->
<view class="slider-box">
<text class="mm">{{timer}}</text>
<slider style="width: 370rpx;" @change="sliderChange" @changing="sliderChanging"
class="audio-slider" block-size="16" :min="0" :max="duration" :value="currentTime"
activeColor="#2fc348" @touchstart="lock= true" @touchend="lock = false" />
<text class="ss" v-if="overTimer!='NaN:NaN'">{{overTimer}}</text>
<text class="ss" v-else>00.00</text>
</view>
<!--进度条-->
</view>
</view>
</view>
<!-- 播放列表 -->
<u-popup mode="bottom" :show="LibVisible" :round="10" @close="LibVisible=false" >
<view class="libTitle">播放列表</view>
<view class="tanchu playList" style="height:400rpx;overflow-y: scroll;">
<scroll-view style="height:400rpx; overflow-y: scroll;" scroll-y="true"
<view class="item" v-for="(item,index) in libLIst" :key="index" >
<view @click="listenOne(item)" :class="userInfo.playIndex == index ? 'playNow' : ''">
<view>{{item.chapter}}</view>
</view>
</view>
</scroll-view>
</view>
</u-popup>
</view>
<view class="h-100"></view>
<!--占位-->
</view>
<!--音频组件 Author:chenxin-->
</template>
<script>
/*
list -- 音频文件传入 不传无法播放/数组形式
Faskms -- 快进秒数 number 默认15秒
Slowms -- 快退秒数 number 默认15秒
autoNext -- 是否自动播放下一首
autoplays -- 进入页面是否自动播放 - 默认false
slideYes -- 滑动进度条时是否开启播放 - 默认false
switAud -- 切换上下音频是否开启播放 - 默认false
BsNav -- 倍数数据传入/数组形式
按钮图片未自定义,如想改动请在组件内部修改,页面头部已注释 -- 逻辑根据自己需求改
目前只测试 微信小程序和H5和APP -- 其他平台未知
Author:chenxin 交流vx:cxalq8-24
*/
import {
mapState,mapMutations
} from 'vuex';
export default {
name: "cx-audio-play",
props: {
list: { //音频数据
Type: Array,
default:[]
},
},
data() {
return {
playIndex: 0,// 播放器index
LibVisible:false,
libLIst:[],
fengUrl:'',
orderPlayBtn:false,
shows: false, //倍数弹框
show: true, //倍数弹框动画默认开启
// jia: true, //加-切换图标
succes: true, //播放按钮
bsid: '', //倍数默认显示第一个
bsindex: 2, //倍数默认显示第一个
num: 0,
current: 0, //当前选中的索引
recorPath: '', //音频播放地址
lock: false, // 锁
currentTime: 0, //当前进度
duration: 1, // 总进度
videoContext: null,
loading: true, //锁 加载
audioMannager:null ,// 背景音乐
saveInterVal:null,
fengImg:'',
nofengImg:'../../static/icon/fengziIcon.jpg'
}
},
created() {
this.libLIst = this.$bgm.musicList
this.fengImg = this.userInfo.fengImg
console.log(this.userInfo.fengImg,'this.userInfo.fengImg++++++++++++++')
this.userInfo.playTimes ? this.setUserInfo({'currentTime': this.userInfo.playTimes}) : ''
},
computed: {
...mapState(['userInfo']),
timer() {
this.currentTime = this.userInfo.currentTime
return calcTimer(this.userInfo.currentTime)
},
overTimer() {
this.duration = this.userInfo.duration
return calcTimer(this.userInfo.duration)
},
},
watch: {
},
methods: {
...mapMutations(['setUserInfo']),
// 上一首
prevMusic(){
if(this.$bgm._options.src == ''){ // 如果直接点下一首,没点播放
this.$music.playBgm({mute:false})
this.$music.setPlayIndex('next')
}else{
this.$music.setPlayIndex('prev')
}
},
nextMusic(){ // 下一首
if(this.$bgm._options.src == ''){ // 如果直接点下一首,没点播放
this.$music.playBgm({mute:false})
this.$music.setPlayIndex('next')
}else{
this.$music.setPlayIndex('next')
}
},
//关闭或开启 音乐
plays() {
this.muteBgMusic = !this.muteBgMusic
console.log(this.muteBgMusic,this.muteBgMusic?'已关闭音乐####':'已开启音乐####');
if (this.userInfo.playFlag) {
// 暂停
// this.$music.playBgm({mute:true})
this.$bgm.pause()
} else {
// 播放
// this.$music.playBgm({mute:false})
if(this.$bgm._options.src == ''){
this.$music.playBgm({mute:false})
}else{
this.$bgm.play()
}
}
},
// 播放单个音频
listenOne(item){
if(this.$bgm._options.src == ''){
this.$music.playBgm({mute:false})
this.$music.setOneMusic(item)
}else{
this.$music.setOneMusic(item)
}
},
// 显示播放列表
showLib(){
this.LibVisible = true
},
// 拖动进度条
sliderChange(data) {
// console.log(data,'拖动进度条',data)
if(this.$bgm._options.src == ''){
this.$music.playBgm({mute:false})
// this.$bgm.pause()
}else{
this.$bgm.play()
}
console.log('拖动',data.detail.value)
uni.getBackgroundAudioManager().seek(data.detail.value)
// this.$bgm.seek(data.detail.value) //获取秒数
},
//拖动中
sliderChanging(data) {
this.$bgm.pause()
this.currentTime = data.detail.value
//console.log('拖动中',this.currentTime)
},
},
watch: {}
}
//时间换算
function calcTimer(timer) {
if (timer === 0 || typeof timer !== 'number') {
return '00:00'
}
let mm = Math.floor(timer / 60)
let ss = Math.floor(timer % 60)
if (mm < 10) {
mm = '0' + mm
}
if (ss < 10) {
ss = '0' + ss
}
return mm + ':' + ss
}
</script>
<style lang="scss" scoped>
.graytitle{color: #999;}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.playAnimate{
-webkit-transform: rotate(360deg);
animation: rotation 6s linear infinite;
-moz-animation: rotation 6s linear infinite;
-webkit-animation: rotation 6s linear infinite;
-o-animation: rotation 6s linear infinite;
}
.fengmianBox{text-align: center; width: 400rpx; height: 400rpx; position: relative; border-radius: 400rpx; margin: 0 auto; margin-bottom: 140rpx; background-size: contain;}
.fengmianBox:after{
content: ''; display: inline-block; left:0; top:0; z-index: 0;width: 400rpx; height: 400rpx; position: absolute; border-radius: 400rpx; box-shadow: rgba(177, 235, 202, 0.8) 0px 0px 29px 0px;
}
.fengmianBox.defaultBg{background-image: url('@/static/icon/home_icon_0.png'); background-size: cover;}
page {
background-color: #F6F6F8;
}
/* #video {
width: 100%;
} */
.audo-video {
padding-bottom: 20rpx;
color: #999;
}
.slider-box {
display: flex;
align-items: center;
justify-content: center;
font-size: 27rpx;
color: #999; margin: 0 auto;
}
button {
display: inline-block;
width: 100rpx;
background-color: #fff;
font-size: 24rpx;
color: #000;
padding: 0;
}
.hidden {
position: fixed;
top: 0;
left: -10rpx;
z-index: -1;
width: 1rpx;
height: 1rpx;
}
.audo-top {
padding: 20rpx 40rpx;
display: flex;
justify-content: space-around;
align-items: center;
image {
width: 45rpx;
height: 45rpx;
}
}
.audo-a {
display: flex;
justify-content: space-between;
align-items: center;
width: 750rpx;
position: relative;
z-index: 9; margin: 0 auto;
}
.beishu {
position: relative;
width: 100rpx;
padding-top: 5rpx;
padding-bottom: 5rpx;
text-align: center;
border-radius: 25rpx;
font-size: 28rpx;
}
.absolute {
position: absolute;
.beishu-a {
width: 200rpx;
border-radius: 20rpx;
text-align: center;
line-height: 90rpx;
background: #fff;
.title {
pdding-left: 30rpx;
}
}
}
</style>

View File

@@ -1,58 +0,0 @@
<template>
<view>
<view v-if="minishow" class="fuchuang" style="width: 750rpx;">
我是浮窗
<view class="libTitle">播放列表</view>
<view class="libTitle"v-for="(item,index) in libLIst">{{item.chapterName}}</view>
<view class="tanchu playList">
<!-- <u-button>显示目录</u-button> -->
</view>
</view>
</view>
</template>
<script>
export default {
name: "miniPlay",
props: {
LibVisible:{
Type: Boolean,
default: false,
},
},
data() {
return {
minishow:true,
playid:1,
libLIst:[
{
'chapterName':1,
'chapterId':1
},
{
'chapterName':2,
'chapterId':1
},
{
'chapterName':3,
'chapterId':1
},
]
}
},
methods: {
showLib(){
this.LibVisible = true
}
}
}
</script>
<style>
.fuchuang{font-size: 20px; position:fixed; right: 0; bottom: 70px; background: green; z-index: 55;}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 967 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -3,21 +3,21 @@
<swiper class="guide_pages_swiper" indicator-dots="true" indicator-color="#d1d1d1" indicator-active-color="#54a966">
<swiper-item>
<view class="guide_pages_bg1">
疯子读书
吴门医述
<br>
让阅读无处不在
</view>
</swiper-item>
<swiper-item>
<view class="guide_pages_bg2">
疯子读书
吴门医述
<br>
古今一统中西互参
</view>
</swiper-item>
<swiper-item>
<view class="guide_pages_bg3">
疯子读书
吴门医述
<br>
和光同尘普惠人间
</view>

View File

@@ -21,7 +21,7 @@
:src="'/' + item.selectedIconPath" mode="aspectFit"></image>
<image v-else class="footer_nav_item_image" :src="'/' + item.iconPath" mode="aspectFit"></image>
<text class="footer_nav_item_text"
:class="[item.pagePath == path ? 'footer_item_text_active' : '']">{{ item.text }}</text>
:class="[item.pagePath == path ? 'footer_item_text_active' : 'normal_text']">{{ item.text }}</text>
</view>
</view>
</view>
@@ -39,68 +39,57 @@
data() {
return {
path: '',
navigationList: [{
pagePath: 'pages/peanut/home',
iconPath: 'static/tab/icon1_n.png',
selectedIconPath: 'static/tab/icon1_y.png',
text: '首页'
},{
"pagePath": "pages/medicaldes/medicaldes",
"iconPath": "static/icon/five5_n.png",
"selectedIconPath": "static/icon/five5.png",
"text": "吴门医述"
navigationList: [
{
"pagePath": "pages/peanut/home",
"iconPath": "static/tab/icon1_n.png",
"selectedIconPath": "static/tab/icon1_y.png",
"text": "首页"
},
{
"pagePath": "pages/peanut/shopping",
"iconPath": "static/tab/tab_nor_02.png",
"selectedIconPath": "static/tab/tab_cur_02.png",
"text": "购物车"
},
{
"pagePath": "pages/library/library",
"iconPath": "static/tab/icon3_n.png",
"selectedIconPath": "static/tab/icon3_y.png",
"text": "我的图书"
},
// {
// pagePath: 'pages/peanut/bookshelf',
// iconPath: 'static/tab/icon3_n.png',
// selectedIconPath: 'static/tab/icon3_y.png',
// text: '我的书架'
// },
// {
// pagePath: 'pages/bookShop/orderList',
// iconPath: 'static/tab/icon2_n.png',
// selectedIconPath: 'static/tab/icon2_y.png',
// text: '我的订单'
// },
"iconPath": "static/tab/tab_nor_03.png",
"selectedIconPath": "static/tab/tab_cur_03.png",
"text": "太湖公益"
},
{
pagePath: 'pages/peanut/mine',
iconPath: 'static/tab/icon4_n.png',
selectedIconPath: 'static/tab/icon4_y.png',
text: '我的'
}
"pagePath": "pages/peanut/mine",
"iconPath": "static/tab/tab_nor_04.png",
"selectedIconPath": "static/tab/tab_cur_04.png",
"text": "我的"
}
],
navigationIos: [{
pagePath: 'pages/peanut/home',
iconPath: 'static/tab/icon1_n.png',
selectedIconPath: 'static/tab/icon1_y.png',
text: '首页'
},
// {
// pagePath: 'pages/bookShop/orderList',
// iconPath: 'static/tab/icon2_n.png',
// selectedIconPath: 'static/tab/icon2_y.png',
// text: '我的订单'
// },
navigationIos: [
{
"pagePath": "pages/medicaldes/medicaldes",
"iconPath": "static/icon/five5.png",
"selectedIconPath": "static/icon/five5.png",
"text": "吴门医述"
"pagePath": "pages/peanut/home",
"iconPath": "static/tab/icon1_n.png",
"selectedIconPath": "static/tab/icon1_y.png",
"text": "首页"
},
{
"pagePath": "pages/peanut/shopping",
"iconPath": "static/tab/tab_nor_02.png",
"selectedIconPath": "static/tab/tab_cur_02.png",
"text": "购物车"
},
{
pagePath: 'pages/peanut/mine',
iconPath: 'static/tab/icon4_n.png',
selectedIconPath: 'static/tab/icon4_y.png',
text: '我的'
"pagePath": "pages/library/library",
"iconPath": "static/tab/tab_nor_03.png",
"selectedIconPath": "static/tab/tab_cur_03.png",
"text": "太湖公益"
},
{
"pagePath": "pages/peanut/mine",
"iconPath": "static/tab/tab_nor_04.png",
"selectedIconPath": "static/tab/tab_cur_04.png",
"text": "我的"
}
],
};
@@ -126,7 +115,9 @@
</script>
<style lang="scss" scoped>
@import '@/style/mixin.scss';
.normal_text{
color: #FFF;
}
.footer_station {
height: 110rpx;
box-sizing: content-box;
@@ -151,7 +142,7 @@
}
.footer_bg {
background-color: #FFF;
background-color: #fff;
box-shadow: 0 0px 10px 1px #0000001a;
}
@@ -176,7 +167,7 @@
.footer_nav_item_text {
font-size: 26rpx;
color: #909090;
color: #7b7b7b;
margin-top: 6rpx;
}
@@ -207,7 +198,7 @@
.footer_item_text_active {
color: #079307;
color: #3983ff;
font-weight: bold;
}
</style>

View File

@@ -1,113 +0,0 @@
<template>
<!-- 日期显示 -->
<view class="date_box">
<view
v-for="(dateInfo, dateIndex) in dates"
:key="dateIndex"
class="calendar_date__box"
>
<view
class="calendar_date"
:class="{ isSelected: dateActiveIndex === dateIndex && dateInfo.type === 'cur' }"
:style="{
height: cellHeight + 'rpx',
width: cellHeight + 'rpx',
color: swiperMode === 'open' ? dateInfo.type === 'cur' ? '#2C2C2C' : '#959595' : '#2C2C2C',
backgroundColor: dateActiveIndex === dateIndex && dateInfo.type === 'cur' ? dateActiveColor : ''
}"
@tap="chooseDate(dateInfo)"
>
<view class="calendar_date__number">{{ dateInfo.date }}</view>
<view class="calendar_date__isToday" v-if="dateInfo.isToday" :style="{ backgroundColor: dateActiveColor }"></view>
<view class="calendar_date__cricle"></view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
dates: {
type: Array,
default: () => []
},
cellHeight: { // 一列的高度
type: Number,
default: 75
},
dateActiveColor: { // 日期选中颜色
type: String,
default: '#FE6601'
},
selectedDate: {
type: String,
default: ''
},
swiperMode: { // 日历显示模式
type: String,
default: 'open'
},
showActive: { // 是否显示选中高亮日期
type: Boolean,
default: false
}
},
computed: {
dateActiveIndex() {
return this.showActive ? this.dates.map(item => item.dateFormat).indexOf(this.selectedDate) : -1
}
},
methods: {
chooseDate(dateInfo) {
this.$emit('chooseDate', dateInfo)
}
}
}
</script>
<style>
/* 日历轮播 */
.date_box {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.date_box .calendar_date__box {
width: calc(100% / 7);
margin-top: 20rpx;
}
.calendar_date__box .calendar_date {
text-align: center;
margin: 0 auto;
font-weight: bold;
font-size: 28rpx;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.calendar_date__box .calendar_date.isSelected {
color: #FFFFFF !important;
}
.calendar_date .calendar_date__isToday {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border-radius: 50%;
z-index: -1;
opacity: 0.4;
}
.calendar_date .calendar_date__cricle {
width: 9rpx;
height: 9rpx;
border-radius: 50%;
margin-top: 5rpx;
background-color: #FFFFFF;
}
/* 日历轮播 */
</style>

View File

@@ -1,68 +0,0 @@
/**
* 时间格式化
* @param {String} time
* @param {String} cFormat
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
if (!time) return ''
/* 修复IOS系统上面的时间不兼容*/
if (time.toString().indexOf('-') > 0) {
time = time.replace(/-/g, '/')
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
return value.toString().padStart(2, '0')
})
return time_str
}
/**
* This is just a simple version of deep copy
* Has a lot of edge cases bug
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
* @param {Object} source
* @returns {Object}
*/
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = Object.prototype.toString.call(source) === "[object Array]" ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}

View File

@@ -1,641 +0,0 @@
<template>
<!-- 日历滚动插件 -->
<view class="zsy_calendar">
<!-- 日历顶部信息 -->
<view class="calendar_info">
<text class="title">每日记录</text>
<text class="desc">
({{ getAssignDateInfo(false, 0) === getAssignDateInfo(true, 0) ? '' : getAssignDateInfo(false, 0) + '年' }}{{ getAssignDateInfo(false, 1) }})
</text>
<text v-show="showBackToTodayBtn" class="backToToday" :style="{color: dateActiveColor}" @tap="goToDate()">回到今天</text>
</view>
<!-- 日历周数 -->
<view class="calendar_week">
<view v-for="(item, index) in week" :key="index" class="calendar_week__item">{{ item }}</view>
</view>
<!-- 日历轮播 -->
<view class="calendar_swiper">
<!-- 展开情况下的日历轮播 -->
<swiper
v-if="swiperMode === 'open'"
key="normalSwiper"
circular
:style="{height: swiperHeight('open')}"
:current="current"
:duration="duration"
:skip-hidden-item-layout="true"
@change="e => current = e.detail.current"
>
<swiper-item v-for="(swiper, swiperIndex) in 3" :key="swiperIndex" class="swiper-item">
<DateBox
:dates="calendarSwiperDates[swiperIndex]"
:cellHeight="cellHeight"
:dateActiveColor="dateActiveColor"
:swiperMode="swiperMode"
@chooseDate="chooseDate"
/>
</swiper-item>
</swiper>
<!-- 收缩情况下的日历轮播 -->
<swiper
v-else
key="shrinkSwiper"
circular
:style="{height: swiperHeight('close')}"
:current="shrinkCurrent"
:duration="duration"
:skip-hidden-item-layout="true"
@change="e => shrinkCurrent = e.detail.current"
>
<swiper-item v-for="(swiper, swiperIndex) in 3" :key="swiperIndex" class="swiper-item">
<DateBox
:dates="calendarSwiperShrinkDates[swiperIndex]"
:cellHeight="cellHeight"
:dateActiveColor="dateActiveColor"
:swiperMode="swiperMode"
@chooseDate="chooseShrinkDate"
/>
</swiper-item>
</swiper>
</view>
<!-- 日历切换模式 -->
<view class="calendar_toggle" @tap="swiperMode = swiperMode === 'open' ? 'close' : 'open'">
<view class="icon" :class="{down: swiperMode === 'close'}"></view>
</view>
</view>
</template>
<script>
import { parseTime, deepClone } from './js/utils.js'
import DateBox from './dateBox.vue'
export default {
name: 'ZsyCalendar',
components: {
DateBox
},
props: {
duration: { // 轮播图动画时长
type: Number,
default: 300
},
cellHeight: { // 一列的高度
type: Number,
default: 75
},
dateActiveColor: { // 日期选中颜色
type: String,
default: '#FE6601'
},
sundayIndex: { // 星期天所在索引0表示第一个、6表示最后一个
type: Number,
default: 6
},
mode: { // 日历模式
type: String,
default: 'open'
},
changeSetDefault: { // 月份切换时是否显示一号还是当前月份选中高亮
type: Boolean,
default: true
},
defaultSelectedDate: { // 默认选中日期
type: String | null,
default: null
},
showArrowBtn: { // 是否显示左右切换按钮
type: Boolean,
default: true
}
},
data() {
return {
today: parseTime(new Date(), '{y}-{m}-{d}'), // 今天日期
selectedDate: null, // 选中日期
week: [], // 日历周数
current: 1, // 当前日历轮播默认显示索引
shrinkCurrent: 1, // 缩放日历轮播默认显示索引
calendarSwiperDates: [], // 日历轮播日期信息
calendarSwiperShrinkDates: [], // 日历轮播收缩时的日期信息
dateActive: -1, // 日期选中索引
swiperByClick: false, // 是否通过点击上月份或下月份的日期进行轮播切换
shrinkSwiperByClick: false, // 是否通过点击上月份或下月份的日期进行收缩日历的轮播切换
swiperMode: this.mode, // 日历轮播显示模式 open展开 close收缩
dateCache: {}, // 日期缓存
emitTimer: null, // 日期改变向父级传递当前选中日期计时器
dateClick: false // 是否进行了日期的点击选择
}
},
computed: {
/* 获取指定日期信息
isCurDate: 是否获取当天的信息还是选中日期的信息
index: 0 表示年份 1 表示月份 2 表示日期 */
getAssignDateInfo() {
return (isCurDate, index) => {
return (isCurDate ? this.today : this.selectedDate).split('-')[index] * 1
}
},
// 是否显示回到今天按钮
showBackToTodayBtn() {
return this.getAssignDateInfo(false, 0) !== this.getAssignDateInfo(true, 0) || this.getAssignDateInfo(false, 1) !== this.getAssignDateInfo(true, 1)
},
// 返回轮播图高度
swiperHeight() {
return (swiperMode) => {
const normalHeight = (this.calendarSwiperDates[this.current] || []).length / 7 * (this.cellHeight + 20) + 'rpx'
const shrinkHeight = this.cellHeight + 20 + 'rpx'
return swiperMode === 'open' ? normalHeight : shrinkHeight
}
}
},
watch: {
// 展开日历轮播切换
current(newV, oldV) {
if (newV === 0 && oldV === 2) { // 右滑
this.swiperChange(1)
return
}
if (newV === 2 && oldV === 0) { // 左滑
this.swiperChange(-1)
return
}
if (newV > oldV) { // 右滑
this.swiperChange(1)
} else { // 左滑
this.swiperChange(-1)
}
},
// 收缩日历轮播切换
shrinkCurrent(newV, oldV) {
if (newV === 0 && oldV === 2) { // 右滑
this.shrinkSwiperChange(1)
return
}
if (newV === 2 && oldV === 0) { // 左滑
this.shrinkSwiperChange(-1)
return
}
if (newV > oldV) { // 右滑
this.shrinkSwiperChange(1)
} else { // 左滑
this.shrinkSwiperChange(-1)
}
},
// 日历显示方式切换
swiperMode(newV) {
// 当收缩时初始化收缩轮播图的日期数据
if (newV === 'close') {
this.initCalendarShrinkSwiperDates()
}
},
selectedDate: {
deep: true,
handler(newV, oldV) {
if (newV && (oldV === null || this.dateClick)) { // 初始化/日历点击选择时直接返回
this.emitDate()
this.dateClick = false
} else { // 其它情况做防抖处理
if (this.emitTimer !== null) {
clearTimeout(this.emitTimer)
}
this.emitTimer = setTimeout(() => {
this.emitDate()
}, this.duration + 200)
}
}
}
},
created() {
this.init() // 初始化数据
},
methods: {
// 初始化数据
init() {
if (this.selectedDate === null) { // 默认选中日期为当天
this.selectedDate = this.defaultSelectedDate || this.today
}
this.initWeek() // 初始化要显示的周数
this.initCalendarSwiperDates() // 初始化日历轮播日期信息
// 解决swiperMode初始化为收缩时没有初始化日历收缩轮播日期信息
if (this.swiperMode === 'close') {
this.initCalendarShrinkSwiperDates()
}
},
// 初始化周数
initWeek() {
const normalWeek = ['日', '一', '二', '三', '四', '五', '六'] // 正常周数
const sIndex = this.sundayIndex < 0 ? 0 : this.sundayIndex >= normalWeek.length ? normalWeek.length - 1 : this.sundayIndex
normalWeek.unshift(...normalWeek.slice(-sIndex))
normalWeek.length = 7
this.week = normalWeek
},
// 初始化展开时的日历轮播日期信息
initCalendarSwiperDates(cb) {
const year = this.getAssignDateInfo(false, 0)
const month = this.getAssignDateInfo(false, 1)
const cur = this.generateCalendar(year, month)
const prev = this.generateCalendar(month === 1 ? year - 1 : year, month === 1 ? 12 : month - 1)
const next = this.generateCalendar(month === 12 ? year + 1 : year, month === 12 ? 1 : month + 1)
// 根据current来判断相邻的轮播存放哪些日历数据
if (this.current === 0) {
this.calendarSwiperDates = [cur, next, prev]
} else if (this.current === 1) {
this.calendarSwiperDates = [prev, cur, next]
} else if (this.current === 2) {
this.calendarSwiperDates = [next, prev, cur]
}
this.swiperByClick = false
// 初始化日期信息完毕执行回调函数
cb && cb()
},
// 生成展开的日历数据
generateCalendar(year, month) {
let calendarDate = []
// 先获取缓存里面有没有该月的日期数据
if (this.dateCache[`${year}-${month}`]) {
calendarDate = deepClone(this.dateCache[`${year}-${month}`])
} else { // 进行月份日期的计算
const monthDates = new Date(year, month, 0).getDate() // 获取此月份总天数
const normalWeek = ['一', '二', '三', '四', '五', '六', '日'] // 正常周数
const monthFirstDay = normalWeek[new Date(year, month - 1, 0).getDay()] // 获取本月一号为星期几
const monthFirstDayIndex = this.week.indexOf(monthFirstDay) // 计算本月一号在日历周数中的索引,索引之前的填充上个月的后几天
// 本月一号在日历中不是第一个位置,需要进行填充
if (monthFirstDayIndex !== 0) {
const prevMonthDates = new Date(year, month - 1, 0).getDate() // 获取上一个月份的总天数
// 填充本月一号之前的数据
for (let i = 0; i < monthFirstDayIndex; i ++) {
const item = {
year: month === 1 ? year - 1 : year,
month: month === 1 ? 12 : month - 1,
date: prevMonthDates - i,
type: 'prev'
}
// 判断填充的日期是否包含今天日期
this.theDateIsToday(item)
calendarDate.unshift(item)
}
}
// 循环生成当月所有日期
for (let i = 1; i <= monthDates; i ++) {
const item = {
year,
month,
date: i,
isSelected: false,
isToday: false,
type: 'cur'
}
// 今天的日期在不在里面
this.theDateIsToday(item)
calendarDate.push(item)
}
const residue = calendarDate.length % 7
// 判断是否需要填充下个月的前几天
if (residue !== 0) {
for (let i = 1; i <= 7 - residue; i ++) {
const item = {
year: month === 12 ? year + 1 : year,
month: month === 12 ? 1 : month + 1,
date: i,
type: 'next'
}
// 下个月的前几天包含今天
this.theDateIsToday(item)
calendarDate.push(item)
}
}
this.dateCache[`${year}-${month}`] = deepClone(calendarDate)
}
// 进行日期的默认选中
if (year === this.getAssignDateInfo(false, 0) && month === this.getAssignDateInfo(false, 1)) {
for (let i = 0, len = calendarDate.length; i < len; i++) {
if (calendarDate[i].type === 'cur' && calendarDate[i].date === this.getAssignDateInfo(false, 2)) {
calendarDate[i].isSelected = true
this.dateActive = i
break
}
}
}
return calendarDate
},
// 判断日期是否为今天
theDateIsToday(item) {
if (item.year + '-' + item.month + '-' + item.date === this.getAssignDateInfo(true, 0) + '-' + this.getAssignDateInfo(true, 1) + '-' + this.getAssignDateInfo(true, 2)) {
item.isToday = true
}
},
// 初始化收缩时的日历轮播日期信息
initCalendarShrinkSwiperDates(swiperChangeType) {
let line = null
/**
* 日历收缩事件/当前滑动不涉及到到上个/下个月的日期数据
* 日历滑动到上一周并且本周不属于第一行并且上一周选中的日期必须是本月份里面的日期
* 日历滑动到下一周且本周不属于最后一行
*/
const curDateLine = Math.floor(this.dateActive / 7)
if (!swiperChangeType ||
(swiperChangeType === -1 && curDateLine !== 0 && this.calendarSwiperDates[this.current][(curDateLine - 1) * 7].type === 'cur') ||
(swiperChangeType === 1 && curDateLine + 1 !== this.calendarSwiperDates[this.current].length / 7)
) {
// 计算当前周选中日期处于日历中的哪一行位置
const curCalendarSwiperDates = this.calendarSwiperDates[this.current]
line = Math.floor(curCalendarSwiperDates.map(item => item.type === 'cur' ? item.date : -1).indexOf(this.getAssignDateInfo(false, 2)) / 7)
// 收缩日历滑动事件需要进行日期的选中处理
if (swiperChangeType) {
// 将当前选中日期清除选中状态
this.calendarSwiperDates[this.current][this.dateActive].isSelected = false
// 重新计算日期选中高亮并把下一个日期进行选中
this.dateActive = line * 7
this.calendarSwiperDates[this.current][this.dateActive].isSelected = true
}
} else { // 收缩日历滑动事件
// 将当前选中日期清除选中状态
this.calendarSwiperDates[this.current][this.dateActive].isSelected = false
// 涉及了上个月/下个月的日期数据,需要重新计算展开日历轮播的日期数据
let currentNum = this.current + swiperChangeType
currentNum = currentNum > 2 ? 0 : currentNum < 0 ? 2 : currentNum
this.current = currentNum
// 计算上一周/下一周选中日期处于日历中的哪一行位置
const curCalendarSwiperDates = this.calendarSwiperDates[this.current]
line = Math.floor(curCalendarSwiperDates.map(item => item.type === 'cur' ? item.date : -1).indexOf(this.getAssignDateInfo(false, 2)) / 7)
// 重新计算日期选中高亮并把下一个日期进行选中
this.dateActive = line * 7
this.calendarSwiperDates[this.current][this.dateActive].isSelected = true
}
const cur = this.generateShrinkCalendar(0, line)
const prev = this.generateShrinkCalendar(-1, line)
const next = this.generateShrinkCalendar(1, line)
// 根据shrinkCurrent来判断相邻的轮播存放哪些日历数据
if (this.shrinkCurrent === 0) {
this.calendarSwiperShrinkDates = [cur, next, prev]
} else if (this.shrinkCurrent === 1) {
this.calendarSwiperShrinkDates = [prev, cur, next]
} else if (this.shrinkCurrent === 2) {
this.calendarSwiperShrinkDates = [next, prev, cur]
}
},
// 生成收缩的日历数据
generateShrinkCalendar(type, line) {
// 返回当前这一周的日期数据
if (type === 0) {
return this.calendarSwiperDates[this.current].slice(line * 7, (line + 1) * 7)
}
// 返回上一周的日期数据
if (type === -1) {
// 当前选中的日期是否位于第一行
if (line === 0) {
/**
* 当前日历的第一行是否包含有上个月的日期
* 如果有包含,则返回上个月的倒数第二行日期
* 如果没有包含,则返回上个月的倒数第一行日期
*/
// 计算上个月的索引值
const prevIndex = this.current === 0 ? 2 : this.current - 1
// 获取上个月的日期数据
const prevCalendarSwiperDates = this.calendarSwiperDates[prevIndex]
// 获取上个月的日历行数
const prevCalendarSwiperDatesLine = prevCalendarSwiperDates.length / 7
if (this.calendarSwiperDates[this.current][0].type === 'prev') { // 倒数第二行
return prevCalendarSwiperDates.slice((prevCalendarSwiperDatesLine - 2) * 7, (prevCalendarSwiperDatesLine - 1) * 7)
} else { // 倒数第一行
return prevCalendarSwiperDates.slice((prevCalendarSwiperDatesLine - 1) * 7)
}
} else {
return this.calendarSwiperDates[this.current].slice((line - 1) * 7, line * 7)
}
}
// 返回下一周的日期数据
if (type === 1) {
// 计算当前日历月份总共有多少行
const curMonthMaxLine = this.calendarSwiperDates[this.current].length / 7
// 当前选中的日期是否位于最后一行
if (line === curMonthMaxLine - 1) {
/**
* 当前日历的最后一行是否包含有下个月的日期
* 如果有包含,则返回下个月的第二行日期
* 如果没有包含,则返回上个月的第一行日期
*/
// 计算下个月的索引值
const nextIndex = this.current === 2 ? 0 : this.current + 1
// 获取下个月的日期数据
const nextCalendarSwiperDates = this.calendarSwiperDates[nextIndex]
// 获取下个月的日历行数
const nextCalendarSwiperDatesLine = nextCalendarSwiperDates.length / 7
if (this.calendarSwiperDates[this.current][this.calendarSwiperDates[this.current].length - 1].type === 'next') { // 第二行
return nextCalendarSwiperDates.slice(7, 14)
} else { // 第一行
return nextCalendarSwiperDates.slice(0, 7)
}
} else {
return this.calendarSwiperDates[this.current].slice((line + 1) * 7, (line + 2) * 7)
}
}
},
// 展开日历轮播切换事件
swiperChange(type) {
// 通过点击上个月/下个月日期进行切换,不需要默认选中下个月的一号,直接选中点击的那个日期
if (!this.swiperByClick && this.swiperMode === 'open') {
this.getPrevOrNextDate(type)
}
setTimeout(() => { // 设置定时器是为了防止轮播切换时生成数据造成页面卡顿
this.initCalendarSwiperDates(() => {
this.swiperMode === 'close' && this.initCalendarShrinkSwiperDates()
}) // 初始化日历轮播日期信息
}, this.swiperMode === 'open' ? this.duration : 0)
},
// 收缩日历轮播切换事件
shrinkSwiperChange(type) {
// 默认选中下个星期的开始日期
this.getPrevOrNextStartDate(type)
setTimeout(() => { // 设置定时器是为了防止轮播切换时生成数据造成页面卡顿
this.initCalendarShrinkSwiperDates(type) // 初始化日历轮播日期信息
}, this.duration)
},
// 手动切换日历
switchCalendar(type) {
const currentKey = this.swiperMode === 'close' ? 'shrinkCurrent' : 'current'
const v = this[currentKey] + (type === 'prev' ? -1 : 1)
this[currentKey] = v === -1 ? 2 : v === 3 ? 0 : v
},
// 获取上一个月/下一个月的一号日期
getPrevOrNextDate(type) {
const year = this.getAssignDateInfo(false, 0)
let month = this.getAssignDateInfo(false, 1)
month = month + type
// 判断切换月份时选中当前日期高亮还是一号,若选中当前日期高亮需进行大小判断
const curActiveDate = this.getAssignDateInfo(false, 2)
const maxDate = new Date(year, month, 0).getDate()
const date = this.changeSetDefault ? 1 : curActiveDate > maxDate ? maxDate : curActiveDate
this.selectedDate = parseTime(new Date(year, month - 1, date), '{y}-{m}-{d}')
},
// 获取上个星期/下一星期的开始日期
getPrevOrNextStartDate(type) {
const date = this.calendarSwiperShrinkDates[this.shrinkCurrent][0]
this.selectedDate = parseTime(new Date(date.year, date.month - 1, date.date), '{y}-{m}-{d}')
},
// 前往某一天 格式 YYYY-MM | YYYY-MM-DD
goToDate(date = this.today) {
try {
if (date.split('-').length < 2 || date.split('-').length > 3) throw '参数有误'
if (date.split('-').length === 2) {
date += '-01'
}
} catch (err) {
throw Error('请检查参数是否符合规范')
}
this.selectedDate = date
this.initCalendarSwiperDates(() => {
this.initCalendarShrinkSwiperDates()
})
},
// 日历轮播展开的情况下选择日期
chooseDate(dateInfo, dateIndex) {
// 重复点击后续不做处理
if (dateInfo.isSelected) return false
// 是否点击了上个月份的后几天或者点击了下个月份的前几天
if (dateInfo.type !== 'cur') {
if (dateInfo.type === 'prev') { // 点击了上个月份的后几天,滑到上个月
this.current = this.current === 0 ? 2 : this.current - 1
} else { // 点击了下个月份的前几天,滑到下个月
this.current = this.current === 2 ? 0 : this.current + 1
}
// 将选中日期赋值为当前点击的那个日期
this.selectedDate = parseTime(new Date(dateInfo.year, dateInfo.month - 1, dateInfo.date), '{y}-{m}-{d}')
this.swiperByClick = true
return false
}
// 将当前选中的日期清空并选中最新的日期
this.calendarSwiperDates[this.current][this.dateActive].isSelected = false
this.dateActive = dateIndex
const date = this.calendarSwiperDates[this.current][this.dateActive]
date.isSelected = true
this.selectedDate = parseTime(new Date(date.year, date.month - 1, date.date), '{y}-{m}-{d}')
this.dateClick = true
},
// 日历轮播收缩的情况下选择日期
chooseShrinkDate(dateInfo, dateIndex) {
// 重复点击后续不做处理
if (dateInfo.isSelected) return false
this.dateClick = true
// 是否点击了上个月份的后几天或者点击了下个月份的前几天
if (dateInfo.type !== 'cur') {
if (dateInfo.type === 'prev') { // 点击了上个月份的后几天,切换到上个月
this.current = this.current === 0 ? 2 : this.current - 1
} else { // 点击了下个月份的前几天,切换到下个月
this.current = this.current === 2 ? 0 : this.current + 1
}
this.dateActive = dateIndex
// 将选中日期赋值为当前点击的那个日期
this.selectedDate = parseTime(new Date(dateInfo.year, dateInfo.month - 1, dateInfo.date), '{y}-{m}-{d}')
return false
}
// 计算当前选中日期之前有多少个日期
const dateActiveLine = Math.floor(this.dateActive / 7) * 7
// 将当前选中的日期清空并选中最新的日期
this.calendarSwiperDates[this.current][this.dateActive].isSelected = false
this.dateActive = dateIndex + dateActiveLine
const date = this.calendarSwiperDates[this.current][this.dateActive]
date.isSelected = true
this.selectedDate = parseTime(new Date(date.year, date.month - 1, date.date), '{y}-{m}-{d}')
},
// 向父组件传递当前选中数据
emitDate() {
const { year, month, date } = this.calendarSwiperDates[this.current][this.dateActive]
const e = {
selectedDate: this.selectedDate,
year,
month,
date
}
this.$emit('change', e)
}
}
}
</script>
<style>
.zsy_calendar {
width: 100%;
padding: 20rpx 0;
box-sizing: border-box;
background-color: #fff;
border-radius: 20rpx;
}
/* 日历顶部信息 */
.calendar_info {
display: flex;
align-items: center;
padding: 0 20rpx;
}
.calendar_info .title {
font-size: 34rpx;
font-weight: bold;
color: #2C2C2C;
}
.calendar_info .desc {
margin-left: 29rpx;
font-size: 28rpx;
color: #959595;
}
.calendar_info .backToToday {
margin-left: auto;
font-size: 24rpx;
}
/* 日历顶部信息 */
/* 日历周数 */
.calendar_week {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 26rpx;
color: #959595;
margin: 20rpx 0rpx;
}
.calendar_week .calendar_week__item {
width: calc(100% / 7);
text-align: center;
}
/* 日历周数 */
/* 日历切换模式 */
.calendar_toggle {
position: relative;
padding: 10rpx 0;
margin: 10rpx 20rpx 0;
display: flex;
justify-content: center;
}
.calendar_toggle .icon {
width: 30rpx;
height: 30rpx;
background-image: url('../../static/zsy-calendar/arrow.png');
background-size: contain;
background-repeat: no-repeat;
margin: 0 auto;
transition: all .3s;
}
.icon.down {
transform: rotate(180deg);
}
.calendar_toggle::before, .calendar_toggle::after {
width: calc(50% - 30rpx);
border-top: solid 2rpx #EAEAEA;
content: '';
display: block;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.calendar_toggle::before {
left: 0;
}
.calendar_toggle::after {
right: 0;
}
/* 日历切换模式 */
</style>

View File

@@ -1,536 +0,0 @@
<template>
<!-- 日历滚动插件 -->
<view class="zsy_calendar">
<!-- 日历顶部信息 -->
<view class="calendar_info" >
<view class="">
<text class="title">读书打卡</text>
<text class="desc">
({{ getAssignDateInfo(false, 0) === getAssignDateInfo(true, 0) ? '' : getAssignDateInfo(false, 0) + '年' }}{{ getAssignDateInfo(false, 1) }})
</text>
</view>
<view class="dakaBtn" @tap="goToDate()">
<u-icon name="checkbox-mark" color="#55aa7f" size="24" style="display: inline;"></u-icon>
<text>打卡</text>
</view>
<!-- <text v-show="showBackToTodayBtn" class="backToToday" :style="{color: dateActiveColor}" @tap="goToDate()">回到今天</text> -->
</view>
<!-- 日历周数 -->
<view class="calendar_week">
<view v-for="(item, index) in week" :key="index" class="calendar_week__item">{{ item }}</view>
</view>
<!-- 日历轮播 -->
<view class="calendar_swiper">
<swiper
key="normalSwiper"
circular
:style="{height: swiperHeight}"
:current="current"
:duration="duration"
:skip-hidden-item-layout="true"
@change="e => current = e.detail.current"
>
<swiper-item v-for="(swiper, swiperIndex) in 3" :key="swiperIndex" class="swiper-item">
<DateBox
:dates="getcurCalendarDates[swiperIndex]"
:cellHeight="cellHeight"
:selectedDate="selectedDate"
:dateActiveColor="dateActiveColor"
:swiperMode="swiperMode"
:showActive="emitTimer === null"
@chooseDate="chooseDate"
/>
</swiper-item>
</swiper>
</view>
<!-- 日历切换模式 -->
<view class="calendar_toggle" @tap="swiperMode = swiperMode === 'open' ? 'close' : 'open'">
<view class="icon" :class="{down: swiperMode === 'close'}"></view>
</view>
</view>
</template>
<script>
import { parseTime, deepClone } from './js/utils.js'
import DateBox from './dateBox.vue'
export default {
name: 'ZsyCalendar',
components: {
DateBox
},
props: {
duration: { // 轮播图动画时长
type: Number,
default: 300
},
cellHeight: { // 一列的高度
type: Number,
default: 75
},
dateActiveColor: { // 日期选中颜色
type: String,
default: '#FE6601'
},
sundayIndex: { // 星期天所在索引0表示第一个、6表示最后一个
type: Number,
default: 6
},
mode: { // 日历模式
type: String,
default: 'close'
},
changeSetDefault: { // 月份切换时是否显示一号还是当前月份选中高亮
type: Boolean,
default: true
},
defaultSelectedDate: { // 默认选中日期
type: String | null,
default: null
},
showArrowBtn: { // 是否显示左右切换按钮
type: Boolean,
default: true
}
},
data() {
return {
today: parseTime(new Date(), '{y}-{m}-{d}'), // 今天日期
selectedDate: null, // 选中日期
week: [], // 日历周数
current: 1, // 当前日历轮播默认显示索引
calendarSwiperDates: [], // 日历轮播日期信息
swiperChangeByClick: false, // 是否通过点击上月份或下月份的日期进行轮播切换
swiperMode: this.mode, // 日历轮播显示模式 open展开 close收缩
monthDateCache: {}, // 月份日期缓存数据
emitTimer: null, // 日期改变向父级传递当前选中日期计时器
dateClick: false // 是否进行了日期的点击选择
}
},
computed: {
// 返回当前日期信息(展开状态下为每月,收缩状态下为每周)
getcurCalendarDates() {
if (this.swiperMode === 'open') { // 展开
return this.calendarSwiperDates
} else {
return this.getCalendarShrinkSwiperDates()
}
},
// 计算选中日期的上月、本月、下月的年月信息
getAdjacentYMD() {
const year = this.getAssignDateInfo(false, 0)
const month = this.getAssignDateInfo(false, 1)
const prev = `${month === 1 ? year - 1 : year}-${month === 1 ? 12 : month - 1}`
const cur = `${year}-${month}`
const next = `${month === 12 ? year + 1 : year}-${month === 12 ? 1 : month + 1}`
return [prev, cur, next]
},
/* 获取指定日期信息
isToday: 是否获取当天的信息还是选中日期的信息
index: 0 表示年份 1 表示月份 2 表示日期 */
getAssignDateInfo() {
return (isToday, index) => {
return (isToday ? this.today : this.selectedDate).split('-')[index] * 1
}
},
// 是否显示回到今天按钮
showBackToTodayBtn() {
return this.getAssignDateInfo(false, 0) !== this.getAssignDateInfo(true, 0) || this.getAssignDateInfo(false, 1) !== this.getAssignDateInfo(true, 1)
},
// 返回轮播图高度
swiperHeight() {
const normalHeight = (this.calendarSwiperDates[this.current] || []).length / 7 * (this.cellHeight + 20) + 'rpx'
const shrinkHeight = this.cellHeight + 20 + 'rpx'
return this.swiperMode === 'open' ? normalHeight : shrinkHeight
}
},
watch: {
// 展开日历轮播切换
current(newV, oldV) {
if (newV === 0 && oldV === 2) { // 右滑
this.swiperChange(1)
return
}
if (newV === 2 && oldV === 0) { // 左滑
this.swiperChange(-1)
return
}
if (newV > oldV) { // 右滑
this.swiperChange(1)
} else { // 左滑
this.swiperChange(-1)
}
},
selectedDate: {
deep: true,
handler(newV, oldV) {
var selectD = new Date(newV).getTime();
var curTime = new Date().getTime();
//console.log(newV)
console.log(curTime,selectD)
if(selectD > curTime){
uni.showToast({
title:'未来日期不可打卡',
icon:'none'
})
return false
}else{
// 判断月历日期数据需不需要改变
if (this.swiperMode === 'close') {
setTimeout(() => {
this.generateAdjacentMonthDate() // 生成临近月份日期缓存数据
}, this.duration);
}
if (newV && (oldV === null || this.dateClick)) { // 初始化/日历点击选择时直接返回
this.emitDate()
this.dateClick = false
} else { // 其它情况做防抖处理
if (this.emitTimer !== null) {
clearTimeout(this.emitTimer)
this.emitTimer = null
}
this.emitTimer = setTimeout(() => {
this.emitDate()
this.emitTimer = null
}, this.duration + 200)
}
}}
}
},
created() {
this.init() // 初始化数据
},
methods: {
// 初始化数据
init() {
if (this.selectedDate === null) { // 默认选中日期为当天
this.selectedDate = this.defaultSelectedDate || this.today
}
this.initWeek() // 初始化要显示的周数
this.generateAdjacentMonthDate() // 生成临近月份日期缓存数据
},
// 初始化周数
initWeek() {
const normalWeek = ['日', '一', '二', '三', '四', '五', '六'] // 正常周数
const sIndex = this.sundayIndex < 0 ? 0 : this.sundayIndex >= normalWeek.length ? normalWeek.length - 1 : this.sundayIndex
normalWeek.unshift(...normalWeek.slice(-sIndex))
normalWeek.length = 7
this.week = normalWeek
},
// 根据current自动对轮播数据进行衔接排序
adjacentSortByCurrent(prev, cur, next) {
let arr
if (this.current === 0) {
arr = [cur, next, prev]
} else if (this.current === 1) {
arr = [prev, cur, next]
} else if (this.current === 2) {
arr = [next, prev, cur]
}
console.log(arr,'arr')
return arr
},
// 生成本月、上个月、下个月日期信息
generateAdjacentMonthDate() {
const arr = []
this.getAdjacentYMD.map(YM => {
const [year, month] = YM.split('-')
arr.push(this.generateMonthDateCache(year, month))
})
console.log(arr,'arr')
const [prev, cur, next] = arr
this.calendarSwiperDates = this.adjacentSortByCurrent(prev, cur, next)
if (this.swiperChangeByClick) {
this.swiperChangeByClick = false
}
},
// 生成月份日期缓存数据并返回
generateMonthDateCache(year, month) {
year = Number(year)
month = Number(month)
// 缓存中已存在
if (this.monthDateCache[`${year}-${month}`]) return this.monthDateCache[`${year}-${month}`]
let calendarDate = []
const monthDates = new Date(year, month, 0).getDate() // 获取此月份总天数
const normalWeek = ['一', '二', '三', '四', '五', '六', '日'] // 正常周数
const monthFirstDay = normalWeek[new Date(year, month - 1, 0).getDay()] // 获取本月一号为星期几
const monthFirstDayIndex = this.week.indexOf(monthFirstDay) // 计算本月一号在日历周数中的索引,索引之前的填充上个月的后几天
// 本月一号在日历中不是第一个位置,需要进行填充
if (monthFirstDayIndex !== 0) {
const prevMonthDates = new Date(year, month - 1, 0).getDate() // 获取上一个月份的总天数
// 填充本月一号之前的数据
for (let i = 0; i < monthFirstDayIndex; i ++) {
const item = {
year: month === 1 ? year - 1 : year,
month: month === 1 ? 12 : month - 1,
date: prevMonthDates - i,
dateFormat: `${month === 1 ? year - 1 : year}-${String(month === 1 ? 12 : month - 1).padStart(2, '0')}-${String(prevMonthDates - i).padStart(2, '0')}`,
type: 'prev'
}
// 判断填充的日期是否包含今天日期
this.theDateIsToday(item)
calendarDate.unshift(item)
}
}
// 循环生成当月所有日期
for (let i = 1; i <= monthDates; i ++) {
const item = {
year,
month,
date: i,
isSelected: false,
dateFormat: `${year}-${String(month).padStart(2, '0')}-${String(i).padStart(2, '0')}`,
type: 'cur'
}
// 今天的日期在不在里面
this.theDateIsToday(item)
calendarDate.push(item)
}
const residue = calendarDate.length % 7
// 判断是否需要填充下个月的前几天
if (residue !== 0) {
for (let i = 1; i <= 7 - residue; i ++) {
const item = {
year: month === 12 ? year + 1 : year,
month: month === 12 ? 1 : month + 1,
date: i,
dateFormat: `${month === 12 ? year + 1 : year}-${String(month === 12 ? 1 : month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`,
type: 'next'
}
// 下个月的前几天包含今天
this.theDateIsToday(item)
calendarDate.push(item)
}
}
this.monthDateCache[`${year}-${month}`] = deepClone(calendarDate)
return this.monthDateCache[`${year}-${month}`]
},
// 轮播图切换结束
swiperChange(e) {
// 切换上个月/下个月,默认选中一号 / 切换上一周/下一周,默认选中第一天
if (!this.swiperChangeByClick) {
this.getPrevOrNextDate(e)
}
if (this.swiperMode === 'open') { // 展开
// 通过点击上个月/下个月日期进行切换,不需要默认选中下个月的一号,直接选中点击的那个日期
setTimeout(() => {
this.generateAdjacentMonthDate() // // 重新生成临近月份日期缓存数据
}, this.duration)
}
},
// 判断日期是否为今天
theDateIsToday(item) {
if (`${item.year}${item.month}${item.date}` === `${this.getAssignDateInfo(true, 0)}${this.getAssignDateInfo(true, 1)}${this.getAssignDateInfo(true, 2)}`) {
item.isToday = true
}
},
// 计算收缩时的日历轮播日期信息
getCalendarShrinkSwiperDates() {
const [prevYM, curYM, nextYM] = this.getAdjacentYMD
// 本月日期数据
const curDates = this.monthDateCache[curYM]
// 计算当前日期所在行
const line = Math.floor(curDates.map(item => item.dateFormat).indexOf(this.selectedDate) / 7)
// 当前周日期信息
const cur = curDates.slice(line * 7, (line + 1) * 7)
let prev, next
/**
* 获取上一周日期信息
* 注意:当选中日期为第一周要额外判断,如果刚好为日历的第一天,则上一周数据应为上一个月的最后一周,否则为上一个月的倒数第二周
*/
if (line === 0) {
// 获取上个月日历数据
const prevDates = this.monthDateCache[prevYM]
// 获取上个月的日历行数
const prevDatesLine = prevDates.length / 7
if (curDates[0].dateFormat === this.selectedDate) { // 选中日期刚好为日历第一天
prev = prevDates.slice((prevDatesLine - 1) * 7) // 上个月倒数第一周数据
} else {
prev = prevDates.slice((prevDatesLine - 2) * 7, (prevDatesLine - 1) * 7) // 上个月倒数第二周数据
}
} else {
prev = curDates.slice((line - 1) * 7, line * 7)
}
/**
* 获取下一周日期信息
* 注意:当选中日期为最后一周要额外判断,如果刚好为日历的最后一天,则下一周数据应为下一个月的第一周,否则为下一个月的第二周
*/
if (line + 1 === curDates.length / 7) {
// 获取下个月的日期数据
const nextDates = this.monthDateCache[nextYM]
if (curDates[curDates.length - 1].dateFormat === this.selectedDate) { // 选中日期刚好为日历最后一天
next = nextDates.slice(0, 7) // 下个月第一周数据
} else {
next = nextDates.slice(7, 14) // 下个月第二周数据
}
} else {
next = curDates.slice((line + 1) * 7, (line + 2) * 7)
}
return this.adjacentSortByCurrent(prev, cur, next)
},
// 手动切换日历
switchCalendar(type) {
const currentKey = this.swiperMode === 'close' ? 'shrinkCurrent' : 'current'
const v = this[currentKey] + (type === 'prev' ? -1 : 1)
this[currentKey] = v === -1 ? 2 : v === 3 ? 0 : v
},
// 获取月的一号日期/周的第一天
getPrevOrNextDate(type) {
if (this.swiperMode === 'open') {
const year = this.getAssignDateInfo(false, 0)
let month = this.getAssignDateInfo(false, 1)
month = month + type
// 判断切换月份时选中当前日期高亮还是一号,若选中当前日期高亮需进行大小判断
const curActiveDate = this.getAssignDateInfo(false, 2)
const maxDate = new Date(year, month, 0).getDate()
const date = this.changeSetDefault ? 1 : curActiveDate > maxDate ? maxDate : curActiveDate
this.selectedDate = parseTime(new Date(year, month - 1, date), '{y}-{m}-{d}')
} else {
let current = this.current + type < 0 ? 2 : this.current + type > 2 ? 0 : this.current + type
this.selectedDate = this.getcurCalendarDates[current][0].dateFormat
}
},
// 前往某一天 格式 YYYY-MM | YYYY-MM-DD
goToDate(date = this.today) {
try {
if (date.split('-').length < 2 || date.split('-').length > 3) throw '参数有误'
if (date.split('-').length === 2) {
date += '-01'
}
} catch (err) {
throw Error('请检查参数是否符合规范')
}
this.selectedDate = date
this.generateAdjacentMonthDate()
},
// 日历轮播展开的情况下选择日期
chooseDate(dateInfo) {
// 重复点击后续不做处理
if (dateInfo.dateFormat === this.selectedDate) return false
if (this.swiperMode === 'open') { // 展开
// 是否点击了上个月份的后几天或者点击了下个月份的前几天
if (dateInfo.type !== 'cur') {
if (dateInfo.type === 'prev') { // 点击了上个月份的后几天,滑到上个月
this.current = this.current === 0 ? 2 : this.current - 1
} else { // 点击了下个月份的前几天,滑到下个月
this.current = this.current === 2 ? 0 : this.current + 1
}
// 将选中日期赋值为当前点击的那个日期
this.swiperChangeByClick = true
} else {
this.dateClick = true
}
} else { // 收缩
// 是否点击了上个月份的后几天或者点击了下个月份的前几天
if (dateInfo.type !== 'cur') {
// 将选中日期赋值为当前点击的那个日期
this.swiperChangeByClick = true
}
this.dateClick = true
}
// 将当前选中的日期清空并选中最新的日期
this.selectedDate = dateInfo.dateFormat
},
// 向父组件传递当前选中数据
emitDate() {
const e = {
selectedDate: this.selectedDate
}
this.$emit('change', e)
}
}
}
</script>
<style>
.zsy_calendar {
width: 100%;
padding: 20rpx 0;
box-sizing: border-box;
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
}
.dakaBtn{
color: #55aa7f; display: flex; padding: 3rpx 5rpx; border: #55aa7f 1px solid; border-radius: 10rpx;
}
/* 日历顶部信息 */
.calendar_info {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
}
.calendar_info .title {
font-size: 34rpx;
font-weight: bold;
color: #2C2C2C;
}
.calendar_info .desc {
margin-left: 29rpx;
font-size: 28rpx;
color: #959595;
}
.calendar_info .backToToday {
margin-left: auto;
font-size: 24rpx;
}
/* 日历顶部信息 */
/* 日历周数 */
.calendar_week {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 26rpx;
color: #959595;
margin: 20rpx 0rpx;
}
.calendar_week .calendar_week__item {
width: calc(100% / 7);
text-align: center;
}
/* 日历周数 */
/* 日历切换模式 */
.calendar_toggle {
position: relative;
padding: 10rpx 0;
margin: 10rpx 20rpx 0;
display: flex;
justify-content: center;
}
.calendar_toggle .icon {
width: 30rpx;
height: 30rpx;
background-image: url('../../static/zsy-calendar/arrow.png');
background-size: contain;
background-repeat: no-repeat;
margin: 0 auto;
transform: rotate(0deg);
transition: all .3s;
}
.icon.down {
transform: rotate(180deg);
}
.calendar_toggle::before, .calendar_toggle::after {
width: calc(50% - 30rpx);
border-top: solid 2rpx #EAEAEA;
content: '';
display: block;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.calendar_toggle::before {
left: 0;
}
.calendar_toggle::after {
right: 0;
}
/* 日历切换模式 */
</style>