This commit is contained in:
2025-08-12 15:04:01 +08:00
parent df3f5f5269
commit 6708fb8eeb
544 changed files with 75169 additions and 17 deletions
@@ -0,0 +1,122 @@
export default {
props: {
// #ifdef VUE2
value: {
type: Number,
default: () => uni.$u.props.dateStrip.value,
},
// #endif
// #ifdef VUE3
modelValue: {
type: Number,
default: () => uni.$u.props.dateStrip.modelValue,
},
// #endif
// 默认选中的日期
defaultDate: {
type: [String, Number],
default: () => uni.$u.props.dateStrip.defaultDate,
},
// 切换模式:none 平铺展示所有日期,不展示切换按钮,week 按周方式切换
mode: {
type: String,
default: () => uni.$u.props.dateStrip.mode,
},
// 可选择的最小日期
minDate: {
type: Number,
default: () => uni.$u.props.dateStrip.minDate,
},
// 可选择的最大日期
maxDate: {
type: Number,
default: () => uni.$u.props.dateStrip.maxDate,
},
// 插件高度
height: {
type: String,
default: () => uni.$u.props.dateStrip.height,
},
// 每格日期宽度,
itemWidth: {
type: String,
default: () => uni.$u.props.dateStrip.itemWidth,
},
// 每格日期圆角,
itemRound: {
type: String,
default: () => uni.$u.props.dateStrip.itemRound,
},
// 选中框背景色
activeBgColor: {
type: String,
default: () => uni.$u.props.dateStrip.activeBgColor,
},
// 选中框文本色
activeColor: {
type: String,
default: () => uni.$u.props.dateStrip.activeColor,
},
// 选中框样式
activeStyle: {
type: Object,
default: () => uni.$u.props.dateStrip.activeStyle,
},
// 横条背景色
bgColor: {
type: String,
default: () => uni.$u.props.dateStrip.bgColor,
},
// 选中框圆角
round: {
type: String,
default: () => uni.$u.props.dateStrip.round,
},
// 第一天从星期几开始,默认 0 = 周日
firstDayOfWeek: {
type: Number,
default: () => uni.$u.props.dateStrip.firstDayOfWeek,
},
// 高亮模式:'both' 同时高亮星期和日期,'date' 只高亮日期,'text' 只高亮文本
activeMode: {
type: String,
default: () => uni.$u.props.dateStrip.activeMode,
},
// 日期格式化函数
formatter: {
type: [Function, null],
default: null,
},
// 最多展示月份数量
monthNum: {
type: [Number, String],
default: ()=> uni.$u.props.dateStrip.monthNum
},
// 禁止选择的日期函数
disabledFun: {
type: [Function, null],
default: null
},
// 禁止选择的日期
disabledDate: {
type: [Array, String, null],
default: ()=> uni.$u.props.dateStrip.disabledDate
},
// 禁用日期的文字颜色
disabledColor: {
type: String,
default: ()=> uni.$u.props.dateStrip.disabledColor
},
// 是否显示农历
showLunar: {
type: Boolean,
default: ()=> uni.$u.props.dateStrip.showLunar
},
// 是否对小于10的数字补0
padZero: {
type: Boolean,
default: ()=> uni.$u.props.dateStrip.padZero
}
},
};
@@ -0,0 +1,465 @@
<template>
<view class="u-date-strip" :style="[containerStyle]">
<!-- 周模式 - swiper切换 -->
<template v-if="mode === 'week'">
<swiper
class="u-date-strip__swiper"
:current="currentIndex"
@change="onSwiperChange"
>
<swiper-item v-for="(week, index) in weekList" :key="index">
<view class="u-date-strip__wrapper">
<view
v-for="(item, idx) in week"
:key="idx"
class="u-date-strip__item"
>
<view class="u-date-strip__item-content" :style="[itemStyle('week',item)]" @click="selectDate(item)">
<view class="u-date-strip__weekday">
{{ item.weekday }}
</view>
<view class="u-date-strip__date" :style="[itemStyle('date',item)]">
<text class="u-date-strip__date-text">{{ item.date }}</text>
<text v-if="showLunar && item.lunar" class="u-date-strip__lunar" :style="[itemStyle('lunar',item)]">{{ item.lunar }}</text>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</template>
<!-- 平铺模式 - scroll-view -->
<template v-else>
<scroll-view
class="u-date-strip__scroll"
scroll-x
:scroll-into-view="scrollIntoView"
>
<view class="u-date-strip__scroll-wrapper">
<view
v-for="(item, index) in allDays"
:key="index"
:id="`item-${index}`"
class="u-date-strip__item"
>
<view class="u-date-strip__item-content" :style="[itemStyle('week',item)]" @click="selectDate(item)">
<text class="u-date-strip__weekday" :style="[itemStyle('weekday',item)]">
{{ item.weekday }}
</text>
<view class="u-date-strip__date" :style="[itemStyle('date',item)]">
<text class="u-date-strip__date-text">{{ item.date }}</text>
<text v-if="item.bottomInfo" class="u-date-strip__lunar" :style="[itemStyle('lunar',item)]">{{ item.bottomInfo }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</template>
</view>
</template>
<script>
import props from './props.js';
import mixin from '../../libs/mixin/mixin'
import mpMixin from '../../libs/mixin/mpMixin';
import dayjs from '../../libs/util/dayjs.js';
import Calendar from '../../libs/util/calendar.js';
/**
* dateStrip 日期横条
* @description 用于展示周日历或一组日历信息
* @tutorial https://uviewui.com/components/dateStrip.html
*
* @property {Number} v-model/modelValue 选中的日期时间戳
* @property {Number} defaultDate 默认选中的日期时间戳
* @property {String} mode 切换模式 week|none (默认 'week' )
* @property {String} activeMode 高亮模式 both|date|text (默认 'both' )
* @property {Number} minDate 可选择的最小日期时间戳
* @property {Number} maxDate 可选择的最大日期时间戳
* @property {String} height 组件高度 (默认 '86px' )
* @property {String} itemWidth 每格日期宽度 (默认 '50px' )
* @property {String} itemRound 每格日期圆角 (默认 '6px' )
* @property {String} activeBgColor 选中框背景色
* @property {String} activeColor 选中框文本色
* @property {String} bgColor 横条背景色
* @property {String} round 选中框圆角
* @property {Number} firstDayOfWeek 第一天从星期几开始 0-6 (默认 0 )
* @property {Number} monthNum 最多展示月份数量 (默认 3 )
* @property {Array|String} disabledDate 禁止选择的日期
* @property {Function} disabledFun 禁止选择的日期函数
* @property {String} disabledColor 禁用日期的文字颜色
* @property {Boolean} showLunar 是否显示农历
* @property {Boolean} padZero 是否对小于10的数字补0
* @property {Function} formatter 日期格式化函数
* @property {Object} customStyle 定义需要用到的外部样式
* @event {Function} change 点击日期时触发
* @example <u-date-strip v-model="date" @change="onChange"></u-date-strip>
*/
export default {
name: 'u-date-strip',
mixins: [mpMixin, mixin, props],
data() {
return {
currentIndex: 0,
weekList: [],
allDays: [],
scrollIntoView: '',
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
innerSelected: null, // 内部选中的时间戳
innerFormatter: (value) => value
};
},
computed: {
// 当前选中的值
currentValue() {
// #ifdef VUE2
return this.value || this.defaultDate;
// #endif
// #ifdef VUE3
return this.modelValue || this.defaultDate;
// #endif
},
// 容器样式
containerStyle() {
const style = {};
if (this.bgColor) {
style.backgroundColor = this.bgColor;
}
if (this.height) {
style.height = this.height;
}
if(this.round){
style.borderRadius = this.round;
}
return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle));
},
// 获取日期数字样式的计算属性
itemStyle() {
return (type, item) => {
let style = {};
if(type == 'week'){
style.width = this.itemWidth;
}
// 禁用日期的样式
if (item.disabled) {
style.color = this.disabledColor;
style.pointerEvents = 'none';
}
if (this.isSameDay(dayjs(this.innerSelected), dayjs(item.timestamp))) {
style.borderRadius = this.itemRound;
if(type == 'week'){
// 整个容器的高亮样式
if (this.activeMode == 'both') {
style.backgroundColor = this.activeBgColor;
style.color = this.activeColor;
style = uni.$u.deepMerge(style, uni.$u.addStyle(this.activeStyle))
}else if(this.activeMode == 'text'){
style.color = this.activeBgColor;
}
}
if(type == 'date'){
if (this.activeMode == 'date') {
//只高亮日期部分
style.backgroundColor = this.activeBgColor;
style.color = this.activeColor;
style = uni.$u.deepMerge(style, uni.$u.addStyle(this.activeStyle))
} else if (this.activeMode == 'both') {
// 只高亮文字颜色
style.color = this.activeColor;
}else if(this.activeMode == 'text'){
style.color = this.activeBgColor;
}
}
if(type == 'lunar'){
style.color = this.activeColor;
}
}
return style;
};
},
},
watch: {
currentValue: {
immediate: true,
handler(newVal) {
this.innerSelected = newVal;
},
}
},
mounted() {
this.$nextTick(() => {
this.init();
});
},
methods: {
// 初始化
init() {
if (this.mode === 'week') {
this.initWeekMode();
} else {
this.initScrollMode();
}
},
// 获取日期范围
getDateRange() {
const monthNum = parseInt(this.monthNum);
const minDate = this.minDate ? dayjs(this.minDate) : dayjs().subtract(monthNum, 'month');
const maxDate = this.maxDate ? dayjs(this.maxDate) : dayjs().add(monthNum, 'month');
return { minDate, maxDate };
},
// 初始化周模式
initWeekMode() {
this.weekList = [];
const current = dayjs(this.currentValue);
const currentWeekStart = this.getWeekStart(current);
const { minDate, maxDate } = this.getDateRange();
// 计算需要生成的周数
const startWeek = this.getWeekStart(minDate);
const endWeek = this.getWeekStart(maxDate);
const totalWeeks = endWeek.diff(startWeek, 'week') + 1;
// 生成指定范围内的周数据
for (let i = 0; i < totalWeeks; i++) {
const weekStart = startWeek.add(i, 'week');
const week = this.generateWeek(weekStart);
this.weekList.push(week);
}
// 设置当前周的索引
const currentWeekIndex = currentWeekStart.diff(startWeek, 'week');
this.currentIndex = Math.max(0, currentWeekIndex + 1);
},
// 初始化滚动模式
initScrollMode() {
this.allDays = [];
const { minDate, maxDate } = this.getDateRange();
let currentDate = minDate;
let index = 0;
let selectedIndex = -1;
while (currentDate.isBefore(maxDate) || currentDate.isSame(maxDate, 'day')) {
const day = this.generateDay(currentDate);
this.allDays.push(day);
// 记录选中日期的索引
if (this.isSameDay(currentDate, dayjs(this.currentValue))) {
selectedIndex = index;
}
currentDate = currentDate.add(1, 'day');
index++;
}
// 滚动到选中的日期
if (selectedIndex >= 0) {
this.$nextTick(() => {
this.scrollIntoView = `item-${selectedIndex}`;
});
}
},
// 获取周的开始日期
getWeekStart(date) {
const day = date.day();
const diff = (day - this.firstDayOfWeek + 7) % 7;
return date.subtract(diff, 'day');
},
// 生成一周的数据
generateWeek(weekStart) {
const week = [];
for (let i = 0; i < 7; i++) {
const date = weekStart.add(i, 'day');
week.push(this.generateDay(date));
}
return week;
},
// 生成单天数据
generateDay(date) {
const day = {
date: this.padZero && date.date() < 10 ? `0${date.date()}` : date.date(),
weekday: this.weekdays[date.day()],
timestamp: date.valueOf(),
disabled: false,
bottomInfo: '',
};
// 计算农历
if (this.showLunar) {
const lunar = Calendar.solar2lunar(
date.year(),
date.month() + 1,
date.date(),
);
day.bottomInfo = lunar.IDayCn;
}
// 检查是否在可选范围内
if (this.minDate && date.isBefore(this.minDate)) {
day.disabled = true;
}
if (this.maxDate && date.isAfter(this.maxDate)) {
day.disabled = true;
}
// 检查是否在禁用日期列表中
if (this.disabledDate) {
if (uni.$u.test.string(this.disabledDate)) {
this.disabledDate = [
this.disabledDate,
];
}
this.disabledDate.forEach((item) => {
if (this.isSameDay(dayjs(item), date)) {
day.disabled = true;
}
});
}
if (this.disabledFun && uni.$u.test.func(this.disabledFun)) {
let result = this.disabledFun(day);
if (uni.$u.test.array(result)) {
day.disabled = result[0];
day.bottomInfo = result[1];
} else {
day.disabled = result;
}
}
// 应用格式化函数
const formatter = this.formatter || this.innerFormatter;
return formatter(day);
},
// 判断是否是同一天
isSameDay(date1, date2) {
return date1.isSame(date2, 'day');
},
// 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
setFormatter(e) {
this.innerFormatter = e
},
// 选择日期
selectDate(item) {
if (item.disabled) return;
this.innerSelected = item.timestamp;
// #ifdef VUE2
this.$emit('input', item.timestamp);
// #endif
// #ifdef VUE3
this.$emit('update:modelValue', item.timestamp);
// #endif
this.$emit('change', {
weekday: item.weekday,
date: item.date,
timestamp: item.timestamp,
lunar: item.lunar
});
},
onSwiperChange(e) {
this.currentIndex = e.detail.current;
},
},
};
</script>
<style lang="scss" scoped>
@import '../../libs/css/components.scss';
.u-date-strip {
padding: 8px 0;
&__swiper {
width: 100%;
height: 100%;
}
&__scroll {
width: 100%;
height: 100%;
white-space: nowrap;
&-wrapper {
@include flex(row);
align-items: center;
height: 100%;
}
}
&__wrapper {
@include flex(row);
align-items: center;
box-sizing: border-box;
height: 100%;
}
&__item {
flex: 1;
height: 100%;
@include flex(column);
align-items: center;
justify-content: center;
&-content{
@include flex(column);
align-items: center;
flex-shrink: 0;
box-sizing: border-box;
padding: 5px 0;
height: 100%;
}
}
&__weekday {
font-size: 14px;
padding-bottom: 5px;
}
&__date {
flex: 1;
@include flex(column);
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 100%;
&-text{
font-size: 16px;
font-weight: bold;
text-align: center;
}
}
&__lunar {
font-size: 12px;
color: #909399;
text-align: center;
margin-bottom: 5px;
}
}
</style>