16 changed files with 4493 additions and 0 deletions
@ -0,0 +1,33 @@ |
|||
## 1.0.5(2023-02-03) |
|||
- 修复 禁用时会显示清空按钮 |
|||
## 1.0.4(2023-02-02) |
|||
- 优化 查询条件短期内多次变更只查询最后一次变更后的结果 |
|||
- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue |
|||
## 1.0.3(2023-01-16) |
|||
- 修复 不关联服务空间报错的问题 |
|||
## 1.0.2(2023-01-14) |
|||
- 新增 属性 `format` 可用于格式化显示选项内容 |
|||
## 1.0.1(2022-12-06) |
|||
- 修复 当where变化时,数据不会自动更新的问题 |
|||
## 0.1.9(2022-09-05) |
|||
- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框 |
|||
## 0.1.8(2022-08-29) |
|||
- 修复 点击的位置不准确 |
|||
## 0.1.7(2022-08-12) |
|||
- 新增 支持 disabled 属性 |
|||
## 0.1.6(2022-07-06) |
|||
- 修复 pc端宽度异常的bug |
|||
## 0.1.5 |
|||
- 修复 pc端宽度异常的bug |
|||
## 0.1.4(2022-07-05) |
|||
- 优化 显示样式 |
|||
## 0.1.3(2022-06-02) |
|||
- 修复 localdata 赋值不生效的 bug |
|||
- 新增 支持 uni.scss 修改颜色 |
|||
- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用) |
|||
## 0.1.2(2022-05-08) |
|||
- 修复 当 value 为 0 时选择不生效的 bug |
|||
## 0.1.1(2022-05-07) |
|||
- 新增 记住上次的选项(仅 collection 存在时有效) |
|||
## 0.1.0(2022-04-22) |
|||
- 初始化 |
|||
@ -0,0 +1,502 @@ |
|||
<template> |
|||
<view class="uni-stat__select"> |
|||
<span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span> |
|||
<view class="uni-stat-box" :class="{'uni-stat__actived': current}"> |
|||
<view class="uni-select" :class="{'uni-select--disabled':disabled}"> |
|||
<view class="uni-select__input-box" @click="toggleSelector"> |
|||
<view v-if="current" class="uni-select__input-text">{{current}}</view> |
|||
<view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view> |
|||
<uni-icons v-if="current && clear && !disabled" type="clear" color="#c0c4cc" size="24" @click="clearVal" /> |
|||
<uni-icons v-else :type="showSelector? 'top' : 'bottom'" size="14" color="#999" /> |
|||
</view> |
|||
<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" /> |
|||
<view class="uni-select__selector" v-if="showSelector"> |
|||
<view class="uni-popper__arrow"></view> |
|||
<scroll-view scroll-y="true" class="uni-select__selector-scroll"> |
|||
<view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0"> |
|||
<text>{{emptyTips}}</text> |
|||
</view> |
|||
<view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index" |
|||
@click="change(item)"> |
|||
<text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
/** |
|||
* DataChecklist 数据选择器 |
|||
* @description 通过数据渲染的下拉框组件 |
|||
* @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select |
|||
* @property {String} value 默认值 |
|||
* @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] |
|||
* @property {Boolean} clear 是否可以清空已选项 |
|||
* @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 |
|||
* @property {String} label 左侧标题 |
|||
* @property {String} placeholder 输入框的提示文字 |
|||
* @property {Boolean} disabled 是否禁用 |
|||
* @event {Function} change 选中发生变化触发 |
|||
*/ |
|||
|
|||
export default { |
|||
name: "uni-stat-select", |
|||
mixins: [uniCloud.mixinDatacom || {}], |
|||
props: { |
|||
localdata: { |
|||
type: Array, |
|||
default () { |
|||
return [] |
|||
} |
|||
}, |
|||
value: { |
|||
type: [String, Number], |
|||
default: '' |
|||
}, |
|||
modelValue: { |
|||
type: [String, Number], |
|||
default: '' |
|||
}, |
|||
label: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
placeholder: { |
|||
type: String, |
|||
default: '请选择' |
|||
}, |
|||
emptyTips: { |
|||
type: String, |
|||
default: '无选项' |
|||
}, |
|||
clear: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
defItem: { |
|||
type: Number, |
|||
default: 0 |
|||
}, |
|||
disabled: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
// 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}" |
|||
format: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
}, |
|||
data() { |
|||
return { |
|||
showSelector: false, |
|||
current: '', |
|||
mixinDatacomResData: [], |
|||
apps: [], |
|||
channels: [], |
|||
cacheKey: "uni-data-select-lastSelectedValue", |
|||
}; |
|||
}, |
|||
created() { |
|||
this.debounceGet = this.debounce(() => { |
|||
this.query(); |
|||
}, 300); |
|||
if (this.collection && !this.localdata.length) { |
|||
this.debounceGet(); |
|||
} |
|||
}, |
|||
computed: { |
|||
typePlaceholder() { |
|||
const text = { |
|||
'opendb-stat-app-versions': '版本', |
|||
'opendb-app-channels': '渠道', |
|||
'opendb-app-list': '应用' |
|||
} |
|||
const common = this.placeholder |
|||
const placeholder = text[this.collection] |
|||
return placeholder ? |
|||
common + placeholder : |
|||
common |
|||
}, |
|||
valueCom(){ |
|||
// #ifdef VUE3 |
|||
return this.modelValue; |
|||
// #endif |
|||
// #ifndef VUE3 |
|||
return this.value; |
|||
// #endif |
|||
} |
|||
}, |
|||
watch: { |
|||
localdata: { |
|||
immediate: true, |
|||
handler(val, old) { |
|||
if (Array.isArray(val) && old !== val) { |
|||
this.mixinDatacomResData = val |
|||
} |
|||
} |
|||
}, |
|||
valueCom(val, old) { |
|||
this.initDefVal() |
|||
}, |
|||
mixinDatacomResData: { |
|||
immediate: true, |
|||
handler(val) { |
|||
if (val.length) { |
|||
this.initDefVal() |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
debounce(fn, time = 100){ |
|||
let timer = null |
|||
return function(...args) { |
|||
if (timer) clearTimeout(timer) |
|||
timer = setTimeout(() => { |
|||
fn.apply(this, args) |
|||
}, time) |
|||
} |
|||
}, |
|||
// 执行数据库查询 |
|||
query(){ |
|||
this.mixinDatacomEasyGet(); |
|||
}, |
|||
// 监听查询条件变更事件 |
|||
onMixinDatacomPropsChange(){ |
|||
if (this.collection) { |
|||
this.debounceGet(); |
|||
} |
|||
}, |
|||
initDefVal() { |
|||
let defValue = '' |
|||
if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) { |
|||
defValue = this.valueCom |
|||
} else { |
|||
let strogeValue |
|||
if (this.collection) { |
|||
strogeValue = this.getCache() |
|||
} |
|||
if (strogeValue || strogeValue === 0) { |
|||
defValue = strogeValue |
|||
} else { |
|||
let defItem = '' |
|||
if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) { |
|||
defItem = this.mixinDatacomResData[this.defItem - 1].value |
|||
} |
|||
defValue = defItem |
|||
} |
|||
if (defValue || defValue === 0) { |
|||
this.emit(defValue) |
|||
} |
|||
} |
|||
const def = this.mixinDatacomResData.find(item => item.value === defValue) |
|||
this.current = def ? this.formatItemName(def) : '' |
|||
}, |
|||
|
|||
/** |
|||
* @param {[String, Number]} value |
|||
* 判断用户给的 value 是否同时为禁用状态 |
|||
*/ |
|||
isDisabled(value) { |
|||
let isDisabled = false; |
|||
|
|||
this.mixinDatacomResData.forEach(item => { |
|||
if (item.value === value) { |
|||
isDisabled = item.disable |
|||
} |
|||
}) |
|||
|
|||
return isDisabled; |
|||
}, |
|||
|
|||
clearVal() { |
|||
this.emit('') |
|||
if (this.collection) { |
|||
this.removeCache() |
|||
} |
|||
}, |
|||
change(item) { |
|||
if (!item.disable) { |
|||
this.showSelector = false |
|||
this.current = this.formatItemName(item) |
|||
this.emit(item.value) |
|||
} |
|||
}, |
|||
emit(val) { |
|||
this.$emit('change', val) |
|||
this.$emit('input', val) |
|||
this.$emit('update:modelValue', val) |
|||
if (this.collection) { |
|||
this.setCache(val); |
|||
} |
|||
}, |
|||
toggleSelector() { |
|||
if (this.disabled) { |
|||
return |
|||
} |
|||
|
|||
this.showSelector = !this.showSelector |
|||
}, |
|||
formatItemName(item) { |
|||
let { |
|||
text, |
|||
value, |
|||
channel_code |
|||
} = item |
|||
channel_code = channel_code ? `(${channel_code})` : '' |
|||
|
|||
if (this.format) { |
|||
// 格式化输出 |
|||
let str = ""; |
|||
str = this.format; |
|||
for (let key in item) { |
|||
str = str.replace(new RegExp(`{${key}}`,"g"),item[key]); |
|||
} |
|||
return str; |
|||
} else { |
|||
return this.collection.indexOf('app-list') > 0 ? |
|||
`${text}(${value})` : |
|||
( |
|||
text ? |
|||
text : |
|||
`未命名${channel_code}` |
|||
) |
|||
} |
|||
}, |
|||
// 获取当前加载的数据 |
|||
getLoadData(){ |
|||
return this.mixinDatacomResData; |
|||
}, |
|||
// 获取当前缓存key |
|||
getCurrentCacheKey(){ |
|||
return this.collection; |
|||
}, |
|||
// 获取缓存 |
|||
getCache(name=this.getCurrentCacheKey()){ |
|||
let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
|||
return cacheData[name]; |
|||
}, |
|||
// 设置缓存 |
|||
setCache(value, name=this.getCurrentCacheKey()){ |
|||
let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
|||
cacheData[name] = value; |
|||
uni.setStorageSync(this.cacheKey, cacheData); |
|||
}, |
|||
// 删除缓存 |
|||
removeCache(name=this.getCurrentCacheKey()){ |
|||
let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
|||
delete cacheData[name]; |
|||
uni.setStorageSync(this.cacheKey, cacheData); |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
$uni-base-color: #6a6a6a !default; |
|||
$uni-main-color: #333 !default; |
|||
$uni-secondary-color: #909399 !default; |
|||
$uni-border-3: #e5e5e5; |
|||
|
|||
|
|||
/* #ifndef APP-NVUE */ |
|||
@media screen and (max-width: 500px) { |
|||
.hide-on-phone { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
/* #endif */ |
|||
.uni-stat__select { |
|||
display: flex; |
|||
align-items: center; |
|||
// padding: 15px; |
|||
cursor: pointer; |
|||
width: 100%; |
|||
flex: 1; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.uni-stat-box { |
|||
width: 100%; |
|||
flex: 1; |
|||
} |
|||
|
|||
.uni-stat__actived { |
|||
width: 100%; |
|||
flex: 1; |
|||
// outline: 1px solid #2979ff; |
|||
} |
|||
|
|||
.uni-label-text { |
|||
font-size: 14px; |
|||
font-weight: bold; |
|||
color: $uni-base-color; |
|||
margin: auto 0; |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
.uni-select { |
|||
font-size: 14px; |
|||
border: 1px solid $uni-border-3; |
|||
box-sizing: border-box; |
|||
border-radius: 4px; |
|||
padding: 0 5px; |
|||
padding-left: 10px; |
|||
position: relative; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
user-select: none; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
align-items: center; |
|||
border-bottom: solid 1px $uni-border-3; |
|||
width: 100%; |
|||
flex: 1; |
|||
height: 35px; |
|||
|
|||
&--disabled { |
|||
background-color: #f5f7fa; |
|||
cursor: not-allowed; |
|||
} |
|||
} |
|||
|
|||
.uni-select__label { |
|||
font-size: 16px; |
|||
// line-height: 22px; |
|||
height: 35px; |
|||
padding-right: 10px; |
|||
color: $uni-secondary-color; |
|||
} |
|||
|
|||
.uni-select__input-box { |
|||
height: 35px; |
|||
position: relative; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex: 1; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
} |
|||
|
|||
.uni-select__input { |
|||
flex: 1; |
|||
font-size: 14px; |
|||
height: 22px; |
|||
line-height: 22px; |
|||
} |
|||
|
|||
.uni-select__input-plac { |
|||
font-size: 14px; |
|||
color: $uni-secondary-color; |
|||
} |
|||
|
|||
.uni-select__selector { |
|||
/* #ifndef APP-NVUE */ |
|||
box-sizing: border-box; |
|||
/* #endif */ |
|||
position: absolute; |
|||
top: calc(100% + 12px); |
|||
left: 0; |
|||
width: 100%; |
|||
background-color: #FFFFFF; |
|||
border: 1px solid #EBEEF5; |
|||
border-radius: 6px; |
|||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
|||
z-index: 3; |
|||
padding: 4px 0; |
|||
} |
|||
|
|||
.uni-select__selector-scroll { |
|||
/* #ifndef APP-NVUE */ |
|||
max-height: 200px; |
|||
box-sizing: border-box; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-select__selector-empty, |
|||
.uni-select__selector-item { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
cursor: pointer; |
|||
/* #endif */ |
|||
line-height: 35px; |
|||
font-size: 14px; |
|||
text-align: center; |
|||
/* border-bottom: solid 1px $uni-border-3; */ |
|||
padding: 0px 10px; |
|||
} |
|||
|
|||
.uni-select__selector-item:hover { |
|||
background-color: #f9f9f9; |
|||
} |
|||
|
|||
.uni-select__selector-empty:last-child, |
|||
.uni-select__selector-item:last-child { |
|||
/* #ifndef APP-NVUE */ |
|||
border-bottom: none; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-select__selector__disabled { |
|||
opacity: 0.4; |
|||
cursor: default; |
|||
} |
|||
|
|||
/* picker 弹出层通用的指示小三角 */ |
|||
.uni-popper__arrow, |
|||
.uni-popper__arrow::after { |
|||
position: absolute; |
|||
display: block; |
|||
width: 0; |
|||
height: 0; |
|||
border-color: transparent; |
|||
border-style: solid; |
|||
border-width: 6px; |
|||
} |
|||
|
|||
.uni-popper__arrow { |
|||
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
|||
top: -6px; |
|||
left: 10%; |
|||
margin-right: 3px; |
|||
border-top-width: 0; |
|||
border-bottom-color: #EBEEF5; |
|||
} |
|||
|
|||
.uni-popper__arrow::after { |
|||
content: " "; |
|||
top: 1px; |
|||
margin-left: -6px; |
|||
border-top-width: 0; |
|||
border-bottom-color: #fff; |
|||
} |
|||
|
|||
.uni-select__input-text { |
|||
// width: 280px; |
|||
width: 100%; |
|||
color: $uni-main-color; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
-o-text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.uni-select__input-placeholder { |
|||
color: $uni-base-color; |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.uni-select--mask { |
|||
position: fixed; |
|||
top: 0; |
|||
bottom: 0; |
|||
right: 0; |
|||
left: 0; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,85 @@ |
|||
{ |
|||
"id": "uni-data-select", |
|||
"displayName": "uni-data-select 下拉框选择器", |
|||
"version": "1.0.5", |
|||
"description": "通过数据驱动的下拉框选择器", |
|||
"keywords": [ |
|||
"uni-ui", |
|||
"select", |
|||
"uni-data-select", |
|||
"下拉框", |
|||
"下拉选" |
|||
], |
|||
"repository": "https://github.com/dcloudio/uni-ui", |
|||
"engines": { |
|||
"HBuilderX": "^3.1.1" |
|||
}, |
|||
"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-load-more"], |
|||
"encrypt": [], |
|||
"platforms": { |
|||
"cloud": { |
|||
"tcb": "y", |
|||
"aliyun": "y" |
|||
}, |
|||
"client": { |
|||
"App": { |
|||
"app-vue": "u", |
|||
"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", |
|||
"阿里": "u", |
|||
"百度": "u", |
|||
"字节跳动": "u", |
|||
"QQ": "u", |
|||
"京东": "u" |
|||
}, |
|||
"快应用": { |
|||
"华为": "u", |
|||
"联盟": "u" |
|||
}, |
|||
"Vue": { |
|||
"vue2": "y", |
|||
"vue3": "y" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
## DataSelect 下拉框选择器 |
|||
> **组件名:uni-data-select** |
|||
> 代码块: `uDataSelect` |
|||
|
|||
当选项过多时,使用下拉菜单展示并选择内容 |
|||
|
|||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select) |
|||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
|||
@ -0,0 +1,130 @@ |
|||
## 2.2.21(2023-02-20) |
|||
- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362) |
|||
## 2.2.20(2023-02-17) |
|||
- 优化 值为空依然选中当天问题 |
|||
- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间 |
|||
- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间 |
|||
- 优化 字节小程序日期时间范围选择,底部日期换行问题 |
|||
## 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 适配 |
|||
@ -0,0 +1,186 @@ |
|||
<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--isDay': weeks.isDay}"></view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
weeks: { |
|||
type: Object, |
|||
default () { |
|||
return {} |
|||
} |
|||
}, |
|||
calendar: { |
|||
type: Object, |
|||
default: () => { |
|||
return {} |
|||
} |
|||
}, |
|||
selected: { |
|||
type: Array, |
|||
default: () => { |
|||
return [] |
|||
} |
|||
}, |
|||
lunar: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
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-lunar-text { |
|||
font-size: 12px; |
|||
color: #333; |
|||
} |
|||
|
|||
.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--isDay { |
|||
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> |
|||
@ -0,0 +1,944 @@ |
|||
<template> |
|||
<view class="uni-calendar" @mouseleave="leaveCale"> |
|||
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" |
|||
@click="clean();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 v-if="left" class="uni-calendar__header-btn-box" @click.stop="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 v-if="right" class="uni-calendar__header-btn-box" @click.stop="next"> |
|||
<view class="uni-calendar__header-btn uni-calendar--right"></view> |
|||
</view> |
|||
<view v-if="!insert" class="dialog-close" @click="clean"> |
|||
<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" :lunar="lunar" :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 {Boolean} lunar 显示农历 |
|||
* @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":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
|||
*/ |
|||
export default { |
|||
components: { |
|||
calendarItem, |
|||
timePicker |
|||
}, |
|||
props: { |
|||
date: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
defTime: { |
|||
type: [String, Object], |
|||
default: '' |
|||
}, |
|||
selectableTimes: { |
|||
type: [Object], |
|||
default () { |
|||
return {} |
|||
} |
|||
}, |
|||
selected: { |
|||
type: Array, |
|||
default () { |
|||
return [] |
|||
} |
|||
}, |
|||
lunar: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
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 |
|||
}, |
|||
left: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
right: { |
|||
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, |
|||
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.resetSatrtDate(val) |
|||
this.cale.setDate(this.nowDate.fullDate) |
|||
this.weeks = this.cale.weeks |
|||
}, |
|||
endDate(val) { |
|||
// 字节小程序 watch 早于 created |
|||
if(!this.cale){ |
|||
return |
|||
} |
|||
this.cale.resetEndDate(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') { |
|||
this.setDate(before) |
|||
this.weeks = this.cale.weeks |
|||
} else { |
|||
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 |
|||
}, |
|||
|
|||
// 取消穿透 |
|||
clean() { |
|||
this.close() |
|||
}, |
|||
|
|||
// 蒙版点击事件 |
|||
maskClick() { |
|||
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() |
|||
}, |
|||
|
|||
bindDateChange(e) { |
|||
const value = e.detail.value + '-1' |
|||
this.init(value) |
|||
}, |
|||
/** |
|||
* 初始化日期显示 |
|||
* @param {Object} date |
|||
*/ |
|||
init(date) { |
|||
// 字节小程序 watch 早于 created |
|||
if(!this.cale){ |
|||
return |
|||
} |
|||
this.cale.setDate(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.cale.setDate(this.date) |
|||
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() { |
|||
if (!this.insert) 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, |
|||
lunar, |
|||
extraInfo |
|||
} = this.calendar |
|||
this.$emit(name, { |
|||
range: this.cale.multipleStatus, |
|||
year, |
|||
month, |
|||
date, |
|||
time: this.time, |
|||
timeRange: this.timeRange, |
|||
fulldate: fullDate, |
|||
lunar, |
|||
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() |
|||
}, |
|||
/** |
|||
* 上个月 |
|||
*/ |
|||
pre() { |
|||
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate |
|||
this.setDate(preDate) |
|||
this.monthSwitch() |
|||
|
|||
}, |
|||
/** |
|||
* 下个月 |
|||
*/ |
|||
next() { |
|||
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate |
|||
this.setDate(nextDate) |
|||
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> |
|||
@ -0,0 +1,22 @@ |
|||
{ |
|||
"uni-datetime-picker.selectDate": "select date", |
|||
"uni-datetime-picker.selectTime": "select time", |
|||
"uni-datetime-picker.selectDateTime": "select datetime", |
|||
"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" |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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": "确认" |
|||
} |
|||
@ -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": "確認" |
|||
} |
|||
@ -0,0 +1,933 @@ |
|||
<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) |
|||
|
|||
/** |
|||
* 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, |
|||
} |
|||
}, |
|||
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(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式 |
|||
this.initTime(false) |
|||
} else { |
|||
this.time = '' |
|||
this.parseValue(Date.now()) |
|||
} |
|||
}, |
|||
immediate: true |
|||
}, |
|||
// #endif |
|||
// #ifdef VUE3 |
|||
modelValue: { |
|||
handler(newVal) { |
|||
if (newVal) { |
|||
this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式 |
|||
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(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式 |
|||
}, |
|||
immediate: true |
|||
}, |
|||
end: { |
|||
handler(newVal) { |
|||
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式 |
|||
}, |
|||
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(); |
|||
}, |
|||
|
|||
//兼容 iOS、safari 日期格式 |
|||
fixIosDateFormat(value) { |
|||
if (typeof value === 'string') { |
|||
value = value.replace(/-/g, '/') |
|||
} |
|||
return value |
|||
}, |
|||
|
|||
/** |
|||
* 生成时间戳 |
|||
* @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 = this.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> |
|||
File diff suppressed because it is too large
@ -0,0 +1,464 @@ |
|||
class Calendar { |
|||
constructor({ |
|||
date, |
|||
selected, |
|||
startDate, |
|||
endDate, |
|||
range, |
|||
// multipleStatus
|
|||
} = {}) { |
|||
// 当前日期
|
|||
this.date = this.getDate(new Date()) // 当前初入日期
|
|||
// 打点信息
|
|||
this.selected = selected || []; |
|||
// 范围开始
|
|||
this.startDate = startDate |
|||
// 范围结束
|
|||
this.endDate = endDate |
|||
this.range = range |
|||
// 多选状态
|
|||
this.cleanMultipleStatus() |
|||
// 每周日期
|
|||
this.weeks = {} |
|||
// this._getWeek(this.date.fullDate)
|
|||
// this.multipleStatus = multipleStatus
|
|||
this.lastHover = false |
|||
} |
|||
/** |
|||
* 设置日期 |
|||
* @param {Object} date |
|||
*/ |
|||
setDate(date) { |
|||
this.selectDate = this.getDate(date) |
|||
this._getWeek(this.selectDate.fullDate) |
|||
} |
|||
|
|||
/** |
|||
* 清理多选状态 |
|||
*/ |
|||
cleanMultipleStatus() { |
|||
this.multipleStatus = { |
|||
before: '', |
|||
after: '', |
|||
data: [] |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 重置开始日期 |
|||
*/ |
|||
resetSatrtDate(startDate) { |
|||
// 范围开始
|
|||
this.startDate = startDate |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 重置结束日期 |
|||
*/ |
|||
resetEndDate(endDate) { |
|||
// 范围结束
|
|||
this.endDate = endDate |
|||
} |
|||
|
|||
/** |
|||
* 获取任意时间 |
|||
*/ |
|||
getDate(date, AddDayCount = 0, str = 'day') { |
|||
if (!date) { |
|||
date = new Date() |
|||
} |
|||
if (typeof date !== 'object') { |
|||
date = date.replace(/-/g, '/') |
|||
} |
|||
const dd = new Date(date) |
|||
switch (str) { |
|||
case 'day': |
|||
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
|
|||
break |
|||
case 'month': |
|||
if (dd.getDate() === 31 && AddDayCount>0) { |
|||
dd.setDate(dd.getDate() + AddDayCount) |
|||
} else { |
|||
const preMonth = dd.getMonth() |
|||
dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
|
|||
const nextMonth = dd.getMonth() |
|||
// 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
|
|||
if(AddDayCount<0 && preMonth!==0 && nextMonth-preMonth>AddDayCount){ |
|||
dd.setMonth(nextMonth+(nextMonth-preMonth+AddDayCount)) |
|||
} |
|||
// 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
|
|||
if(AddDayCount>0 && nextMonth-preMonth>AddDayCount){ |
|||
dd.setMonth(nextMonth-(nextMonth-preMonth-AddDayCount)) |
|||
} |
|||
} |
|||
break |
|||
case 'year': |
|||
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
|
|||
break |
|||
} |
|||
const y = dd.getFullYear() |
|||
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
|
|||
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
|
|||
return { |
|||
fullDate: y + '-' + m + '-' + d, |
|||
year: y, |
|||
month: m, |
|||
date: d, |
|||
day: dd.getDay() |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 获取上月剩余天数 |
|||
*/ |
|||
_getLastMonthDays(firstDay, full) { |
|||
let dateArr = [] |
|||
for (let i = firstDay; i > 0; i--) { |
|||
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate() |
|||
dateArr.push({ |
|||
date: beforeDate, |
|||
month: full.month - 1, |
|||
disable: true |
|||
}) |
|||
} |
|||
return dateArr |
|||
} |
|||
/** |
|||
* 获取本月天数 |
|||
*/ |
|||
_currentMonthDys(dateData, full) { |
|||
let dateArr = [] |
|||
let fullDate = this.date.fullDate |
|||
for (let i = 1; i <= dateData; i++) { |
|||
let isinfo = false |
|||
let nowDate = full.year + '-' + (full.month < 10 ? |
|||
full.month : full.month) + '-' + (i < 10 ? |
|||
'0' + i : i) |
|||
// 是否今天
|
|||
let isDay = fullDate === nowDate |
|||
// 获取打点信息
|
|||
let info = this.selected && this.selected.find((item) => { |
|||
if (this.dateEqual(nowDate, item.date)) { |
|||
return item |
|||
} |
|||
}) |
|||
|
|||
// 日期禁用
|
|||
let disableBefore = true |
|||
let disableAfter = true |
|||
if (this.startDate) { |
|||
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
|
|||
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
|
|||
disableBefore = this.dateCompare(this.startDate, nowDate) |
|||
} |
|||
|
|||
if (this.endDate) { |
|||
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
|
|||
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
|
|||
disableAfter = this.dateCompare(nowDate, this.endDate) |
|||
} |
|||
let multiples = this.multipleStatus.data |
|||
let checked = false |
|||
let multiplesStatus = -1 |
|||
if (this.range) { |
|||
if (multiples) { |
|||
multiplesStatus = multiples.findIndex((item) => { |
|||
return this.dateEqual(item, nowDate) |
|||
}) |
|||
} |
|||
if (multiplesStatus !== -1) { |
|||
checked = true |
|||
} |
|||
} |
|||
let data = { |
|||
fullDate: nowDate, |
|||
year: full.year, |
|||
date: i, |
|||
multiple: this.range ? checked : false, |
|||
beforeMultiple: this.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after), |
|||
afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after), |
|||
month: full.month, |
|||
disable: !(disableBefore && disableAfter), |
|||
isDay, |
|||
userChecked: false |
|||
} |
|||
if (info) { |
|||
data.extraInfo = info |
|||
} |
|||
|
|||
dateArr.push(data) |
|||
} |
|||
return dateArr |
|||
} |
|||
/** |
|||
* 获取下月天数 |
|||
*/ |
|||
_getNextMonthDays(surplus, full) { |
|||
let dateArr = [] |
|||
for (let i = 1; i < surplus + 1; i++) { |
|||
dateArr.push({ |
|||
date: i, |
|||
month: Number(full.month) + 1, |
|||
disable: true |
|||
}) |
|||
} |
|||
return dateArr |
|||
} |
|||
|
|||
/** |
|||
* 获取当前日期详情 |
|||
* @param {Object} date |
|||
*/ |
|||
getInfo(date) { |
|||
if (!date) { |
|||
date = new Date() |
|||
} |
|||
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate) |
|||
return dateInfo |
|||
} |
|||
|
|||
/** |
|||
* 比较时间大小 |
|||
*/ |
|||
dateCompare(startDate, endDate) { |
|||
// 计算截止时间
|
|||
startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
|||
// 计算详细项的截止时间
|
|||
endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
|||
if (startDate <= endDate) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 比较时间是否相等 |
|||
*/ |
|||
dateEqual(before, after) { |
|||
// 计算截止时间
|
|||
before = new Date(before.replace('-', '/').replace('-', '/')) |
|||
// 计算详细项的截止时间
|
|||
after = new Date(after.replace('-', '/').replace('-', '/')) |
|||
if (before.getTime() - after.getTime() === 0) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 比较真实起始日期 |
|||
*/ |
|||
|
|||
isLogicBefore(currentDay, before, after) { |
|||
let logicBefore = before |
|||
if (before && after) { |
|||
logicBefore = this.dateCompare(before, after) ? before : after |
|||
} |
|||
return this.dateEqual(logicBefore, currentDay) |
|||
} |
|||
|
|||
isLogicAfter(currentDay, before, after) { |
|||
let logicAfter = after |
|||
if (before && after) { |
|||
logicAfter = this.dateCompare(before, after) ? after : before |
|||
} |
|||
return this.dateEqual(logicAfter, currentDay) |
|||
} |
|||
|
|||
/** |
|||
* 获取日期范围内所有日期 |
|||
* @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.getDate(new Date(parseInt(k))).fullDate) |
|||
} |
|||
return arr |
|||
} |
|||
|
|||
/** |
|||
* 获取多选状态 |
|||
*/ |
|||
setMultiple(fullDate) { |
|||
let { |
|||
before, |
|||
after |
|||
} = this.multipleStatus |
|||
if (!this.range) return |
|||
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.lastHover = false |
|||
} else { |
|||
this.multipleStatus.after = fullDate |
|||
if (this.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._getWeek(fullDate) |
|||
} |
|||
|
|||
/** |
|||
* 鼠标 hover 更新多选状态 |
|||
*/ |
|||
setHoverMultiple(fullDate) { |
|||
let { |
|||
before, |
|||
after |
|||
} = this.multipleStatus |
|||
|
|||
if (!this.range) return |
|||
if (this.lastHover) return |
|||
|
|||
if (!before) { |
|||
this.multipleStatus.before = fullDate |
|||
} else { |
|||
this.multipleStatus.after = fullDate |
|||
if (this.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._getWeek(fullDate) |
|||
} |
|||
|
|||
/** |
|||
* 更新默认值多选状态 |
|||
*/ |
|||
setDefaultMultiple(before, after) { |
|||
this.multipleStatus.before = before |
|||
this.multipleStatus.after = after |
|||
if (before && after) { |
|||
if (this.dateCompare(before, after)) { |
|||
this.multipleStatus.data = this.geDateAll(before, after); |
|||
this._getWeek(after) |
|||
} else { |
|||
this.multipleStatus.data = this.geDateAll(after, before); |
|||
this._getWeek(before) |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取每周数据 |
|||
* @param {Object} dateData |
|||
*/ |
|||
_getWeek(dateData) { |
|||
const { |
|||
fullDate, |
|||
year, |
|||
month, |
|||
date, |
|||
day |
|||
} = this.getDate(dateData) |
|||
let firstDay = new Date(year, month - 1, 1).getDay() |
|||
let currentDay = new Date(year, month, 0).getDate() |
|||
let dates = { |
|||
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
|
|||
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
|
|||
nextMonthDays: [], // 下个月开始几天
|
|||
weeks: [] |
|||
} |
|||
let canlender = [] |
|||
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) |
|||
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) |
|||
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) |
|||
let weeks = {} |
|||
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
|
|||
for (let i = 0; i < canlender.length; i++) { |
|||
if (i % 7 === 0) { |
|||
weeks[parseInt(i / 7)] = new Array(7) |
|||
} |
|||
weeks[parseInt(i / 7)][i % 7] = canlender[i] |
|||
} |
|||
this.canlender = canlender |
|||
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(startDate.replace(/-/g, '/')) |
|||
endDate = new Date(endDate.replace(/-/g, '/')) |
|||
return startDate <= endDate |
|||
} |
|||
|
|||
function checkDate(date){ |
|||
const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g |
|||
return date.match(dateReg) |
|||
} |
|||
|
|||
function fixIosDateFormat(value) { |
|||
// #ifndef MP
|
|||
if (typeof value === 'string') { |
|||
value = value.replace(/-/g, '/') |
|||
} |
|||
// #endif
|
|||
return value |
|||
} |
|||
|
|||
export {Calendar, getDateTime, getDate, getTime, addZero, getDefaultSecond, dateCompare, checkDate, fixIosDateFormat} |
|||
@ -0,0 +1,87 @@ |
|||
{ |
|||
"id": "uni-datetime-picker", |
|||
"displayName": "uni-datetime-picker 日期选择器", |
|||
"version": "2.2.21", |
|||
"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" |
|||
}, |
|||
"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" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
Loading…
Reference in new issue