1
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
<script lang="ts" setup>
|
||||
import { type PropType,getCurrentInstance } 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"
|
||||
const i18n = xConfig.i18n;
|
||||
// #ifdef APP
|
||||
import { calendarDraw } from "./calendarDraw"
|
||||
const proxy = getCurrentInstance()?.proxy;
|
||||
const xCalendarViewItemRef = ref<UniElement|null>(null)
|
||||
let calendarDom:calendarDraw|null = null;
|
||||
// #endif
|
||||
type xCalendarMultiplePropsType = {
|
||||
/**
|
||||
* 同步当前时间v-model
|
||||
* 不想受控:model-value
|
||||
*/
|
||||
modelValue : string,
|
||||
/**
|
||||
* 范围选择模式
|
||||
* day:天数多选,通过multipleMax可以设置允许选择的天数
|
||||
* range:天数的范围选择,起始和终止
|
||||
* week:按周次选择范围
|
||||
* quarter:按季度选择范围
|
||||
* year:按年
|
||||
*/
|
||||
model: xCalendarMode,
|
||||
|
||||
/**
|
||||
* 禁用的日期字符串如"2023-12-12"
|
||||
* 它与下面的start,end不冲突。
|
||||
*/
|
||||
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,
|
||||
/**
|
||||
* 给日期设定状态类型为:xCalendarDateStyleStatusProps
|
||||
*/
|
||||
dateStatus:xCalendarDateStyleStatusType[],
|
||||
}
|
||||
|
||||
const emit = defineEmits(['change','click'])
|
||||
const props = withDefaults(defineProps<xCalendarMultiplePropsType>(), {
|
||||
modelValue: "",
|
||||
currentDate:'',
|
||||
model:'day' as xCalendarMode,
|
||||
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 _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 _dateStatus = computed(() : xCalendarDateStyleStatusType[] => props.dateStatus)
|
||||
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;
|
||||
if(!calendar.isInCurrentMonth(new Date(item.date.date),new Date(props.currentDate))) 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,():any => _dateStatus.value],()=>{
|
||||
draw()
|
||||
})
|
||||
|
||||
// #endif
|
||||
onMounted(()=>{
|
||||
// #ifdef APP
|
||||
calendarDom = new calendarDraw(xCalendarViewItemRef.value,proxy)
|
||||
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,185 @@
|
||||
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;
|
||||
constructor(target : UniElement | null, proxyx : any | null) {
|
||||
this.ele = target
|
||||
this.proxy = proxyx
|
||||
}
|
||||
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 '本日'
|
||||
if (item.isInstart && !item.isInEnd && this.model == 'range') return '开始'
|
||||
if (!item.isInstart && item.isInEnd && this.model == 'range') return '结束'
|
||||
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 - 5;
|
||||
let lasty = _realCellWidth * (i) + 5
|
||||
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 - 10;
|
||||
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,99 @@
|
||||
import { xDateDayInfoType, xCalendarDateStyle_type } from "../../interface.uts"
|
||||
import { xDate } from "../../core/util/xDate.uts"
|
||||
export type xDateArrayItem = {
|
||||
date : xDateDayInfoType,
|
||||
//是否禁用
|
||||
isDisabled : boolean,
|
||||
//是否在当前月份
|
||||
isInCureentMonth : boolean,
|
||||
//是否已经选中
|
||||
isSelected : boolean,
|
||||
//样式
|
||||
style : dateStyleType
|
||||
}
|
||||
|
||||
export type xDateArrayItemType = {
|
||||
date : xDateDayInfoType,
|
||||
//是否禁用
|
||||
disabled : boolean,
|
||||
//是否在当前月份
|
||||
inCurrentMonth : boolean,
|
||||
//是否已经选中
|
||||
inRange : boolean,
|
||||
isInstart : boolean,
|
||||
isInEnd : boolean,
|
||||
//样式
|
||||
style : dateStyleType
|
||||
}
|
||||
|
||||
/**
|
||||
* 日历单个日期的样式对象
|
||||
*/
|
||||
export type xCalendarDateStyle_real_type = {
|
||||
/** 是否显示右角标 */
|
||||
dot : boolean,
|
||||
/** 右角标背景颜色 */
|
||||
dotColor : string,
|
||||
/** 右角标文字颜色 */
|
||||
dotLabelColor : string,
|
||||
/** 注意如果dot为true,此内容为空就会显示小圆点。如果有内容优先显示本文本 */
|
||||
dotLabel : string,
|
||||
/** 底部文本 */
|
||||
label : string,
|
||||
/** 背景颜色 */
|
||||
color : string,
|
||||
/** 日期文字颜色 */
|
||||
fontColor : string,
|
||||
date : string
|
||||
}
|
||||
export type dateStyleDot = {
|
||||
/** 是否显示右角标 */
|
||||
dot : boolean,
|
||||
/** 右角标背景颜色 */
|
||||
dotColor : string,
|
||||
/** 右角标文字颜色 */
|
||||
dotLabelColor : string,
|
||||
/** 注意如果dot为true,此内容为空就会显示小圆点。如果有内容优先显示本文本 */
|
||||
dotLabel : string,
|
||||
}
|
||||
export type dateStyleBg = {
|
||||
/** 底部文本 */
|
||||
label : string,
|
||||
/** 日期文字颜色 */
|
||||
fontColor : string,
|
||||
backgroundColor : string,
|
||||
opacity : number
|
||||
}
|
||||
export type dateStyleType = {
|
||||
dot : dateStyleDot,
|
||||
dstyle : dateStyleBg
|
||||
}
|
||||
export type xCalendarMode = "day" | "range" | "week" | "quarter" | "year"
|
||||
|
||||
export type BODY_SIZE_TYPE = {
|
||||
width : number,
|
||||
height : number
|
||||
}
|
||||
export type xCalendarArgs = {
|
||||
color:string,
|
||||
fontColor:string,
|
||||
activeFontColor:string,
|
||||
rangColor:string
|
||||
rangFontColor:string,
|
||||
}
|
||||
|
||||
export type xCalendarViewUpdateType = {
|
||||
ar : xDateArrayItem[],
|
||||
disabledDays : string[],
|
||||
selectedDate : xDate | null,
|
||||
nowDate : xDate,
|
||||
start : xDate,
|
||||
end : xDate,
|
||||
color : string,
|
||||
dateStyle : xCalendarDateStyle_real_type[]
|
||||
}
|
||||
|
||||
export type GRID_SIZE = {
|
||||
width : 0,
|
||||
height : 0
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
<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 } 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:固定此值
|
||||
*/
|
||||
model : "day" | "range" | "week" | "quarter" | "year",
|
||||
|
||||
/**
|
||||
* 禁用的日期字符串如"2023-12-12"
|
||||
* 它与下面的start,end不冲突。
|
||||
*/
|
||||
disabledDays : string[],
|
||||
/**
|
||||
* 是否禁用用户交互,相当于展示日期。
|
||||
*/
|
||||
disabled:boolean,
|
||||
/**
|
||||
* 是否上下切换日历
|
||||
*/
|
||||
vertical:boolean,
|
||||
/**
|
||||
* 允许选择的开始日期
|
||||
*/
|
||||
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,
|
||||
/**
|
||||
* 头的背景颜色,默认为透明
|
||||
*/
|
||||
headBgColor : string,
|
||||
/**
|
||||
* 头的文字颜色,提供了后暗黑失效会以这个为准。
|
||||
*/
|
||||
headFontColor : string,
|
||||
/**
|
||||
* 头部自定义样式。
|
||||
*/
|
||||
headStyle : string,
|
||||
/**
|
||||
* 循环渲染时,是否只渲染当前面板(如果你在pad等10年前的低端机上渲染日历有压力请打开此值为true)
|
||||
* 关闭后可以提升滑动体验。
|
||||
*/
|
||||
renderOnly:boolean,
|
||||
/**
|
||||
* 你当前的一周的第一天的索引值是几:0: 周一,1: 周二,2: 周三,3: 周四,4: 周五,5: 周六,6: 周日
|
||||
*/
|
||||
seekDay:number,
|
||||
/**
|
||||
* 给日期设定状态
|
||||
* 类型为:xCalendarDateStyleStatusType
|
||||
*/
|
||||
dateStatus:xCalendarDateStyleStatusType[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 日历 xCalendar
|
||||
* @page /pages/index/calendar-view
|
||||
* @category 表单组件
|
||||
* @description 日历面板,支持指定日期新式设置,角标,底部文本设置等暂不同时支持多选,因为不支持联合类型后期需要分开组件使用。
|
||||
* @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: "",
|
||||
model: 'day',
|
||||
disabledDays:():string[] => [] as string[],
|
||||
disabled:false,
|
||||
vertical:false,
|
||||
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: '',
|
||||
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.modelValue).format("YYYY-MM-DD"))
|
||||
const _currentDateSwipersIndex = ref(0)
|
||||
|
||||
const _currentDateSwipers = ref<string[]>([])
|
||||
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.modelValue).getYear())
|
||||
let _modelValueDate = computed(() : Date => {
|
||||
if(_modelValue.value=="") return new Date(_currentDate.value.replace(/-/g, '/'));
|
||||
return new Date(_modelValue.value.replace(/-/g, '/'))
|
||||
})
|
||||
const _tipsText = computed(() : string => {
|
||||
// return _modelValue.value!='' ? `已选择` : '未选择日期'
|
||||
return _modelValue.value!='' ? i18n.t('tmui4x.calendar.selectedStatus',2) : 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 isInselected = calendar.isInCurrente(new Date(item.date.date), _modelValueDate.value)
|
||||
|
||||
emit('click', item.date.date)
|
||||
if(props.disabled) return;
|
||||
|
||||
let dates = ''
|
||||
if(_modelValue.value==''){
|
||||
dates = item.date.date
|
||||
}else{
|
||||
dates = isInselected?'':item.date.date
|
||||
}
|
||||
const nowFormat = new xDate(dates).format(props.format)
|
||||
_modelValue.value = dates
|
||||
|
||||
emit('update:modelValue',dates==''?'':nowFormat)
|
||||
emit('change', dates==''?'':nowFormat)
|
||||
}
|
||||
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 == "") return;
|
||||
_modelValue.value = ""
|
||||
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 = props.modelValue;
|
||||
if(newVal!=""){
|
||||
let nowdate = new xDate(newVal).setDateOf(1,'d')
|
||||
let nowcurrentDate = nowdate
|
||||
if(nowcurrentDate.format("YYYY-MM-DD") == _currentDate.value) return;
|
||||
_currentDate.value = nowcurrentDate.format("YYYY-MM-DD")
|
||||
_currentYear.value = nowcurrentDate.getYear()
|
||||
_currentDateSwipersIndex.value = 0;
|
||||
_currentDateSwipers.value = getSwiperListCurrentDates(_currentDate.value);
|
||||
}
|
||||
|
||||
})
|
||||
onMounted(() => {
|
||||
if (props.modelValue !="") {
|
||||
let nowdate = new xDate(props.modelValue)
|
||||
_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">
|
||||
<!-- {{weeksCn[index]}} -->
|
||||
<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 :vertical="props.vertical" @change="swiperChange" :current="_currentDateSwipersIndex" v-if="_currentDateSwipers.length>0" :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">
|
||||
<!-- 打开下方,可以在更低端机上提高2/3的性能 -->
|
||||
<calendar-multiple-uvue v-if="index == _currentDateSwipersIndex||!props.renderOnly" @click="dateClick" :currentDate="item"
|
||||
:dateStatus="_dateStatus"
|
||||
:seekDay="props.seekDay"
|
||||
:modelValue="props.modelValue" :model="props.model"
|
||||
:disabledDays="props.disabledDays" :startDate="props.startDate" :endDate="props.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">
|
||||
<x-stepper @change="stepperChangeYear" v-model="(_currentYear as number)" :min="1900" :max="5000"
|
||||
width="120"></x-stepper>
|
||||
</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,237 @@
|
||||
import { xDate } 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
|
||||
}
|
||||
isInpanel(current:Date|string):boolean{
|
||||
console.log(this.calendar)
|
||||
if(this.calendar.length==0) return false;
|
||||
let s = new xDate(this.calendar[0].date.date);
|
||||
let e = new xDate(this.calendar[this.calendar.length-1].date.date);
|
||||
let c = new xDate(current);
|
||||
|
||||
return c.getTime('d')<=e.getTime('d')&&c.getTime('d')>=s.getTime('d')
|
||||
}
|
||||
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()&¤t.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()&¤t.getTime()<end.getTime()) return -1
|
||||
if(current.getTime()==start.getTime()) return 0
|
||||
if(current.getTime()==end.getTime()) return 1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
isInCurrente(current:Date,targets:Date):boolean{
|
||||
return targets.getTime() == current.getTime()
|
||||
}
|
||||
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()){
|
||||
disabled = disabled || (start.getTime() > current.getTime() || end.getTime() < current.getTime())
|
||||
}
|
||||
} 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 = inRange ? defaultStyle.activeFontColor:(inRange?defaultStyle.rangFontColor:fontColor)
|
||||
let bgColor = (item?.color??'transparent') as string;
|
||||
bgColor = 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){
|
||||
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=周日
|
||||
// 将周日(0)映射为6,周一(1)映射为0
|
||||
let mappedWeek = (firstDayWeek + 6) % 7
|
||||
// 计算需要向前填充的天数
|
||||
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]
|
||||
}
|
||||
// 补齐到完整的6周(42天)
|
||||
if(dates.length < 42){
|
||||
//补齐最后一周的内容
|
||||
let lastWeek = new xDate(dates[dates.length-1].date).getDaysOfNum(42-dates.length,'after')
|
||||
dates = [...dates,...lastWeek]
|
||||
}
|
||||
}else{
|
||||
dates = dateAr
|
||||
}
|
||||
|
||||
let selectedTargets = selectedDate==""?new Date():new Date(selectedDate.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.isInCurrente(checkDate,selectedTargets);
|
||||
const disabled = this.isDisabled(checkDate,start,end,disabledDaysAs)
|
||||
const isInstart = false
|
||||
const isInEnd = false
|
||||
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
|
||||
})
|
||||
|
||||
}
|
||||
this.calendar = list;
|
||||
return list
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user