guifei zhu
2024-11-27 6017f46b762663b9393cdae8422e0de1ed3db218
添加mqtt数据接口
已修改14个文件
已重命名1个文件
已添加28个文件
6225 ■■■■■ 文件已修改
App.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
colorui/components/cu-custom.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
colorui/main.css 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/constant.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/loadshget.js 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/drop-down-menu/dy-Date/dy-Date.vue 427 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/drop-down-menu/dy-Date/用法示例.vue 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/drop-down-menu/index.vue 299 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/drop-down-menu/readme.txt 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dryuniapp.keystore 补丁 | 查看 | 原始文档 | blame | 历史
lib/mqttTool.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.js 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manifest.json 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mqtt/MqttView.vue 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mqtt/mqttTool.js 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/login.vue 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/me/analy.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/device.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/general.vue 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/me.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/warning.vue 755 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
static/image/pic_gz.jpg 补丁 | 查看 | 原始文档 | blame | 历史
static/image/pic_gzj.jpg 补丁 | 查看 | 原始文档 | blame | 历史
static/push/push_small_18.png 补丁 | 查看 | 原始文档 | blame | 历史
static/push/push_small_24.png 补丁 | 查看 | 原始文档 | blame | 历史
static/push/push_small_36.png 补丁 | 查看 | 原始文档 | blame | 历史
static/push/push_small_48.png 补丁 | 查看 | 原始文档 | blame | 历史
static/push/push_small_72.png 补丁 | 查看 | 原始文档 | blame | 历史
store/index.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/changelog.md 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue 947 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue 940 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue 1064 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js 421 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/package.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-datetime-picker/readme.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App.vue
@@ -1,10 +1,46 @@
<script>
    import Vue from 'vue'
    import Vue from 'vue'
    export default {
        globalData: {
            token: ""
        },
        onLaunch: function() {
            //此处为unipush2.0的推送方法
            // uni-app客户端获取push客户端标记
            // uni.getPushClientId({
            //     success: (res) => {
            //         let push_clientid = res.cid
            //         console.log('客户端推送标识:', push_clientid)
            //     },
            //     fail(err) {
            //         console.log(err)
            //     }
            // })
            // uni.onPushMessage((res) => {
            //     console.log("收到推送消息:", res) //监听推送消息
            // })
            //推送仅支持Android ios原生平台
            // #ifdef APP-PLUS
            console.log('APP-PLUS:')
            // æ­¤å¤„为unipush1.0的推送方法
            plus.push.getClientInfoAsync((info) => {
                let cid = info["clientid"];
                console.log('客户端推送标识:', cid)
                this.$store.dispatch('setCid', cid); // ä½¿ç”¨ action æ›´æ–° cid
            });
            // #endif
            uni.onPushMessage((res) => {
                console.log("收到推送消息:", res) //监听推送消息
            })
            uni.getSystemInfo({
                success: function(e) {
                    // #ifndef MP
@@ -32,42 +68,42 @@
            console.log('App Launch')
        },
        onShow: function() {
             console.log('App Show')
             // this.conn()
            console.log('App Show')
            // this.conn()
        },
        onHide: function() {
             console.log('App Hide')
             // this.disconn()
        },
        methods:{
            //处理掉线重连
            conn(){
                console.log(this.$mqttTool.client)
                if(this.$mqttTool.client){
                    console.log(this.$mqttTool.client.connected)
                }
                if(this.$mqttTool.client && !this.$mqttTool.client.connected){
                    console.info("开始重连")
                    this.$mqttTool.reconnect()
                }else {
                }
            },
            disconn(){
                console.log(this.$mqttTool.client)
                if(this.$mqttTool.client){
                    console.log(this.$mqttTool.client.connected)
                }
                // if(this.$mqttTool.client){
                //     this.$mqttTool.end().then(res =>{
                //         console.log('终止:')
                //     })
                // }
            }
            console.log('App Hide')
            // this.disconn()
        },
        methods: {
            //处理掉线重连
            conn() {
                console.log(this.$mqttTool.client)
                if (this.$mqttTool.client) {
                    console.log(this.$mqttTool.client.connected)
                }
                if (this.$mqttTool.client && !this.$mqttTool.client.connected) {
                    console.info("开始重连")
                    this.$mqttTool.reconnect()
                } else {
                }
            },
            disconn() {
                console.log(this.$mqttTool.client)
                if (this.$mqttTool.client) {
                    console.log(this.$mqttTool.client.connected)
                }
                // if(this.$mqttTool.client){
                //     this.$mqttTool.end().then(res =>{
                //         console.log('终止:')
                //     })
                // }
            }
        },
        mounted() {
@@ -80,4 +116,4 @@
    @import "colorui/main.css";
    @import "colorui/icon.css";
    @import "uview-ui/index.scss";
</style>
</style>
colorui/components/cu-custom.vue
@@ -9,7 +9,11 @@
                <view class="content" :style="[{top:StatusBar + 'px'}]">
                    <slot name="content"></slot>
                </view>
                <slot name="right"></slot>
                <!-- <slot name="right"></slot> -->
                <view class="right" @tap="HandleRight" v-if="isRight">
                    <text class="cuIcon-discover"></text>
                    <slot name="rihjtText"></slot>
                </view>
            </view>
        </view>
    </view>
@@ -44,6 +48,10 @@
            isBack: {
                type: [Boolean, String],
                default: false
            },
            isRight: {
                type: [Boolean, String],
                default: false
            },
            bgImage: {
                type: String,
@@ -55,6 +63,9 @@
                uni.navigateBack({
                    delta: 1
                });
            },
            HandleRight(){
                 this.$emit('rightclick'); // è§¦å‘自定义事件
            }
        }
    }
colorui/main.css
@@ -1494,7 +1494,12 @@
    height: 100%;
    justify-content: center;
    max-width: 100%;
}
}
    .cu-bar .right {
        position: absolute;
         right: 30upx;
         font-size: 36upx;
    }
.cu-bar .action.border-title {
    position: relative;
common/constant.js
@@ -22,6 +22,22 @@
const DEVICE_ID = "device_id"
/***********************服务端广播********************************/
//广播不在乎客户端id,向有所在线客户端发送
//广播前缀
const SERVICE_BROADCAST = "service/broadcast"
//  //服务端向各租户客户端发送实时故障广播
const SERVICE_BROADCAST_TENANT_REAL_FAULT = SERVICE_BROADCAST + "/real/fault/%s"
/***********************EventBus function********************************/
 const  MQTT_TOPIC_MESSAGE = "mqtt_topic_message"
export default {
    MOBILE_QUERY_EQU_STATU,
    SERVICE_RES_EQU_STATU,
@@ -29,7 +45,9 @@
    MOBILE_REQ_EQU_CMD,
    SERVICE_RES_EQU_CMD,
    SERVICE_BROADCAST_TENANT_REAL_FAULT,
    MQTT_TOPIC_MESSAGE,
    DEVICE_ID
}
common/loadshget.js
@@ -1,6 +1,8 @@
import get from 'lodash.get'
export default function lget(data, item) {
export default function lget(data, item) {
    if(!data || data == null) return "--";
    if(data & (!item || item == null)) return data;
    let res  =  get(data, item)
    return res == null ? "--" :res 
components/drop-down-menu/dy-Date/dy-Date.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,427 @@
<template>
    <view>
        <picker :title="index" @change="bindTimeChange" @columnchange="columnchange" mode="multiSelector" :disabled="disabled" :class="{disabled:disabled}"
         :value="index" :range="array">
            <view :style="{'line-height':inputHeight+'rpx','font-size':inputFont+'rpx'}" v-if="showTime">{{showTime}}
            </view>
            <view v-else class="placeholder" :style="{'line-height':inputHeight+'rpx','font-size':inputFont+'rpx'}">{{placeholder}}</view>
        </picker>
    </view>
    <!-- åªè¯» -->
</template>
<script>
    export default {
        name: 'dy-Date-Picker',
        props: {
            timeType: {
                type: String,
                default: () => 'day'
            },
            // è¾“入框高度/行高
            inputHeight:{
                type: Number,
                default:56
            },
            // è¾“入框高度/提示语字体大小
            inputFont:{
                type: Number,
                default:24
            },
            disabled: {
                type: Boolean,
                default: () => false
            },
            // æ˜¯å¦æ˜¾ç¤ºå›¾æ ‡
            iconshow: {
                type: Boolean,
                default: () => true
            },
            placeholder: {
                type: String,
                default () {
                    return '请选择'
                }
            },
            childValue: {
                default () {
                    return ''
                }
            },
            minSelect: {
                type: String,
                default: () => '1900/01/01'
            },
            maxSelect: {
                type: String,
                default: () => '2050/12/31'
            }
        },
        data() {
            return {
                index: [0],
                array: [],
                yearArr: [], // å¹´ä»½æ•°ç»„
                monthArr: [], // æœˆä»½æ•°ç»„
                yearIndex: 0, // å¹´ä»½é€‰ä¸­ä¸‹æ ‡
                showTime: this.moment(this.childValue)
            }
        },
        methods: {
            clear(){
                this.showTime = ''
                this.$emit('getData', '')
            },
            moment(strTime) {
                let type = this.timeType
                if (!strTime) {
                    return
                }
                let time = new Date(strTime)
                let y = time.getFullYear()
                let m = time.getMonth() + 1
                m = m < 10 ? `0${m}` : m
                let d = time.getDate()
                d = d < 10 ? `0${d}` : d
                let hh = time.getHours()
                hh = hh < 10 ? `0${hh}` : hh
                let mm = time.getMinutes()
                mm = mm < 10 ? `0${mm}` : mm
                let ss = time.getSeconds()
                ss = ss < 10 ? `0${ss}` : ss
                let value = `${y}/${m}/${d} ${hh}:${mm}:${ss}`
                if (type === 'year') {
                    value = `${y}`
                }
                if (type === 'month') {
                    value = `${y}-${m}`
                }
                if (type === 'day') {
                    value = `${y}-${m}-${d}`
                }
                return value
            },
            /**
             * [setDefaultValue è®¾ç½®é»˜è®¤å€¼]
             */
            setDefaultValue() {
                let date = this.moment(new Date().getTime())
                this.valueEchoed(date)
            },
            bindDateChange(e) {
                this.childValue = e.target.value
            },
            dateInit() {
                this.array = []
                this.yearArr = []
                this.monthArr = []
                let minDate = this.moment(this.minSelect) || []
                let maxDate = this.moment(this.maxSelect) || []
                minDate = minDate ? minDate.split(' ') : ''
                maxDate = maxDate ? maxDate.split(' ') : ''
                minDate = minDate[0] ? minDate[0].split('-') : 1900
                maxDate = maxDate[0] ? maxDate[0].split('-') : 2050
                let type = this.timeType
                let {
                    monthStar,
                    monthEnd
                } = ''
                let yearStar = minDate[0] ? parseInt(minDate[0]) : 1900
                let yearEnd = maxDate[0] ? parseInt(maxDate[0]) : 2050
                if (type === 'day') {
                    let dateStar = minDate[2] ? parseInt(minDate[2]) : 1
                    let dateEnd = maxDate[2] ? parseInt(maxDate[2]) : 31
                }
                if (type === 'month' || type === 'day') {
                    monthStar = minDate[1] ? parseInt(minDate[1]) : 1
                    monthEnd = maxDate[1] ? parseInt(maxDate[1]) : 12
                }
                for (let y = yearStar; y <= yearEnd; y++) {
                    let ytext = y < 10 ? `0${y}` : y
                    this.yearArr.push(`${ytext}å¹´`)
                    if (type === 'month' || type === 'day') {
                        let mGroup = this.getMonthArr(
                            y,
                            yearStar,
                            yearEnd,
                            monthStar,
                            monthEnd,
                            type
                        )
                        mGroup.length && this.monthArr.push(mGroup)
                    }
                }
                this.array[0] = this.yearArr
                if (type === 'month' || type === 'day') {
                    this.array[1] = this.monthArr.length && this.monthArr[0]
                }
                if (type === 'day') {
                    this.array[2] = this.getDateArr(
                        parseInt(this.yearArr[0]),
                        parseInt(this.monthArr[0])
                    )
                }
            },
            getMonthArr(y, yearStar, yearEnd, monthStar, monthEnd, type) {
                let mGroup = []
                let dateGroup = []
                if (y === yearStar && y !== yearEnd) {
                    for (let m = monthStar; m <= 12; m++) {
                        let mtext = m < 10 ? `0${m}` : m
                        mGroup.push(`${mtext}月`)
                    }
                }
                if (y === yearEnd && y !== yearStar) {
                    for (let m = 1; m <= monthEnd; m++) {
                        let mtext = m < 10 ? `0${m}` : m
                        mGroup.push(`${mtext}月`)
                    }
                }
                if (y !== yearStar && y !== yearEnd) {
                    for (let m = 1; m <= 12; m++) {
                        let mtext = m < 10 ? `0${m}` : m
                        mGroup.push(`${mtext}月`)
                    }
                }
                if (y === yearStar && y === yearEnd) {
                    for (let m = monthStar; m <= monthEnd; m++) {
                        let mtext = m < 10 ? `0${m}` : m
                        mGroup.push(`${mtext}月`)
                    }
                }
                return mGroup
            },
            getDateArr(y, m) {
                let minDate = this.moment(this.minSelect) || []
                let maxDate = this.moment(this.maxSelect) || []
                minDate = minDate.length && minDate.split('-')
                maxDate = maxDate.length && maxDate.split('-')
                let yearStar = minDate[0] ? parseInt(minDate[0]) : 1900
                let yearEnd = maxDate[0] ? parseInt(maxDate[0]) : 2050
                let monthStar = minDate[1] ? parseInt(minDate[1]) : 1
                let monthEnd = maxDate[1] ? parseInt(maxDate[1]) : 12
                let datearr = []
                let maxnum = 30
                let date31 = [1, 3, 5, 7, 8, 10, 12]
                if (date31.includes(m)) {
                    maxnum = 31
                }
                // åˆ¤æ–­æ˜¯å¹³å¹´è¿˜æ˜¯é—°å¹´çš„2月份
                if (m === 2) {
                    if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) {
                        maxnum = 29
                    } else {
                        maxnum = 28
                    }
                }
                let dateStar = minDate[2] ? parseInt(minDate[2]) : 1
                let dateEnd = maxDate[2] ? parseInt(maxDate[2]) : maxnum
                if (
                    (y !== yearEnd && y !== yearStar) ||
                    (y === yearStar && m !== monthStar && m !== monthEnd) ||
                    (y === yearEnd && m !== monthEnd && m !== monthStar) ||
                    (yearStar === yearEnd && m !== monthStar && m !== monthEnd) ||
                    (yearStar !== yearEnd && y === yearEnd && m !== monthEnd) ||
                    (y === yearStar && m !== monthStar && y < yearEnd)
                ) {
                    for (let d = 1; d <= maxnum; d++) {
                        let dtext = d < 10 ? `0${d}` : d
                        datearr.push(`${dtext}日`)
                    }
                }
                if ((yearStar === yearEnd && y === yearStar && m === monthStar && m !== monthEnd) || (yearStar !== yearEnd && y ===
                        yearStar && m === monthStar)) {
                    for (let d = dateStar; d <= maxnum; d++) {
                        let dtext = d < 10 ? `0${d}` : d
                        datearr.push(`${dtext}日`)
                    }
                }
                if ((y === yearEnd && m === monthEnd && m !== monthStar && yearStar === yearEnd) || (y === yearEnd && yearStar !==
                        yearEnd && m === monthEnd)) {
                    for (let d = 1; d <= dateEnd; d++) {
                        let dtext = d < 10 ? `0${d}` : d
                        datearr.push(`${dtext}日`)
                    }
                }
                if (
                    y === yearStar &&
                    yearStar === yearEnd &&
                    m === monthStar &&
                    monthStar === monthEnd
                ) {
                    for (let d = dateStar; d <= dateEnd; d++) {
                        let dtext = d < 10 ? `0${d}` : d
                        datearr.push(`${dtext}日`)
                    }
                }
                return datearr
            },
            bindTimeChange(e) {
                let timeValue = ''
                let indexArr = e.detail.value
                let type = this.timeType
                let year = parseInt(this.array[0][parseInt(indexArr[0]) || 0])
                let month = ''
                timeValue = year + ''
                if (type === 'month' || type === 'day') {
                    let index = parseInt(indexArr[1]) || 0
                    index = index < 0 ? 0 : index
                    month = parseInt(this.array[1][index])
                    month = month < 10 ? `0${month}` : month
                    timeValue = `${timeValue}-${month}`
                }
                if (type === 'day') {
                    let dateindex = parseInt(indexArr[2]) || 0
                    dateindex = dateindex < 0 ? 0 : dateindex
                    let date = parseInt(this.array[2][dateindex])
                    date = date < 10 ? `0${date}` : date
                    timeValue = `${timeValue}-${date}`
                }
                this.showTime = timeValue
                this.$emit('getData', timeValue)
            },
            // å½“时发生改变时 åŠ è½½å¯¹åº”çš„åˆ†é’Ÿæ•°ç»„
            columnchange(e) {
                const minIndex = e.detail.value
                const column = e.detail.column
                let type = this.timeType
                this.$set(this.index, column, minIndex)
                if (column === 0) {
                    this.yearIndex = minIndex
                    if (type === 'month' || type === 'day') {
                        this.$set(this.array, 1, this.monthArr[minIndex])
                    }
                    if (type === 'day') {
                        let monthindex = this.index[1] || 0
                        let newDateArr = this.getDateArr(
                            parseInt(this.yearArr[minIndex]),
                            parseInt(this.monthArr[this.yearIndex][monthindex])
                        )
                        this.$set(this.array, 2, newDateArr)
                    }
                }
                if (column === 1 && type === 'day') {
                    let newDateArr = this.getDateArr(
                        parseInt(this.yearArr[this.yearIndex]),
                        parseInt(this.monthArr[this.yearIndex][minIndex])
                    )
                    this.$set(this.array, 2, newDateArr)
                }
            },
            valueEchoed(defaultTime) {
                this.index = [0]
                if (this.childValue || defaultTime) {
                    let value = this.childValue || defaultTime
                    value = this.moment(value)
                    value = value.split('-')
                    let index =
                        this.array[0].findIndex(
                            item => parseInt(item) === parseInt(value[0])
                        ) || 0
                        index = index === -1 ? 0 : index
                    this.index[0] = index
                    this.yearIndex = index
                    let type = this.timeType
                    if (type === 'month' || type === 'day') {
                        this.array[1] = this.monthArr.length && this.monthArr[index]
                        const monthindex =
                            (this.array[1] && this.array[1].length &&
                                this.array[1].findIndex(
                                    item => parseInt(item) === parseInt(value[1])
                                )) ||
                            0
                        this.index[1] = monthindex
                    }
                    if (type === 'day') {
                        let index0 = this.index[0] || 0
                        let index1 = this.index[1] || 0
                        index0 = index0 === -1 ? 0 : index0
                        index1 = index1 === -1 ? 0 : index1
                        let newDay = this.getDateArr(
                            parseInt(this.yearArr[index0]),
                            parseInt(this.monthArr[index0][index1])
                        )
                        this.getDateIndex(newDay)
                    }
                }
            },
            getDateIndex(newDay) {
                let defaultTime = this.moment(new Date().getTime())
                let value = this.childValue || defaultTime
                value = this.moment(value)
                value = value.split('-')
                this.array[2] = newDay
                let dateindex =
                    this.array[2].findIndex(
                        item => parseInt(item) === parseInt(value[2])
                    ) || 0
                this.index[2] = dateindex
            }
        },
        watch: {
            // ç›‘测组件最小选择范围发生改变 åˆå§‹åŒ–日期数据
            minSelect() {
                this.dateInit()
                this.setDefaultValue()
            },
            // ç›‘测组件最大可选范围发生改变 åˆå§‹åŒ–日期数据
            maxSelect() {
                this.dateInit()
                this.setDefaultValue()
            },
            childValue() {
                this.showTime = ''
                this.showTime = this.moment(this.childValue)
                this.dateInit()
                this.setDefaultValue()
            }
        },
        created() {
            this.dateInit()
            this.valueEchoed()
            if (!this.childValue) {
                this.setDefaultValue()
            }
        }
    }
</script>
<style lang="less">
    .tx_r {
        line-height: 48px;
        font-size: 15px;
        font-weight: normal;
        color: #848b9a;
    }
    .placeholder {
        color: #b5b8c2;
    }
    .fa-angle-right {
        font-size: 36rpx;
        padding-left: 12rpx;
    }
</style>
components/drop-down-menu/dy-Date/Ó÷¨Ê¾Àý.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
<template>
    <view class="content">
        <div class="item filter_input">
            <div class="lable">日期期间:</div>
            <view class="date_item">
                <dyDatePicker placeholder="起始日期" :childValue="from" :minSelect="from_minSelect" :maxSelect="from_maxSelect"
                 :iconshow="false" @getData="getFromData"></dyDatePicker>
            </view>
            <view class="filter_inputline"></view>
            <view class="date_item">
                <dyDatePicker placeholder="结束日期" :minSelect="to_minSelect" :childValue="to" :maxSelect="to_maxSelect" :iconshow="false"
                 @getData="getToData"></dyDatePicker>
            </view>
        </div>
        <div class="item">
            <div class="lable">禁止选择:</div>
            <dyDatePicker childValue="2019/08/08" :disabled="true" @getData="getData" placeholder="请选择日期"></dyDatePicker>
        </div>
        <div class="item">
            <div class="lable">指定开始、结束时间:</div>
            <dyDatePicker @getData="getData" placeholder="请选择日期" minSelect="2018/08/23" maxSelect="2019/08/28"></dyDatePicker>
        </div>
        <div class="item">
            <div class="lable">指定值:</div>
            <dyDatePicker @getData="getData" childValue="2019/08/08" placeholder="请选择日期" minSelect="2000/01/01" maxSelect="2025/12/31"></dyDatePicker>
        </div>
        <div class="item">
            <div class="lable">选择月份:</div>
            <dyDatePicker timeType="month" @getData="getData" placeholder="请选择日期" minSelect="2000/01/01" maxSelect="2025/12/31"></dyDatePicker>
        </div>
        <div class="item">
            <div class="lable">选择年份:</div>
            <dyDatePicker timeType="year" @getData="getData" placeholder="请选择日期" minSelect="2000/01/01" maxSelect="2025/12/31"></dyDatePicker>
        </div>
    </view>
</template>
<script>
    import dyDatePicker from '../../components/dy-Date/dy-Date.vue'
    export default {
        components: {
            dyDatePicker
        },
        data() {
            return {
                from_minSelect: '1900/01/01',
                from_maxSelect: '2050/12/31',
                to_minSelect: '1900/01/01',
                to_maxSelect: '2050/12/31',
                from: '',
                to: '',
            }
        },
        onLoad() {
        },
        methods: {
            /**
             * @param {to_minSelect|from}  to_minSelect结束时间的最小选择范围 from=>开始日期
             */
            getFromData(time) {
                this.to_minSelect = time
                this.from = time
            },
            /**
             * @param {from_maxSelect|to} from_maxSelect=>开始日期可选最大可选值 to=> ç»“束日期
             */
            getToData(time) {
                this.from_maxSelect = time
                this.to = time
            },
            getData() {
                //
            }
        }
    }
</script>
<style lang="less" scoped>
    * {
        padding: 0px;
        margin: 0 auto;
        box-sizing: border-box;
    }
    .content {
        text-align: center;
        margin-top: 20px;
        font-size: 24rpx;
    }
    .item {
        box-sizing: border-box;
        width: 100%;
        height: 40px;
        line-height: 40px;
        background-color: #ffffff;
        position: relative;
        padding: 0px 10px 0px 100px;
        margin-bottom: 10px;
        text-align: right;
    }
    .item .lable {
        position: absolute;
        left: 10px;
        top: 0px;
        color: #333333;
    }
    uni-picker-view-column {
        font-size: 24rpx;
    }
    .filter_input {
        height: 92rpx;
        padding-top: 10rpx;
    }
    .date_item {
        float: left;
        width: 240upx;
        overflow: hidden;
        display: inline-block;
        text-align: center;
        border: 1px solid #ececec;
        height: 72rpx;
        line-height: 72rpx;
        // font-size: 28rpx;
        &:last-child {
            // float: right;
        }
        input {
            height: 72rpx;
            .uni-input-placeholder {
                color: #b5b8c2;
            }
        }
    }
    .filter_inputline {
        float: left;
        margin: 0 4rpx;
        line-height: 72rpx;
        &:after {
            content: '—';
            font-size: 28rpx;
            color: #848b9a;
        }
    }
</style>
components/drop-down-menu/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,299 @@
<template>
    <view class="dropdown">
        <view class="item">
            <view class="item-title">设备</view>
            <view class="item-box">
                <view v-for="(item,index) in menuList" :key="item.code" :class="['item-menu',item.ck?'active':'']"
                    @click="changeMenu(index)">{{item.name}}
                </view>
            </view>
            <view class="item-title">时间</view>
            <view class="item-box">
                <view v-for="(item,index) in dateList" :key="item.code"
                    :class="['item-menu',currentDate==index?'active':'']" @click="changeDate(index)">{{item.name}}
                </view>
            </view>
            <view class="custom-date" v-if="currentDate==3">
                <view class="custom-date-ipute">
                    <dyDatePicker placeholder="开始日期" :minSelect="from_minSelect" :maxSelect="from_maxSelect"
                        :iconshow="false" @getData="starteDate" ref="startDatePickerRef"></dyDatePicker>
                </view>
                <view class="custom-date-line"></view>
                <view class="custom-date-ipute">
                    <dyDatePicker placeholder="结束日期" :minSelect="to_minSelect"
                        :maxSelect="to_maxSelect" :iconshow="false" @getData="endDate" ref="endDatePickerRef">
                    </dyDatePicker>
                </view>
            </view>
        </view>
        <view class="dropdown-footer">
            <view class="dropdown-footer-btn reset" @click="resetDropdown">重置</view>
            <view class="dropdown-footer-btn submit" @click="submitSelect">确定</view>
        </view>
    </view>
</template>
<script>
    import dyDatePicker from '@/components/drop-down-menu/dy-Date/dy-Date.vue';
    import dayjs from 'dayjs'
    export default {
        components: {
            dyDatePicker
        },
        props: {
            list: {
                type: Array,
                default: () => []
            }
        },
        data() {
            return {
                menuList: [],
                dateList: [{
                        code: 1,
                        name: '今天',
                        subDay: 0
                    },
                    {
                        code: 2,
                        name: '昨天',
                        subDay: 1
                    },
                    {
                        code: 3,
                        name: '近一周',
                        subDay: 7
                    },
                    {
                        code: 4,
                        name: '自定义时间',
                        subDay: ''
                    }
                ],
                from_minSelect: '1900/01/01',
                from_maxSelect: '2050/12/31',
                to_minSelect: '1900/01/01',
                to_maxSelect: '2050/12/31',
                currentDate: undefined,
                selectData: {
                    menu: [],
                    startTime: undefined,
                    endTime: undefined
                },
            }
        },
        watch: {
            list: {
                handler(newVal, oldVal) {
                    this.init()
                },
                deep: true,
                immediate: true
            }
        },
        methods: {
            init() {
                const menuList = JSON.parse(JSON.stringify(this.list));
                menuList.forEach(item => item.ck = false);
                this.menuList = menuList
                const todayDate = dayjs().format('YYYY-MM-DD');
                const beforeDate = dayjs().subtract(5, 'year').format('YYYY-MM-DD')
                this.from_minSelect = beforeDate;
                this.from_maxSelect = todayDate;
                this.to_minSelect = beforeDate;
                this.to_maxSelect = todayDate;
                this.resetDropdown(false)
            },
            // change为false时不发送 change事件  ä»…仅重置
            resetDropdown(change=true) {
                this.menuList.forEach(item => item.ck = false);
                if (this.currentDate == 3) {
                    this.$refs.startDatePickerRef.clear();
                    this.$refs.endDatePickerRef.clear();
                }
                this.currentDate = 999;
                this.initDate();
                this.selectData.menu = [];
                this.selectData.startTime = undefined;
                this.selectData.endTime = undefined;
                if(change){
                    this.$emit('reset', this.selectData);
                }
            },
            submitSelect() {
                if (this.currentDate == 3) {
                    if (!this.selectData.startTime || !this.selectData.endTime) {
                        uni.showToast({
                            icon: "none",
                            title: "开始日期和结束日期都不能为空!",
                            duration: 3000
                        })
                        return
                    };
                };
                this.$emit('change', this.selectData);
            },
            initDate() {
                const todayDate = dayjs().format('YYYY-MM-DD');
                const beforeDate = dayjs().subtract(5, 'year').format('YYYY-MM-DD')
                this.from_minSelect = beforeDate;
                this.from_maxSelect = todayDate;
                this.to_minSelect = beforeDate;
                this.to_maxSelect = todayDate
            },
            changeMenu(index) {
                const handelItem = this.menuList[index].ck;
                this.menuList[index].ck = !handelItem;
                const checkList = [];
                this.menuList.forEach(item => {
                    if (item.ck) {
                        checkList.push(item.code)
                    }
                })
                this.selectData.menu = checkList;
            },
            changeDate(index) {
                this.currentDate = index;
                if (index == 3) {
                    this.selectData.startTime = undefined;
                    this.selectData.endTime = undefined;
                    return
                }
                const todayDate = dayjs().format('YYYY-MM-DD');
                const beforeDate = dayjs().subtract(this.dateList[index].subDay, 'day').format('YYYY-MM-DD')
                this.selectData.startTime = beforeDate;
                this.selectData.endTime = todayDate;
            },
            starteDate(val) {
                if (this.selectData.endTime) {
                    const diffDate = dayjs(this.selectData.endTime).diff(val, 'day');
                    if (diffDate < 0) {
                        uni.showToast({
                            icon: "none",
                            title: "开始日期不能大于结束日期",
                            duration: 3000
                        })
                        this.$refs.startDatePickerRef.clear();
                        return
                    }
                }
                this.selectData.startTime = val;
            },
            endDate(val) {
                if (this.selectData.startTime) {
                    const diffDate = dayjs(val).diff(this.selectData.startTime, 'day');
                    if (diffDate < 0) {
                        uni.showToast({
                            icon: "none",
                            title: "结束日期不能小于开始日期",
                            duration: 3000
                        })
                        this.$refs.endDatePickerRef.clear()
                        return
                    }
                }
                this.selectData.endTime = val;
            }
        }
    }
</script>
<style scoped lang="scss">
    .dropdown {
        ;
        padding: 32rpx 28rpx 48rpx 28rpx;
        box-sizing: border-box;
        background: #FFFFFF;
        box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(0, 0, 0, 0.15);
        border-radius: 16rpx;
        .item {
            &-title {
                font-size: 24rpx;
                font-family: PingFangTC-Medium, PingFangTC;
                font-weight: 600;
                color: #000000;
            }
            &-box {
                width: 100%;
                display: flex;
                flex-wrap: wrap;
                margin-bottom: 32rpx;
            }
            &-menu {
                box-sizing: border-box;
                flex: 0 0 calc(33.33% - 32rpx);
                height: 56rpx;
                background: #ECEDF3;
                border-radius: 8rpx;
                text-align: center;
                line-height: 56rpx;
                margin:  24rpx 10rpx 0 20rpx;
                font-size: 24rpx;
                font-weight: 400;
                color: #000000;
            }
            .active {
                background: #0064FF;
                color: #fff;
            }
        }
        .custom-date {
            display: flex;
            align-items: center;
            .custom-date-line {
                width: 30rpx;
                height: 2px;
                background: #eee;
                margin: 0 10rpx;
            }
            .custom-date-ipute {
                border: 1px solid #0064FF;
                width: 50%;
                text-align: center;
                height: 56rpx;
                color: #0064FF;
                border-radius: 8rpx;
            }
        }
        &-footer {
            display: flex;
            justify-content: space-around;
            align-items: center;
            margin-top: 32rpx;
            &-btn {
                width: 298rpx;
                height: 82rpx;
                text-align: center;
                border-radius: 12rpx;
                font-weight: 500;
                line-height: 82rpx;
                font-size: 32rpx;
            }
            .reset {
                border: 2rpx solid #0064FF;
                color: #0064FF;
            }
            .submit {
                color: #fff;
                background: #0064FF;
                border: 2rpx solid #0064FF;
            }
        }
    }
</style>
components/drop-down-menu/readme.txt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
插件ID:gx-drop-down-menu
修改自uniapp æ’件市场 ã€Šdrop-down-menu下拉菜单、多选,自定义日期范围选择》
**依赖dayjs,请npm  install  dayjs ã€Š"dayjs": "^1.11.10"》
Vue2    Vue3
√         Ã—
App    å¿«åº”用    å¾®ä¿¡å°ç¨‹åº    æ”¯ä»˜å®å°ç¨‹åº    ç™¾åº¦å°ç¨‹åº    å­—节小程序    QQ小程序
app-vue app-nvue    âˆš    âˆš    âˆš    âˆš    âˆš    âˆš
钉钉小程序    å¿«æ‰‹å°ç¨‹åº    é£žä¹¦å°ç¨‹åº    äº¬ä¸œå°ç¨‹åº
√    âˆš    âˆš    âˆš
H5-Safari    Android Browser    å¾®ä¿¡æµè§ˆå™¨(Android)    QQ浏览器(Android)    Chrome    IE    Edge    Firefox    PC-Safari
√    âˆš    âˆš    âˆš    âˆš    âˆš    âˆš    âˆš
dryuniapp.keystore
Binary files differ
lib/mqttTool.js
@@ -113,28 +113,6 @@
    })
}
//消息处理
mqttTool.message = function(){
    return new Promise((resolve, reject) => {
        if(mqttTool.client == null){
            resolve('未连接')
            console.log('App_text' + ":unconnect æœªè¿žæŽ¥")
            return;
        }
        if(mqttTool.client != null && ! mqttTool.client.connected){
            mqttTool.client.reconnect()
        }
        mqttTool.client.on('message', function(topic, message, buffer) {
            console.info(topic)
            console.info(message)
             setTimeout(() => {
                  resolve({topic, message, buffer })
                }, 300)
        })
    })
}
main.js
@@ -1,11 +1,16 @@
import App from './App'
import Vue from 'vue'
import Vue from 'vue'
import store from './store'; // å¼•å…¥ Vuex store
// æ­¤å¤„为引用自定义顶部
import cuCustom from './colorui/components/cu-custom.vue'
Vue.component('cu-custom',cuCustom);
import TnCustom from './components/TnCustom/TnCustom.vue'
Vue.component('tn-custom', TnCustom)
import MqttView from './mqtt/MqttView.vue'
Vue.component('mqtt-view', MqttView)
import uView from "uview-ui";
Vue.use(uView);
@@ -26,8 +31,11 @@
Vue.prototype.$lget = lget
import mqttTool from './lib/mqttTool.js'
Vue.prototype.$mqttTool = mqttTool
Vue.prototype.$mqttTool = mqttTool
const EventBus = new Vue();
Vue.prototype.$eventBus = EventBus
 
