123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- <template>
- <view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper">
- <view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]"
- :ref="`u-calendar-month-${index}`" :id="`month-${index}`">
- <text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</text>
- <view class="u-calendar-month__days">
- <view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper">
- <text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text>
- </view>
- <view class="u-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1"
- :style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)"
- :class="[item1.selected && 'u-calendar-month__days__day__select--selected']">
- <view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]">
- <text class="u-calendar-month__days__day__select__info"
- :class="[item1.disabled && 'u-calendar-month__days__day__select__info--disabled']"
- :style="[textStyle(item1)]">{{ item1.day }}</text>
- <text v-if="getBottomInfo(index, index1, item1)"
- class="u-calendar-month__days__day__select__buttom-info"
- :class="[item1.disabled && 'u-calendar-month__days__day__select__buttom-info--disabled']"
- :style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text>
- <text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text>
- </view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
-
-
- const dom = uni.requireNativePlugin('dom')
-
- import dayjs from '../../libs/util/dayjs.js';
- export default {
- name: 'u-calendar-month',
- mixins: [uni.$u.mpMixin, uni.$u.mixin],
- props: {
-
- showMark: {
- type: Boolean,
- default: true
- },
-
- color: {
- type: String,
- default: '#3c9cff'
- },
-
- months: {
- type: Array,
- default: () => []
- },
-
- mode: {
- type: String,
- default: 'single'
- },
-
- rowHeight: {
- type: [String, Number],
- default: 58
- },
-
- maxCount: {
- type: [String, Number],
- default: Infinity
- },
-
- startText: {
- type: String,
- default: '开始'
- },
-
- endText: {
- type: String,
- default: '结束'
- },
-
- defaultDate: {
- type: [Array, String, Date],
- default: null
- },
-
- minDate: {
- type: [String, Number],
- default: 0
- },
-
- maxDate: {
- type: [String, Number],
- default: 0
- },
-
- maxMonth: {
- type: [String, Number],
- default: 2
- },
-
- readonly: {
- type: Boolean,
- default: uni.$u.props.calendar.readonly
- },
-
- maxRange: {
- type: [Number, String],
- default: Infinity
- },
-
- rangePrompt: {
- type: String,
- default: ''
- },
-
- showRangePrompt: {
- type: Boolean,
- default: true
- },
-
- allowSameDay: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
-
- width: 0,
-
- item: {},
- selected: []
- }
- },
- watch: {
- selectedChange: {
- immediate: true,
- handler(n) {
- this.setDefaultDate()
- }
- }
- },
- computed: {
-
- selectedChange() {
- return [this.minDate, this.maxDate, this.defaultDate]
- },
- dayStyle(index1, index2, item) {
- return (index1, index2, item) => {
- const style = {}
- let week = item.week
-
- const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1))
-
-
- style.width = uni.$u.addUnit(dayWidth)
-
- style.height = uni.$u.addUnit(this.rowHeight)
- if (index2 === 0) {
-
- week = (week === 0 ? 7 : week) - 1
-
- style.marginLeft = (week * dayWidth)+'px';
- }
- if (this.mode === 'range') {
-
- style.paddingLeft = 0
- style.paddingRight = 0
- style.paddingBottom = 0
- style.paddingTop = 0
- }
- return style
- }
- },
- daySelectStyle() {
- return (index1, index2, item) => {
- let date = dayjs(item.date).format("YYYY-MM-DD"),
- style = {}
-
- if (this.selected.some(item => this.dateSame(item, date))) {
- style.backgroundColor = this.color
- }
- if (this.mode === 'single') {
- if (date === this.selected[0]) {
-
- style.borderTopLeftRadius = '3px'
- style.borderBottomLeftRadius = '3px'
- style.borderTopRightRadius = '3px'
- style.borderBottomRightRadius = '3px'
- }
- } else if (this.mode === 'range') {
- if (this.selected.length >= 2) {
- const len = this.selected.length - 1
-
- if (this.dateSame(date, this.selected[0])) {
- style.borderTopLeftRadius = '3px'
- style.borderBottomLeftRadius = '3px'
- }
-
- if (this.dateSame(date, this.selected[len])) {
- style.borderTopRightRadius = '3px'
- style.borderBottomRightRadius = '3px'
- }
-
- if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
- .selected[len]))) {
- style.backgroundColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[90]
-
- style.opacity = 0.7
- }
- } else if (this.selected.length === 1) {
-
-
- style.borderTopLeftRadius = '3px'
- style.borderBottomLeftRadius = '3px'
- }
- } else {
- if (this.selected.some(item => this.dateSame(item, date))) {
- style.borderTopLeftRadius = '3px'
- style.borderBottomLeftRadius = '3px'
- style.borderTopRightRadius = '3px'
- style.borderBottomRightRadius = '3px'
- }
- }
- return style
- }
- },
-
- textStyle() {
- return (item) => {
- const date = dayjs(item.date).format("YYYY-MM-DD"),
- style = {}
-
- if (this.selected.some(item => this.dateSame(item, date))) {
- style.color = '#ffffff'
- }
- if (this.mode === 'range') {
- const len = this.selected.length - 1
-
- if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
- .selected[len]))) {
- style.color = this.color
- }
- }
- return style
- }
- },
-
- getBottomInfo() {
- return (index1, index2, item) => {
- const date = dayjs(item.date).format("YYYY-MM-DD")
- const bottomInfo = item.bottomInfo
-
- if (this.mode === 'range' && this.selected.length > 0) {
- if (this.selected.length === 1) {
-
- if (this.dateSame(date, this.selected[0])) return this.startText
- else return bottomInfo
- } else {
- const len = this.selected.length - 1
-
- if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) &&
- len === 1) {
-
- return `${this.startText}/${this.endText}`
- } else if (this.dateSame(date, this.selected[0])) {
- return this.startText
- } else if (this.dateSame(date, this.selected[len])) {
- return this.endText
- } else {
- return bottomInfo
- }
- }
- } else {
- return bottomInfo
- }
- }
- }
- },
- mounted() {
- this.init()
- },
- methods: {
- init() {
-
- this.$emit('monthSelected', this.selected)
- this.$nextTick(() => {
-
-
- uni.$u.sleep(10).then(() => {
- this.getWrapperWidth()
- this.getMonthRect()
- })
- })
- },
-
- dateSame(date1, date2) {
- return dayjs(date1).isSame(dayjs(date2))
- },
-
- getWrapperWidth() {
-
- dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => {
- this.width = res.size.width
- })
-
-
- this.$uGetRect('.u-calendar-month-wrapper').then(size => {
- this.width = size.width
- })
-
- },
- getMonthRect() {
-
- const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise(
- `u-calendar-month-${index}`))
-
- Promise.all(promiseAllArr).then(
- sizes => {
- let height = 1
- const topArr = []
- for (let i = 0; i < this.months.length; i++) {
-
- topArr[i] = height
- height += sizes[i].height
- }
-
- this.$emit('updateMonthTop', topArr)
- })
- },
-
- getMonthRectByPromise(el) {
-
-
-
- return new Promise(resolve => {
- this.$uGetRect(`.${el}`).then(size => {
- resolve(size)
- })
- })
-
-
-
-
- return new Promise(resolve => {
- dom.getComponentRect(this.$refs[el][0], res => {
- resolve(res.size)
- })
- })
-
- },
-
- clickHandler(index1, index2, item) {
- if (this.readonly) {
- return;
- }
- this.item = item
- const date = dayjs(item.date).format("YYYY-MM-DD")
- if (item.disabled) return
-
- let selected = uni.$u.deepClone(this.selected)
- if (this.mode === 'single') {
-
- selected = [date]
- } else if (this.mode === 'multiple') {
- if (selected.some(item => this.dateSame(item, date))) {
-
- const itemIndex = selected.findIndex(item => item === date)
- selected.splice(itemIndex, 1)
- } else {
-
- if (selected.length < this.maxCount) selected.push(date)
- }
- } else {
-
- if (selected.length === 0 || selected.length >= 2) {
-
- selected = [date]
- } else if (selected.length === 1) {
-
- const existsDate = selected[0]
-
- if (dayjs(date).isBefore(existsDate)) {
- selected = [date]
- } else if (dayjs(date).isAfter(existsDate)) {
-
- if (dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this
- .showRangePrompt) {
- if (this.rangePrompt) {
- uni.$u.toast(this.rangePrompt)
- } else {
- uni.$u.toast(`选择天数不能超过 ${this.maxRange} 天`)
- }
- return
- }
-
- selected.push(date)
- const startDate = selected[0]
- const endDate = selected[1]
- const arr = []
- let i = 0
- do {
-
- arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD"))
- i++
-
- } while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate)))
-
- arr.push(endDate)
- selected = arr
- } else {
-
- if (selected[0] === date && !this.allowSameDay) return
- selected.push(date)
- }
- }
- }
- this.setSelected(selected)
- },
-
- setDefaultDate() {
- if (!this.defaultDate) {
-
- const selected = [dayjs().format("YYYY-MM-DD")]
- return this.setSelected(selected, false)
- }
- let defaultDate = []
- const minDate = this.minDate || dayjs().format("YYYY-MM-DD")
- const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD")
- if (this.mode === 'single') {
-
- if (!uni.$u.test.array(this.defaultDate)) {
- defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")]
- } else {
- defaultDate = [this.defaultDate[0]]
- }
- } else {
-
- if (!uni.$u.test.array(this.defaultDate)) return
- defaultDate = this.defaultDate
- }
-
- defaultDate = defaultDate.filter(item => {
- return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs(
- maxDate).add(1, 'day'))
- })
- this.setSelected(defaultDate, false)
- },
- setSelected(selected, event = true) {
- this.selected = selected
- event && this.$emit('monthSelected', this.selected)
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- @import "../../libs/css/components.scss";
- .u-calendar-month-wrapper {
- margin-top: 4px;
- }
- .u-calendar-month {
- &__title {
- font-size: 14px;
- line-height: 42px;
- height: 42px;
- color: $u-main-color;
- text-align: center;
- font-weight: bold;
- }
- &__days {
- position: relative;
- @include flex;
- flex-wrap: wrap;
- &__month-mark-wrapper {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- @include flex;
- justify-content: center;
- align-items: center;
- &__text {
- font-size: 155px;
- color: rgba(231, 232, 234, 0.83);
- }
- }
- &__day {
- @include flex;
- padding: 2px;
-
-
- width: calc(100% / 7);
- box-sizing: border-box;
-
- &__select {
- flex: 1;
- @include flex;
- align-items: center;
- justify-content: center;
- position: relative;
- &__dot {
- width: 7px;
- height: 7px;
- border-radius: 100px;
- background-color: $u-error;
- position: absolute;
- top: 12px;
- right: 7px;
- }
- &__buttom-info {
- color: $u-content-color;
- text-align: center;
- position: absolute;
- bottom: 5px;
- font-size: 10px;
- text-align: center;
- left: 0;
- right: 0;
- &--selected {
- color: #ffffff;
- }
- &--disabled {
- color: #cacbcd;
- }
- }
- &__info {
- text-align: center;
- font-size: 16px;
- &--selected {
- color: #ffffff;
- }
- &--disabled {
- color: #cacbcd;
- }
- }
- &--selected {
- background-color: $u-primary;
- @include flex;
- justify-content: center;
- align-items: center;
- flex: 1;
- border-radius: 3px;
- }
- &--range-selected {
- opacity: 0.3;
- border-radius: 0;
- }
- &--range-start-selected {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- }
- &--range-end-selected {
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
- }
- }
- }
- }
- }
- </style>
|