This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
@@ -0,0 +1,17 @@
<script lang="ts" setup>
import { type PropType } from "vue"
import { getUid, rpx2px } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, hexToRgb } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { xDate, dateCovertXdate, xDateTypeTime } from "../../core/util/xDate.uts"
import { xDateDayInfoType, xCalendarDateStyle_type } from "../../interface.uts"
</script>
<template>
<view class="xCalendarView">
</view>
</template>
<style scoped>
</style>
@@ -0,0 +1,17 @@
<script lang="ts" setup>
import { type PropType } from "vue"
import { getUid, rpx2px } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, hexToRgb } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { xDate, dateCovertXdate, xDateTypeTime } from "../../core/util/xDate.uts"
import { xDateDayInfoType, xCalendarDateStyle_type } from "../../interface.uts"
</script>
<template>
<view class="xCalendarView">
</view>
</template>
<style scoped>
</style>
@@ -0,0 +1,17 @@
<script lang="ts" setup>
import { type PropType } from "vue"
import { getUid, rpx2px } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, hexToRgb } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { xDate, dateCovertXdate, xDateTypeTime } from "../../core/util/xDate.uts"
import { xDateDayInfoType, xCalendarDateStyle_type } from "../../interface.uts"
</script>
<template>
<view class="xCalendarView">
</view>
</template>
<style scoped>
</style>
@@ -0,0 +1,17 @@
<script lang="ts" setup>
import { type PropType } from "vue"
import { getUid, rpx2px } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, hexToRgb } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { xDate, dateCovertXdate, xDateTypeTime } from "../../core/util/xDate.uts"
import { xDateDayInfoType, xCalendarDateStyle_type } from "../../interface.uts"
</script>
<template>
<view class="xCalendarView">
</view>
</template>
<style scoped>
</style>
@@ -0,0 +1,389 @@
<script lang="ts" setup>
import { type PropType } from "vue"
import { getUid, rpx2px } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, hexToRgb, rgbToHex } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { xDate, dateCovertXdate, xDateTypeTime } from "../../core/util/xDate.uts"
import { xDateDayInfoType, xCalendarDateStyle_type,xCalendarDateStyleStatusType } from "../../interface.uts"
import { xCalendar } from "./xCalendar"
import { xDateArrayItem,xDateArrayItemType,xCalendarArgs,xCalendarMode } from "../x-calendar-view/interface.uts"
// #ifdef APP
import { calendarDraw } from "./calendarDraw"
const proxy = getCurrentInstance()?.proxy;
const xCalendarViewItemRef = ref<UniElement|null>(null)
let calendarDom:calendarDraw|null = null;
// #endif
const i18n = xConfig.i18n;
type xCalendarMultiplePropsType = {
/**
* 同步当前时间v-model
* 不想受控:model-value
*/
modelValue : string[],
/**
* 范围选择模式
* day:天数多选,通过multipleMax可以设置允许选择的天数
* range:天数的范围选择,起始和终止
* week:按周次选择范围
* quarter:按季度选择范围
* year:按年
*/
model: xCalendarMode,
/**
* 多选模式时,允许选择的最大天数。
*/
multipleMax : number,
/**
* 禁用的日期字符串如"2023-12-12"
* 它与下面的startend不冲突。
*/
disabledDays: string[],
/**
* 允许选择的开始日期
*/
startDate: string,
/**
* 允许选择的结束日期
*/
endDate: string,
/**
* 设置指定日期的样式
* 数据类型见:xCalendarDateStyle_type
*/
dateStyle: xCalendarDateStyle_type[],
/**
* 同步vmodel时格式化模板
*/
format: string,
/**
* 选中的主题色,默认空值,取全局主题色
* 如果提供了dateStyle,以dateStyle为准
*/
color: string,
/**
* 默认的文字颜色
* 如果提供了dateStyle,以dateStyle为准
*/
fontColor:string,
/**
* 默认的暗黑文字颜色
* 如果提供了dateStyle,以dateStyle为准
*/
fontDarkColor:string,
/**
* 默认选中时的文字颜色
* 如果提供了dateStyle,以dateStyle为准
*/
activeFontColor:string,
/**
* 范围选中时,范围中间的选中颜色,
* 如果为空,为color的透明度0.5;
*/
rangColor:string,
rangFontColor:string,
currentDate:string,
/**
* 你当前的一周的第一天的索引值是几:0: 周一,1: 周二,2: 周三,3: 周四,4: 周五,5: 周六,6: 周日
*/
seekDay:number,
/**
* 给日期设定状态
* 类型为:xCalendarDateStyleStatusType[]
*/
dateStatus:xCalendarDateStyleStatusType[]
}
const emit = defineEmits(['change','click'])
const props = withDefaults(defineProps<xCalendarMultiplePropsType>(), {
modelValue: [] as string[],
currentDate:'',
model:'day' as xCalendarMode,
multipleMax: -1,
disabledDays:[] as string[],
startDate:'1900-1-1',
endDate:'2025-5-13',
dateStyle:[] as xCalendarDateStyle_type[],
format:'YYYY-MM-DD',
color:'',
fontColor:'#333333',
fontDarkColor:'#ffffff',
activeFontColor:'#ffffff',
rangColor:'',
rangFontColor:'',
seekDay:0,
dateStatus:[] as xCalendarDateStyleStatusType[]
})
const calendar = new xCalendar()
const _dateStatus = computed(() : xCalendarDateStyleStatusType[] => props.dateStatus)
const _rangColor = computed(()=>{
let color = props.rangColor == ''?xConfig.color:props.rangColor
let rgba = hexToRgb(getDefaultColor(color));
return `rgba(${rgba.getNumber('r')},${rgba.getNumber('g')},${rgba.getNumber('b')},${props.rangColor==''?0.2:1})`
})
const _modelValue = computed(():string[]=> props.modelValue )
const _model = computed(():xCalendarMode=> props.model )
function splitArray<T>(ar : Array<T>, len : number) : Array<Array<T>> {
const result : Array<Array<T>> = [];
for (let i = 0; i < ar.length; i += len) {
result.push(ar.slice(i, i + len));
}
return result
}
const _fontSize = computed(():string=> checkIsCssUnit('16',''))
const dateArrayList = computed(():xDateArrayItemType[][]=>{
const primaryColor = getDefaultColor(props.color==''?xConfig.color:props.color);
const dates = calendar.getCalendar(
props.seekDay,
props.model,
props.currentDate,
props.modelValue,
props.startDate!=''?new Date(props.startDate.replace(/-/g,'/')):null,
props.endDate!=''?new Date(props.endDate.replace(/-/g,'/')):null,
{
color:primaryColor,
fontColor:getDefaultColor(xConfig.dark=='dark'?props.fontDarkColor:props.fontColor),
activeFontColor:getDefaultColor(props.activeFontColor),
rangColor:_rangColor.value,
rangFontColor:props.rangFontColor==''?primaryColor:getDefaultColor(props.rangFontColor)
} as xCalendarArgs,props.dateStyle,props.disabledDays);
return splitArray<xDateArrayItemType>(dates,7)
})
function showLabel(item:xDateArrayItemType):string {
if(item.isInstart&&item.isInEnd&&_model.value=='range') return i18n.t('tmui4x.calendar.rangStatus',2)
if(item.isInstart&&!item.isInEnd&&_model.value=='range') return i18n.t('tmui4x.calendar.rangStatus',0)
if(!item.isInstart&&item.isInEnd&&_model.value=='range') return i18n.t('tmui4x.calendar.rangStatus',1)
return ""
}
function dateClick(item:xDateArrayItemType){
if(item.disabled) return;
emit('click',item)
}
function checkDataIsInDateStatus(date:string|null):string{
if(date ==''||date == null) return '';
for(let k =0 ;k <_dateStatus.value.length;k++){
let itemStatus = _dateStatus.value[k]
let dates = itemStatus?.date??[];
let start = itemStatus?.between?.start??''
let end = itemStatus?.between?.end??''
let betweenColor = itemStatus?.between?.color??''
let notDates = itemStatus?.between?.notDate??[]
let nowDate = new xDate(date);
let isInBetweenDate = false;
if(start!=''&&end!=''){
let isBetween = nowDate.isBetween(new xDate(start),new xDate(end),'d','[]');
let isNotDate = false
for(let i=0;i<notDates.length;i++){
let item = notDates[i];
if(nowDate.isBetweenOf(new xDate(item),'=','d')){
isNotDate = true;
break;
}
}
isInBetweenDate = isBetween && !isNotDate
}
if(isInBetweenDate) return getDefaultColor(betweenColor==''?'primary':betweenColor)
let selfColor = ''
for(let i=0;i<dates.length;i++){
let item = dates[i];
if(nowDate.isBetweenOf(new xDate(item.date),'=','d')){
selfColor = item.color==''?'primary':item.color
break;
}
}
if(selfColor!=''){
return getDefaultColor(selfColor)
}
}
return ""
}
// #ifdef APP
function draw():Promise<any>{
return new Promise(()=>{
calendarDom?.draw(dateArrayList.value,_model.value,_dateStatus.value)
})
}
function canvsClick(e:UniPointerEvent){
xCalendarViewItemRef.value?.getBoundingClientRectAsync()
?.then((rect:DOMRect)=>{
let cellHeight = 50;
let cellWidth = rect.width / 7;
let top = rect.top;
let left = rect.left;
let x = e.clientX - left;
let y = e.clientY - top;
let col = Math.floor(x/cellWidth);
let row = Math.floor(y/cellHeight);
let item = dateArrayList.value[row][col];
function testThread():Promise<any>{
return new Promise(()=>{
dateClick(item)
})
}
testThread()
})
.catch((er)=>{
console.error(er)
})
}
watch(():any => dateArrayList.value,()=>{
draw()
})
// #endif
onMounted(()=>{
// #ifdef APP
calendarDom = new calendarDraw(
xCalendarViewItemRef.value,
proxy,
[i18n.t('tmui4x.calendar.rangStatus',0),i18n.t('tmui4x.calendar.rangStatus',1),i18n.t('tmui4x.calendar.rangStatus',2)]
)
nextTick(()=>{
draw()
})
// #endif
})
</script>
<template>
<view class="xCalendarViewItem"
ref="xCalendarViewItemRef"
<!-- #ifdef APP -->
@click="canvsClick"
<!-- #endif -->
>
<!-- #ifndef APP -->
<view class="xCalendarViewItemCol" v-for="(children,index) in dateArrayList" :key="index">
<view class="xCalendarViewItemColItem"
@click="dateClick(item)"
v-for="(item,index2) in (children as xDateArrayItemType[])" :key="index2">
<view class="xCalendarViewItemColItemBox"
:style="{
backgroundColor:item.style.dstyle.backgroundColor,
opacity:item.style.dstyle.opacity,
}"
>
<view v-if="item.style.dot.dot"
class="xCalendarViewItemDot"
:class="[item.style.dot.dotLabel==''?'xCalendarViewItemDotNolabel':'']"
:style="{
color:item.style.dot.dotLabelColor,
backgroundColor:item.style.dot.dotColor
}"
>
{{item.style.dot.dotLabel}}
</view>
<text class="xCalendarViewItemColDate"
:style="{
color:item.style.dstyle.fontColor,
fontSize:_fontSize
}"
>
{{item.date.day}}
</text>
<text class="xCalendarViewItemColLabel"
:style="{
color:item.style.dstyle.fontColor
}"
>{{showLabel(item)||item.style.dstyle.label}}</text>
<view class="xCalendarViewStatus" :style="{backgroundColor:checkDataIsInDateStatus(item.date.date)}" v-if="checkDataIsInDateStatus(item.date.date)!=''"></view>
</view>
</view>
</view>
<!-- #endif -->
</view>
</template>
<style scoped lang="scss">
.xCalendarViewStatus{
width:4px;
height:4px;
border-radius: 2px;
position: absolute;
bottom: 1px;
}
// #ifdef APP
.xCalendarViewItem{
width:100%;
height:100%;
}
// #endif
// #ifndef APP
.xCalendarViewItemDot{
padding:2px 4px;
min-width:18px;
min-height:18px;
font-size:10px;
border-radius:9px;
position: absolute;
right:0px;
top:0px;
&.xCalendarViewItemDotNolabel{
padding:0;
min-width:8px;
min-height:8px;
font-size:10px;
border-radius:9px;
}
}
.xCalendarViewItem{
width:100%;
height:100%;
.xCalendarViewItemCol{
display: flex;
flex-direction: row;
justify-content: flex-start;
align-content: center;
height: 50px;
.xCalendarViewItemColItem{
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
height: 100%;
width:14.285%;
.xCalendarViewItemColItemBox{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 45px;
height: 45px;
border-radius: 25px;
overflow: visible;
}
.xCalendarViewItemColDate{
text-align: center;
height: 27px;
line-height: 27px;
margin-top: -4px;
// font-weight: bold;
// font-size: 16px;
display: block;
}
.xCalendarViewItemColLabel{
text-align: center;
font-size: 10px;
margin-top: -4px;
display: block;
min-height:10px;
}
}
}
}
// #endif
</style>
@@ -0,0 +1,189 @@
import { xDateArrayItem, xDateArrayItemType, xCalendarArgs, xCalendarMode } from "../x-calendar-view/interface.uts"
import { getDefaultColor, hexToRgb, rgbToHex } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import {xCalendarDateStyleStatusType} from "@/uni_modules/tmx-ui/interface.uts"
import { xDate } from "../../core/util/xDate.uts"
export class calendarDraw {
ele : UniElement | null = null
proxy : any | null = null
cellHeight = 50;
sapce = 5
model : xCalendarMode = 'day'
_fontSize : string = checkIsCssUnit('16', xConfig.unit)
_dateStatus : xCalendarDateStyleStatusType[] | null = null;
dateCnStrs:string[] = ["开始","结束","本日"]
constructor(target : UniElement | null, proxyx : any | null,dateCn:string[]) {
this.ele = target
this.proxy = proxyx
this.dateCnStrs = dateCn
}
checkDataIsInDateStatus(date:string|null):string{
if(date ==''||date == null || this._dateStatus == null) return '';
for(let k =0 ;k <this._dateStatus.length;k++){
let itemStatus = this._dateStatus[k]
let dates = itemStatus?.date??[];
let start = itemStatus?.between?.start??''
let end = itemStatus?.between?.end??''
let betweenColor = itemStatus?.between?.color??''
let notDates = itemStatus?.between?.notDate??[]
let nowDate = new xDate(date);
let isInBetweenDate = false;
if(start!=''&&end!=''){
let isBetween = nowDate.isBetween(new xDate(start),new xDate(end),'d','[]');
let isNotDate = false
for(let i=0;i<notDates.length;i++){
let item = notDates[i];
if(nowDate.isBetweenOf(new xDate(item),'=','d')){
isNotDate = true;
break;
}
}
isInBetweenDate = isBetween && !isNotDate
}
if(isInBetweenDate) return getDefaultColor(betweenColor==''?'primary':betweenColor)
let selfColor = ''
for(let i=0;i<dates.length;i++){
let item = dates[i];
if(nowDate.isBetweenOf(new xDate(item.date),'=','d')){
selfColor = item.color==''?'primary':item.color
break;
}
}
if(selfColor!=''){
return getDefaultColor(selfColor)
}
}
return ""
}
showLabel(item : xDateArrayItemType) : string {
if (item.isInstart && item.isInEnd && this.model == 'range') return this.dateCnStrs[2]
if (item.isInstart && !item.isInEnd && this.model == 'range') return this.dateCnStrs[0]
if (!item.isInstart && item.isInEnd && this.model == 'range') return this.dateCnStrs[1]
return ""
}
getColor(color : string, alpha : number) : string {
if (alpha == 1) return color;
let rgba = hexToRgb(getDefaultColor(color));
return `rgba(${rgba.getNumber('r')},${rgba.getNumber('g')},${rgba.getNumber('b')},${alpha})`
}
_draw(element : UniElement, Grect : DOMRect, list : xDateArrayItemType[][]) {
const ctx = element.getDrawableContext()!;
ctx.reset()
ctx.fillStyle = 'rgba(0,0,0,0)'
ctx.fillRect(0, 0, Grect.width, Grect.height)
ctx.textAlign = 'center'
const _realCellWidth = Grect.width / 7
const _h = Math.min(_realCellWidth, 50) - this.sapce * 2
for (let i = 0; i < list.length; i++) {
const children = list[i]
for (let col = 0; col < children.length; col++) {
let item = children[col];
let dstyle = item.style
let xy_x = _realCellWidth * col + _realCellWidth / 2;
let xy_y = this.cellHeight * i + this.cellHeight / 2;
// 绘制选中的背景
ctx.fillStyle = this.getColor(dstyle.dstyle.backgroundColor, (item.disabled || !item.inCurrentMonth) && !item.isInstart && !item.isInEnd ? 0.3 : 1);
if (dstyle.dstyle.backgroundColor != 'transparent') {
ctx.beginPath()
ctx.arc(xy_x, xy_y, _h / 2, 0, Math.PI * 2)
ctx.closePath()
ctx.fill()
}
// 绘制右角标。
if (dstyle.dot.dot) {
if (dstyle.dot.dotLabel == '') {
let lastx = _realCellWidth * col + _realCellWidth / 2 + this.cellHeight / 2-10;
let lasty = _realCellWidth * (i) + 10
ctx.fillStyle = dstyle.dot.dotColor
ctx.beginPath()
ctx.arc(lastx, lasty, 4, 0, Math.PI * 2)
ctx.closePath()
ctx.fill()
} else {
let lastx = _realCellWidth * col + _realCellWidth / 2 + this.cellHeight / 2 - 5;
let lasty = _realCellWidth * (i) + 10
ctx.fillStyle = dstyle.dot.dotColor;
ctx.beginPath()
ctx.arc(lastx, lasty, 10, 0, Math.PI * 2)
ctx.closePath()
ctx.fill()
ctx.font = "10px"
ctx.fillStyle = dstyle.dot.dotLabelColor
// #ifdef APP-ANDROID || APP-HARMONY
ctx.fillText(dstyle.dot.dotLabel, lastx, lasty + 3);
// #endif
// #ifdef APP-IOS
ctx.fillText(dstyle.dot.dotLabel, lastx, lasty + 5);
// #endif
}
}
// 绘制文字
const text = item.date.day.toString();
ctx.fillStyle = this.getColor(dstyle.dstyle.fontColor, (item.disabled || !item.inCurrentMonth) ? 0.7 : 1);
ctx.font = this._fontSize
const textHeight = 34;
const _t_x = _realCellWidth * col + _realCellWidth / 2
let _t_y = this.cellHeight * i + this.cellHeight / 2 + 2;
// #ifdef APP-IOS
_t_y = this.cellHeight * i + this.cellHeight / 2 + 6;
// #endif
ctx.fillText(text, _t_x, _t_y);
let label = this.showLabel(item)
label = label == '' ? dstyle.dstyle.label : label
// 绘制底部的label
if (label != '') {
let labely = this.cellHeight * i + this.cellHeight - 9;
// #ifdef APP-ANDROID || APP-HARMONY
labely = this.cellHeight * i + this.cellHeight - 12;
// #endif
ctx.font = "8px"
ctx.fillText(label, _t_x, labely);
}
// 绘制状态小点点。
let statusColor = this.checkDataIsInDateStatus(item.date.date);
if(statusColor!=''){
ctx.fillStyle = statusColor
ctx.beginPath()
let statusY = this.cellHeight * i + this.cellHeight - this.sapce - 3;
ctx.arc(_t_x,statusY,2,0,Math.PI*2)
ctx.closePath()
ctx.fill()
}
}
}
ctx.update()
}
draw(list : xDateArrayItemType[][], modelv : xCalendarMode = 'day',status:xCalendarDateStyleStatusType[]|null = null) {
let _this = this;
_this.model = modelv;
_this._dateStatus = status;
this.ele?.getBoundingClientRectAsync()
?.then((rect : DOMRect) => {
_this._draw(_this.ele!, rect, list)
})
.catch((er) => {
console.error(er)
})
}
}
@@ -0,0 +1,750 @@
<script lang="ts" setup>
import { type PropType, getCurrentInstance } from "vue"
import { getUid, rpx2px } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, hexToRgb } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig, getI18n } from "../../config/xConfig.uts"
import { xDate, dateCovertXdate, xDateTypeTime } from "../../core/util/xDate.uts"
import { xDateDayInfoType, xCalendarDateStyle_type, xCalendarDateStyleStatusType } from "../../interface.uts"
import { xCalendar } from "./xCalendar"
import calendarMultipleUvue from "./calendar-multiple.uvue"
import { xDateArrayItem, xDateArrayItemType, xCalendarArgs, xCalendarMode } from "../x-calendar-view/interface.uts"
const i18n = xConfig.i18n
type xCalendarMultiplePropsType = {
/**
* 同步当前时间v-model
* 不想受控:model-value
*/
modelValue : string[],
/**
* 范围选择模式
* day:天数多选,通过multipleMax可以设置允许选择的天数
* range:天数的范围选择,起始和终止
* week:按周次选择范围(未开放)
* quarter:按季度选择范围(未开放)
* year:按年(未开放)
*/
model : "day" | "range" | "week" | "quarter" | "year",
/**
* 多选模式时,允许选择的最大天数。
*/
multipleMax : number,
/**
* 禁用的日期字符串如"2023-12-12"
* 它与下面的startend不冲突。
*/
disabledDays : string[],
/**
* 允许选择的开始日期
*/
startDate : string,
/**
* 是否上下切换日历
*/
vertical : boolean,
/**
* 允许选择的结束日期
*/
endDate : string,
/**
* 当前显示的月份,默认以modalValue中的第一项为初始月
* 如果为空,显示本月,可以控制这里切换显示的日期
*/
currentDate : string,
/**
* 设置指定日期的样式
* 数据类型见:xCalendarDateStyle_type
*/
dateStyle : xCalendarDateStyle_type[],
/**
* 同步vmodel时格式化模板
*/
format : string,
/**
* 选中的主题色,默认空值,取全局主题色
* 如果提供了dateStyle,以dateStyle为准
*/
color : string,
/**
* 默认的文字颜色
* 如果提供了dateStyle,以dateStyle为准
*/
fontColor : string,
/**
* 默认的暗黑文字颜色
* 如果提供了dateStyle,以dateStyle为准
*/
fontDarkColor : string,
/**
* 默认选中时的文字颜色
* 如果提供了dateStyle,以dateStyle为准
*/
activeFontColor : string,
/**
* 范围选中时,范围中间的选中颜色,
* 如果为空,为color的透明度0.5;
*/
rangColor : string,
rangFontColor : string,
/**
* 头的背景颜色,默认为透明
*/
headBgColor : string,
/**
* 头的文字颜色,提供了后暗黑失效会以这个为准。
*/
headFontColor : string,
/**
* 头部自定义样式。
*/
headStyle : string,
/**
* 循环渲染时,是否只渲染当前面板(如果你在pad等10年前的低端机上渲染日历有压力请打开此值为true)
* 关闭后可以提升滑动体验。
*/
renderOnly : boolean,
/**
* 你当前的一周的第一天的索引值是几:0: 周一,1: 周二,2: 周三,3: 周四,4: 周五,5: 周六,6: 周日
*/
seekDay : number,
/**
* 给日期设定状态
* 类型为:xCalendarDateStyleStatusType[]
*/
dateStatus : xCalendarDateStyleStatusType[]
}
/**
* @name 多选日历 xCalendarMultiple
* @description 可以单选和多选,无限循环滚动,目前4.67dev以下版本使用可能会有性能风险,请关注后续官方针对性的优化。
* @page /pages/index/calendar-multiple
* @category 表单组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| - | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({ name: "xCalendarMultiple" })
const calendar = new xCalendar()
const emit = defineEmits([
/**
* 时间变化时触发
* @param {string[]} value - 当前变化的日期
*/
'change',
/**
* 当前日历面板的日期被点击时触发
* @param {string} value - 当前被点击的日期
*/
'click',
/**
* 当前激活面板月份改变时触发(就是当前看到的月份面板)
* @param {string} value - 当前激活面板的日期
*/
'currentChange',
/**
* 同步当前的选中的日期绑定
* @param {string[]} value - 当前选中日期
*/
'update:modelValue',
/**
* 同步当前查看的月份日期,请以日期形式提供值
* @param {string} value - 当前查看的月分日期
*/
'update:currentDate'
])
const props = withDefaults(defineProps<xCalendarMultiplePropsType>(), {
modelValue: () : string[] => [] as string[],
model: 'day',
multipleMax: -1,
disabledDays: () : string[] => [] as string[],
startDate: '1900-1-1',
endDate: '2100-1-1',
dateStyle: () : xCalendarDateStyle_type[] => [] as xCalendarDateStyle_type[],
format: 'YYYY-MM-DD',
color: '',
fontColor: '#333333',
fontDarkColor: '#ffffff',
activeFontColor: '#ffffff',
rangColor: '',
rangFontColor: '',
headBgColor: 'transparent',
headFontColor: '',
headStyle: '',
currentDate: '',
vertical: false,
renderOnly: true,
seekDay: 0,
dateStatus: () : xCalendarDateStyleStatusType[] => [] as xCalendarDateStyleStatusType[]
})
const _dateStatus = computed(() : xCalendarDateStyleStatusType[] => props.dateStatus)
const weeksCn = computed(() : string[] => {
// ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
// 根据 seekDay 属性调整周名称的顺序
const weekNames = [
i18n!.t("tmui4x.calendar.week", 0),
i18n!.t("tmui4x.calendar.week", 1),
i18n!.t("tmui4x.calendar.week", 2),
i18n!.t("tmui4x.calendar.week", 3),
i18n!.t("tmui4x.calendar.week", 4),
i18n!.t("tmui4x.calendar.week", 5),
i18n!.t("tmui4x.calendar.week", 6),
]
// 如果 seekDay 为 0(默认周一),直接返回原数组
if (props.seekDay == 0) {
return weekNames
}
// 根据 seekDay 重新排列数组
const result : string[] = []
for (let i = 0; i < 7; i++) {
const index = (i + props.seekDay) % 7
result.push(weekNames[index])
}
return result
})
const _headBgColor = computed(() : string => getDefaultColor(props.headBgColor))
const _headFontColor = computed(() : string => {
if (props.headFontColor == '') return '#333333'
return getDefaultColor(props.headFontColor)
})
const _modelValue = ref(props.modelValue)
const _currentDate = ref(new xDate(props.currentDate).format("YYYY-MM-DD"))
const _currentDateSwipersIndex = ref(0)
const _currentDateSwipers = ref<string[]>([])
const _startDate = computed(() : string => props.startDate)
const _endDate = computed(() : string => props.endDate)
const _currentDateLabel = computed(() : string => {
let ars = _currentDate.value.split('-');
// `${ars[0]}年${ars[1]}月`
return i18n.t('tmui4x.calendar.titleCurrentMonth', [ars[0], ars[1]])
})
const _currentYear = ref(new xDate(props.currentDate).getYear())
let _modelValueDate = computed(() : Date[] => {
return _modelValue.value.map((d : string) : Date => {
return new Date(d.replace(/-/g, '/'))
})
})
const _tipsText = computed(() : string => {
if (props.model == 'day') {
// `已选择${_modelValue.value.length}日` : '未选择日期'
return _modelValue.value.length > 0 ? i18n.t('tmui4x.calendar.selectedStatus', 0, { count: _modelValue.value.length }) : i18n.t('tmui4x.calendar.selectedStatus', 1)
} else if (props.model == 'range') {
if (_modelValue.value.length == 0) return i18n.t('tmui4x.calendar.selectedStatus', 1);
if (_modelValue.value.length == 1) return i18n.t('tmui4x.calendar.selectedStatus', 3);
if (_modelValue.value.length > 1) {
let start = _modelValueDate.value[0].getTime()
let end = _modelValueDate.value[1].getTime()
let diff = Math.abs(start - end);
let diffDay = diff / (24 * 60 * 60 * 1000)
if (start - end > 0) return i18n.t('tmui4x.calendar.selectedStatus', 1);
return i18n.t('tmui4x.calendar.selectedStatus', 0, { count: (diffDay + 1) })
}
}
// '未选择日期'
return i18n.t('tmui4x.calendar.selectedStatus', 1);
})
const _monthBgColor = computed(() : string => xConfig.dark == 'dark' ? xConfig.sheetDarkColor : '#ffffff')
const _color = computed(() : string => props.color == '' ? getDefaultColor(xConfig.color) : getDefaultColor(props.color))
const showPanel = ref(false)
function dateClick(item : xDateArrayItemType) {
const isInIndex = calendar.isInRangeDateByIndex(new Date(item.date.date), _modelValueDate.value, props.model)
console.log(item.date.date)
emit('click', item.date.date)
let dates = _modelValue.value.slice(0)
function showToastFun() {
// `最大${props.multipleMax}天`
uni.showToast({
title: i18n.t('tmui4x.calendar.tips', props.multipleMax),
icon: 'none'
})
}
if (props.model == 'day') {
if (isInIndex == -1) {
if (props.multipleMax <= dates.length && props.multipleMax > -1) {
showToastFun()
return;
}
dates.push(item.date.date)
} else {
dates.splice(isInIndex, 1)
}
} else if (props.model == 'range') {
if (isInIndex == -1) {
if (dates.length == 0) {
dates = [item.date.date] as string[]
} else if (dates.length == 1) {
dates.push(item.date.date)
} else if (dates.length > 1) {
dates = [item.date.date] as string[]
}
} else if (isInIndex == 0) {
if (dates.length == 1) {
dates = [] as string[]
} else if (dates.length > 1) {
dates = [item.date.date] as string[]
}
} else if (isInIndex == 1) {
dates = [item.date.date] as string[]
}
if (dates.length >= 2) {
let adates = dates.map((d : string) : Date => {
return new Date(d.replace(/-/g, '/'))
})
let start = adates[0].getTime()
let end = adates[1].getTime()
let diff = Math.abs(start - end);
let diffDay = diff / (24 * 60 * 60 * 1000)
if (props.multipleMax <= diffDay && props.multipleMax > -1) {
showToastFun()
return;
}
if (start > end) {
dates = [dates[1], dates[0]] as string[]
}
}
}
_modelValue.value = dates
emit('update:modelValue', _modelValue.value)
if ((_modelValue.value.length >= 2 || _modelValue.value.length == 0) && props.model == 'range') {
emit('change', _modelValue.value)
} else if (props.model == 'day') {
emit('change', _modelValue.value)
}
}
function getSwiperListCurrentDates(nowCurrentDate : string) : string[] {
let index = _currentDateSwipersIndex.value
let currentData = new xDate(nowCurrentDate)
let xd = currentData.getClone().setDateOf(1, 'd').format('YYYY-MM-DD')
let start = currentData.getClone().setDateOf(1, 'd').subtraction(1, 'm').format('YYYY-MM-DD')
let end = currentData.getClone().setDateOf(1, 'd').add(1, 'm').format('YYYY-MM-DD')
let datas = [xd, end, start]
if (_currentDateSwipers.value.length == 0) return datas;
if (index == 0) {
datas = [xd, end, start]
} else if (index == 1) {
datas = [start, xd, end]
} else if (index == 2) {
datas = [end, start, xd]
}
return datas;
}
function clear() {
if (_modelValue.value.length == 0) return;
_modelValue.value = [] as string[]
emit('update:modelValue', _modelValue.value)
emit('change', _modelValue.value)
}
function nowMonth() {
let nowdate = new xDate()
_currentDate.value = nowdate.format("YYYY-MM-DD")
_currentYear.value = nowdate.getYear()
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
emit('currentChange', _currentDate.value)
emit('update:currentDate', _currentDate.value)
}
function stepperChangeYear(eyear : number) {
let nowdate = new xDate(_currentDate.value)
nowdate.setDateOf(eyear, 'y')
_currentDate.value = nowdate.format("YYYY-MM-DD")
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
emit('currentChange', _currentDate.value)
emit('update:currentDate', _currentDate.value)
}
function changeMonth(eyear : number) {
let nowdate = new xDate(_currentDate.value)
nowdate.setDateOf(eyear, 'm')
_currentDate.value = nowdate.format("YYYY-MM-DD")
_currentYear.value = nowdate.getYear()
showPanel.value = false;
emit('currentChange', _currentDate.value)
emit('update:currentDate', _currentDate.value)
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
}
function nextMonth() {
let nowdate = new xDate(_currentDate.value)
nowdate.add(1, 'm')
_currentDate.value = nowdate.format("YYYY-MM-DD")
_currentYear.value = nowdate.getYear()
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
emit('currentChange', _currentDate.value)
emit('update:currentDate', _currentDate.value)
}
function prevMonth() {
let nowdate = new xDate(_currentDate.value)
nowdate.subtraction(1, 'm')
_currentDate.value = nowdate.format("YYYY-MM-DD")
_currentYear.value = nowdate.getYear()
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
emit('currentChange', _currentDate.value)
emit('update:currentDate', _currentDate.value)
}
function swiperChange(evt : UniSwiperChangeEvent) {
_currentDateSwipersIndex.value = evt.detail.current;
nextTick(() => {
_currentDate.value = _currentDateSwipers.value[evt.detail.current]
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
let nowdate = new xDate(_currentDate.value)
_currentYear.value = nowdate.getYear()
emit('currentChange', _currentDate.value)
emit('update:currentDate', _currentDate.value)
})
}
watch(() : string[] => props.modelValue, (newVal : string[]) => {
_modelValue.value = newVal.slice(0);
})
watch(() : string => props.currentDate, (newVal : string) => {
let nowdate = new xDate(newVal).setDateOf(1, 'd')
if (nowdate.format("YYYY-MM-DD") == _currentDate.value) return;
_currentDate.value = nowdate.format("YYYY-MM-DD")
_currentYear.value = nowdate.getYear()
_currentDateSwipersIndex.value = 0;
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
// console.log(_currentDate.value)
})
onMounted(() => {
if (props.modelValue.length > 0 && props.currentDate == '') {
let nowdate = new xDate(props.modelValue[0])
_currentDate.value = nowdate.format("YYYY-MM-DD")
_currentYear.value = nowdate.getYear()
}
nextTick(() => {
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
emit('update:currentDate', _currentDate.value)
})
})
function getmonth(item : string) : number {
let vls = item.split("-");
return parseInt(vls[1])
}
defineExpose({
/**
* 下月
*/
next() {
nextMonth();
},
/**
* 上月
*/
prev() {
prevMonth();
},
/**
* 设置日历返回到本月
*/
setCurrentMonth() {
nowMonth()
},
/**
* 清空选择
*/
clear() {
clear()
}
})
</script>
<template>
<view class="xCalendarView">
<!--
@slot 日历头,隐藏使用空插槽,将隐藏,如果想自定,请通过ref函数来翻页控制日历走向。
-->
<slot name="header">
<view class="xCalendarViewDataHeaderWrap"
:class="[showPanel?'xCalendarViewMonthOff':'xCalendarViewMonthOn']"
:style="[{backgroundColor:_headBgColor},headStyle]">
<view class="xCalendarViewHeader" style="padding: 0 12px">
<view @click="showPanel= !showPanel" class="xCalendarViewHeaderLeft">
<!-- 2024年5月 -->
<x-text :color="_headFontColor" font-size="21">{{_currentDateLabel}}</x-text>
<x-icon :color="_headFontColor" font-size="21" name="arrow-down-s-fill"></x-icon>
</view>
<view class="xCalendarViewHeaderRight">
<!-- <view @click="prevMonth" class="px-10 py-15">
<x-icon font-size="21" :color="_headFontColor" name="arrow-up-s-fill">上月</x-icon>
</view>
<view @click="nextMonth" class="px-10 py-15">
<x-icon font-size="21" :color="_headFontColor" name="arrow-down-s-fill">下月</x-icon>
</view> -->
<!-- 清空 -->
<x-text @click="clear" :color="_headFontColor"
style="padding: 10px 20px;">{{i18n.t('tmui4x.clear')}}</x-text>
<!-- 本月 -->
<x-text @click="nowMonth" :color="_headFontColor"
style="padding: 10px 0px;">{{i18n.t('tmui4x.calendar.currentMonthTitle')}}</x-text>
</view>
</view>
<view class="xCalendarViewDataHeader">
<view class="xCalendarViewDataHeaderItem" v-for="(item,index) in 7" :key="item">
<x-text :color="_headFontColor" font-size="12">{{weeksCn[index]}}</x-text>
</view>
</view>
</view>
</slot>
<view class="xCalendarViewSpace"></view>
<view :class="[showPanel?'xCalendarViewMonthOff':'xCalendarViewMonthOn']" class="xCalendarViewWrap">
<swiper v-if="_currentDateSwipers.length>0" :vertical="props.vertical" @change="swiperChange"
:current="_currentDateSwipersIndex" :circular="true" style="width:100%;height:100%">
<swiper-item v-for="(item,index) in _currentDateSwipers" :key="index" style="width:100%;height:100%;">
<view style="width:100%;height:100%;position: relative;">
<view class="xCalendarViewContentBox">
<calendar-multiple-uvue v-if="index == _currentDateSwipersIndex||!props.renderOnly"
@click="dateClick" :currentDate="item" :dateStatus="_dateStatus"
:seekDay="props.seekDay" :modelValue="props.modelValue" :model="props.model"
:multipleMax="props.multipleMax" :disabledDays="props.disabledDays"
:startDate="_startDate" :endDate="_endDate" :dateStyle="props.dateStyle"
:format="props.format" :color="props.color" :fontColor="props.fontColor"
:fontDarkColor="props.fontDarkColor" :activeFontColor="props.activeFontColor"
:rangColor="props.rangColor" :rangFontColor="props.rangFontColor">
</calendar-multiple-uvue>
</view>
<view class="xCalendarViewNum">
<text class="xCalendarViewNumText">{{getmonth(item)}}</text>
</view>
</view>
</swiper-item>
</swiper>
</view>
<view class="xCalendarViewSpace"></view>
<!--
@slot 日历尾部
-->
<slot name="footer">
<view class="xCalendarViewFooter">
<x-text color="#707070" font-size="14">{{_tipsText}}</x-text>
</view>
</slot>
<!-- 月和年 -->
<view :class="[showPanel?'xCalendarViewMonthOn':'xCalendarViewMonthOff']" class="xCalendarViewMonth"
:style="{backgroundColor:_monthBgColor}">
<view class="xCalendarViewHeader" style="padding: 0 12px">
<view @click="showPanel= !showPanel" class="xCalendarViewHeaderLeft">
<!-- 2024年5月 -->
<x-text :color="_color" font-size="21">{{_currentDateLabel}}</x-text>
<x-icon :color="_color" font-size="21" name="arrow-up-s-fill"></x-icon>
</view>
<view class="xCalendarViewHeaderRight">
<!-- #ifdef APP-ANDROID -->
<x-stepper @change="stepperChangeYear" v-model="(_currentYear as number)" :min="1900" :max="5000"
width="120"></x-stepper>
<!-- #endif -->
<!-- #ifndef APP-ANDROID -->
<x-stepper @change="stepperChangeYear" v-model="_currentYear" :min="1900" :max="5000"
width="120"></x-stepper>
<!-- #endif -->
</view>
</view>
<view class="xCalendarViewMonthWrap" style="flex:1">
<view @click="changeMonth(index)" class="xCalendarViewMonthItem" v-for="(item,index) in 12"
:key="index">
<!-- <x-text font-size="18">{{item}}月</x-text> -->
<x-text font-size="18">{{i18n.t('tmui4x.calendar.monthCountSelected',item)}}</x-text>
</view>
</view>
</view>
</view>
</template>
<style scoped lang="scss">
$headerHeight: 50px;
$weekheaderHeight: 40px;
$footerHeight: 40px;
$spaceHeight: 5px;
$cellHeight: 50px;
$minBodyHeight: (
50px * 6) + $headerHeight + $weekheaderHeight + $footerHeight + ($spaceHeight * 2
);
.xCalendarViewMonth {
position: absolute;
z-index: 3;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
transition-property: transform, opacity;
transition-timing-function: cubic-bezier(.42, .38, .15, .93);
transition-duration: 0.3s;
&.xCalendarViewMonthOff {
pointer-events: none;
// #ifdef APP-IOS
opacity: 0;
transform: scale(0, 0);
width: 0;
height: 0;
// #endif
// #ifndef APP-IOS
opacity: 0;
transform: scale(0, 0);
// #endif
}
&.xCalendarViewMonthOn {
pointer-events: auto;
opacity: 1;
transform: scale(1, 1);
}
.xCalendarViewMonthWrap {
display: flex;
flex-direction: row;
flex-wrap: wrap;
.xCalendarViewMonthItem {
width: 33.3333%;
height: 25%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
}
}
.xCalendarViewWrap {
transition-property: transform, opacity;
transition-timing-function: cubic-bezier(.42, .38, .15, .93);
transition-duration: 0.3s;
&.xCalendarViewMonthOff {
pointer-events: none;
opacity: 0;
// #ifndef APP-IOS
transform: scale(2, 2);
// #endif
}
&.xCalendarViewMonthOn {
pointer-events: auto;
opacity: 1;
// #ifndef APP-IOS
transform: scale(1, 1);
// #endif
}
position: relative;
width: 100%;
flex: 1;
.xCalendarViewContentBox {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
z-index: 3;
}
.xCalendarViewNum {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
.xCalendarViewNumText {
font-size: 200px;
color: rgba(125, 125, 125, 0.1);
font-weight: bold;
}
}
}
.xCalendarViewDataHeaderWrap {
transition-property: transform, opacity;
transition-timing-function: cubic-bezier(.42, .38, .15, .93);
transition-duration: 0.3s;
&.xCalendarViewMonthOff {
pointer-events: none;
opacity: 0;
}
&.xCalendarViewMonthOn {
pointer-events: auto;
opacity: 1;
}
}
.xCalendarViewSpace {
height: $spaceHeight;
}
.xCalendarViewHeader {
height: $headerHeight;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.xCalendarViewHeaderLeft {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.xCalendarViewHeaderRight {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
}
.xCalendarViewDataHeader {
height: $weekheaderHeight;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
.xCalendarViewDataHeaderItem {
width: 14.285%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
}
.xCalendarView {
position: relative;
display: flex;
flex-direction: column;
min-height: $minBodyHeight;
}
.xCalendarViewFooter {
height: $footerHeight;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
</style>
@@ -0,0 +1,243 @@
import { xDate,createDate } from "../../core/util/xDate.uts"
import { xDateDayInfoType, xCalendarDateStyle_type } from "../../interface.uts"
import { xDateArrayItemType,xCalendarArgs,dateStyleDot,dateStyleBg,dateStyleType,xCalendarMode } from "../x-calendar-view/interface.uts"
export class xCalendar {
date : xDate;
calendar:xDateArrayItemType[] = [];
constructor(currentDate : string | number | Date | null = null) {
this.date = new xDate(currentDate)
}
isInCurrentMonth(current:Date,target:Date):boolean{
let y1 = current.getFullYear()
let m1 = current.getMonth()
// let d1 = current.getDate()
let y2 = target.getFullYear()
let m2 = target.getMonth()
// let d2 = target.getDate()
return y1 == y2 && m1 == m2
}
isInRangeDate(current:Date,targets:Date[],mode:xCalendarMode):boolean{
let y1 = current.getFullYear()
let m1 = current.getMonth()
let d1 = current.getDate()
if(mode == 'day'){
for(let i=0;i<targets.length;i++){
let target = targets[i]
let y2 = target.getFullYear()
let m2 = target.getMonth()
let d2 = target.getDate()
if(y1 == y2 && m1 == m2 && d1 == d2){
return true
}
}
}else if(mode == 'range'){
if(targets.length<2) return false;
let start = targets[0]
let end = targets[targets.length-1]
return current.getTime()>start.getTime()&&current.getTime()<end.getTime()
}
return false
}
isInRangeDateByIndex(current:Date,targets:Date[],mode:xCalendarMode):number{
let y1 = current.getFullYear()
let m1 = current.getMonth()
let d1 = current.getDate()
if(mode == 'day'){
for(let i=0;i<targets.length;i++){
let target = targets[i]
let y2 = target.getFullYear()
let m2 = target.getMonth()
let d2 = target.getDate()
if(y1 == y2 && m1 == m2 && d1 == d2){
return i
}
}
}else if(mode == 'range'){
if(targets.length<2) return -1;
let start = targets[0]
let end = targets[targets.length-1]
if(current.getTime()>start.getTime()&&current.getTime()<end.getTime()) return -1
if(current.getTime()==start.getTime()) return 0
if(current.getTime()==end.getTime()) return 1
}
return -1
}
isDisabled(current:Date,start:Date|null,end:Date|null,targets:Date[]):boolean{
let y1 = current.getFullYear()
let m1 = current.getMonth()
let d1 = current.getDate()
let disabled = false
for(let i=0;i<targets.length;i++){
let target = targets[i]
let y2 = target.getFullYear()
let m2 = target.getMonth()
let d2 = target.getDate()
if(y1 == y2 && m1 == m2 && d1 == d2){
disabled = true;
break;
}
}
if(start!=null && end!=null){
if(start.getTime() <= end.getTime()){
// 正常的日期范围:start <= current <= end
disabled = disabled || (start.getTime() > current.getTime() || end.getTime() < current.getTime())
}
// 如果start > end,则忽略范围限制,只应用disabledDays
} else {
// 只有一个边界时,正常应用
if(start!=null){
disabled = disabled || start.getTime() > current.getTime()
}
if(end!=null){
disabled = disabled || end.getTime() < current.getTime()
}
}
return disabled
}
isInStart(current:Date,targets:Date[]):boolean{
if(targets.length==0) return false;
return targets[0].getTime() == current.getTime()
}
isInEnd(current:Date,targets:Date[]):boolean{
if(targets.length<2) return false;
return targets[targets.length-1].getTime() == current.getTime()
}
diffDays(start:Date,end:Date):number{
return end.getTime()-start.getTime()
}
getDateStyle(current:Date,defaultStyle:xCalendarArgs,disabled:boolean,inMonth:boolean,inRange:boolean,isInStart:boolean,isInEnd:boolean,dateStyle:xCalendarDateStyle_type[],mode:xCalendarMode):dateStyleType{
let nowdatestyleIndex = dateStyle.findIndex((d:xCalendarDateStyle_type):boolean => {
return new Date(d.date.replace(/-/g,'/')).getTime() == current.getTime()
})
let item:xCalendarDateStyle_type|null = nowdatestyleIndex==-1?null:dateStyle[nowdatestyleIndex]
const label = (item?.label??'') as string;
let fontColor = (item?.fontColor??defaultStyle.fontColor) as string;
fontColor = isInStart||isInEnd||(mode!='range'&&inRange) ? defaultStyle.activeFontColor:(inRange?defaultStyle.rangFontColor:fontColor)
let bgColor = (item?.color??'transparent') as string;
bgColor = isInStart||isInEnd||(mode!='range'&&inRange) ? defaultStyle.color : (inRange?defaultStyle.rangColor:bgColor)
const bgstyle = {
/** 底部文本 */
label : label,
/** 日期文字颜色 */
fontColor : fontColor,
backgroundColor : bgColor,
opacity : disabled||!inMonth?0.5:1
} as dateStyleBg
const dotstyle = {
/** 是否显示右角标 */
dot : item?.dot??false,
/** 右角标背景颜色 */
dotColor : item?.dotColor??defaultStyle.color,
/** 右角标文字颜色 */
dotLabelColor : item?.dotLabelColor??'#ffffff',
/** 注意如果dot为true,此内容为空就会显示小圆点。如果有内容优先显示本文本 */
dotLabel : item?.dotLabel??'',
} as dateStyleDot
return {
dot : dotstyle,
dstyle : bgstyle
} as dateStyleType
}
getCalendar(
seekDay:number,
mode:xCalendarMode,
currentDate : string | number | Date | null = null,
selectedDate:string[],
start:Date|null,
end:Date|null,
defaultStyle:xCalendarArgs,
dateStyle:xCalendarDateStyle_type[] = [],
disabledDays:string[] = [],
isPadding:boolean = true):xDateArrayItemType[]{
const nowCutime = Date.now()
let nowdate = (currentDate == null?this.date:new xDate(currentDate)) as xDate;
const dateAr = nowdate.getDaysOf('m')
let dates = [] as xDateDayInfoType[]
if(isPadding){
// 使用 seekDay 参数控制月份第一天的周偏移量
// seekDay: 0=周一, 1=周二, 2=周三, 3=周四, 4=周五, 5=周六, 6=周日
// 注意:week 值实际是 0=周日, 1=周一, 2=周二, 3=周三, 4=周四, 5=周五, 6=周六
// 需要转换为 0=周一, 1=周二, 2=周三, 3=周四, 4=周五, 5=周六, 6=周日的映射
let firstDayOfMonth = dateAr[0]
let firstDayWeek = firstDayOfMonth.week
// 将 week 值转换为 0=周一, 1=周二, ..., 6=周日的映射
// 原始:0=周日, 1=周一, 2=周二, 3=周三, 4=周四, 5=周五, 6=周六
// 目标:0=周一, 1=周二, 2=周三, 3=周四, 4=周五, 5=周六, 6=周日
let mappedWeek = (firstDayWeek + 6) % 7 // 将周日(0)映射为6,周一(1)映射为0
// 计算需要向前填充的天数
let beforeNum = 0
if (seekDay === 0) {
// 周一开始:mappedWeek 为 0 时不需要填充
beforeNum = mappedWeek
} else {
// 其他日期开始:计算到目标起始日的偏移量
beforeNum = (mappedWeek - seekDay + 7) % 7
}
// 如果 beforeNum 为 0,说明第一天正好是目标起始日,不需要填充
if (beforeNum > 0) {
const beforeDates = new xDate(firstDayOfMonth.date).getDaysOfNum(beforeNum,'before')
dates = [...beforeDates,...dateAr]
} else {
dates = [...dateAr]
}
if(dates.length<42){
//补齐最后一周的内容
const lastDate = new xDate(dates[dates.length-1].date);
let lastWeek = lastDate.getDaysOfNum(42-dates.length,'after')
dates = [...dates,...lastWeek]
}
}else{
dates = dateAr
}
let selectedTargets = selectedDate.map((d:string):Date =>{
return new Date(d.replace(/-/g,'/'))
})
let disabledDaysAs = disabledDays.map((d:string):Date =>{
return new Date(d.replace(/-/g,'/'))
})
const current = nowdate.date
const list = [] as xDateArrayItemType[]
for(let i=0;i<dates.length;i++){
let item = dates[i]
let checkDate = new Date(item.date);
const inmonth = this.isInCurrentMonth(checkDate,current);
const inRange = this.isInRangeDate(checkDate,selectedTargets,mode);
const disabled = this.isDisabled(checkDate,start,end,disabledDaysAs)
const isInstart = this.isInStart(checkDate,selectedTargets)
const isInEnd = this.isInEnd(checkDate,selectedTargets)
const astyle = this.getDateStyle(checkDate,defaultStyle,disabled,inmonth,inRange,isInstart,isInEnd,dateStyle,mode)
list.push({
date : item,
disabled : disabled,
inCurrentMonth : inmonth,
inRange : inRange,
isInstart : isInstart,
isInEnd : isInEnd,
style : astyle
})
}
// console.log(`执行组时间:${Date.now()-nowCutime}毫秒,循环数组:${dates.length}`)
return list
}
}