//全局监控定时器(在页面使用局部定时器会出现无法关闭问题)
@@ -38,7 +46,8 @@
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
const app = new Vue({
    store, // æ³¨å…¥ store
  ...App
})
app.$mount()
manifest.json
@@ -1,5 +1,5 @@
{
    "name" : "lbdry-uniapp",
    "name" : "智能中草药干燥机",
    "appid" : "__UNI__1E83F66",
    "description" : "",
    "versionName" : "1.0.0",
@@ -17,7 +17,9 @@
            "delay" : 0
        },
        /* æ¨¡å—配置 */
        "modules" : {},
        "modules" : {
            "Push" : {}
        },
        /* åº”用发布信息 */
        "distribute" : {
            /* android打包配置 */
@@ -38,41 +40,59 @@
                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
                    "<uses-feature android:name=\"android.hardware.camera\"/>",
                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
                ]
                ],
                "targetSdkVersion" : 30,
                "minSdkVersion" : 23
            },
            /* ios打包配置 */
            "ios" : {},
            "ios" : {
                "dSYMs" : false
            },
            /* SDK配置 */
            "sdkConfigs" : {},
            "sdkConfigs" : {
                "push" : {
                    "unipush" : {
                        "icons" : {
                            "small" : {
                                "ldpi" : "static/push/push_small_18.png",
                                "mdpi" : "static/push/push_small_24.png",
                                "hdpi" : "static/push/push_small_36.png",
                                "xhdpi" : "static/push/push_small_48.png",
                                "xxhdpi" : "static/push/push_small_72.png"
                            }
                        }
                    }
                }
            },
            "icons" : {
                "android" : {
                    "hdpi" : "",
                    "xhdpi" : "",
                    "xxhdpi" : "",
                    "xxxhdpi" : ""
                    "hdpi" : "unpackage/res/icons/72x72.png",
                    "xhdpi" : "unpackage/res/icons/96x96.png",
                    "xxhdpi" : "unpackage/res/icons/144x144.png",
                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
                },
                "ios" : {
                    "appstore" : "",
                    "appstore" : "unpackage/res/icons/1024x1024.png",
                    "ipad" : {
                        "app" : "",
                        "app@2x" : "",
                        "notification" : "",
                        "notification@2x" : "",
                        "proapp@2x" : "",
                        "settings" : "",
                        "settings@2x" : "",
                        "spotlight" : "",
                        "spotlight@2x" : ""
                        "app" : "unpackage/res/icons/76x76.png",
                        "app@2x" : "unpackage/res/icons/152x152.png",
                        "notification" : "unpackage/res/icons/20x20.png",
                        "notification@2x" : "unpackage/res/icons/40x40.png",
                        "proapp@2x" : "unpackage/res/icons/167x167.png",
                        "settings" : "unpackage/res/icons/29x29.png",
                        "settings@2x" : "unpackage/res/icons/58x58.png",
                        "spotlight" : "unpackage/res/icons/40x40.png",
                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
                    },
                    "iphone" : {
                        "app@2x" : "",
                        "app@3x" : "",
                        "notification@2x" : "",
                        "notification@3x" : "",
                        "settings@2x" : "",
                        "settings@3x" : "",
                        "spotlight@2x" : "",
                        "spotlight@3x" : ""
                        "app@2x" : "unpackage/res/icons/120x120.png",
                        "app@3x" : "unpackage/res/icons/180x180.png",
                        "notification@2x" : "unpackage/res/icons/40x40.png",
                        "notification@3x" : "unpackage/res/icons/60x60.png",
                        "settings@2x" : "unpackage/res/icons/58x58.png",
                        "settings@3x" : "unpackage/res/icons/87x87.png",
                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
                    }
                }
            },
mqtt/MqttView.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
<template>
    <view>
    </view>
</template>
<script>
    export default {
        name: 'MqttView',
        data() {
            return {
            }
        },
        onReady() {
        },
        methods: {
            initMqtt(){
                uni.getSystemInfo({
                    success: (res) => {
                        let deviceId = res.deviceId
                        if (!deviceId) {
                            deviceId = 'mobile-' + this.tenantId + '-' + Date.parse(new Date())
                        }
                        uni.setStorageSync(this.$constant.DEVICE_ID, 'mobile-' + this.tenantId + '-' + res.deviceId);
                    }
                })
                    this.startConnect();
            },
             /* è¿žæŽ¥MQTT */
             async startConnect() {
                 var _this = this
                 const account = uni.getStorageSync('account');
                 const deviceid = uni.getStorageSync(this.$constant.DEVICE_ID);
                 if (!account) {
                     return false
                 }
                 let opts = {
                     // #ifdef H5
                     url: 'ws://' + this.$api.mqttBaseUrl + ':8083/mqtt',
                     // #endif
                     // #ifdef APP-PLUS
                     url: 'wx://' + this.$api.mqttBaseUrl + ':8083/mqtt',
                     // #endif
                     clientId: deviceid,
                     username: account.username,
                     password: account.password
                 }
                 if (!this.$mqttTool.client) {
                     var client = await this.$mqttTool.connect(opts);
                 }
                 //订阅查询设备状态返回数据
                 this.$mqttTool.subscribe({
                     topic: this.$constant.SERVICE_DOWN + '/' + deviceid + '/#',
                     qos: 0
                 }).then(res => {
                     console.error(res)
                 })
                 //订阅设备故障广播(广播不在乎客户端id,发送给租户下所有在线的设备)
                 this.$mqttTool.subscribe({
                     topic: this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this.tenantId),
                     qos: 0
                 }).then(res => {
                     console.error(res)
                 })
                 //订阅发送指令返回结果
                 // this.$mqttTool.subscribe({
                 //     topic: this.$constant.SERVICE_RES_EQU_CMD,
                 //     qos: 0
                 // }).then(res => {
                 //     console.error(res)
                 // })
                 // if (!client) {
                 //     return false
                 // }
                let that = this
                 client.on('connect', function(res) {
                     console.error('连接成功')
                 })
                 client.on('reconnect', function(res) {
                     console.error('重新连接')
                 })
                 client.on('error', function(res) {
                     console.info('连接错误')
                 })
                 client.on('close', function(res) {
                     console.error('关闭成功')
                 })
                 client.on('message', function(topic, message, buffer) {
                    that.$eventBus.$emit(that.$constant.MQTT_TOPIC_MESSAGE, message);
                     console.info(message)
                 })
             },
        },
        computed: {
            tenantId() {
                const userinfo = uni.getStorageSync('userinfo');
                const tenantid = userinfo.loginTenantId
                return tenantid;
            }
        }
    }
</script>
<style>
</style>
mqtt/mqttTool.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
/* main.js é¡¹ç›®ä¸»å…¥å£æ³¨å…¥å®žä¾‹ */
// import mqttTool from './lib/mqttTool.js'
// Vue.prototype.$mqttTool = mqttTool
/* ä½¿ç”¨èŒƒä¾‹è§ /pages/index/index.vue */
// mqtt协议:H5使用ws/wss APP-PLUS使用wx/wxs
var mqtt = require('mqtt/dist/mqtt.js')
//客户端
let mqttTool = {
    client: null
}
//连接息
mqttTool.connect = function(params){
    let options = {
        clientId: params.clientId,
        username: params.username,
        password: params.password,
        clean: true,
        keepalive:10,  //心跳 å•位:s
        connectTimeout: 10*1000, //连接超时 å•位:ms
        cleanSession: true
    }
    let client = mqtt.connect(params.url, options);
    mqttTool.client = client
    return client;
}
mqttTool.end = function(){
    return new Promise((resolve, reject) => {
        if(mqttTool.client == null){
            resolve('未连接')
            console.log('App_text' + ":end æœªè¿žæŽ¥")
            return;
        }
        mqttTool.client.end()
        mqttTool.client = null
        resolve('连接终止')
    })
}
mqttTool.reconnect = function(){
    return new Promise((resolve, reject) => {
        if(mqttTool.client == null){
            resolve('未连接')
            console.log('App_text' + ":reconnect æœªè¿žæŽ¥")
            return;
        }
        mqttTool.client.reconnect()
    })
}
mqttTool.subscribe = function(params){
    return new Promise((resolve, reject) => {
        if(mqttTool.client == null){
            resolve('未连接')
            console.log('App_text' + ":unconnect æœªè¿žæŽ¥")
            return;
        }
        mqttTool.client.subscribe(params.topic, {qos:params.qos}, function(err,res) {
            console.log(err,res)
            if (!err && res.length>0) {
                resolve('订阅成功')
                console.log('App_text' + ":subscribe success è®¢é˜…成功")
            }else{
                resolve('订阅失败')
                console.log('App_text' + ":subscribe failed è®¢é˜…失败")
                return;
            }
        })
    })
}
mqttTool.unsubscribe = function(params){
    return new Promise((resolve, reject) => {
        if(mqttTool.client == null){
            resolve('未连接')
            console.log('App_text' + ":unconnect æœªè¿žæŽ¥")
            return;
        }
        mqttTool.client.unsubscribe(params.topic, function(err) {
            if (!err) {
                resolve('取消订阅成功')
                console.log('App_text' + ":unsubscribe success å–消订阅成功")
            }else{
                resolve('取消订阅失败')
                console.log('App_text' + ":unsubscribe failed å–消订阅失败")
                return;
            }
        })
    })
}
mqttTool.publish = function(params){
    return new Promise((resolve, reject) => {
        if(mqttTool.client == null){
            resolve('未连接')
            console.log('App_text' + ":unconnect æœªè¿žæŽ¥")
            return;
        }
        mqttTool.client.publish(params.topic, params.message, function(err){
            if (!err) {
                resolve(params.topic + '-' + params.message + '-发送成功')
                console.log('App_text' + ":publish success å‘送成功")
            }else{
                resolve(params.topic + '-' + params.message + '-发送失败')
                console.log('App_text' + ":publish failed å‘送失败")
                return;
            }
        })
    })
}
export default mqttTool
package.json
@@ -12,6 +12,7 @@
    "dependencies": {
        "ezuikit-js": "^0.7.2",
        "lodash.get": "^4.4.2",
        "mqtt": "^3.0.0"
        "mqtt": "^3.0.0",
        "dayjs": "^1.11.10"
    }
}
pages.json
@@ -76,7 +76,7 @@
            }
        }, {
            "path": "pages/tabBar/analy",
            "path": "pages/me/analy",
            "style": {
                "navigationStyle": "custom"
            }
@@ -123,6 +123,16 @@
                    "titleNView": false
                }
            }
        },
        {
            "path" : "pages/tabBar/warning",
            "style" :
            {
                "navigationStyle": "custom",
                "app-plus": {
                    "titleNView": false
                }
            }
        },
        {
            "path" : "pages/device/control",
@@ -134,6 +144,7 @@
                }
            }
        }
    ],
    "globalStyle": {
        "navigationBarTextStyle": "white",
@@ -158,10 +169,10 @@
            "selectedIconPath": "static/tabBar/shop_cur.png",
            "text": "监控"
        }, {
            "pagePath": "pages/tabBar/analy",
            "pagePath": "pages/tabBar/warning",
            "iconPath": "static/tabBar/analy.png",
            "selectedIconPath": "static/tabBar/analy_cur.png",
            "text": "分析"
            "text": "报警"
        }, {
            "pagePath": "pages/tabBar/formula",
            "iconPath": "static/tabBar/order.png",
pages/login/login.vue
@@ -51,7 +51,7 @@
                        trigger: ['blur', 'change']
                    },
                },
            };
        },
        onLoad() {
@@ -59,17 +59,41 @@
            if (account) {
                this.model = account
            }
            let _that = this
            // uni-app客户端获取push客户端标记
            // uni.getPushClientId({
            //     success: (res) => {
            //         let push_clientid = res.cid
            //         _that.model.username  = push_clientid
            //         console.log('客户端推送标识:', push_clientid)
            //     },
            //     fail(err) {
            //         _that.model.username  = err
            //         console.log(err)
            //     }
            // })
            // uni.onPushMessage((res) => {
            //     _that.model.username  = JSON.stringify(res)
            //     console.log("收到推送消息:", res) //监听推送消息
            // })
        },
        computed: {
            cid() {
                return this.$store.getters.getCid; // ä½¿ç”¨ getter èŽ·å– cid
            },
        },
        methods: {
        methods: {
            forget() {
                uni.navigateTo({
                    url: "/pages/tabBar/demo"
                })
            },
            submit() {
            submit() {
                if (this.model.username.startsWith("http")) {
                    uni.setStorageSync('baseurl', this.model.username);
                    this.model.username = null
@@ -86,8 +110,8 @@
            },
            login() {
                this.$api.login(this.model).then((res) => {
                    if (res.success) {
                    if (res.success) {
                        console.log('request success', res)
                        uni.showToast({
@@ -98,8 +122,9 @@
                        uni.setStorageSync('account', this.model);
                        uni.setStorageSync('userinfo', res.result.userInfo);
                        uni.setStorageSync('token', res.result.token);
                        uni.setStorageSync('token', res.result.token);
                        uni.switchTab({
                            url: '/pages/tabBar/general'
pages/me/analy.vue
ÎļþÃû´Ó pages/tabBar/analy.vue ÐÞ¸Ä
@@ -1,6 +1,6 @@
<template>
    <view>
        <cu-custom bgColor="bg-gradual-blue" :isBack="false">
        <cu-custom bgColor="bg-gradual-blue" :isBack="true">
            <block slot="content">生产记录分析</block>
        </cu-custom>
pages/tabBar/device.vue
@@ -19,7 +19,7 @@
                            </view>
                            <view>
                                <text
                                    class="text-gray margin-right-lg">{{new Date().toLocaleDateString().replace(/\//g, '-')}}</text>
                                    class="text-gray margin-right-lg">{{curDate}}</text>
                            </view>
                        </view>
@@ -72,7 +72,9 @@
                            <view class="title text-cut">{{$lget(item,'name')}}</view>
                            <view class="flex title text-green text-sm">
                                <u-tag v-if="item.online" size="mini" text="在线" type="success" plain plainFill></u-tag>
                                <u-tag v-else class="" size="mini" text="停机" type="error" plain plainFill></u-tag>
                                <u-tag v-else  size="mini" text="离线" type="warning" plain plainFill></u-tag>
                                <u-tag v-if="item.online" class="margin-left-sm" size="mini" text="在线" type="success" plain plainFill></u-tag>
                                <u-tag  v-else class="margin-left-sm" size="mini" text="停机" type="error" plain plainFill></u-tag>
                            </view>
                        </view>
                        <view class="right" style="min-width: 240rpx;">
@@ -128,6 +130,7 @@
                loading: true,
                // v-model绑定的这个变量不要在分页请求结束中自己赋值!!!
                dataList: [],
                curDate:uni.$u.timeFormat(new Date(), 'yyyy-mm-dd'),
            }
        },
        methods: {
pages/tabBar/general.vue
@@ -11,9 +11,10 @@
         </view>
         
         -->
        <cu-custom bgColor="bg-gradual-blue" :isBack="false">
            <block slot="content">智能中草药干燥设备配套系统</block>
        <cu-custom  bgColor="bg-gradual-blue" :isBack="false">
            <block slot="content">智能中草药</block>
        </cu-custom>
        <mqtt-view  ref="mqttView"></mqtt-view>
        <u-toast ref="uToast"></u-toast>
        <!--         <view class="card-box dynamic shadow cu-list menu">
            <view class="title-box">
@@ -232,7 +233,7 @@
                </view>
                <u-line color="#f1f1f1" margin="15rpx 0 15rpx 0"></u-line>
            </view>
        </view>
        <!-- 
        <view class="page-box" v-show="false">
@@ -277,11 +278,15 @@
                </view>
            </view>
        </view> -->
    </view>
</template>
<script>
    export default {
        data() {
            return {
                list: [{
@@ -353,22 +358,30 @@
        },
        onShow() {
            console.info('onShow')
            uni.showTabBarRedDot({
                index: 2 // æ˜¾ç¤ºç¬¬2个tabbar项(索引从0开始)的红点
            });
        },
        mounted() {
        },
        onReady() {
            const userinfo = uni.getStorageSync('userinfo');
            const tenantid = userinfo.loginTenantId
            uni.getSystemInfo({
                success: (res) => {
                    let deviceId = res.deviceId
                    if (!deviceId) {
                        deviceId = 'mobile-' + tenantid + '-' + Date.parse(new Date())
                    }
                    uni.setStorageSync(this.$constant.DEVICE_ID, 'mobile-' + tenantid + '-' + res.deviceId);
                }
            })
            this.startConnect();
            this.$refs.mqttView.initMqtt()
            // uni.getSystemInfo({
            //     success: (res) => {
            //         let deviceId = res.deviceId
            //         if (!deviceId) {
            //             deviceId = 'mobile-' + this.tenantId + '-' + Date.parse(new Date())
            //         }
            //         uni.setStorageSync(this.$constant.DEVICE_ID, 'mobile-' + this.tenantId + '-' + res.deviceId);
            //     }
            // })
            // this.startConnect();
@@ -380,7 +393,11 @@
        },
        onLoad() {
        },
        methods: {
            /* è¿žæŽ¥MQTT */
            async startConnect() {
                var _this = this
@@ -408,6 +425,14 @@
                //订阅查询设备状态返回数据
                this.$mqttTool.subscribe({
                    topic: this.$constant.SERVICE_DOWN + '/' + deviceid + '/#',
                    qos: 0
                }).then(res => {
                    console.error(res)
                })
                //订阅设备故障广播(广播不在乎客户端id,发送给租户下所有在线的设备)
                this.$mqttTool.subscribe({
                    topic: this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this.tenantId),
                    qos: 0
                }).then(res => {
                    console.error(res)
@@ -571,6 +596,14 @@
        },
        computed:{
            tenantId(){
                const userinfo = uni.getStorageSync('userinfo');
                const tenantid = userinfo.loginTenantId
                return   tenantid;
            }
        }
    }
pages/tabBar/me.vue
@@ -130,7 +130,14 @@
                        <image src='../../static/me/icon/diannao.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>远程控制</text>
                    </button>
                </view> -->
                </view> -->
                <view class="cu-item">
                    <button class='content cu-btn' @click="itemClick('analy')">
                        <image src='../../static/me/icon/diannao.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>分析</text>
                    </button>
                </view>
                
                
                <view class="cu-item">
@@ -222,6 +229,10 @@
                    uni.navigateTo({
                        url:"/pages/me/video"
                    })
                }else if(mode == 'analy'){
                    uni.navigateTo({
                        url:"/pages/me/analy"
                    })
                }else if(mode == 'other'){
                    uni.navigateTo({
                        url:"/pages/tabBar/monitor"
pages/tabBar/warning.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,755 @@
<template>
    <view class="app">
        <cu-custom bgColor="bg-gradual-blue" :isBack="false" :isRight="true" @rightclick="rclick">
            <block slot="content">报警</block>
        </cu-custom>
        <u-notice-bar text="12:20:10 1车间GM001机台电机过流(电气)报警" mode="closable"></u-notice-bar>
        <!--弹出层start-->
        <view style="width: 100%; position: absolute;z-index: 1000;top:300rpx;padding: 20rpx;" @touchmove.prevent >
            <dropdown-menu v-show="filterMenuShow"@reset='resetMenu'  @change='changeMenu' :list='equList' ref='dropdownMenuRef'/>
        </view>
        <view class="lock-page dropdown-mask" @touchmove.prevent @click.stop="1==1" v-if="filterMenuShow"></view>
        <!--弹出层end-->
        <view class="card-box dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">总览</view>
                    </view>
                    <view>
                        <text class="text-gray text-sm">2024-10-01 è‡³ 2024-12-01</text>
                    </view>
                </view>
            </view>
            <view class="flex flex-direction padding-xs">
                <view class="flex">
                    <view class="flex-sub flex flex-direction">
                        <text class="text-df">报警总数</text>
                        <text class="text-bold text-sl margin-top-xs text-red margin-top-sm">10
                            <text class="text-gray text-sm margin-left-xs">次</text></text>
                    </view>
                    <view class="flex-twice flex flex-direction justify-between">
                        <view class="flex-sub flex">
                            <view class="flex flex-direction flex-sub">
                                <text class="text-gray text-xs">报警信息</text>
                                <text class="text-black">
                                    <text class="text-black">机械故障:</text>
                                    <text class="margin-lr-xs text-orange">0</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                            </view>
                            <view class="flex flex-direction flex-sub">
                                <text class="text-white text-xs">电气故障</text>
                                <text class="text-black">
                                    <text class="text-black">电气故障:</text>
                                    <text class="margin-lr-xs text-orange">1</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                            </view>
                        </view>
                        <view class="flex-sub flex margin-top-sm">
                            <view class="flex flex-direction flex-sub">
                                <text class="text-gray text-xs">报警信息</text>
                                <text>
                                    <text class="text-black">通信故障:</text>
                                    <text class="margin-lr-xs text-orange">4</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                            </view>
                            <view class="flex flex-direction flex-sub">
                                <text class="text-white text-xs">其他故障</text>
                                <text class="text-cyan">
                                    <text class="text-black">其他故障:</text>
                                    <text class="margin-lr-xs text-orange">5</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                            </view>
                        </view>
                    </view>
                </view>
                <view class="margin-top">
                    <u-scroll-list>
                    </u-scroll-list>
                </view>
            </view>
        </view>
        <u-sticky>
            <view class="tab-box card-box top dynamic shadow">
                <z-tabs ref="tabs" :active-style="{'font-size':'30rpx','font-weight':'bold'}" :current="tabCurrent"
                    :list="tabList" @change="tabsChange">
                    <!-- è‡ªå®šä¹‰å³ä¾§æ’æ§½ -->
                    <!-- <template v-slot:right>
                        <u-icon name="setting"  ></u-icon>
                    </template> -->
                </z-tabs>
            </view>
        </u-sticky>
        <!-- å¯é€šè¿‡è®¾ç½®bar-animate-mode="worm"开启毛毛虫模式-->
        <swiper :style="'height:' + swiperItemHeight[tabCurrent] + 'rpx;'" :current="tabCurrent"
            @transition="swiperTransition" @animationfinish="swiperAnimationfinish">
            <swiper-item :key="0">
                <view :style="'height:' + swiperItemHeight[0] + 'rpx;'">
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">设备 </view>
                                </view>
                                <view>
                                    <!-- <text class="text-blue text-sm">更多></text> -->
                                </view>
                            </view>
                        </view>
                        <view class="borderTop">
                            <image style="width: 100%;border-radius: 20rpx; " src="../../static/image/pic_gz.jpg"
                                mode="aspectFit"></image>
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">实时报警 </view>
                                </view>
                                <view>
                                    <!-- <text class="text-blue text-sm">更多></text> -->
                                </view>
                            </view>
                        </view>
                        <view class="borderTop"  v-for="i in 10">
                            <view style="height: 180rpx;display: flex;align-items: center;">
                                <view>
                                    <image style="width: 80rpx;height: 80rpx;margin:20rpx;border-radius: 20rpx; "
                                        src="../../static/image/pic_gz.jpg"></image>
                                </view>
                                <view
                                    style="display: flex;flex: 1;flex-direction: column; justify-content: space-between;  ;height: 100%;padding: 20rpx;">
                                    <view class="text-bold">
                                        æ»šç­’电机过流报警
                                    </view>
                                    <view class="text-gray text-sm">
                                        è®¾å¤‡:1号干燥机
                                    </view>
                                    <view class="text-gray text-sm">
                                        æè¿°:同济堂1号滚筒电机过流报警,需要xxxx处理,东方航空的恢复很快的回复
                                    </view>
                                </view>
                                <view style="width: 100rpx;">{{i}}</view>
                            </view>
                        </view>
                    </view>
                    <view class="card-box bot dynamic shadow">
                        <view style="height: 20rpx;"></view>
                    </view>
                </view>
            </swiper-item>
            <swiper-item :key="1">
                <view :style="'height:' + swiperItemHeight[1] + 'rpx;'">
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">最近报警</view>
                                </view>
                                <view>
                                    <text class="text-blue text-sm">更多></text>
                                </view>
                            </view>
                        </view>
                        <view class="borderTop">
                            <view class="h-table">
                                <view class="h-tr h-tr-2 ">
                                    <view class="h-td">车间</view>
                                    <view class="h-td">机台</view>
                                    <view class="h-td">时间</view>
                                    <view class="h-td">类型</view>
                                    <view class="h-td">等级</view>
                                </view>
                                <view class="h-tr h-tr-2">
                                    <view class="h-td">001</view>
                                    <view class="h-td">GM001</view>
                                    <view class="h-td">12:00:00</view>
                                    <view class="h-td">机械</view>
                                    <view class="h-td">高</view>
                                </view>
                                <view class="h-tr h-tr-2">
                                    <view class="h-td">001</view>
                                    <view class="h-td">GM001</view>
                                    <view class="h-td">12:00:00</view>
                                    <view class="h-td">通信</view>
                                    <view class="h-td">中</view>
                                </view>
                                <view class="h-tr h-tr-2">
                                    <view class="h-td">001</view>
                                    <view class="h-td">GM001</view>
                                    <view class="h-td">12:00:00</view>
                                    <view class="h-td">电气</view>
                                    <view class="h-td">高</view>
                                </view>
                            </view>
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警统计</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <qiun-data-charts type="mount" :opts="opts" :animation="true" :chartData="Mount" />
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警对比</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <qiun-data-charts type="pie" :opts="optsPie" :chartData="PieA" />
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警频率</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <!-- <qiun-data-charts type="mount" :opts="{extra:{mount:{type:'mount',widthRatio:1.5}}}"
                    :chartData="Mount" /> -->
                            <qiun-data-charts type="word" :chartData="Word" />
                        </view>
                    </view>
                    <view class="card-box bot dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警分析</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <qiun-data-charts type="radar" :opts="optsRadar" :chartData="dataRadar" />
                        </view>
                    </view>
                </view>
            </swiper-item>
        </swiper>
    </view>
</template>
<script>
    import dropdownMenu from '@/components/drop-down-menu/index.vue'
    export default {
        components: {
            dropdownMenu
        },
        data() {
            return {
                equList: [{
                        code: 1,
                        name: '1#干燥机'
                    },
                    {
                        code: 2,
                        name: '2#干燥机'
                    }
                ],
                model: {},
                filterMenuShow: false,
                tabCurrent: 0,
                swiperItemHeight: [2800, 2140],
                tabList: [{
                    name: '实时报警',
                    badge: {
                        count: 6
                    }
                }, {
                    name: '报警统计',
                }],
                PieA: {
                    "series": [{
                        "data": [{
                            "name": "机械",
                            "value": 50,
                            "labelText": "机械:50次"
                        }, {
                            "name": "电气",
                            "value": 30,
                            "labelText": "电气:30次"
                        }, {
                            "name": "通信",
                            "value": 20,
                            "labelText": "通信:20次"
                        }, {
                            "name": "其他",
                            "value": 18,
                            "labelText": "其他:18次"
                        }]
                    }]
                },
                dataRadar: {
                    categories: ["机械", "电气", "通信", "其他"],
                    series: [{
                        name: "报警占比",
                        data: [99, 30, 18, 73]
                    }]
                },
                Mount: {
                    "series": [{
                        "data": [{
                            "name": "机械",
                            "value": 82
                        }, {
                            "name": "电气",
                            "value": 63
                        }, {
                            "name": "通信",
                            "value": 86
                        }, {
                            "name": "其他",
                            "value": 65
                        }]
                    }]
                },
                Word: {
                    "series": [{
                        "name": "风机过流",
                        "textSize": 25
                    }, {
                        "name": "后门未关闭",
                        "textSize": 20
                    }, {
                        "name": "风箱升异常",
                        "textSize": 20
                    }, {
                        "name": "电机过流",
                        "textSize": 20
                    }, {
                        "name": "加热位传感器",
                        "textSize": 20
                    }, {
                        "name": "加热时间异常",
                        "textSize": 20
                    }, {
                        "name": "滚筒升异常",
                        "textSize": 20
                    }, {
                        "name": "风箱不在加热位",
                        "textSize": 20
                    }, {
                        "name": "滚筒不在加热位",
                        "textSize": 10
                    }]
                },
                opts: {
                    color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4",
                        "#ea7ccc"
                    ],
                    padding: [15, 15, 0, 5],
                    enableScroll: false,
                    legend: {
                        show: true
                    },
                    xAxis: {
                        disableGrid: true
                    },
                    yAxis: {
                        gridColor: "rgba(230,230,230,0.6)",
                        data: [
                            {
                                min: 0
                            }
                        ]
                    },
                    extra: {
                        mount: {
                            type: "bar",
                            widthRatio: 0.3,
                            borderWidth: 0,
                            barBorderRadius: [
                                50,
                                50,
                                50,
                                50
                            ],
                            linearType: "custom"
                        }
                    }
                },
                optsPie: {
                    color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4",
                        "#ea7ccc"
                    ],
                    padding: [5, 5, 5, 5],
                    enableScroll: false,
                    extra: {
                        pie: {
                            activeOpacity: 0.5,
                            activeRadius: 10,
                            offsetAngle: 0,
                            labelWidth: 1,
                            border: true,
                            borderWidth: 3,
                            borderColor: "#FFFFFF",
                            linearType: "custom"
                        }
                    }
                },
                optsRadar: {
                    color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4",
                        "#ea7ccc"
                    ],
                    padding: [5, 5, 5, 5],
                    dataLabel: true,
                    dataPointShape: false,
                    enableScroll: false,
                    legend: {
                        show: false,
                        position: "right",
                        lineHeight: 25
                    },
                    extra: {
                        radar: {
                            gridType: "circle",
                            gridColor: "#CCCCCC",
                            gridCount: 3,
                            opacity: 1,
                            max: 100,
                            labelShow: true,
                            linearType: "custom",
                            border: false
                        }
                    }
                },
            }
        },
        computed: {
        },
        created() {
        },
        beforeDestroy() {
        },
        methods: {
            mqttTopicMessage(){
            //处理消息逻辑
            switch(topic){
                case _this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', that.tenantId):
                   console.error(that.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', that.tenantId))
                   console.error("收到广播")
                break
            }
            },
            changeMenu(selectData){
                console.info(selectData)
                this.filterMenuShow = false
            },
            resetMenu(selectData){
                console.info(selectData)
            },
            rclick() {
                this.filterMenuShow=!this.filterMenuShow
            },
            //tabs通知swiper切换
            tabsChange(index) {
                this.tabCurrent = index;
            },
            //swiper滑动中
            swiperTransition(e) {
                this.$refs.tabs.setDx(e.detail.dx);
            },
            //swiper滑动结束
            swiperAnimationfinish(e) {
                this.tabCurrent = e.detail.current;
                this.$refs.tabs.unlockDx();
            },
        }
    }
</script>
<style lang="scss" scoped>
    @import "components/table/helang-table";
    .app {
        position: relative;
    }
    .card-box {
        margin: 20rpx;
        padding: 20rpx;
        box-sizing: border-box;
        background-color: white;
        border-radius: 20rpx;
        font-family: Helvetica Neue, Helvetica, sans-serif;
    }
    .top {
        margin: 0 20rpx;
        border-radius: 0;
        border-top-left-radius: 20rpx;
        border-top-right-radius: 20rpx;
        border-bot-left-radius: 0;
        border-bot-right-radius: 0;
    }
    .center {
        margin: 0 20rpx;
        border-radius: 0;
    }
    .bot {
        margin: 0 20rpx 20rpx 20rpx;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-bot-left-radius: 20rpx;
        border-bot-right-radius: 20rpx;
    }
    .title-box {
        display: flex;
        flex-direction: row;
        align-items: center;
        .left {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
                font-weight: bold;
            }
        }
        .right {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
                font-weight: bold;
            }
        }
    }
    .info-box {
        margin-top: 20rpx;
        display: flex;
        flex-direction: row;
        align-items: center;
        .left {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
            }
        }
        .right {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
            }
        }
    }
    .chartsMain {
        width: 100%;
        height: 320rpx;
        padding-top: 15rpx;
        background: #fff;
        margin-bottom: 24rpx;
        border-top: 2rpx solid #f2f2f2;
        .charts {
            width: 50%;
            height: 450rpx;
            box-sizing: border-box;
        }
    }
    .tab-box {
        display: flex;
        justify-content: center;
        /* å¼ºåˆ¶æ°´å¹³å±…中 */
    }
    .swiper {
        height: 2116rpx;
    }
    .swiper-item-view {
        height: 2116rpx;
    }
    .picBox {
        margin: 0 20rpx;
        background-color: white;
        image {
            border-radius: 8rpx;
            width: 100%;
        }
    }
    .borderTop {
        border-top: 2rpx solid #f2f2f2;
        padding-top: 20rpx;
    }
    // å¼¹å‡ºå±‚背景遮罩start
    .dropdown-mask {
        background: rgba(0, 0, 0, 0.5);
    }
    .lock-page {
        height: 100vh;
        width: 100vw;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 998;
    }
    // å¼¹å‡ºå±‚背景遮罩end
</style>
static/image/pic_gz.jpg
static/image/pic_gzj.jpg
static/push/push_small_18.png
static/push/push_small_24.png
static/push/push_small_36.png
static/push/push_small_48.png
static/push/push_small_72.png
store/index.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
  state: {
    // å®šä¹‰cid默认值
    cid: null
  },
  mutations: {
    setCid(state, cid) {
          state.cid = cid;
    },
  },
  actions: {
    setCid({ commit }, cid) {
         commit('setCid', cid);
       },
  },
  getters: {
   getCid(state) {
         return state.cid;
       }
  }
});
export default store;
uni_modules/uni-datetime-picker/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
## 2.2.38(2024-10-15)
- ä¿®å¤ å¾®ä¿¡å°ç¨‹åºä¸­çš„getSystemInfo警告
## 2.2.37(2024-10-12)
- ä¿®å¤ å¾®ä¿¡å°ç¨‹åºä¸­çš„getSystemInfo警告
## 2.2.36(2024-10-12)
- ä¿®å¤ å¾®ä¿¡å°ç¨‹åºä¸­çš„getSystemInfo警告
## 2.2.35(2024-09-21)
- ä¿®å¤ æ²¡æœ‰é€‰ä¸­æ—¥æœŸæ—¶ç‚¹å‡»ç¡®å®šç›´æŽ¥æŠ¥é”™çš„Bug [详情](https://ask.dcloud.net.cn/question/198168)
## 2.2.34(2024-04-24)
- æ–°å¢ž æ—¥æœŸç‚¹å‡»äº‹ä»¶ï¼Œåœ¨ç‚¹å‡»æ—¥æœŸæ—¶ä¼šè§¦å‘该事件。
## 2.2.33(2024-04-15)
- ä¿®å¤ æŠ–音小程序事件传递失效bug
## 2.2.32(2024-02-20)
- ä¿®å¤ æ—¥åŽ†çš„close事件触发异常的bug [详情](https://github.com/dcloudio/uni-ui/issues/844)
## 2.2.31(2024-02-20)
- ä¿®å¤ h5平台 å³è¾¹æ—¥åŽ†çš„æœˆä»½é»˜è®¤+1的bug [详情](https://github.com/dcloudio/uni-ui/issues/841)
## 2.2.30(2024-01-31)
- ä¿®å¤ éšè—â€œç§’”时,在IOS15及以下版本时出现 ç»“束时间在开始时间之前 çš„bug [详情](https://github.com/dcloudio/uni-ui/issues/788)
## 2.2.29(2024-01-20)
- æ–°å¢ž show事件,弹窗弹出时触发该事件 [详情](https://github.com/dcloudio/uni-app/issues/4694)
## 2.2.28(2024-01-18)
- å޻除 noChange事件,当进行日期范围选择时,若只选了一天,则开始结束日期都为同一天 [详情](https://github.com/dcloudio/uni-ui/issues/815)
## 2.2.27(2024-01-10)
- ä¼˜åŒ– å¢žåŠ noChange事件,当进行日期范围选择时,若有空值,则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815)
## 2.2.26(2024-01-08)
- ä¿®å¤ å­—节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834)
## 2.2.25(2023-10-18)
- ä¿®å¤ PC端初次修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
## 2.2.24(2023-06-02)
- ä¿®å¤ éƒ¨åˆ†æƒ…况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146)
- ä¼˜åŒ– å½“前月可以选择上月、下月的日期的Bug
## 2.2.23(2023-05-02)
- ä¿®å¤ éƒ¨åˆ†æƒ…况修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
- ä¿®å¤ éƒ¨åˆ†å¹³å°åŠè®¾å¤‡ç¬¬ä¸€æ¬¡ç‚¹å‡»æ— æ³•显示弹框的Bug
- ä¿®å¤ ios æ—¥æœŸæ ¼å¼æœªè¡¥é›¶æ˜¾ç¤ºåŠä½¿ç”¨å¼‚常的Bug [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.22(2023-03-30)
- ä¿®å¤ æ—¥åކ picker ä¿®æ”¹å¹´æœˆåŽï¼Œè‡ªåŠ¨é€‰ä¸­å½“æœˆ1日的Bug [详情](https://ask.dcloud.net.cn/question/165937)
- ä¿®å¤ å°ç¨‹åºç«¯ ä½Žç‰ˆæœ¬ ios NaN的Bug [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.21(2023-02-20)
- ä¿®å¤ firefox æµè§ˆå™¨æ˜¾ç¤ºåŒºåŸŸç‚¹å‡»æ— æ³•拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362)
## 2.2.20(2023-02-17)
- ä¼˜åŒ– å€¼ä¸ºç©ºä¾ç„¶é€‰ä¸­å½“天问题
- ä¼˜åŒ– æä¾› default-value å±žæ€§æ”¯æŒé…ç½®é€‰æ‹©å™¨æ‰“开时默认显示的时间
- ä¼˜åŒ– éžèŒƒå›´é€‰æ‹©æœªé€‰æ‹©æ—¥æœŸæ—¶é—´ï¼Œç‚¹å‡»ç¡®è®¤æŒ‰é’®é€‰ä¸­å½“前日期时间
- ä¼˜åŒ– å­—节小程序日期时间范围选择,底部日期换行的Bug
## 2.2.19(2023-02-09)
- ä¿®å¤ 2.2.18 å¼•起范围选择配置 end é€‰æ‹©æ— æ•ˆçš„Bug [详情](https://github.com/dcloudio/uni-ui/issues/686)
## 2.2.18(2023-02-08)
- ä¿®å¤ ç§»åŠ¨ç«¯èŒƒå›´é€‰æ‹©change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684)
- ä¼˜åŒ– PC端输入日期格式错误时返回当前日期时间
- ä¼˜åŒ– PC端输入日期时间超出 start、end é™åˆ¶çš„Bug
- ä¼˜åŒ– ç§»åŠ¨ç«¯æ—¥æœŸæ—¶é—´èŒƒå›´ç”¨æ³•æ—¶é—´å±•ç¤ºä¸å®Œæ•´é—®é¢˜
## 2.2.17(2023-02-04)
- ä¿®å¤ å°ç¨‹åºç«¯ç»‘定 Date ç±»åž‹æŠ¥é”™çš„Bug [详情](https://github.com/dcloudio/uni-ui/issues/679)
- ä¿®å¤ vue3 time-picker æ— æ³•显示绑定时分秒的Bug
## 2.2.16(2023-02-02)
- ä¿®å¤ å­—节小程序报错的Bug
## 2.2.15(2023-02-02)
- ä¿®å¤ æŸäº›æƒ…况切换月份错误的Bug
## 2.2.14(2023-01-30)
- ä¿®å¤ æŸäº›æƒ…况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033)
## 2.2.13(2023-01-10)
- ä¿®å¤ å¤šæ¬¡åŠ è½½ç»„ä»¶é€ æˆå†…å­˜å ç”¨çš„Bug
## 2.2.12(2022-12-01)
- ä¿®å¤ vue3 ä¸‹ i18n å›½é™…化初始值不正确的Bug
## 2.2.11(2022-09-19)
- ä¿®å¤ æ”¯ä»˜å®å°ç¨‹åºæ ·å¼é”™ä¹±çš„Bug [详情](https://github.com/dcloudio/uni-app/issues/3861)
## 2.2.10(2022-09-19)
- ä¿®å¤ åå‘选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
## 2.2.9(2022-09-16)
- å¯ä»¥ä½¿ç”¨ uni-scss æŽ§åˆ¶ä¸»é¢˜è‰²
## 2.2.8(2022-09-08)
- ä¿®å¤ close事件无效的Bug
## 2.2.7(2022-09-05)
- ä¿®å¤ ç§»åŠ¨ç«¯ maskClick æ— æ•ˆçš„Bug [详情](https://ask.dcloud.net.cn/question/140824)
## 2.2.6(2022-06-30)
- ä¼˜åŒ– ç»„件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致
## 2.2.5(2022-06-24)
- ä¿®å¤ æ—¥åŽ†é¡¶éƒ¨å¹´æœˆåŠåº•éƒ¨ç¡®è®¤æœªå›½é™…åŒ–çš„Bug
## 2.2.4(2022-03-31)
- ä¿®å¤ Vue3 ä¸‹åŠ¨æ€èµ‹å€¼,单选类型未响应的Bug
## 2.2.3(2022-03-28)
- ä¿®å¤ Vue3 ä¸‹åŠ¨æ€èµ‹å€¼æœªå“åº”çš„Bug
## 2.2.2(2021-12-10)
- ä¿®å¤ clear-icon å±žæ€§åœ¨å°ç¨‹åºå¹³å°ä¸ç”Ÿæ•ˆçš„Bug
## 2.2.1(2021-12-10)
- ä¿®å¤ æ—¥æœŸèŒƒå›´é€‰åœ¨å°ç¨‹åºå¹³å°ï¼Œå¿…须多点击一次才能取消选中状态的Bug
## 2.2.0(2021-11-19)
- ä¼˜åŒ– ç»„ä»¶UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource)
- æ–‡æ¡£è¿ç§» [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
## 2.1.5(2021-11-09)
- æ–°å¢ž æä¾›ç»„件设计资源,组件样式调整
## 2.1.4(2021-09-10)
- ä¿®å¤ hide-second åœ¨ç§»åŠ¨ç«¯çš„Bug
- ä¿®å¤ å•选赋默认值时,赋值日期未高亮的Bug
- ä¿®å¤ èµ‹é»˜è®¤å€¼æ—¶ï¼Œç§»åŠ¨ç«¯æœªæ­£ç¡®æ˜¾ç¤ºæ—¶é—´çš„Bug
## 2.1.3(2021-09-09)
- æ–°å¢ž hide-second å±žæ€§ï¼Œæ”¯æŒåªä½¿ç”¨æ—¶åˆ†ï¼Œéšè—ç§’
## 2.1.2(2021-09-03)
- ä¼˜åŒ– å–消选中时(范围选)直接开始下一次选择, é¿å…å¤šç‚¹ä¸€æ¬¡
- ä¼˜åŒ– ç§»åŠ¨ç«¯æ”¯æŒæ¸…é™¤æŒ‰é’®ï¼ŒåŒæ—¶æ”¯æŒé€šè¿‡ ref è°ƒç”¨ç»„ä»¶çš„ clear æ–¹æ³•
- ä¼˜åŒ– è°ƒæ•´å­—号大小,美化日历界面
- ä¿®å¤ å› å›½é™…化导致的 placeholder å¤±æ•ˆçš„Bug
## 2.1.1(2021-08-24)
- æ–°å¢ž æ”¯æŒå›½é™…化
- ä¼˜åŒ– èŒƒå›´é€‰æ‹©å™¨åœ¨ pc ç«¯è¿‡å®½çš„问题
## 2.1.0(2021-08-09)
- æ–°å¢ž é€‚配 vue3
## 2.0.19(2021-08-09)
- æ–°å¢ž æ”¯æŒä½œä¸º uni-forms å­ç»„件相关功能
- ä¿®å¤ åœ¨ uni-forms ä¸­ä½¿ç”¨æ—¶ï¼Œé€‰æ‹©æ—¶é—´æŠ¥ NAN é”™è¯¯çš„Bug
## 2.0.18(2021-08-05)
- ä¿®å¤ type å±žæ€§åŠ¨æ€èµ‹å€¼æ— æ•ˆçš„Bug
- ä¿®å¤ â€˜ç¡®è®¤â€™æŒ‰é’®è¢« tabbar é®ç›– bug
- ä¿®å¤ ç»„件未赋值时范围选左、右日历相同的Bug
## 2.0.17(2021-08-04)
- ä¿®å¤ èŒƒå›´é€‰æœªæ­£ç¡®æ˜¾ç¤ºå½“前值的Bug
- ä¿®å¤ h5 å¹³å°ï¼ˆç§»åŠ¨ç«¯ï¼‰æŠ¥é”™ 'cale' of undefined çš„Bug
## 2.0.16(2021-07-21)
- æ–°å¢ž return-type å±žæ€§æ”¯æŒè¿”回 date æ—¥æœŸå¯¹è±¡
## 2.0.15(2021-07-14)
- ä¿®å¤ å•选日期类型,初始赋值后不在当前日历的Bug
- æ–°å¢ž clearIcon å±žæ€§ï¼Œæ˜¾ç¤ºæ¡†çš„æ¸…空按钮可配置显示隐藏(仅 pc æœ‰æ•ˆï¼‰
- ä¼˜åŒ– ç§»åŠ¨ç«¯ç§»é™¤æ˜¾ç¤ºæ¡†çš„æ¸…ç©ºæŒ‰é’®ï¼Œæ— å®žé™…ç”¨é€”
## 2.0.14(2021-07-14)
- ä¿®å¤ ç»„件赋值为空,界面未更新的Bug
- ä¿®å¤ start å’Œ end ä¸èƒ½åŠ¨æ€èµ‹å€¼çš„Bug
- ä¿®å¤ èŒƒå›´é€‰ç±»åž‹ï¼Œç”¨æˆ·é€‰æ‹©åŽå†æ¬¡é€‰æ‹©å³ä¾§æ—¥åŽ†ï¼ˆç»“æŸæ—¥æœŸï¼‰æ˜¾ç¤ºä¸æ­£ç¡®çš„Bug
## 2.0.13(2021-07-08)
- ä¿®å¤ èŒƒå›´é€‰æ‹©ä¸èƒ½åŠ¨æ€èµ‹å€¼çš„Bug
## 2.0.12(2021-07-08)
- ä¿®å¤ èŒƒå›´é€‰æ‹©çš„初始时间在一个月内时,造成无法选择的bug
## 2.0.11(2021-07-08)
- ä¼˜åŒ– å¼¹å‡ºå±‚在超出视窗边缘定位不准确的问题
## 2.0.10(2021-07-08)
- ä¿®å¤ èŒƒå›´èµ·å§‹ç‚¹æ ·å¼çš„背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug
- ä¼˜åŒ– å¼¹å‡ºå±‚在超出视窗边缘被遮盖的问题
## 2.0.9(2021-07-07)
- æ–°å¢ž maskClick äº‹ä»¶
- ä¿®å¤ ç‰¹æ®Šæƒ…况日历 rpx å¸ƒå±€é”™è¯¯çš„Bug,rpx -> px
- ä¿®å¤ èŒƒå›´é€‰æ‹©æ—¶æ¸…空返回值不合理的bug,['', ''] -> []
## 2.0.8(2021-07-07)
- æ–°å¢ž æ—¥æœŸæ—¶é—´æ˜¾ç¤ºæ¡†æ”¯æŒæ’æ§½
## 2.0.7(2021-07-01)
- ä¼˜åŒ– æ·»åŠ  uni-icons ä¾èµ–
## 2.0.6(2021-05-22)
- ä¿®å¤ å›¾æ ‡åœ¨å°ç¨‹åºä¸Šä¸æ˜¾ç¤ºçš„Bug
- ä¼˜åŒ– é‡å‘½åå¼•用组件,避免潜在组件命名冲突
## 2.0.5(2021-05-20)
- ä¼˜åŒ– ä»£ç ç›®å½•扁平化
## 2.0.4(2021-05-12)
- æ–°å¢ž ç»„件示例地址
## 2.0.3(2021-05-10)
- ä¿®å¤ ios ä¸‹ä¸è¯†åˆ« '-' æ—¥æœŸæ ¼å¼çš„Bug
- ä¼˜åŒ– pc ä¸‹å¼¹å‡ºå±‚添加边框和阴影
## 2.0.2(2021-05-08)
- ä¿®å¤ åœ¨ admin ä¸­èŽ·å–å¼¹å‡ºå±‚å®šä½é”™è¯¯çš„bug
## 2.0.1(2021-05-08)
- ä¿®å¤ type å±žæ€§å‘下兼容,默认值从 date å˜æ›´ä¸º datetime
## 2.0.0(2021-04-30)
- æ”¯æŒæ—¥åŽ†å½¢å¼çš„æ—¥æœŸ+时间的范围选择
 > æ³¨æ„ï¼šæ­¤ç‰ˆæœ¬ä¸å‘后兼容,不再支持单独时间选择(type=time)及相关的 hide-second å±žæ€§ï¼ˆæ—¶é—´é€‰å¯ä½¿ç”¨å†…置组件 picker)
## 1.0.6(2021-03-18)
- æ–°å¢ž hide-second å±žæ€§ï¼Œæ—¶é—´æ”¯æŒä»…选择时、分
- ä¿®å¤ é€‰æ‹©è·Ÿæ˜¾ç¤ºçš„æ—¥æœŸä¸ä¸€æ ·çš„Bug
- ä¿®å¤ chang事件触发2次的Bug
- ä¿®å¤ åˆ†ã€ç§’ end èŒƒå›´é”™è¯¯çš„Bug
- ä¼˜åŒ– æ›´å¥½çš„ nvue é€‚配
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,177 @@
<template>
    <view class="uni-calendar-item__weeks-box" :class="{
        'uni-calendar-item--disable':weeks.disable,
        'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
        'uni-calendar-item--multiple': weeks.multiple,
        'uni-calendar-item--after-checked-x':weeks.afterMultiple,
        }" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
        <view class="uni-calendar-item__weeks-box-item" :class="{
                'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
                'uni-calendar-item--checked-range-text': checkHover,
                'uni-calendar-item--before-checked':weeks.beforeMultiple,
                'uni-calendar-item--multiple': weeks.multiple,
                'uni-calendar-item--after-checked':weeks.afterMultiple,
                'uni-calendar-item--disable':weeks.disable,
                }">
            <text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
            <text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
        </view>
        <view :class="{'uni-calendar-item--today': weeks.isToday}"></view>
    </view>
</template>
<script>
    export default {
        props: {
            weeks: {
                type: Object,
                default () {
                    return {}
                }
            },
            calendar: {
                type: Object,
                default: () => {
                    return {}
                }
            },
            selected: {
                type: Array,
                default: () => {
                    return []
                }
            },
            checkHover: {
                type: Boolean,
                default: false
            }
        },
        methods: {
            choiceDate(weeks) {
                this.$emit('change', weeks)
            },
            handleMousemove(weeks) {
                this.$emit('handleMouse', weeks)
            }
        }
    }
</script>
<style lang="scss" >
    $uni-primary: #007aff !default;
    .uni-calendar-item__weeks-box {
        flex: 1;
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: column;
        justify-content: center;
        align-items: center;
        margin: 1px 0;
        position: relative;
    }
    .uni-calendar-item__weeks-box-text {
        font-size: 14px;
        // font-family: Lato-Bold, Lato;
        font-weight: bold;
        color: darken($color: $uni-primary, $amount: 40%);
    }
    .uni-calendar-item__weeks-box-item {
        position: relative;
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: column;
        justify-content: center;
        align-items: center;
        width: 40px;
        height: 40px;
        /* #ifdef H5 */
        cursor: pointer;
        /* #endif */
    }
    .uni-calendar-item__weeks-box-circle {
        position: absolute;
        top: 5px;
        right: 5px;
        width: 8px;
        height: 8px;
        border-radius: 8px;
        background-color: #dd524d;
    }
    .uni-calendar-item__weeks-box .uni-calendar-item--disable {
        cursor: default;
    }
    .uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
        color: #D1D1D1;
    }
    .uni-calendar-item--today {
        position: absolute;
        top: 10px;
        right: 17%;
        background-color: #dd524d;
        width:6px;
        height: 6px;
        border-radius: 50%;
    }
    .uni-calendar-item--extra {
        color: #dd524d;
        opacity: 0.8;
    }
    .uni-calendar-item__weeks-box .uni-calendar-item--checked {
        background-color: $uni-primary;
        border-radius: 50%;
        box-sizing: border-box;
        border: 3px solid #fff;
    }
    .uni-calendar-item--checked .uni-calendar-item--checked-text {
        color: #fff;
    }
    .uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
        color: #333;
    }
    .uni-calendar-item--multiple {
        background-color:  #F6F7FC;
        // color: #fff;
    }
    .uni-calendar-item--multiple .uni-calendar-item--before-checked,
    .uni-calendar-item--multiple .uni-calendar-item--after-checked {
        background-color: $uni-primary;
        border-radius: 50%;
        box-sizing: border-box;
        border: 3px solid #F6F7FC;
    }
    .uni-calendar-item--before-checked .uni-calendar-item--checked-text,
    .uni-calendar-item--after-checked .uni-calendar-item--checked-text {
        color: #fff;
    }
    .uni-calendar-item--before-checked-x {
        border-top-left-radius: 50px;
        border-bottom-left-radius: 50px;
        box-sizing: border-box;
        background-color: #F6F7FC;
    }
    .uni-calendar-item--after-checked-x {
        border-top-right-radius: 50px;
        border-bottom-right-radius: 50px;
        background-color: #F6F7FC;
    }
</style>
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,947 @@
<template>
    <view class="uni-calendar" @mouseleave="leaveCale">
        <view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
            @click="maskClick"></view>
        <view v-if="insert || show" class="uni-calendar__content"
            :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
            <view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
                <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')">
                    <view class="uni-calendar__header-btn uni-calendar--left"></view>
                </view>
                <picker mode="date" :value="date" fields="month" @change="bindDateChange">
                    <text
                        class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text>
                </picker>
                <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')">
                    <view class="uni-calendar__header-btn uni-calendar--right"></view>
                </view>
                <view v-if="!insert" class="dialog-close" @click="maskClick">
                    <view class="dialog-close-plus" data-id="close"></view>
                    <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
                </view>
            </view>
            <view class="uni-calendar__box">
                <view v-if="showMonth" class="uni-calendar__box-bg">
                    <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
                </view>
                <view class="uni-calendar__weeks" style="padding-bottom: 7px;">
                    <view class="uni-calendar__weeks-day">
                        <text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
                    </view>
                    <view class="uni-calendar__weeks-day">
                        <text class="uni-calendar__weeks-day-text">{{MONText}}</text>
                    </view>
                    <view class="uni-calendar__weeks-day">
                        <text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
                    </view>
                    <view class="uni-calendar__weeks-day">
                        <text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
                    </view>
                    <view class="uni-calendar__weeks-day">
                        <text class="uni-calendar__weeks-day-text">{{THUText}}</text>
                    </view>
                    <view class="uni-calendar__weeks-day">
                        <text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
                    </view>
                    <view class="uni-calendar__weeks-day">
                        <text class="uni-calendar__weeks-day-text">{{SATText}}</text>
                    </view>
                </view>
                <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
                    <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
                        <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected"
                            :checkHover="range" @change="choiceDate" @handleMouse="handleMouse">
                        </calendar-item>
                    </view>
                </view>
            </view>
            <view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top"
                style="padding: 0 80px;">
                <view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
                <time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time"
                    :disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
                </time-picker>
            </view>
            <view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top">
                <view class="uni-date-changed--time-start">
                    <view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
                    </view>
                    <time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false"
                        :hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
                    </time-picker>
                </view>
                <view style="line-height: 50px;">
                    <uni-icons type="arrowthinright" color="#999"></uni-icons>
                </view>
                <view class="uni-date-changed--time-end">
                    <view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
                    <time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false"
                        :hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
                    </time-picker>
                </view>
            </view>
            <view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
                <view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view>
            </view>
        </view>
    </view>
</template>
<script>
    import {
        Calendar,
        getDate,
        getTime
    } from './util.js';
    import calendarItem from './calendar-item.vue'
    import timePicker from './time-picker.vue'
    import {
        initVueI18n
    } from '@dcloudio/uni-i18n'
    import i18nMessages from './i18n/index.js'
    const {
        t
    } = initVueI18n(i18nMessages)
    /**
     * Calendar æ—¥åކ
     * @description æ—¥åŽ†ç»„ä»¶å¯ä»¥æŸ¥çœ‹æ—¥æœŸï¼Œé€‰æ‹©ä»»æ„èŒƒå›´å†…çš„æ—¥æœŸï¼Œæ‰“ç‚¹æ“ä½œã€‚å¸¸ç”¨åœºæ™¯å¦‚ï¼šé…’åº—æ—¥æœŸé¢„è®¢ã€ç«è½¦æœºç¥¨é€‰æ‹©è´­ä¹°æ—¥æœŸã€ä¸Šä¸‹ç­æ‰“å¡ç­‰
     * @tutorial https://ext.dcloud.net.cn/plugin?id=56
     * @property {String} date è‡ªå®šä¹‰å½“前时间,默认为今天
     * @property {String} startDate æ—¥æœŸé€‰æ‹©èŒƒå›´-开始日期
     * @property {String} endDate æ—¥æœŸé€‰æ‹©èŒƒå›´-结束日期
     * @property {Boolean} range èŒƒå›´é€‰æ‹©
     * @property {Boolean} insert = [true|false] æ’入模式,默认为false
     *     @value true å¼¹çª—模式
     *     @value false æ’入模式
     * @property {Boolean} clearDate = [true|false] å¼¹çª—模式是否清空上次选择内容
     * @property {Array} selected æ‰“点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
     * @property {Boolean} showMonth æ˜¯å¦é€‰æ‹©æœˆä»½ä¸ºèƒŒæ™¯
     * @property {[String} defaultValue é€‰æ‹©å™¨æ‰“开时默认显示的时间
     * @event {Function} change æ—¥æœŸæ”¹å˜ï¼Œ`insert :ture` æ—¶ç”Ÿæ•ˆ
     * @event {Function} confirm ç¡®è®¤é€‰æ‹©`insert :false` æ—¶ç”Ÿæ•ˆ
     * @event {Function} monthSwitch åˆ‡æ¢æœˆä»½æ—¶è§¦å‘
     * @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
     */
    export default {
        components: {
            calendarItem,
            timePicker
        },
        options: {
            // #ifdef MP-TOUTIAO
            virtualHost: false,
            // #endif
            // #ifndef MP-TOUTIAO
            virtualHost: true
            // #endif
        },
        props: {
            date: {
                type: String,
                default: ''
            },
            defTime: {
                type: [String, Object],
                default: ''
            },
            selectableTimes: {
                type: [Object],
                default () {
                    return {}
                }
            },
            selected: {
                type: Array,
                default () {
                    return []
                }
            },
            startDate: {
                type: String,
                default: ''
            },
            endDate: {
                type: String,
                default: ''
            },
            startPlaceholder: {
                type: String,
                default: ''
            },
            endPlaceholder: {
                type: String,
                default: ''
            },
            range: {
                type: Boolean,
                default: false
            },
            hasTime: {
                type: Boolean,
                default: false
            },
            insert: {
                type: Boolean,
                default: true
            },
            showMonth: {
                type: Boolean,
                default: true
            },
            clearDate: {
                type: Boolean,
                default: true
            },
            checkHover: {
                type: Boolean,
                default: true
            },
            hideSecond: {
                type: [Boolean],
                default: false
            },
            pleStatus: {
                type: Object,
                default () {
                    return {
                        before: '',
                        after: '',
                        data: [],
                        fulldate: ''
                    }
                }
            },
            defaultValue: {
                type: [String, Object, Array],
                default: ''
            }
        },
        data() {
            return {
                show: false,
                weeks: [],
                calendar: {},
                nowDate: {},
                aniMaskShow: false,
                firstEnter: true,
                time: '',
                timeRange: {
                    startTime: '',
                    endTime: ''
                },
                tempSingleDate: '',
                tempRange: {
                    before: '',
                    after: ''
                }
            }
        },
        watch: {
            date: {
                immediate: true,
                handler(newVal) {
                    if (!this.range) {
                        this.tempSingleDate = newVal
                        setTimeout(() => {
                            this.init(newVal)
                        }, 100)
                    }
                }
            },
            defTime: {
                immediate: true,
                handler(newVal) {
                    if (!this.range) {
                        this.time = newVal
                    } else {
                        this.timeRange.startTime = newVal.start
                        this.timeRange.endTime = newVal.end
                    }
                }
            },
            startDate(val) {
                // å­—节小程序 watch æ—©äºŽ created
                if (!this.cale) {
                    return
                }
                this.cale.setStartDate(val)
                this.cale.setDate(this.nowDate.fullDate)
                this.weeks = this.cale.weeks
            },
            endDate(val) {
                // å­—节小程序 watch æ—©äºŽ created
                if (!this.cale) {
                    return
                }
                this.cale.setEndDate(val)
                this.cale.setDate(this.nowDate.fullDate)
                this.weeks = this.cale.weeks
            },
            selected(newVal) {
                // å­—节小程序 watch æ—©äºŽ created
                if (!this.cale) {
                    return
                }
                this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
                this.weeks = this.cale.weeks
            },
            pleStatus: {
                immediate: true,
                handler(newVal) {
                    const {
                        before,
                        after,
                        fulldate,
                        which
                    } = newVal
                    this.tempRange.before = before
                    this.tempRange.after = after
                    setTimeout(() => {
                        if (fulldate) {
                            this.cale.setHoverMultiple(fulldate)
                            if (before && after) {
                                this.cale.lastHover = true
                                if (this.rangeWithinMonth(after, before)) return
                                this.setDate(before)
                            } else {
                                this.cale.setMultiple(fulldate)
                                this.setDate(this.nowDate.fullDate)
                                this.calendar.fullDate = ''
                                this.cale.lastHover = false
                            }
                        } else {
                            // å­—节小程序 watch æ—©äºŽ created
                            if (!this.cale) {
                                return
                            }
                            this.cale.setDefaultMultiple(before, after)
                            if (which === 'left' && before) {
                                this.setDate(before)
                                this.weeks = this.cale.weeks
                            } else if (after) {
                                this.setDate(after)
                                this.weeks = this.cale.weeks
                            }
                            this.cale.lastHover = true
                        }
                    }, 16)
                }
            }
        },
        computed: {
            timepickerStartTime() {
                const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
                return activeDate === this.startDate ? this.selectableTimes.start : ''
            },
            timepickerEndTime() {
                const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
                return activeDate === this.endDate ? this.selectableTimes.end : ''
            },
            /**
             * for i18n
             */
            selectDateText() {
                return t("uni-datetime-picker.selectDate")
            },
            startDateText() {
                return this.startPlaceholder || t("uni-datetime-picker.startDate")
            },
            endDateText() {
                return this.endPlaceholder || t("uni-datetime-picker.endDate")
            },
            okText() {
                return t("uni-datetime-picker.ok")
            },
            yearText() {
                return t("uni-datetime-picker.year")
            },
            monthText() {
                return t("uni-datetime-picker.month")
            },
            MONText() {
                return t("uni-calender.MON")
            },
            TUEText() {
                return t("uni-calender.TUE")
            },
            WEDText() {
                return t("uni-calender.WED")
            },
            THUText() {
                return t("uni-calender.THU")
            },
            FRIText() {
                return t("uni-calender.FRI")
            },
            SATText() {
                return t("uni-calender.SAT")
            },
            SUNText() {
                return t("uni-calender.SUN")
            },
            confirmText() {
                return t("uni-calender.confirm")
            },
        },
        created() {
            // èŽ·å–æ—¥åŽ†æ–¹æ³•å®žä¾‹
            this.cale = new Calendar({
                selected: this.selected,
                startDate: this.startDate,
                endDate: this.endDate,
                range: this.range,
            })
            // é€‰ä¸­æŸä¸€å¤©
            this.init(this.date)
        },
        methods: {
            leaveCale() {
                this.firstEnter = true
            },
            handleMouse(weeks) {
                if (weeks.disable) return
                if (this.cale.lastHover) return
                let {
                    before,
                    after
                } = this.cale.multipleStatus
                if (!before) return
                this.calendar = weeks
                // è®¾ç½®èŒƒå›´é€‰
                this.cale.setHoverMultiple(this.calendar.fullDate)
                this.weeks = this.cale.weeks
                // hover时,进入一个日历,更新另一个
                if (this.firstEnter) {
                    this.$emit('firstEnterCale', this.cale.multipleStatus)
                    this.firstEnter = false
                }
            },
            rangeWithinMonth(A, B) {
                const [yearA, monthA] = A.split('-')
                const [yearB, monthB] = B.split('-')
                return yearA === yearB && monthA === monthB
            },
            // è’™ç‰ˆç‚¹å‡»äº‹ä»¶
            maskClick() {
                this.close()
                this.$emit('maskClose')
            },
            clearCalender() {
                if (this.range) {
                    this.timeRange.startTime = ''
                    this.timeRange.endTime = ''
                    this.tempRange.before = ''
                    this.tempRange.after = ''
                    this.cale.multipleStatus.before = ''
                    this.cale.multipleStatus.after = ''
                    this.cale.multipleStatus.data = []
                    this.cale.lastHover = false
                } else {
                    this.time = ''
                    this.tempSingleDate = ''
                }
                this.calendar.fullDate = ''
                this.setDate(new Date())
            },
            bindDateChange(e) {
                const value = e.detail.value + '-1'
                this.setDate(value)
            },
            /**
             * åˆå§‹åŒ–日期显示
             * @param {Object} date
             */
            init(date) {
                // å­—节小程序 watch æ—©äºŽ created
                if (!this.cale) {
                    return
                }
                this.cale.setDate(date || new Date())
                this.weeks = this.cale.weeks
                this.nowDate = this.cale.getInfo(date)
                this.calendar = {
                    ...this.nowDate
                }
                if (!date) {
                    // ä¼˜åŒ–date为空默认不选中今天
                    this.calendar.fullDate = ''
                    if (this.defaultValue && !this.range) {
                        // æš‚时只支持移动端非范围选择
                        const defaultDate = new Date(this.defaultValue)
                        const fullDate = getDate(defaultDate)
                        const year = defaultDate.getFullYear()
                        const month = defaultDate.getMonth() + 1
                        const date = defaultDate.getDate()
                        const day = defaultDate.getDay()
                        this.calendar = {
                                fullDate,
                                year,
                                month,
                                date,
                                day
                            },
                            this.tempSingleDate = fullDate
                        this.time = getTime(defaultDate, this.hideSecond)
                    }
                }
            },
            /**
             * æ‰“开日历弹窗
             */
            open() {
                // å¼¹çª—模式并且清理数据
                if (this.clearDate && !this.insert) {
                    this.cale.cleanMultipleStatus()
                    this.init(this.date)
                }
                this.show = true
                this.$nextTick(() => {
                    setTimeout(() => {
                        this.aniMaskShow = true
                    }, 50)
                })
            },
            /**
             * å…³é—­æ—¥åŽ†å¼¹çª—
             */
            close() {
                this.aniMaskShow = false
                this.$nextTick(() => {
                    setTimeout(() => {
                        this.show = false
                        this.$emit('close')
                    }, 300)
                })
            },
            /**
             * ç¡®è®¤æŒ‰é’®
             */
            confirm() {
                this.setEmit('confirm')
                this.close()
            },
            /**
             * å˜åŒ–触发
             */
            change(isSingleChange) {
                if (!this.insert && !isSingleChange) return
                this.setEmit('change')
            },
            /**
             * é€‰æ‹©æœˆä»½è§¦å‘
             */
            monthSwitch() {
                let {
                    year,
                    month
                } = this.nowDate
                this.$emit('monthSwitch', {
                    year,
                    month: Number(month)
                })
            },
            /**
             * æ´¾å‘事件
             * @param {Object} name
             */
            setEmit(name) {
                if (!this.range) {
                    if (!this.calendar.fullDate) {
                        this.calendar = this.cale.getInfo(new Date())
                        this.tempSingleDate = this.calendar.fullDate
                    }
                    if (this.hasTime && !this.time) {
                        this.time = getTime(new Date(), this.hideSecond)
                    }
                }
                let {
                    year,
                    month,
                    date,
                    fullDate,
                    extraInfo
                } = this.calendar
                this.$emit(name, {
                    range: this.cale.multipleStatus,
                    year,
                    month,
                    date,
                    time: this.time,
                    timeRange: this.timeRange,
                    fulldate: fullDate,
                    extraInfo: extraInfo || {}
                })
            },
            /**
             * é€‰æ‹©å¤©è§¦å‘
             * @param {Object} weeks
             */
            choiceDate(weeks) {
                if (weeks.disable) return
                this.calendar = weeks
                this.calendar.userChecked = true
                // è®¾ç½®å¤šé€‰
                this.cale.setMultiple(this.calendar.fullDate, true)
                this.weeks = this.cale.weeks
                this.tempSingleDate = this.calendar.fullDate
                const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
                const afterDate = new Date(this.cale.multipleStatus.after).getTime()
                if (beforeDate > afterDate && afterDate) {
                    this.tempRange.before = this.cale.multipleStatus.after
                    this.tempRange.after = this.cale.multipleStatus.before
                } else {
                    this.tempRange.before = this.cale.multipleStatus.before
                    this.tempRange.after = this.cale.multipleStatus.after
                }
                this.change(true)
            },
            changeMonth(type) {
                let newDate
                if (type === 'pre') {
                    newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate
                } else if (type === 'next') {
                    newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate
                }
                this.setDate(newDate)
                this.monthSwitch()
            },
            /**
             * è®¾ç½®æ—¥æœŸ
             * @param {Object} date
             */
            setDate(date) {
                this.cale.setDate(date)
                this.weeks = this.cale.weeks
                this.nowDate = this.cale.getInfo(date)
            }
        }
    }
</script>
<style lang="scss">
    $uni-primary: #007aff !default;
    .uni-calendar {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: column;
    }
    .uni-calendar__mask {
        position: fixed;
        bottom: 0;
        top: 0;
        left: 0;
        right: 0;
        background-color: rgba(0, 0, 0, 0.4);
        transition-property: opacity;
        transition-duration: 0.3s;
        opacity: 0;
        /* #ifndef APP-NVUE */
        z-index: 99;
        /* #endif */
    }
    .uni-calendar--mask-show {
        opacity: 1
    }
    .uni-calendar--fixed {
        position: fixed;
        bottom: calc(var(--window-bottom));
        left: 0;
        right: 0;
        transition-property: transform;
        transition-duration: 0.3s;
        transform: translateY(460px);
        /* #ifndef APP-NVUE */
        z-index: 99;
        /* #endif */
    }
    .uni-calendar--ani-show {
        transform: translateY(0);
    }
    .uni-calendar__content {
        background-color: #fff;
    }
    .uni-calendar__content-mobile {
        border-top-left-radius: 10px;
        border-top-right-radius: 10px;
        box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
    }
    .uni-calendar__header {
        position: relative;
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
        justify-content: center;
        align-items: center;
        height: 50px;
    }
    .uni-calendar__header-mobile {
        padding: 10px;
        padding-bottom: 0;
    }
    .uni-calendar--fixed-top {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
        justify-content: space-between;
        border-top-color: rgba(0, 0, 0, 0.4);
        border-top-style: solid;
        border-top-width: 1px;
    }
    .uni-calendar--fixed-width {
        width: 50px;
    }
    .uni-calendar__backtoday {
        position: absolute;
        right: 0;
        top: 25rpx;
        padding: 0 5px;
        padding-left: 10px;
        height: 25px;
        line-height: 25px;
        font-size: 12px;
        border-top-left-radius: 25px;
        border-bottom-left-radius: 25px;
        color: #fff;
        background-color: #f1f1f1;
    }
    .uni-calendar__header-text {
        text-align: center;
        width: 100px;
        font-size: 15px;
        color: #666;
    }
    .uni-calendar__button-text {
        text-align: center;
        width: 100px;
        font-size: 14px;
        color: $uni-primary;
        /* #ifndef APP-NVUE */
        letter-spacing: 3px;
        /* #endif */
    }
    .uni-calendar__header-btn-box {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
        align-items: center;
        justify-content: center;
        width: 50px;
        height: 50px;
    }
    .uni-calendar__header-btn {
        width: 9px;
        height: 9px;
        border-left-color: #808080;
        border-left-style: solid;
        border-left-width: 1px;
        border-top-color: #555555;
        border-top-style: solid;
        border-top-width: 1px;
    }
    .uni-calendar--left {
        transform: rotate(-45deg);
    }
    .uni-calendar--right {
        transform: rotate(135deg);
    }
    .uni-calendar__weeks {
        position: relative;
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
    }
    .uni-calendar__weeks-item {
        flex: 1;
    }
    .uni-calendar__weeks-day {
        flex: 1;
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: column;
        justify-content: center;
        align-items: center;
        height: 40px;
        border-bottom-color: #F5F5F5;
        border-bottom-style: solid;
        border-bottom-width: 1px;
    }
    .uni-calendar__weeks-day-text {
        font-size: 12px;
        color: #B2B2B2;
    }
    .uni-calendar__box {
        position: relative;
        // padding: 0 10px;
        padding-bottom: 7px;
    }
    .uni-calendar__box-bg {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        justify-content: center;
        align-items: center;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
    }
    .uni-calendar__box-bg-text {
        font-size: 200px;
        font-weight: bold;
        color: #999;
        opacity: 0.1;
        text-align: center;
        /* #ifndef APP-NVUE */
        line-height: 1;
        /* #endif */
    }
    .uni-date-changed {
        padding: 0 10px;
        // line-height: 50px;
        text-align: center;
        color: #333;
        border-top-color: #DCDCDC;
        ;
        border-top-style: solid;
        border-top-width: 1px;
        flex: 1;
    }
    .uni-date-btn--ok {
        padding: 20px 15px;
    }
    .uni-date-changed--time-start {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        align-items: center;
    }
    .uni-date-changed--time-end {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        align-items: center;
    }
    .uni-date-changed--time-date {
        color: #999;
        line-height: 50px;
        /* #ifdef MP-TOUTIAO */
        font-size: 16px;
        /* #endif */
        margin-right: 5px;
        // opacity: 0.6;
    }
    .time-picker-style {
        // width: 62px;
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        justify-content: center;
        align-items: center
    }
    .mr-10 {
        margin-right: 10px;
    }
    .dialog-close {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
        align-items: center;
        padding: 0 25px;
        margin-top: 10px;
    }
    .dialog-close-plus {
        width: 16px;
        height: 2px;
        background-color: #737987;
        border-radius: 2px;
        transform: rotate(45deg);
    }
    .dialog-close-rotate {
        position: absolute;
        transform: rotate(-45deg);
    }
    .uni-datetime-picker--btn {
        border-radius: 100px;
        height: 40px;
        line-height: 40px;
        background-color: $uni-primary;
        color: #fff;
        font-size: 16px;
        letter-spacing: 2px;
    }
    /* #ifndef APP-NVUE */
    .uni-datetime-picker--btn:active {
        opacity: 0.7;
    }
    /* #endif */
</style>
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
{
    "uni-datetime-picker.selectDate": "select date",
    "uni-datetime-picker.selectTime": "select time",
    "uni-datetime-picker.selectDateTime": "select date and time",
    "uni-datetime-picker.startDate": "start date",
    "uni-datetime-picker.endDate": "end date",
    "uni-datetime-picker.startTime": "start time",
    "uni-datetime-picker.endTime": "end time",
    "uni-datetime-picker.ok": "ok",
    "uni-datetime-picker.clear": "clear",
    "uni-datetime-picker.cancel": "cancel",
    "uni-datetime-picker.year": "-",
    "uni-datetime-picker.month": "",
    "uni-calender.MON": "MON",
    "uni-calender.TUE": "TUE",
    "uni-calender.WED": "WED",
    "uni-calender.THU": "THU",
    "uni-calender.FRI": "FRI",
    "uni-calender.SAT": "SAT",
    "uni-calender.SUN": "SUN",
    "uni-calender.confirm": "confirm"
}
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
    en,
    'zh-Hans': zhHans,
    'zh-Hant': zhHant
}
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
{
    "uni-datetime-picker.selectDate": "选择日期",
    "uni-datetime-picker.selectTime": "选择时间",
    "uni-datetime-picker.selectDateTime": "选择日期时间",
    "uni-datetime-picker.startDate": "开始日期",
    "uni-datetime-picker.endDate": "结束日期",
    "uni-datetime-picker.startTime": "开始时间",
    "uni-datetime-picker.endTime": "结束时间",
    "uni-datetime-picker.ok": "确定",
    "uni-datetime-picker.clear": "清除",
    "uni-datetime-picker.cancel": "取消",
    "uni-datetime-picker.year": "å¹´",
    "uni-datetime-picker.month": "月",
    "uni-calender.SUN": "日",
    "uni-calender.MON": "一",
    "uni-calender.TUE": "二",
    "uni-calender.WED": "三",
    "uni-calender.THU": "四",
    "uni-calender.FRI": "五",
    "uni-calender.SAT": "六",
    "uni-calender.confirm": "确认"
}
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
{
  "uni-datetime-picker.selectDate": "選擇日期",
  "uni-datetime-picker.selectTime": "選擇時間",
  "uni-datetime-picker.selectDateTime": "選擇日期時間",
  "uni-datetime-picker.startDate": "開始日期",
  "uni-datetime-picker.endDate": "結束日期",
  "uni-datetime-picker.startTime": "開始时间",
  "uni-datetime-picker.endTime": "結束时间",
  "uni-datetime-picker.ok": "確定",
  "uni-datetime-picker.clear": "清除",
  "uni-datetime-picker.cancel": "取消",
  "uni-datetime-picker.year": "å¹´",
  "uni-datetime-picker.month": "月",
  "uni-calender.SUN": "日",
  "uni-calender.MON": "一",
  "uni-calender.TUE": "二",
  "uni-calender.WED": "三",
  "uni-calender.THU": "四",
  "uni-calender.FRI": "五",
  "uni-calender.SAT": "六",
  "uni-calender.confirm": "確認"
}
uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,940 @@
<template>
    <view class="uni-datetime-picker">
        <view @click="initTimePicker">
            <slot>
                <view class="uni-datetime-picker-timebox-pointer"
                    :class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
                    <text class="uni-datetime-picker-text">{{time}}</text>
                    <view v-if="!time" class="uni-datetime-picker-time">
                        <text class="uni-datetime-picker-text">{{selectTimeText}}</text>
                    </view>
                </view>
            </slot>
        </view>
        <view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
        <view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
            :style="fixNvueBug">
            <view class="uni-title">
                <text class="uni-datetime-picker-text">{{selectTimeText}}</text>
            </view>
            <view v-if="dateShow" class="uni-datetime-picker__container-box">
                <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
                    @change="bindDateChange">
                    <picker-view-column>
                        <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
                            <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
                        </view>
                    </picker-view-column>
                    <picker-view-column>
                        <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
                            <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
                        </view>
                    </picker-view-column>
                    <picker-view-column>
                        <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
                            <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
                        </view>
                    </picker-view-column>
                </picker-view>
                <!-- å…¼å®¹ nvue ä¸æ”¯æŒä¼ªç±» -->
                <text class="uni-datetime-picker-sign sign-left">-</text>
                <text class="uni-datetime-picker-sign sign-right">-</text>
            </view>
            <view v-if="timeShow" class="uni-datetime-picker__container-box">
                <picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
                    :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
                    <picker-view-column>
                        <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
                            <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
                        </view>
                    </picker-view-column>
                    <picker-view-column>
                        <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
                            <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
                        </view>
                    </picker-view-column>
                    <picker-view-column v-if="!hideSecond">
                        <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
                            <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
                        </view>
                    </picker-view-column>
                </picker-view>
                <!-- å…¼å®¹ nvue ä¸æ”¯æŒä¼ªç±» -->
                <text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
                <text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
            </view>
            <view class="uni-datetime-picker-btn">
                <view @click="clearTime">
                    <text class="uni-datetime-picker-btn-text">{{clearText}}</text>
                </view>
                <view class="uni-datetime-picker-btn-group">
                    <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
                        <text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
                    </view>
                    <view @click="setTime">
                        <text class="uni-datetime-picker-btn-text">{{okText}}</text>
                    </view>
                </view>
            </view>
        </view>
    </view>
</template>
<script>
    import {
        initVueI18n
    } from '@dcloudio/uni-i18n'
    import i18nMessages from './i18n/index.js'
    const {
        t
    } = initVueI18n(i18nMessages)
    import {
        fixIosDateFormat
    } from './util'
    /**
     * DatetimePicker æ—¶é—´é€‰æ‹©å™¨
     * @description å¯ä»¥åŒæ—¶é€‰æ‹©æ—¥æœŸå’Œæ—¶é—´çš„选择器
     * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
     * @property {String} type = [datetime | date | time] æ˜¾ç¤ºæ¨¡å¼
     * @property {Boolean} multiple = [true|false] æ˜¯å¦å¤šé€‰
     * @property {String|Number} value é»˜è®¤å€¼
     * @property {String|Number} start èµ·å§‹æ—¥æœŸæˆ–æ—¶é—´
     * @property {String|Number} end èµ·å§‹æ—¥æœŸæˆ–æ—¶é—´
     * @property {String} return-type = [timestamp | string]
     * @event {Function} change  é€‰ä¸­å‘生变化触发
     */
    export default {
        name: 'UniDatetimePicker',
        data() {
            return {
                indicatorStyle: `height: 50px;`,
                visible: false,
                fixNvueBug: {},
                dateShow: true,
                timeShow: true,
                title: '日期和时间',
                // è¾“入框当前时间
                time: '',
                // å½“前的年月日时分秒
                year: 1920,
                month: 0,
                day: 0,
                hour: 0,
                minute: 0,
                second: 0,
                // èµ·å§‹æ—¶é—´
                startYear: 1920,
                startMonth: 1,
                startDay: 1,
                startHour: 0,
                startMinute: 0,
                startSecond: 0,
                // ç»“束时间
                endYear: 2120,
                endMonth: 12,
                endDay: 31,
                endHour: 23,
                endMinute: 59,
                endSecond: 59,
            }
        },
        options: {
            // #ifdef MP-TOUTIAO
            virtualHost: false,
            // #endif
            // #ifndef MP-TOUTIAO
            virtualHost: true
            // #endif
        },
        props: {
            type: {
                type: String,
                default: 'datetime'
            },
            value: {
                type: [String, Number],
                default: ''
            },
            modelValue: {
                type: [String, Number],
                default: ''
            },
            start: {
                type: [Number, String],
                default: ''
            },
            end: {
                type: [Number, String],
                default: ''
            },
            returnType: {
                type: String,
                default: 'string'
            },
            disabled: {
                type: [Boolean, String],
                default: false
            },
            border: {
                type: [Boolean, String],
                default: true
            },
            hideSecond: {
                type: [Boolean, String],
                default: false
            }
        },
        watch: {
            // #ifndef VUE3
            value: {
                handler(newVal) {
                    if (newVal) {
                        this.parseValue(fixIosDateFormat(newVal))
                        this.initTime(false)
                    } else {
                        this.time = ''
                        this.parseValue(Date.now())
                    }
                },
                immediate: true
            },
            // #endif
            // #ifdef VUE3
            modelValue: {
                handler(newVal) {
                    if (newVal) {
                        this.parseValue(fixIosDateFormat(newVal))
                        this.initTime(false)
                    } else {
                        this.time = ''
                        this.parseValue(Date.now())
                    }
                },
                immediate: true
            },
            // #endif
            type: {
                handler(newValue) {
                    if (newValue === 'date') {
                        this.dateShow = true
                        this.timeShow = false
                        this.title = '日期'
                    } else if (newValue === 'time') {
                        this.dateShow = false
                        this.timeShow = true
                        this.title = '时间'
                    } else {
                        this.dateShow = true
                        this.timeShow = true
                        this.title = '日期和时间'
                    }
                },
                immediate: true
            },
            start: {
                handler(newVal) {
                    this.parseDatetimeRange(fixIosDateFormat(newVal), 'start')
                },
                immediate: true
            },
            end: {
                handler(newVal) {
                    this.parseDatetimeRange(fixIosDateFormat(newVal), 'end')
                },
                immediate: true
            },
            // æœˆã€æ—¥ã€æ—¶ã€åˆ†ã€ç§’可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项
            months(newVal) {
                this.checkValue('month', this.month, newVal)
            },
            days(newVal) {
                this.checkValue('day', this.day, newVal)
            },
            hours(newVal) {
                this.checkValue('hour', this.hour, newVal)
            },
            minutes(newVal) {
                this.checkValue('minute', this.minute, newVal)
            },
            seconds(newVal) {
                this.checkValue('second', this.second, newVal)
            }
        },
        computed: {
            // å½“前年、月、日、时、分、秒选择范围
            years() {
                return this.getCurrentRange('year')
            },
            months() {
                return this.getCurrentRange('month')
            },
            days() {
                return this.getCurrentRange('day')
            },
            hours() {
                return this.getCurrentRange('hour')
            },
            minutes() {
                return this.getCurrentRange('minute')
            },
            seconds() {
                return this.getCurrentRange('second')
            },
            // picker å½“前值数组
            ymd() {
                return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
            },
            hms() {
                return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
            },
            // å½“前 date æ˜¯ start
            currentDateIsStart() {
                return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
            },
            // å½“前 date æ˜¯ end
            currentDateIsEnd() {
                return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
            },
            // å½“前年、月、日、时、分、秒的最小值和最大值
            minYear() {
                return this.startYear
            },
            maxYear() {
                return this.endYear
            },
            minMonth() {
                if (this.year === this.startYear) {
                    return this.startMonth
                } else {
                    return 1
                }
            },
            maxMonth() {
                if (this.year === this.endYear) {
                    return this.endMonth
                } else {
                    return 12
                }
            },
            minDay() {
                if (this.year === this.startYear && this.month === this.startMonth) {
                    return this.startDay
                } else {
                    return 1
                }
            },
            maxDay() {
                if (this.year === this.endYear && this.month === this.endMonth) {
                    return this.endDay
                } else {
                    return this.daysInMonth(this.year, this.month)
                }
            },
            minHour() {
                if (this.type === 'datetime') {
                    if (this.currentDateIsStart) {
                        return this.startHour
                    } else {
                        return 0
                    }
                }
                if (this.type === 'time') {
                    return this.startHour
                }
            },
            maxHour() {
                if (this.type === 'datetime') {
                    if (this.currentDateIsEnd) {
                        return this.endHour
                    } else {
                        return 23
                    }
                }
                if (this.type === 'time') {
                    return this.endHour
                }
            },
            minMinute() {
                if (this.type === 'datetime') {
                    if (this.currentDateIsStart && this.hour === this.startHour) {
                        return this.startMinute
                    } else {
                        return 0
                    }
                }
                if (this.type === 'time') {
                    if (this.hour === this.startHour) {
                        return this.startMinute
                    } else {
                        return 0
                    }
                }
            },
            maxMinute() {
                if (this.type === 'datetime') {
                    if (this.currentDateIsEnd && this.hour === this.endHour) {
                        return this.endMinute
                    } else {
                        return 59
                    }
                }
                if (this.type === 'time') {
                    if (this.hour === this.endHour) {
                        return this.endMinute
                    } else {
                        return 59
                    }
                }
            },
            minSecond() {
                if (this.type === 'datetime') {
                    if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
                        return this.startSecond
                    } else {
                        return 0
                    }
                }
                if (this.type === 'time') {
                    if (this.hour === this.startHour && this.minute === this.startMinute) {
                        return this.startSecond
                    } else {
                        return 0
                    }
                }
            },
            maxSecond() {
                if (this.type === 'datetime') {
                    if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
                        return this.endSecond
                    } else {
                        return 59
                    }
                }
                if (this.type === 'time') {
                    if (this.hour === this.endHour && this.minute === this.endMinute) {
                        return this.endSecond
                    } else {
                        return 59
                    }
                }
            },
            /**
             * for i18n
             */
            selectTimeText() {
                return t("uni-datetime-picker.selectTime")
            },
            okText() {
                return t("uni-datetime-picker.ok")
            },
            clearText() {
                return t("uni-datetime-picker.clear")
            },
            cancelText() {
                return t("uni-datetime-picker.cancel")
            }
        },
        mounted() {
            // #ifdef APP-NVUE
            const res = uni.getSystemInfoSync();
            this.fixNvueBug = {
                top: res.windowHeight / 2,
                left: res.windowWidth / 2
            }
            // #endif
        },
        methods: {
            /**
             * @param {Object} item
             * å°äºŽ 10 åœ¨å‰é¢åŠ ä¸ª 0
             */
            lessThanTen(item) {
                return item < 10 ? '0' + item : item
            },
            /**
             * è§£æžæ—¶åˆ†ç§’字符串,例如:00:00:00
             * @param {String} timeString
             */
            parseTimeType(timeString) {
                if (timeString) {
                    let timeArr = timeString.split(':')
                    this.hour = Number(timeArr[0])
                    this.minute = Number(timeArr[1])
                    this.second = Number(timeArr[2])
                }
            },
            /**
             * è§£æžé€‰æ‹©å™¨åˆå§‹å€¼ï¼Œç±»åž‹å¯ä»¥æ˜¯å­—符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000
             * @param {String | Number} datetime
             */
            initPickerValue(datetime) {
                let defaultValue = null
                if (datetime) {
                    defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
                } else {
                    defaultValue = Date.now()
                    defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
                }
                this.parseValue(defaultValue)
            },
            /**
             * åˆå§‹å€¼è§„则:
             * - ç”¨æˆ·è®¾ç½®åˆå§‹å€¼ value
             *     - è®¾ç½®äº†èµ·å§‹æ—¶é—´ start、终止时间 end,并 start < value < end,初始值为 value, å¦åˆ™åˆå§‹å€¼ä¸º start
             *     - åªè®¾ç½®äº†èµ·å§‹æ—¶é—´ start,并 start < value,初始值为 value,否则初始值为 start
             *     - åªè®¾ç½®äº†ç»ˆæ­¢æ—¶é—´ end,并 value < end,初始值为 value,否则初始值为 end
             *     - æ— èµ·å§‹ç»ˆæ­¢æ—¶é—´ï¼Œåˆ™åˆå§‹å€¼ä¸º value
             * - æ— åˆå§‹å€¼ value,则初始值为当前本地时间 Date.now()
             * @param {Object} value
             * @param {Object} dateBase
             */
            compareValueWithStartAndEnd(value, start, end) {
                let winner = null
                value = this.superTimeStamp(value)
                start = this.superTimeStamp(start)
                end = this.superTimeStamp(end)
                if (start && end) {
                    if (value < start) {
                        winner = new Date(start)
                    } else if (value > end) {
                        winner = new Date(end)
                    } else {
                        winner = new Date(value)
                    }
                } else if (start && !end) {
                    winner = start <= value ? new Date(value) : new Date(start)
                } else if (!start && end) {
                    winner = value <= end ? new Date(value) : new Date(end)
                } else {
                    winner = new Date(value)
                }
                return winner
            },
            /**
             * è½¬æ¢ä¸ºå¯æ¯”较的时间戳,接受日期、时分秒、时间戳
             * @param {Object} value
             */
            superTimeStamp(value) {
                let dateBase = ''
                if (this.type === 'time' && value && typeof value === 'string') {
                    const now = new Date()
                    const year = now.getFullYear()
                    const month = now.getMonth() + 1
                    const day = now.getDate()
                    dateBase = year + '/' + month + '/' + day + ' '
                }
                if (Number(value)) {
                    value = parseInt(value)
                    dateBase = 0
                }
                return this.createTimeStamp(dateBase + value)
            },
            /**
             * è§£æžé»˜è®¤å€¼ value,字符串、时间戳
             * @param {Object} defaultTime
             */
            parseValue(value) {
                if (!value) {
                    return
                }
                if (this.type === 'time' && typeof value === "string") {
                    this.parseTimeType(value)
                } else {
                    let defaultDate = null
                    defaultDate = new Date(value)
                    if (this.type !== 'time') {
                        this.year = defaultDate.getFullYear()
                        this.month = defaultDate.getMonth() + 1
                        this.day = defaultDate.getDate()
                    }
                    if (this.type !== 'date') {
                        this.hour = defaultDate.getHours()
                        this.minute = defaultDate.getMinutes()
                        this.second = defaultDate.getSeconds()
                    }
                }
                if (this.hideSecond) {
                    this.second = 0
                }
            },
            /**
             * è§£æžå¯é€‰æ‹©æ—¶é—´èŒƒå›´ start、end,年月日字符串、时间戳
             * @param {Object} defaultTime
             */
            parseDatetimeRange(point, pointType) {
                // æ—¶é—´ä¸ºç©ºï¼Œåˆ™é‡ç½®ä¸ºåˆå§‹å€¼
                if (!point) {
                    if (pointType === 'start') {
                        this.startYear = 1920
                        this.startMonth = 1
                        this.startDay = 1
                        this.startHour = 0
                        this.startMinute = 0
                        this.startSecond = 0
                    }
                    if (pointType === 'end') {
                        this.endYear = 2120
                        this.endMonth = 12
                        this.endDay = 31
                        this.endHour = 23
                        this.endMinute = 59
                        this.endSecond = 59
                    }
                    return
                }
                if (this.type === 'time') {
                    const pointArr = point.split(':')
                    this[pointType + 'Hour'] = Number(pointArr[0])
                    this[pointType + 'Minute'] = Number(pointArr[1])
                    this[pointType + 'Second'] = Number(pointArr[2])
                } else {
                    if (!point) {
                        pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
                        return
                    }
                    if (Number(point)) {
                        point = parseInt(point)
                    }
                    // datetime çš„ end æ²¡æœ‰æ—¶åˆ†ç§’, åˆ™ä¸é™åˆ¶
                    const hasTime = /[0-9]:[0-9]/
                    if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
                            point)) {
                        point = point + ' 23:59:59'
                    }
                    const pointDate = new Date(point)
                    this[pointType + 'Year'] = pointDate.getFullYear()
                    this[pointType + 'Month'] = pointDate.getMonth() + 1
                    this[pointType + 'Day'] = pointDate.getDate()
                    if (this.type === 'datetime') {
                        this[pointType + 'Hour'] = pointDate.getHours()
                        this[pointType + 'Minute'] = pointDate.getMinutes()
                        this[pointType + 'Second'] = pointDate.getSeconds()
                    }
                }
            },
            // èŽ·å– å¹´ã€æœˆã€æ—¥ã€æ—¶ã€åˆ†ã€ç§’ å½“前可选范围
            getCurrentRange(value) {
                const range = []
                for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
                    range.push(i)
                }
                return range
            },
            // å­—符串首字母大写
            capitalize(str) {
                return str.charAt(0).toUpperCase() + str.slice(1)
            },
            // æ£€æŸ¥å½“前值是否在范围内,不在则当前值重置为可选范围第一项
            checkValue(name, value, values) {
                if (values.indexOf(value) === -1) {
                    this[name] = values[0]
                }
            },
            // æ¯ä¸ªæœˆçš„实际天数
            daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
                return new Date(year, month, 0).getDate();
            },
            /**
             * ç”Ÿæˆæ—¶é—´æˆ³
             * @param {Object} time
             */
            createTimeStamp(time) {
                if (!time) return
                if (typeof time === "number") {
                    return time
                } else {
                    time = time.replace(/-/g, '/')
                    if (this.type === 'date') {
                        time = time + ' ' + '00:00:00'
                    }
                    return Date.parse(time)
                }
            },
            /**
             * ç”Ÿæˆæ—¥æœŸæˆ–时间的字符串
             */
            createDomSting() {
                const yymmdd = this.year +
                    '-' +
                    this.lessThanTen(this.month) +
                    '-' +
                    this.lessThanTen(this.day)
                let hhmmss = this.lessThanTen(this.hour) +
                    ':' +
                    this.lessThanTen(this.minute)
                if (!this.hideSecond) {
                    hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
                }
                if (this.type === 'date') {
                    return yymmdd
                } else if (this.type === 'time') {
                    return hhmmss
                } else {
                    return yymmdd + ' ' + hhmmss
                }
            },
            /**
             * åˆå§‹åŒ–返回值,并抛出 change äº‹ä»¶
             */
            initTime(emit = true) {
                this.time = this.createDomSting()
                if (!emit) return
                if (this.returnType === 'timestamp' && this.type !== 'time') {
                    this.$emit('change', this.createTimeStamp(this.time))
                    this.$emit('input', this.createTimeStamp(this.time))
                    this.$emit('update:modelValue', this.createTimeStamp(this.time))
                } else {
                    this.$emit('change', this.time)
                    this.$emit('input', this.time)
                    this.$emit('update:modelValue', this.time)
                }
            },
            /**
             * ç”¨æˆ·é€‰æ‹©æ—¥æœŸæˆ–æ—¶é—´æ›´æ–° data
             * @param {Object} e
             */
            bindDateChange(e) {
                const val = e.detail.value
                this.year = this.years[val[0]]
                this.month = this.months[val[1]]
                this.day = this.days[val[2]]
            },
            bindTimeChange(e) {
                const val = e.detail.value
                this.hour = this.hours[val[0]]
                this.minute = this.minutes[val[1]]
                this.second = this.seconds[val[2]]
            },
            /**
             * åˆå§‹åŒ–弹出层
             */
            initTimePicker() {
                if (this.disabled) return
                const value = fixIosDateFormat(this.time)
                this.initPickerValue(value)
                this.visible = !this.visible
            },
            /**
             * è§¦å‘或关闭弹框
             */
            tiggerTimePicker(e) {
                this.visible = !this.visible
            },
            /**
             * ç”¨æˆ·ç‚¹å‡»â€œæ¸…空”按钮,清空当前值
             */
            clearTime() {
                this.time = ''
                this.$emit('change', this.time)
                this.$emit('input', this.time)
                this.$emit('update:modelValue', this.time)
                this.tiggerTimePicker()
            },
            /**
             * ç”¨æˆ·ç‚¹å‡»â€œç¡®å®šâ€æŒ‰é’®
             */
            setTime() {
                this.initTime()
                this.tiggerTimePicker()
            }
        }
    }
</script>
<style lang="scss">
    $uni-primary: #007aff !default;
    .uni-datetime-picker {
        /* #ifndef APP-NVUE */
        /* width: 100%; */
        /* #endif */
    }
    .uni-datetime-picker-view {
        height: 130px;
        width: 270px;
        /* #ifndef APP-NVUE */
        cursor: pointer;
        /* #endif */
    }
    .uni-datetime-picker-item {
        height: 50px;
        line-height: 50px;
        text-align: center;
        font-size: 14px;
    }
    .uni-datetime-picker-btn {
        margin-top: 60px;
        /* #ifndef APP-NVUE */
        display: flex;
        cursor: pointer;
        /* #endif */
        flex-direction: row;
        justify-content: space-between;
    }
    .uni-datetime-picker-btn-text {
        font-size: 14px;
        color: $uni-primary;
    }
    .uni-datetime-picker-btn-group {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
    }
    .uni-datetime-picker-cancel {
        margin-right: 30px;
    }
    .uni-datetime-picker-mask {
        position: fixed;
        bottom: 0px;
        top: 0px;
        left: 0px;
        right: 0px;
        background-color: rgba(0, 0, 0, 0.4);
        transition-duration: 0.3s;
        z-index: 998;
    }
    .uni-datetime-picker-popup {
        border-radius: 8px;
        padding: 30px;
        width: 270px;
        /* #ifdef APP-NVUE */
        height: 500px;
        /* #endif */
        /* #ifdef APP-NVUE */
        width: 330px;
        /* #endif */
        background-color: #fff;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        transition-duration: 0.3s;
        z-index: 999;
    }
    .fix-nvue-height {
        /* #ifdef APP-NVUE */
        height: 330px;
        /* #endif */
    }
    .uni-datetime-picker-time {
        color: grey;
    }
    .uni-datetime-picker-column {
        height: 50px;
    }
    .uni-datetime-picker-timebox {
        border: 1px solid #E5E5E5;
        border-radius: 5px;
        padding: 7px 10px;
        /* #ifndef APP-NVUE */
        box-sizing: border-box;
        cursor: pointer;
        /* #endif */
    }
    .uni-datetime-picker-timebox-pointer {
        /* #ifndef APP-NVUE */
        cursor: pointer;
        /* #endif */
    }
    .uni-datetime-picker-disabled {
        opacity: 0.4;
        /* #ifdef H5 */
        cursor: not-allowed !important;
        /* #endif */
    }
    .uni-datetime-picker-text {
        font-size: 14px;
        line-height: 50px
    }
    .uni-datetime-picker-sign {
        position: absolute;
        top: 53px;
        /* å‡æŽ‰ 10px çš„元素高度,兼容nvue */
        color: #999;
        /* #ifdef APP-NVUE */
        font-size: 16px;
        /* #endif */
    }
    .sign-left {
        left: 86px;
    }
    .sign-right {
        right: 86px;
    }
    .sign-center {
        left: 135px;
    }
    .uni-datetime-picker__container-box {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-top: 40px;
    }
    .time-hide-second {
        width: 180px;
    }
</style>
uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1064 @@
<template>
    <view class="uni-date">
        <view class="uni-date-editor" @click="show">
            <slot>
                <view class="uni-date-editor--x"
                    :class="{'uni-date-editor--x__disabled': disabled,'uni-date-x--border': border}">
                    <view v-if="!isRange" class="uni-date-x uni-date-single">
                        <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
                        <view class="uni-date__x-input">{{ displayValue || singlePlaceholderText }}</view>
                    </view>
                    <view v-else class="uni-date-x uni-date-range">
                        <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
                        <view class="uni-date__x-input text-center">{{ displayRangeValue.startDate || startPlaceholderText }}</view>
                        <view class="range-separator">{{rangeSeparator}}</view>
                        <view class="uni-date__x-input text-center">{{ displayRangeValue.endDate || endPlaceholderText }}</view>
                    </view>
                    <view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
                        <uni-icons type="clear" color="#c0c4cc" size="22"></uni-icons>
                    </view>
                </view>
            </slot>
        </view>
        <view v-show="pickerVisible" class="uni-date-mask--pc" @click="close"></view>
        <view v-if="!isPhone" v-show="pickerVisible" ref="datePicker" class="uni-date-picker__container">
            <view v-if="!isRange" class="uni-date-single--x" :style="pickerPositionStyle">
                <view class="uni-popper__arrow"></view>
                <view v-if="hasTime" class="uni-date-changed popup-x-header">
                    <input class="uni-date__input text-center" type="text" v-model="inputDate" :placeholder="selectDateText" />
                    <time-picker type="time" v-model="pickerTime" :border="false" :disabled="!inputDate"
                        :start="timepickerStartTime" :end="timepickerEndTime" :hideSecond="hideSecond" style="width: 100%;">
                        <input class="uni-date__input text-center" type="text" v-model="pickerTime" :placeholder="selectTimeText"
                            :disabled="!inputDate" />
                    </time-picker>
                </view>
                <Calendar ref="pcSingle" :showMonth="false" :start-date="calendarRange.startDate"
                    :end-date="calendarRange.endDate" :date="calendarDate" @change="singleChange" :default-value="defaultValue"
                    style="padding: 0 8px;" />
                <view v-if="hasTime" class="popup-x-footer">
                    <text class="confirm-text" @click="confirmSingleChange">{{okText}}</text>
                </view>
            </view>
            <view v-else class="uni-date-range--x" :style="pickerPositionStyle">
                <view class="uni-popper__arrow"></view>
                <view v-if="hasTime" class="popup-x-header uni-date-changed">
                    <view class="popup-x-header--datetime">
                        <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate"
                            :placeholder="startDateText" />
                        <time-picker type="time" v-model="tempRange.startTime" :start="timepickerStartTime" :border="false"
                            :disabled="!tempRange.startDate" :hideSecond="hideSecond">
                            <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startTime"
                                :placeholder="startTimeText" :disabled="!tempRange.startDate" />
                        </time-picker>
                    </view>
                    <uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
                    <view class="popup-x-header--datetime">
                        <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate"
                            :placeholder="endDateText" />
                        <time-picker type="time" v-model="tempRange.endTime" :end="timepickerEndTime" :border="false"
                            :disabled="!tempRange.endDate" :hideSecond="hideSecond">
                            <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime"
                                :placeholder="endTimeText" :disabled="!tempRange.endDate" />
                        </time-picker>
                    </view>
                </view>
                <view class="popup-x-body">
                    <Calendar ref="left" :showMonth="false" :start-date="calendarRange.startDate"
                        :end-date="calendarRange.endDate" :range="true" :pleStatus="endMultipleStatus" @change="leftChange"
                        @firstEnterCale="updateRightCale" style="padding: 0 8px;"/>
                    <Calendar ref="right" :showMonth="false" :start-date="calendarRange.startDate"
                        :end-date="calendarRange.endDate" :range="true" @change="rightChange" :pleStatus="startMultipleStatus"
                        @firstEnterCale="updateLeftCale" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
                </view>
                <view v-if="hasTime" class="popup-x-footer">
                    <text @click="clear">{{clearText}}</text>
                    <text class="confirm-text" @click="confirmRangeChange">{{okText}}</text>
                </view>
            </view>
        </view>
        <Calendar v-if="isPhone" ref="mobile" :clearDate="false" :date="calendarDate" :defTime="mobileCalendarTime"
            :start-date="calendarRange.startDate" :end-date="calendarRange.endDate" :selectableTimes="mobSelectableTime"
            :startPlaceholder="startPlaceholder" :endPlaceholder="endPlaceholder" :default-value="defaultValue"
            :pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :hasTime="hasTime" :insert="false"
            :hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" @change="calendarClick"/>
    </view>
</template>
<script>
    /**
     * DatetimePicker æ—¶é—´é€‰æ‹©å™¨
     * @description åŒæ—¶æ”¯æŒ PC å’Œç§»åŠ¨ç«¯ä½¿ç”¨æ—¥åŽ†é€‰æ‹©æ—¥æœŸå’Œæ—¥æœŸèŒƒå›´
     * @tutorial https://ext.dcloud.net.cn/plugin?id=3962
     * @property {String} type é€‰æ‹©å™¨ç±»åž‹
     * @property {String|Number|Array|Date} value ç»‘定值
     * @property {String} placeholder å•选择时的占位内容
     * @property {String} start èµ·å§‹æ—¶é—´
     * @property {String} end ç»ˆæ­¢æ—¶é—´
     * @property {String} start-placeholder èŒƒå›´é€‰æ‹©æ—¶å¼€å§‹æ—¥æœŸçš„占位内容
     * @property {String} end-placeholder èŒƒå›´é€‰æ‹©æ—¶ç»“束日期的占位内容
     * @property {String} range-separator é€‰æ‹©èŒƒå›´æ—¶çš„分隔符
     * @property {Boolean} border = [true|false] æ˜¯å¦æœ‰è¾¹æ¡†
     * @property {Boolean} disabled = [true|false] æ˜¯å¦ç¦ç”¨
     * @property {Boolean} clearIcon = [true|false] æ˜¯å¦æ˜¾ç¤ºæ¸…除按钮(仅PC端适用)
     * @property {[String} defaultValue é€‰æ‹©å™¨æ‰“开时默认显示的时间
     * @event {Function} change ç¡®å®šæ—¥æœŸæ—¶è§¦å‘的事件
     * @event {Function} maskClick ç‚¹å‡»é®ç½©å±‚触发的事件
     * @event {Function} show æ‰“开弹出层
     * @event {Function} close å…³é—­å¼¹å‡ºå±‚
     * @event {Function} clear æ¸…除上次选中的状态和值
     **/
    import Calendar from './calendar.vue'
    import TimePicker from './time-picker.vue'
    import {
        initVueI18n
    } from '@dcloudio/uni-i18n'
    import i18nMessages from './i18n/index.js'
    import {
        getDateTime,
        getDate,
        getTime,
        getDefaultSecond,
        dateCompare,
        checkDate,
        fixIosDateFormat
    } from './util'
    export default {
        name: 'UniDatetimePicker',
        options: {
            // #ifdef MP-TOUTIAO
            virtualHost: false,
            // #endif
            // #ifndef MP-TOUTIAO
            virtualHost: true
            // #endif
        },
        components: {
            Calendar,
            TimePicker
        },
        data() {
            return {
                isRange: false,
                hasTime: false,
                displayValue: '',
                inputDate: '',
                calendarDate: '',
                pickerTime: '',
                calendarRange: {
                    startDate: '',
                    startTime: '',
                    endDate: '',
                    endTime: ''
                },
                displayRangeValue: {
                    startDate: '',
                    endDate: '',
                },
                tempRange: {
                    startDate: '',
                    startTime: '',
                    endDate: '',
                    endTime: ''
                },
                // å·¦å³æ—¥åŽ†åŒæ­¥æ•°æ®
                startMultipleStatus: {
                    before: '',
                    after: '',
                    data: [],
                    fulldate: ''
                },
                endMultipleStatus: {
                    before: '',
                    after: '',
                    data: [],
                    fulldate: ''
                },
                pickerVisible: false,
                pickerPositionStyle: null,
                isEmitValue: false,
                isPhone: false,
                isFirstShow: true,
                i18nT: () => {}
            }
        },
        props: {
            type: {
                type: String,
                default: 'datetime'
            },
            value: {
                type: [String, Number, Array, Date],
                default: ''
            },
            modelValue: {
                type: [String, Number, Array, Date],
                default: ''
            },
            start: {
                type: [Number, String],
                default: ''
            },
            end: {
                type: [Number, String],
                default: ''
            },
            returnType: {
                type: String,
                default: 'string'
            },
            placeholder: {
                type: String,
                default: ''
            },
            startPlaceholder: {
                type: String,
                default: ''
            },
            endPlaceholder: {
                type: String,
                default: ''
            },
            rangeSeparator: {
                type: String,
                default: '-'
            },
            border: {
                type: [Boolean],
                default: true
            },
            disabled: {
                type: [Boolean],
                default: false
            },
            clearIcon: {
                type: [Boolean],
                default: true
            },
            hideSecond: {
                type: [Boolean],
                default: false
            },
            defaultValue: {
                type: [String, Object, Array],
                default: ''
            }
        },
        watch: {
            type: {
                immediate: true,
                handler(newVal) {
                    this.hasTime = newVal.indexOf('time') !== -1
                    this.isRange = newVal.indexOf('range') !== -1
                }
            },
            // #ifndef VUE3
            value: {
                immediate: true,
                handler(newVal) {
                    if (this.isEmitValue) {
                        this.isEmitValue = false
                        return
                    }
                    this.initPicker(newVal)
                }
            },
            // #endif
            // #ifdef VUE3
            modelValue: {
                immediate: true,
                handler(newVal) {
                    if (this.isEmitValue) {
                        this.isEmitValue = false
                        return
                    }
                    this.initPicker(newVal)
                }
            },
            // #endif
            start: {
                immediate: true,
                handler(newVal) {
                    if (!newVal) return
                    this.calendarRange.startDate = getDate(newVal)
                    if (this.hasTime) {
                        this.calendarRange.startTime = getTime(newVal)
                    }
                }
            },
            end: {
                immediate: true,
                handler(newVal) {
                    if (!newVal) return
                    this.calendarRange.endDate = getDate(newVal)
                    if (this.hasTime) {
                        this.calendarRange.endTime = getTime(newVal, this.hideSecond)
                    }
                }
            },
        },
        computed: {
            timepickerStartTime() {
                const activeDate = this.isRange ? this.tempRange.startDate : this.inputDate
                return activeDate === this.calendarRange.startDate ? this.calendarRange.startTime : ''
            },
            timepickerEndTime() {
                const activeDate = this.isRange ? this.tempRange.endDate : this.inputDate
                return activeDate === this.calendarRange.endDate ? this.calendarRange.endTime : ''
            },
            mobileCalendarTime() {
                const timeRange = {
                    start: this.tempRange.startTime,
                    end: this.tempRange.endTime
                }
                return this.isRange ? timeRange : this.pickerTime
            },
            mobSelectableTime() {
                return {
                    start: this.calendarRange.startTime,
                    end: this.calendarRange.endTime
                }
            },
            datePopupWidth() {
                // todo
                return this.isRange ? 653 : 301
            },
            /**
             * for i18n
             */
            singlePlaceholderText() {
                return this.placeholder || (this.type === 'date' ? this.selectDateText : this.selectDateTimeText)
            },
            startPlaceholderText() {
                return this.startPlaceholder || this.startDateText
            },
            endPlaceholderText() {
                return this.endPlaceholder || this.endDateText
            },
            selectDateText() {
                return this.i18nT("uni-datetime-picker.selectDate")
            },
            selectDateTimeText() {
                return this.i18nT("uni-datetime-picker.selectDateTime")
            },
            selectTimeText() {
                return this.i18nT("uni-datetime-picker.selectTime")
            },
            startDateText() {
                return this.startPlaceholder || this.i18nT("uni-datetime-picker.startDate")
            },
            startTimeText() {
                return this.i18nT("uni-datetime-picker.startTime")
            },
            endDateText() {
                return this.endPlaceholder || this.i18nT("uni-datetime-picker.endDate")
            },
            endTimeText() {
                return this.i18nT("uni-datetime-picker.endTime")
            },
            okText() {
                return this.i18nT("uni-datetime-picker.ok")
            },
            clearText() {
                return this.i18nT("uni-datetime-picker.clear")
            },
            showClearIcon() {
                return this.clearIcon && !this.disabled && (this.displayValue || (this.displayRangeValue.startDate && this
                    .displayRangeValue.endDate))
            }
        },
        created() {
            this.initI18nT()
            this.platform()
        },
        methods: {
            initI18nT() {
                const vueI18n = initVueI18n(i18nMessages)
                this.i18nT = vueI18n.t
            },
            initPicker(newVal) {
                if ((!newVal && !this.defaultValue) || Array.isArray(newVal) && !newVal.length) {
                    this.$nextTick(() => {
                        this.clear(false)
                    })
                    return
                }
                if (!Array.isArray(newVal) && !this.isRange) {
                    if (newVal) {
                        this.displayValue = this.inputDate = this.calendarDate = getDate(newVal)
                        if (this.hasTime) {
                            this.pickerTime = getTime(newVal, this.hideSecond)
                            this.displayValue = `${this.displayValue} ${this.pickerTime}`
                        }
                    } else if (this.defaultValue) {
                        this.inputDate = this.calendarDate = getDate(this.defaultValue)
                        if (this.hasTime) {
                            this.pickerTime = getTime(this.defaultValue, this.hideSecond)
                        }
                    }
                } else {
                    const [before, after] = newVal
                    if (!before && !after) return
                    const beforeDate = getDate(before)
                    const beforeTime = getTime(before, this.hideSecond)
                    const afterDate = getDate(after)
                    const afterTime = getTime(after, this.hideSecond)
                    const startDate = beforeDate
                    const endDate = afterDate
                    this.displayRangeValue.startDate = this.tempRange.startDate = startDate
                    this.displayRangeValue.endDate = this.tempRange.endDate = endDate
                    if (this.hasTime) {
                        this.displayRangeValue.startDate = `${beforeDate} ${beforeTime}`
                        this.displayRangeValue.endDate = `${afterDate} ${afterTime}`
                        this.tempRange.startTime = beforeTime
                        this.tempRange.endTime = afterTime
                    }
                    const defaultRange = {
                        before: beforeDate,
                        after: afterDate
                    }
                    this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
                        which: 'right'
                    })
                    this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
                        which: 'left'
                    })
                }
            },
            updateLeftCale(e) {
                const left = this.$refs.left
                // è®¾ç½®èŒƒå›´é€‰
                left.cale.setHoverMultiple(e.after)
                left.setDate(this.$refs.left.nowDate.fullDate)
            },
            updateRightCale(e) {
                const right = this.$refs.right
                // è®¾ç½®èŒƒå›´é€‰
                right.cale.setHoverMultiple(e.after)
                right.setDate(this.$refs.right.nowDate.fullDate)
            },
            platform() {
                if (typeof navigator !== "undefined") {
                    this.isPhone = navigator.userAgent.toLowerCase().indexOf('mobile') !== -1
                    return
                }
                // #ifdef MP-WEIXIN
                const {
                    windowWidth
                } = uni.getWindowInfo()
                // #endif
                // #ifndef MP-WEIXIN
                const {
                    windowWidth
                } = uni.getSystemInfoSync()
                // #endif
                this.isPhone = windowWidth <= 500
                this.windowWidth = windowWidth
            },
            show() {
                this.$emit("show")
                if (this.disabled) {
                    return
                }
                this.platform()
                if (this.isPhone) {
                    setTimeout(() => {
                        this.$refs.mobile.open()
                    }, 0);
                    return
                }
                this.pickerPositionStyle = {
                    top: '10px'
                }
                const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
                dateEditor.boundingClientRect(rect => {
                    if (this.windowWidth - rect.left < this.datePopupWidth) {
                        this.pickerPositionStyle.right = 0
                    }
                }).exec()
                setTimeout(() => {
                    this.pickerVisible = !this.pickerVisible
                    if (!this.isPhone && this.isRange && this.isFirstShow) {
                        this.isFirstShow = false
                        const {
                            startDate,
                            endDate
                        } = this.calendarRange
                        if (startDate && endDate) {
                            if (this.diffDate(startDate, endDate) < 30) {
                                this.$refs.right.changeMonth('pre')
                            }
                        } else {
                            // this.$refs.right.changeMonth('next')
                            if (this.isPhone) {
                                this.$refs.right.cale.lastHover = false;
                            }
                        }
                    }
                }, 50)
            },
            close() {
                setTimeout(() => {
                    this.pickerVisible = false
                    this.$emit('maskClick', this.value)
                    this.$refs.mobile && this.$refs.mobile.close()
                }, 20)
            },
            setEmit(value) {
                if (this.returnType === "timestamp" || this.returnType === "date") {
                    if (!Array.isArray(value)) {
                        if (!this.hasTime) {
                            value = value + ' ' + '00:00:00'
                        }
                        value = this.createTimestamp(value)
                        if (this.returnType === "date") {
                            value = new Date(value)
                        }
                    } else {
                        if (!this.hasTime) {
                            value[0] = value[0] + ' ' + '00:00:00'
                            value[1] = value[1] + ' ' + '00:00:00'
                        }
                        value[0] = this.createTimestamp(value[0])
                        value[1] = this.createTimestamp(value[1])
                        if (this.returnType === "date") {
                            value[0] = new Date(value[0])
                            value[1] = new Date(value[1])
                        }
                    }
                }
                this.$emit('update:modelValue', value)
                this.$emit('input', value)
                this.$emit('change', value)
                this.isEmitValue = true
            },
            createTimestamp(date) {
                date = fixIosDateFormat(date)
                return Date.parse(new Date(date))
            },
            singleChange(e) {
                this.calendarDate = this.inputDate = e.fulldate
                if (this.hasTime) return
                this.confirmSingleChange()
            },
            confirmSingleChange() {
                if (!checkDate(this.inputDate)) {
                    const now = new Date()
                    this.calendarDate = this.inputDate = getDate(now)
                    this.pickerTime = getTime(now, this.hideSecond)
                }
                let startLaterInputDate = false
                let startDate, startTime
                if (this.start) {
                    let startString = this.start
                    if (typeof this.start === 'number') {
                        startString = getDateTime(this.start, this.hideSecond)
                    }
                    [startDate, startTime] = startString.split(' ')
                    if (this.start && !dateCompare(startDate, this.inputDate)) {
                        startLaterInputDate = true
                        this.inputDate = startDate
                    }
                }
                let endEarlierInputDate = false
                let endDate, endTime
                if (this.end) {
                    let endString = this.end
                    if (typeof this.end === 'number') {
                        endString = getDateTime(this.end, this.hideSecond)
                    }
                    [endDate, endTime] = endString.split(' ')
                    if (this.end && !dateCompare(this.inputDate, endDate)) {
                        endEarlierInputDate = true
                        this.inputDate = endDate
                    }
                }
                if (this.hasTime) {
                    if (startLaterInputDate) {
                        this.pickerTime = startTime || getDefaultSecond(this.hideSecond)
                    }
                    if (endEarlierInputDate) {
                        this.pickerTime = endTime || getDefaultSecond(this.hideSecond)
                    }
                    if (!this.pickerTime) {
                        this.pickerTime = getTime(Date.now(), this.hideSecond)
                    }
                    this.displayValue = `${this.inputDate} ${this.pickerTime}`
                } else {
                    this.displayValue = this.inputDate
                }
                this.setEmit(this.displayValue)
                this.pickerVisible = false
            },
            leftChange(e) {
                const {
                    before,
                    after
                } = e.range
                this.rangeChange(before, after)
                const obj = {
                    before: e.range.before,
                    after: e.range.after,
                    data: e.range.data,
                    fulldate: e.fulldate
                }
                this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
                this.$emit('calendarClick', e)
            },
            rightChange(e) {
                const {
                    before,
                    after
                } = e.range
                this.rangeChange(before, after)
                const obj = {
                    before: e.range.before,
                    after: e.range.after,
                    data: e.range.data,
                    fulldate: e.fulldate
                }
                this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
                this.$emit('calendarClick', e)
            },
            mobileChange(e) {
                if (this.isRange) {
                    const {
                        before,
                        after
                    } = e.range
                    if (!before) {
                        return;
                    }
                    this.handleStartAndEnd(before, after, true)
                    if (this.hasTime) {
                        const {
                            startTime,
                            endTime
                        } = e.timeRange
                        this.tempRange.startTime = startTime
                        this.tempRange.endTime = endTime
                    }
                    this.confirmRangeChange()
                } else {
                    if (this.hasTime) {
                        this.displayValue = e.fulldate + ' ' + e.time
                    } else {
                        this.displayValue = e.fulldate
                    }
                    this.setEmit(this.displayValue)
                }
                this.$refs.mobile.close()
            },
            rangeChange(before, after) {
                if (!(before && after)) return
                this.handleStartAndEnd(before, after, true)
                if (this.hasTime) return
                this.confirmRangeChange()
            },
            confirmRangeChange() {
                if (!this.tempRange.startDate || !this.tempRange.endDate) {
                    this.pickerVisible = false
                    return
                }
                if (!checkDate(this.tempRange.startDate)) {
                    this.tempRange.startDate = getDate(Date.now())
                }
                if (!checkDate(this.tempRange.endDate)) {
                    this.tempRange.endDate = getDate(Date.now())
                }
                let start, end
                let startDateLaterRangeStartDate = false
                let startDateLaterRangeEndDate = false
                let startDate, startTime
                if (this.start) {
                    let startString = this.start
                    if (typeof this.start === 'number') {
                        startString = getDateTime(this.start, this.hideSecond)
                    }
                    [startDate, startTime] = startString.split(' ')
                    if (this.start && !dateCompare(this.start, `${this.tempRange.startDate} ${this.tempRange.startTime}`)) {
                        startDateLaterRangeStartDate = true
                        this.tempRange.startDate = startDate
                    }
                    if (this.start && !dateCompare(this.start, `${this.tempRange.endDate} ${this.tempRange.endTime}`)) {
                        startDateLaterRangeEndDate = true
                        this.tempRange.endDate = startDate
                    }
                }
                let endDateEarlierRangeStartDate = false
                let endDateEarlierRangeEndDate = false
                let endDate, endTime
                if (this.end) {
                    let endString = this.end
                    if (typeof this.end === 'number') {
                        endString = getDateTime(this.end, this.hideSecond)
                    }
                    [endDate, endTime] = endString.split(' ')
                    if (this.end && !dateCompare(`${this.tempRange.startDate} ${this.tempRange.startTime}`, this.end)) {
                        endDateEarlierRangeStartDate = true
                        this.tempRange.startDate = endDate
                    }
                    if (this.end && !dateCompare(`${this.tempRange.endDate} ${this.tempRange.endTime}`, this.end)) {
                        endDateEarlierRangeEndDate = true
                        this.tempRange.endDate = endDate
                    }
                }
                if (!this.hasTime) {
                    start = this.displayRangeValue.startDate = this.tempRange.startDate
                    end = this.displayRangeValue.endDate = this.tempRange.endDate
                } else {
                    if (startDateLaterRangeStartDate) {
                        this.tempRange.startTime = startTime || getDefaultSecond(this.hideSecond)
                    } else if (endDateEarlierRangeStartDate) {
                        this.tempRange.startTime = endTime || getDefaultSecond(this.hideSecond)
                    }
                    if (!this.tempRange.startTime) {
                        this.tempRange.startTime = getTime(Date.now(), this.hideSecond)
                    }
                    if (startDateLaterRangeEndDate) {
                        this.tempRange.endTime = startTime || getDefaultSecond(this.hideSecond)
                    } else if (endDateEarlierRangeEndDate) {
                        this.tempRange.endTime = endTime || getDefaultSecond(this.hideSecond)
                    }
                    if (!this.tempRange.endTime) {
                        this.tempRange.endTime = getTime(Date.now(), this.hideSecond)
                    }
                    start = this.displayRangeValue.startDate = `${this.tempRange.startDate} ${this.tempRange.startTime}`
                    end = this.displayRangeValue.endDate = `${this.tempRange.endDate} ${this.tempRange.endTime}`
                }
                if (!dateCompare(start, end)) {
                    [start, end] = [end, start]
                }
                this.displayRangeValue.startDate = start
                this.displayRangeValue.endDate = end
                const displayRange = [start, end]
                this.setEmit(displayRange)
                this.pickerVisible = false
            },
            handleStartAndEnd(before, after, temp = false) {
                if (!before) return
                if (!after) after = before;
                const type = temp ? 'tempRange' : 'range'
                const isStartEarlierEnd = dateCompare(before, after)
                this[type].startDate = isStartEarlierEnd ? before : after
                this[type].endDate = isStartEarlierEnd ? after : before
            },
            /**
             * æ¯”较时间大小
             */
            dateCompare(startDate, endDate) {
                // è®¡ç®—截止时间
                startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
                // è®¡ç®—详细项的截止时间
                endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
                return startDate <= endDate
            },
            /**
             * æ¯”较时间差
             */
            diffDate(startDate, endDate) {
                // è®¡ç®—截止时间
                startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
                // è®¡ç®—详细项的截止时间
                endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
                const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
                return Math.abs(diff)
            },
            clear(needEmit = true) {
                if (!this.isRange) {
                    this.displayValue = ''
                    this.inputDate = ''
                    this.pickerTime = ''
                    if (this.isPhone) {
                        this.$refs.mobile && this.$refs.mobile.clearCalender()
                    } else {
                        this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
                    }
                    if (needEmit) {
                        this.$emit('change', '')
                        this.$emit('input', '')
                        this.$emit('update:modelValue', '')
                    }
                } else {
                    this.displayRangeValue.startDate = ''
                    this.displayRangeValue.endDate = ''
                    this.tempRange.startDate = ''
                    this.tempRange.startTime = ''
                    this.tempRange.endDate = ''
                    this.tempRange.endTime = ''
                    if (this.isPhone) {
                        this.$refs.mobile && this.$refs.mobile.clearCalender()
                    } else {
                        this.$refs.left && this.$refs.left.clearCalender()
                        this.$refs.right && this.$refs.right.clearCalender()
                        this.$refs.right && this.$refs.right.changeMonth('next')
                    }
                    if (needEmit) {
                        this.$emit('change', [])
                        this.$emit('input', [])
                        this.$emit('update:modelValue', [])
                    }
                }
            },
            calendarClick(e) {
                this.$emit('calendarClick', e)
            }
        }
    }
</script>
<style lang="scss">
    $uni-primary: #007aff !default;
    .uni-date {
        width: 100%;
        flex: 1;
    }
    .uni-date-x {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        border-radius: 4px;
        background-color: #fff;
        color: #666;
        font-size: 14px;
        flex: 1;
        .icon-calendar {
            padding-left: 3px;
        }
        .range-separator {
            height: 35px;
            /* #ifndef MP */
            padding: 0 2px;
            /* #endif */
            line-height: 35px;
        }
    }
    .uni-date-x--border {
        box-sizing: border-box;
        border-radius: 4px;
        border: 1px solid #e5e5e5;
    }
    .uni-date-editor--x {
        display: flex;
        align-items: center;
        position: relative;
    }
    .uni-date-editor--x .uni-date__icon-clear {
        padding-right: 3px;
        display: flex;
        align-items: center;
        /* #ifdef H5 */
        cursor: pointer;
        /* #endif */
    }
    .uni-date__x-input {
        width: auto;
        height: 35px;
        /* #ifndef MP */
        padding-left: 5px;
        /* #endif */
        position: relative;
        flex: 1;
        line-height: 35px;
        font-size: 14px;
        overflow: hidden;
    }
    .text-center {
        text-align: center;
    }
    .uni-date__input {
        height: 40px;
        width: 100%;
        line-height: 40px;
        font-size: 14px;
    }
    .uni-date-range__input {
        text-align: center;
        max-width: 142px;
    }
    .uni-date-picker__container {
        position: relative;
    }
    .uni-date-mask--pc {
        position: fixed;
        bottom: 0px;
        top: 0px;
        left: 0px;
        right: 0px;
        background-color: rgba(0, 0, 0, 0);
        transition-duration: 0.3s;
        z-index: 996;
    }
    .uni-date-single--x {
        background-color: #fff;
        position: absolute;
        top: 0;
        z-index: 999;
        border: 1px solid #EBEEF5;
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        border-radius: 4px;
    }
    .uni-date-range--x {
        background-color: #fff;
        position: absolute;
        top: 0;
        z-index: 999;
        border: 1px solid #EBEEF5;
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        border-radius: 4px;
    }
    .uni-date-editor--x__disabled {
        opacity: 0.4;
        cursor: default;
    }
    .uni-date-editor--logo {
        width: 16px;
        height: 16px;
        vertical-align: middle;
    }
    /* æ·»åŠ æ—¶é—´ */
    .popup-x-header {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
    }
    .popup-x-header--datetime {
        /* #ifndef APP-NVUE */
        display: flex;
        /* #endif */
        flex-direction: row;
        flex: 1;
    }
    .popup-x-body {
        display: flex;
    }
    .popup-x-footer {
        padding: 0 15px;
        border-top-color: #F1F1F1;
        border-top-style: solid;
        border-top-width: 1px;
        line-height: 40px;
        text-align: right;
        color: #666;
    }
    .popup-x-footer text:hover {
        color: $uni-primary;
        cursor: pointer;
        opacity: 0.8;
    }
    .popup-x-footer .confirm-text {
        margin-left: 20px;
        color: $uni-primary;
    }
    .uni-date-changed {
        text-align: center;
        color: #333;
        border-bottom-color: #F1F1F1;
        border-bottom-style: solid;
        border-bottom-width: 1px;
    }
    .uni-date-changed--time text {
        height: 50px;
        line-height: 50px;
    }
    .uni-date-changed .uni-date-changed--time {
        flex: 1;
    }
    .uni-date-changed--time-date {
        color: #333;
        opacity: 0.6;
    }
    .mr-50 {
        margin-right: 50px;
    }
    /* picker å¼¹å‡ºå±‚通用的指示小三角, todo:扩展至上下左右方向定位 */
    .uni-popper__arrow,
    .uni-popper__arrow::after {
        position: absolute;
        display: block;
        width: 0;
        height: 0;
        border: 6px solid transparent;
        border-top-width: 0;
    }
    .uni-popper__arrow {
        filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
        top: -6px;
        left: 10%;
        margin-right: 3px;
        border-bottom-color: #EBEEF5;
    }
    .uni-popper__arrow::after {
        content: " ";
        top: 1px;
        margin-left: -6px;
        border-bottom-color: #fff;
    }
</style>
uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,421 @@
class Calendar {
    constructor({
        selected,
        startDate,
        endDate,
        range,
    } = {}) {
        // å½“前日期
        this.date = this.getDateObj(new Date()) // å½“前初入日期
        // æ‰“点信息
        this.selected = selected || [];
        // èµ·å§‹æ—¶é—´
        this.startDate = startDate
        // ç»ˆæ­¢æ—¶é—´
        this.endDate = endDate
        // æ˜¯å¦èŒƒå›´é€‰æ‹©
        this.range = range
        // å¤šé€‰çŠ¶æ€
        this.cleanMultipleStatus()
        // æ¯å‘¨æ—¥æœŸ
        this.weeks = {}
        this.lastHover = false
    }
    /**
     * è®¾ç½®æ—¥æœŸ
     * @param {Object} date
     */
    setDate(date) {
        const selectDate = this.getDateObj(date)
        this.getWeeks(selectDate.fullDate)
    }
    /**
     * æ¸…理多选状态
     */
    cleanMultipleStatus() {
        this.multipleStatus = {
            before: '',
            after: '',
            data: []
        }
    }
    setStartDate(startDate) {
        this.startDate = startDate
    }
    setEndDate(endDate) {
        this.endDate = endDate
    }
    getPreMonthObj(date) {
        date = fixIosDateFormat(date)
        date = new Date(date)
        const oldMonth = date.getMonth()
        date.setMonth(oldMonth - 1)
        const newMonth = date.getMonth()
        if (oldMonth !== 0 && newMonth - oldMonth === 0) {
            date.setMonth(newMonth - 1)
        }
        return this.getDateObj(date)
    }
    getNextMonthObj(date) {
        date = fixIosDateFormat(date)
        date = new Date(date)
        const oldMonth = date.getMonth()
        date.setMonth(oldMonth + 1)
        const newMonth = date.getMonth()
        if (newMonth - oldMonth > 1) {
            date.setMonth(newMonth - 1)
        }
        return this.getDateObj(date)
    }
    /**
     * èŽ·å–æŒ‡å®šæ ¼å¼Date对象
     */
    getDateObj(date) {
        date = fixIosDateFormat(date)
        date = new Date(date)
        return {
            fullDate: getDate(date),
            year: date.getFullYear(),
            month: addZero(date.getMonth() + 1),
            date: addZero(date.getDate()),
            day: date.getDay()
        }
    }
    /**
     * èŽ·å–ä¸Šä¸€ä¸ªæœˆæ—¥æœŸé›†åˆ
     */
    getPreMonthDays(amount, dateObj) {
        const result = []
        for (let i = amount - 1; i >= 0; i--) {
            const month = dateObj.month - 1
            result.push({
                date: new Date(dateObj.year, month, -i).getDate(),
                month,
                disable: true
            })
        }
        return result
    }
    /**
     * èŽ·å–æœ¬æœˆæ—¥æœŸé›†åˆ
     */
    getCurrentMonthDays(amount, dateObj) {
        const result = []
        const fullDate = this.date.fullDate
        for (let i = 1; i <= amount; i++) {
            const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}`
            const isToday = fullDate === currentDate
            // èŽ·å–æ‰“ç‚¹ä¿¡æ¯
            const info = this.selected && this.selected.find((item) => {
                if (this.dateEqual(currentDate, item.date)) {
                    return item
                }
            })
            // æ—¥æœŸç¦ç”¨
            let disableBefore = true
            let disableAfter = true
            if (this.startDate) {
                disableBefore = dateCompare(this.startDate, currentDate)
            }
            if (this.endDate) {
                disableAfter = dateCompare(currentDate, this.endDate)
            }
            let multiples = this.multipleStatus.data
            let multiplesStatus = -1
            if (this.range && multiples) {
                multiplesStatus = multiples.findIndex((item) => {
                    return this.dateEqual(item, currentDate)
                })
            }
            const checked = multiplesStatus !== -1
            result.push({
                fullDate: currentDate,
                year: dateObj.year,
                date: i,
                multiple: this.range ? checked : false,
                beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after),
                afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after),
                month: dateObj.month,
                disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(
                    currentDate, this.endDate)),
                isToday,
                userChecked: false,
                extraInfo: info
            })
        }
        return result
    }
    /**
     * èŽ·å–ä¸‹ä¸€ä¸ªæœˆæ—¥æœŸé›†åˆ
     */
    _getNextMonthDays(amount, dateObj) {
        const result = []
        const month = dateObj.month + 1
        for (let i = 1; i <= amount; i++) {
            result.push({
                date: i,
                month,
                disable: true
            })
        }
        return result
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸè¯¦æƒ…
     * @param {Object} date
     */
    getInfo(date) {
        if (!date) {
            date = new Date()
        }
        const res = this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate)
        return res ? res : this.getDateObj(date)
    }
    /**
     * æ¯”较时间是否相等
     */
    dateEqual(before, after) {
        before = new Date(fixIosDateFormat(before))
        after = new Date(fixIosDateFormat(after))
        return before.valueOf() === after.valueOf()
    }
    /**
     *  æ¯”较真实起始日期
     */
    isLogicBefore(currentDate, before, after) {
        let logicBefore = before
        if (before && after) {
            logicBefore = dateCompare(before, after) ? before : after
        }
        return this.dateEqual(logicBefore, currentDate)
    }
    isLogicAfter(currentDate, before, after) {
        let logicAfter = after
        if (before && after) {
            logicAfter = dateCompare(before, after) ? after : before
        }
        return this.dateEqual(logicAfter, currentDate)
    }
    /**
     * èŽ·å–æ—¥æœŸèŒƒå›´å†…æ‰€æœ‰æ—¥æœŸ
     * @param {Object} begin
     * @param {Object} end
     */
    geDateAll(begin, end) {
        var arr = []
        var ab = begin.split('-')
        var ae = end.split('-')
        var db = new Date()
        db.setFullYear(ab[0], ab[1] - 1, ab[2])
        var de = new Date()
        de.setFullYear(ae[0], ae[1] - 1, ae[2])
        var unixDb = db.getTime() - 24 * 60 * 60 * 1000
        var unixDe = de.getTime() - 24 * 60 * 60 * 1000
        for (var k = unixDb; k <= unixDe;) {
            k = k + 24 * 60 * 60 * 1000
            arr.push(this.getDateObj(new Date(parseInt(k))).fullDate)
        }
        return arr
    }
    /**
     *  èŽ·å–å¤šé€‰çŠ¶æ€
     */
    setMultiple(fullDate) {
        if (!this.range) return
        let {
            before,
            after
        } = this.multipleStatus
        if (before && after) {
            if (!this.lastHover) {
                this.lastHover = true
                return
            }
            this.multipleStatus.before = fullDate
            this.multipleStatus.after = ''
            this.multipleStatus.data = []
            this.multipleStatus.fulldate = ''
            this.lastHover = false
        } else {
            if (!before) {
                this.multipleStatus.before = fullDate
                this.multipleStatus.after = undefined;
                this.lastHover = false
            } else {
                this.multipleStatus.after = fullDate
                if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
                    this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
                        .after);
                } else {
                    this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
                        .before);
                }
                this.lastHover = true
            }
        }
        this.getWeeks(fullDate)
    }
    /**
     *  é¼ æ ‡ hover æ›´æ–°å¤šé€‰çŠ¶æ€
     */
    setHoverMultiple(fullDate) {
        //抖音小程序点击会触发hover事件,需要避免一下
        // #ifndef MP-TOUTIAO
        if (!this.range || this.lastHover) return
        const {
            before
        } = this.multipleStatus
        if (!before) {
            this.multipleStatus.before = fullDate
        } else {
            this.multipleStatus.after = fullDate
            if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
                this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
            } else {
                this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
            }
        }
        this.getWeeks(fullDate)
        // #endif
    }
    /**
     * æ›´æ–°é»˜è®¤å€¼å¤šé€‰çŠ¶æ€
     */
    setDefaultMultiple(before, after) {
        this.multipleStatus.before = before
        this.multipleStatus.after = after
        if (before && after) {
            if (dateCompare(before, after)) {
                this.multipleStatus.data = this.geDateAll(before, after);
                this.getWeeks(after)
            } else {
                this.multipleStatus.data = this.geDateAll(after, before);
                this.getWeeks(before)
            }
        }
    }
    /**
     * èŽ·å–æ¯å‘¨æ•°æ®
     * @param {Object} dateData
     */
    getWeeks(dateData) {
        const {
            year,
            month,
        } = this.getDateObj(dateData)
        const preMonthDayAmount = new Date(year, month - 1, 1).getDay()
        const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData))
        const currentMonthDayAmount = new Date(year, month, 0).getDate()
        const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData))
        const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount
        const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData))
        const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays]
        const weeks = new Array(6)
        for (let i = 0; i < calendarDays.length; i++) {
            const index = Math.floor(i / 7)
            if (!weeks[index]) {
                weeks[index] = new Array(7)
            }
            weeks[index][i % 7] = calendarDays[i]
        }
        this.calendar = calendarDays
        this.weeks = weeks
    }
}
function getDateTime(date, hideSecond) {
    return `${getDate(date)} ${getTime(date, hideSecond)}`
}
function getDate(date) {
    date = fixIosDateFormat(date)
    date = new Date(date)
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    return `${year}-${addZero(month)}-${addZero(day)}`
}
function getTime(date, hideSecond) {
    date = fixIosDateFormat(date)
    date = new Date(date)
    const hour = date.getHours()
    const minute = date.getMinutes()
    const second = date.getSeconds()
    return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`
}
function addZero(num) {
    if (num < 10) {
        num = `0${num}`
    }
    return num
}
function getDefaultSecond(hideSecond) {
    return hideSecond ? '00:00' : '00:00:00'
}
function dateCompare(startDate, endDate) {
    startDate = new Date(fixIosDateFormat(startDate))
    endDate = new Date(fixIosDateFormat(endDate))
    return startDate <= endDate
}
function checkDate(date) {
    const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
    return date.match(dateReg)
}
//ios低版本15及以下,无法匹配 æ²¡æœ‰ â€™ç§’‘ æ—¶çš„æƒ…况,所以需要在末尾 ç§’ åŠ ä¸Š é—®å·
const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?)?$/;
function fixIosDateFormat(value) {
    if (typeof value === 'string' && dateTimeReg.test(value)) {
        value = value.replace(/-/g, '/')
    }
    return value
}
export {
    Calendar,
    getDateTime,
    getDate,
    getTime,
    addZero,
    getDefaultSecond,
    dateCompare,
    checkDate,
    fixIosDateFormat
}
uni_modules/uni-datetime-picker/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
{
  "id": "uni-datetime-picker",
  "displayName": "uni-datetime-picker æ—¥æœŸé€‰æ‹©å™¨",
  "version": "2.2.38",
  "description": "uni-datetime-picker æ—¥æœŸæ—¶é—´é€‰æ‹©å™¨ï¼Œæ”¯æŒæ—¥åŽ†ï¼Œæ”¯æŒèŒƒå›´é€‰æ‹©",
  "keywords": [
    "uni-datetime-picker",
    "uni-ui",
    "uniui",
    "日期时间选择器",
    "日期时间"
],
  "repository": "https://github.com/dcloudio/uni-ui",
  "engines": {
    "HBuilderX": ""
  },
  "directories": {
    "example": "../../temps/example_temps"
  },
"dcloudext": {
    "sale": {
      "regular": {
        "price": "0.00"
      },
      "sourcecode": {
        "price": "0.00"
      }
    },
    "contact": {
      "qq": ""
    },
    "declaration": {
      "ads": "无",
      "data": "无",
      "permissions": "无"
    },
    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
    "type": "component-vue"
  },
  "uni_modules": {
    "dependencies": [
            "uni-scss",
            "uni-icons"
        ],
    "encrypt": [],
    "platforms": {
      "cloud": {
        "tcb": "y",
        "aliyun": "y",
        "alipay": "n"
      },
      "client": {
        "App": {
          "app-vue": "y",
          "app-nvue": "n"
        },
        "H5-mobile": {
          "Safari": "y",
          "Android Browser": "y",
          "微信浏览器(Android)": "y",
          "QQ浏览器(Android)": "y"
        },
        "H5-pc": {
          "Chrome": "y",
          "IE": "y",
          "Edge": "y",
          "Firefox": "y",
          "Safari": "y"
        },
        "小程序": {
          "微信": "y",
          "阿里": "y",
          "百度": "y",
          "字节跳动": "y",
          "QQ": "y"
        },
        "快应用": {
          "华为": "u",
          "联盟": "u"
        },
        "Vue": {
            "vue2": "y",
            "vue3": "y"
        }
      }
    }
  }
}
uni_modules/uni-datetime-picker/readme.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
> `重要通知:组件升级更新 2.0.0 åŽï¼Œæ”¯æŒæ—¥æœŸ+时间范围选择,组件 ui å°†ä½¿ç”¨æ—¥åŽ†é€‰æ‹©æ—¥æœŸï¼Œui å˜åŒ–较大,同时支持 PC å’Œ ç§»åŠ¨ç«¯ã€‚æ­¤ç‰ˆæœ¬ä¸å‘åŽå…¼å®¹ï¼Œä¸å†æ”¯æŒå•ç‹¬çš„æ—¶é—´é€‰æ‹©ï¼ˆtype=time)及相关的 hide-second å±žæ€§ï¼ˆæ—¶é—´é€‰å¯ä½¿ç”¨å†…置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
## DatetimePicker æ—¶é—´é€‰æ‹©å™¨
> **组件名:uni-datetime-picker**
> ä»£ç å—: `uDatetimePicker`
该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker ç»„件。
**_点击 picker é»˜è®¤å€¼è§„则:_**
- è‹¥è®¾ç½®åˆå§‹å€¼ value, ä¼šæ˜¾ç¤ºåœ¨ picker æ˜¾ç¤ºæ¡†ä¸­
- è‹¥æ— åˆå§‹å€¼ value,则初始值 value ä¸ºå½“前本地时间 Date.now(), ä½†ä¸ä¼šæ˜¾ç¤ºåœ¨ picker æ˜¾ç¤ºæ¡†ä¸­
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
#### å¦‚使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui äº¤æµç¾¤ï¼š871950839