新增时间日期时间组件

master
于肖磊 2025-06-30 13:36:51 +08:00
parent 7392774e3a
commit 3070eb7572
8 changed files with 809 additions and 2 deletions

View File

@ -932,4 +932,70 @@ export const formatNumber = (num, is_convert) => {
// 如果不需要转换,移除所有逗号并返回
return num.toString().replace(/,/g, '');
}
};
};
// 明确声明 styles 类型
const date_styles = {
horizontal: {
year: '-',
month: '-',
day: ' ',
hour: ':',
minute: ':',
second: '',
},
slash: {
year: '/',
month: '/',
day: ' ',
hour: ':',
minute: ':',
second: '',
},
chinese: {
year: '年',
month: '月',
day: '日 ',
hour: '时',
minute: '分',
second: '秒',
},
};
/**
* 将时间戳转换为指定格式的日期字符串
* @param {number|string} time - 时间
* @param {string} [date_style='horizontal'] - 日期格式风格horizontal/slash/chinese
* @returns {string} 格式化后的日期时间字符串
*/
export const time_stamp = (time, date_style = 'horizontal', date_type) => {
// 如果时间为空或不是数字,则返回空字符串
if (isEmpty(time)) {
return '';
}
const date = new Date(time);
// 获取各时间组件
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
// 获取对应的分隔符配置
const style = date_styles[date_style] || date_styles.horizontal;
// 组合成完整日期时间字符串
if (date_type == 'option1') {
return `${hours}${style.hour}${minutes}${date_style == 'chinese' ? style.minute : ''}`;
} else if (date_type == 'option2'){
return `${hours}${style.hour}${minutes}${style.minute}${seconds}${date_style == 'chinese' ? style.second : ''}`;
} else if (date_type == 'option3'){
return `${year}${style.year}${month}${date_style == 'chinese' ? style.month : ''}`;
} else if (date_type == 'option4'){
return `${year}${style.year}${month}${style.month}${day}${ date_style == 'chinese' ? style.day : ''}`;
} else if (date_type == 'option5'){
return `${year}${style.year}${month}${style.month}${day}${style.day}${hours}${style.hour}${minutes}${date_style == 'chinese' ? style.minute : ''}`;
} else {
return `${year}${style.year}${month}${style.month}${day}${style.day}${hours}${style.hour}${minutes}${style.minute}${seconds}${date_style == 'chinese' ? style.second : ''}`;
}
}

View File

@ -0,0 +1,281 @@
<template>
<view>
<uni-popup ref="popup" type="bottom" :animation="true" @change="change">
<view class="popup-content">
<view>
<view class="headBox">
<view @click="close"><text class="text1">取消</text></view>
<view class="uni-page-head-title" v-if="titleShow">{{timeTitle}}</view>
<view><text class="text1 blue" @click="ok"></text></view>
</view>
<picker-view :indicator-style="indicatorStyle" :value="value" @change="bindChange">
<picker-view-column v-for="(arr, n) in dateTimeArr" :key="n">
<view class="item" v-for="(obj, index) in arr" :key="index">{{obj}}{{ unitArr[n] || '' }}</view>
</picker-view-column>
<!-- <picker-view-column>
<view class="item" v-for="(item, index) in years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in hourArr" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in minArr" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in secondArr" :key="index">{{item}}</view>
</picker-view-column> -->
</picker-view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import uniPopup from '../uni-popup/uni-popup.vue'
export default {
name: 'MyDatetime',
components: {
'uni-popup': uniPopup
},
props: {
titleShow: {
type: Boolean,
default: true
},
timestr: {
//
type: String,
default: ''
},
//
shownum: {
type: [String, Number],
default: 6
}
},
data() {
const date = new Date()
const years = []
const year = date.getFullYear()
const months = []
const month = date.getMonth() + 1
const days = []
const day = date.getDate()
for (let i = 1970, len = date.getFullYear() + 100; i <= len; i++) {
years.push(i)
}
for (let i = 1; i <= 12; i++) {
months.push(i < 10 ? ('0'+ i.toString()) : i)
}
for (let i = 1; i <= 31; i++) {
days.push(i < 10 ? ('0'+ i.toString()) : i)
}
const hourArr = []
for (let i = 0; i < 24; i++) {
hourArr.push(i < 10 ? ('0'+ i.toString()) : i)
}
//
const minSecond = []
for (let i = 0; i <= 59; i++) {
minSecond.push(i < 10 ? ('0'+ i.toString()) : i)
}
return {
thirtyOne: days,
dateArr: [years, months, days, hourArr, minSecond, minSecond ],
unitArr: [ '年', '月', '日', '时', '分', '秒'],
obj: { 0: 'year', 1: 'month', 2: 'day', 3: 'hour', 4: 'minute', 5: 'second' },
years,
months,
days,
hourArr,
minArr: minSecond,
secondArr: minSecond,
year,
month,
day,
hour: '00',
minute: '00',
second: '00',
value: [ year - 1970, month - 1, day - 1],
visible: true,
indicatorStyle: `height: 100rpx;`,
timer: null
}
},
computed: {
//
dateTimeArr () {
var num = parseInt(this.shownum) || 6
if (num <= 6 && num > 0) {
return this.dateArr.slice(0, num)
}
return this.dateArr
},
//
timeTitle () {
var str = ''
var num = parseInt(this.shownum) || 6
if ( num > 0 && num < 6 ) {
for (var i = 0; i < num; i++) {
str = str + this[this.obj[i]] + this.unitArr[i]
}
return str
}
return `${this.year}${this.month}${this.day}${this.hour}${this.minute}${this.second}`
}
},
watch: {
//
month () {
this.countDay()
},
year () {
this.countDay()
}
},
created() {
this.countDay();
},
destroyed() {
this.timer && clearTimeout(this.timer)
},
methods: {
//
countDay () {
var index = new Date(this.year, parseInt(this.month), 0).getDate()
var days = this.thirtyOne.slice(0, index)
this.dateArr.splice(2, 1, days)
this.days = days
},
// timeStr: 2020-01-01 00:00:00
timeFill (timeStr) {
var dateObj = timeStr ? new Date(timeStr.replace(/-/g,'/')) : new Date();
var timeObj = {
year: dateObj.getFullYear(),
month: dateObj.getMonth() + 1,
day: dateObj.getDate(),
h: dateObj.getHours(),
m: dateObj.getMinutes(),
s: dateObj.getSeconds()
}
this.timer = setTimeout(() => {
this.value = [ (timeObj.year - 1970) || 0, timeObj.month - 1, timeObj.day - 1, timeObj.h, timeObj.m, timeObj.s ]
this.year = this.years[this.value[0]]
this.month = this.months[this.value[1]]
this.day = this.days[this.value[2]]
this.hour = this.hourArr[this.value[3]]
this.minute = this.minArr[this.value[4]]
this.second = this.secondArr[this.value[5]]
}, 100)
},
//
ok () {
var obj = {
year: this.year,
month: this.month,
day: this.day,
hour: this.hour,
minute: this.minute,
second: this.second,
1: this.year,
2: `${this.year}-${this.month}`,
3: `${this.year}-${this.month}-${this.day}`,
4: `${this.year}-${this.month}-${this.day} ${this.hour}`,
5: `${this.year}-${this.month}-${this.day} ${this.hour}:${this.minute}`,
6: `${this.year}-${this.month}-${this.day} ${this.hour}:${this.minute}:${this.second}`,
}
var num = parseInt(this.shownum) || 6
if (num <= 0 || num > 6) {
num = 6
}
this.$emit('ok', obj[num], obj)
this.close()
},
//
open (timeStr) {
this.$nextTick(res => {
this.timeFill(timeStr || this.timestr || '')
this.$refs.popup.open()
})
},
/**
* 关闭窗口
*/
close () {
// close
this.$refs.popup.close()
},
/**
* popup 状态发生变化触发
* @param {Object} e
*/
change (e) {
// console.log('popup ' + e.type + ' ', e.show)
},
bindChange(e) {
// console.log(e)
const val = e.detail.value
this.year = this.years[val[0]]
this.month = this.months[val[1]]
this.day = this.days[val[2]]
this.hour = this.hourArr[val[3] || 0]
this.minute = this.minArr[val[4] || 0]
this.second = this.secondArr[val[5] || 0]
}
}
}
</script>
<style scoped>
.popup-content{
background-color: #FFFFFF;
}
.text1{
padding: 0 28rpx;
font-size: 34rpx;
line-height: 90rpx;
color: #888;
float: left;
}
.blue{
float: right;
color: #007aff;
}
.uni-page-head-title {
display: inline-block;
padding: 0 20rpx;
font-size: 26rpx;
height: 88rpx;
line-height: 88rpx;
color: #BEBEBE;
box-sizing: border-box;
}
.headBox{
display: flex;
justify-content: space-between;
border-bottom: 1px solid #e5e5e5;
}
picker-view {
width: 100%;
height: 600rpx;
margin-top:20rpx;
}
.item {
line-height: 100rpx;
height: 100rpx;
text-align: center;
font-size: 30rpx;
}
</style>

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"id": "my-datetime",
"name": "日期-时间选择器",
"version": "1.0.1",
"description": "日期选择器、时间选择器、年月选择器、日期时间选择器",
"keywords": [
"日期选择器、年月选择器、时间选择器、日期时间选择器"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
}
}

View File

@ -0,0 +1,138 @@
<template>
<view class="flex-row align-c wh-auto pr" :style="propStyle">
<template v-if="['option1', 'option2'].includes(date_type)">
<myDatetime ref="option4" dataType="time" :shownum="date_type == 'option1' ? 2 : 3" @timeSubmit="time_change"></myDatetime>
</template>
<template v-else-if="date_type == 'option3'">
<myDatetime ref="option4" :shownum="2" @timeSubmit="time_change"></myDatetime>
</template>
<template v-else>
<uni-datetime-picker ref="option4" class="datetime-picker" :value="form_value" :border="false" :type="date_type == 'option4' ? 'date' : 'datetime'" :hideSecond="date_type == 'option4'" @change="data_date_change" />
</template>
<view class="bg-white wh-auto ht-auto flex-row align-c" @tap="data_time_change">
<template v-if="isEmpty(form_value)">
<view class="datetime-placeholder flex-1">{{ com_data.placeholder }}</view>
</template>
<template v-else>
<view class="datetime-value flex-1">{{ form_value }}</view>
</template>
<iconfont :name="'icon-'+ com_data.icon_name" class="ml-5" size="28rpx" color="#333333" />
</view>
</view>
</template>
<script>
import { get_format_checks, isEmpty, time_stamp } from '@/common/js/common/common.js';
import myDatetime from '@/pages/form-input/components/form-input/modules/my-datetime/my-datetime.vue';
export default {
name: 'diy',
components: {
myDatetime
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String, Number],
default: 0,
},
propDataIndex: {
type: Number,
default: 0,
},
propStyle: {
type: String,
default: '',
}
},
data() {
return {
placeholder: '请输入内容...',
form_value: '',
com_data: {},
date_style: '',
date_type: 'option1',
};
},
watch: {
propKey(val) {
//
this.init();
},
},
mounted() {
this.init();
},
methods: {
isEmpty,
init() {
const com_data = this.propValue;
const date = time_stamp(com_data?.form_value || '', com_data.date_style, com_data.date_type);
debugger;
this.$nextTick(() => {
this.setData({
com_data: com_data,
placeholder: com_data?.placeholder || '请输入内容...',
form_value: date,
date_style: com_data.date_style,
date_type: com_data.date_type,
});
});
},
data_time_change() {
console.log(this.$refs.option4);
if (['option1', 'option2', 'option3'].includes(this.date_type)) {
this.$refs.option4.open(this.form_value || '');
} else {
this.$refs.option4.show();
}
},
data_check(e) {
const { is_error = '0', error_text = '' } = get_format_checks(this.com_data, e.detail.value);
this.$emit('dataCheck', { is_error, error_text, value: e.detail.value, index: this.propDataIndex });
},
data_date_change(e) {
const date = time_stamp(e, this.date_style, this.date_type);
//
this.setData({
form_value: date,
});
this.$emit('dataChange', { value: date, index: this.propDataIndex });
},
data_change(e) {
const date = time_stamp(e.detail.value, this.date_style, this.date_type);
debugger;
//
this.setData({
form_value: date,
});
this.$emit('dataChange', { value: date, index: this.propDataIndex });
},
time_change(e) {
console.log(e);
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .uniui-calendar::before {
content: '',
}
.datetime-picker {
position: absolute;
top: 160rpx;
left: 0;
width: 0;
}
.datetime-placeholder {
color: #606266;
opacity: 0.6;
}
.datetime-value {
color: #606266;
line-height: 1.5;
}
</style>

View File

@ -40,6 +40,9 @@
<view v-else-if="item.key == 'number'" class="wh-auto" :style="item.com_data.common_style">
<componentNumber :propValue="item.com_data" :propKey="propKey" :propDataIndex="index" :propMobile="mobile" :propStyle="component_style" @dataCheck="data_check" @dataChange="data_change"></componentNumber>
</view>
<view v-else-if="item.key == 'date'" class="wh-auto" :style="item.com_data.common_style">
<componentDate :propValue="item.com_data" :propKey="propKey" :propDataIndex="index" :propMobile="mobile" :propStyle="component_style" @dataCheck="data_check" @dataChange="data_change"></componentDate>
</view>
<view v-if="!isEmpty(item.com_data.common_config.error_text)" class="field-invalid-info">{{ item.com_data.common_config.error_text }}</view>
</view>
</view>
@ -74,6 +77,7 @@ import componentCheckbox from '@/pages/form-input/components/form-input/checkbox
import componentRadio from '@/pages/form-input/components/form-input/radio.vue';
import componentSelect from '@/pages/form-input/components/form-input/select.vue';
import componentNumber from '@/pages/form-input/components/form-input/number.vue';
import componentDate from '@/pages/form-input/components/form-input/date.vue';
import componentSelectMulti from '@/pages/form-input/components/form-input/select-multi.vue';
export default {
name: 'diy',
@ -85,6 +89,7 @@ export default {
componentNumber,
componentSelect,
componentSelectMulti,
componentDate,
},
props: {
propValue: {

View File

@ -0,0 +1,262 @@
<template>
<view>
<uni-popup ref="popup" type="bottom" :animation="true">
<view class="popup-content">
<view>
<view class="headBox">
<view @tap="close"><text class="text1">取消</text></view>
<!-- <view class="uni-page-head-title" v-if="titleShow">{{timeTitle}}</view> -->
<view><text class="text1 blue" @tap="submit_event"></text></view>
</view>
<picker-view :indicator-style="indicatorStyle" :value="value" @change="bindChange">
<picker-view-column v-for="(arr, n) in dateTimeArr" :key="n">
<view class="item" v-for="(obj, index) in arr" :key="index">{{obj}}{{ dataType == 'date' ? dateUnitArr[n] || '' : timeUnitArr[n] || '' }}</view>
</picker-view-column>
</picker-view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
name: 'MyDatetime',
props: {
titleShow: {
type: Boolean,
default: true
},
timestr: {
//
type: String,
default: ''
},
dataType: {
type: String,
default: 'date'
},
//
shownum: {
type: [String, Number],
default: 3
}
},
data() {
const date = new Date()
const years = []
const year = date.getFullYear()
const months = []
const month = date.getMonth() + 1
const days = []
const day = date.getDate()
for (let i = 1970, len = date.getFullYear() + 100; i <= len; i++) {
years.push(i)
}
for (let i = 1; i <= 12; i++) {
months.push(i < 10 ? ('0'+ i.toString()) : i)
}
for (let i = 1; i <= 31; i++) {
days.push(i < 10 ? ('0'+ i.toString()) : i)
}
const hourArr = []
for (let i = 0; i < 24; i++) {
hourArr.push(i < 10 ? ('0'+ i.toString()) : i)
}
//
const minSecond = []
for (let i = 0; i <= 59; i++) {
minSecond.push(i < 10 ? ('0'+ i.toString()) : i)
}
return {
thirtyOne: days,
dateArr: [years, months, days ],
timeArr: [hourArr, minSecond, minSecond ],
dateUnitArr: [ '年', '月', '日', '时', '分', '秒'],
timeUnitArr: [ '时', '分', '秒'],
obj: { 0: 'year', 1: 'month', 2: 'day', 3: 'hour', 4: 'minute', 5: 'second' },
years,
months,
days,
hourArr,
minArr: minSecond,
secondArr: minSecond,
year,
month,
day,
hour: '00',
minute: '00',
second: '00',
value: [ year - 1970, month - 1, day - 1],
visible: true,
indicatorStyle: `height: 100rpx;`,
timer: null
}
},
computed: {
//
dateTimeArr () {
var num = parseInt(this.shownum) || 3
if (num <= 3 && num > 0) {
if (this.dataType == 'date') {
return this.dateArr.slice(0, num)
} else {
return this.timeArr.slice(0, num)
}
}
return this.dateArr
},
},
watch: {
//
month () {
this.countDay()
},
year () {
this.countDay()
}
},
created() {
this.countDay();
},
destroyed() {
this.timer && clearTimeout(this.timer)
},
methods: {
//
countDay () {
var index = new Date(this.year, parseInt(this.month), 0).getDate()
var days = this.thirtyOne.slice(0, index)
this.dateArr.splice(2, 1, days)
this.days = days
},
// timeStr: 2020-01-01 00:00:00
timeFill (timeStr) {
//
let dateObj = new Date();
//
if (timeStr) {
const time = this.dataType == 'date' ? timeStr : '1970-01-01 ' + timeStr.replace(/时|分|秒/g, ':').replace(/:+$/, ''); // ;
dateObj = new Date(time.replace(/-/g,'/'));
}
const timeObj = {
year: dateObj.getFullYear(),
month: dateObj.getMonth() + 1,
day: dateObj.getDate(),
h: dateObj.getHours(),
m: dateObj.getMinutes(),
s: dateObj.getSeconds()
}
this.timer = setTimeout(() => {
if (this.dataType == 'date') {
this.value = [ (timeObj.year - 1970) || 0, timeObj.month - 1, timeObj.day - 1]
this.year = this.years[this.value[0]]
this.month = this.months[this.value[1]]
this.day = this.days[this.value[2]]
} else {
this.value = [ timeObj.h, timeObj.m, timeObj.s ]
this.hour = this.hourArr[this.value[0]]
this.minute = this.minArr[this.value[1]]
this.second = this.secondArr[this.value[2]]
}
}, 100)
},
//
submit_event () {
debugger;
var num = parseInt(this.shownum) || 3
if (num <= 0 || num > 3) {
num = 3
}
let date = '';
if (this.dataType == 'date') {
if (num == 1) {
date = this.year;
} else if (num == 2) {
date = this.year + '-' + this.month;
} else if (num == 3) {
date = this.year + '-' + this.month + '-' + this.day;
}
} else {
if (num == 1) {
date = this.hour;
} else if (num == 2) {
date = this.hour + ':' + this.minute;
} else if (num == 3) {
date = this.hour + ':' + this.minute + ':' + this.second;
}
}
this.$emit('timeSubmit', date)
this.close()
},
//
open (timeStr) {
this.$nextTick(res => {
this.timeFill(timeStr || this.timestr || '')
this.$refs.popup.open()
})
},
/**
* 关闭窗口
*/
close () {
// close
this.$refs.popup.close()
},
bindChange(e) {
const val = e.detail.value
if (this.dataType == 'date') {
this.year = this.years[val[0]]
this.month = this.months[val[1]]
this.day = this.days[val[2]];
} else {
this.hour = this.hourArr[val[0] || 0]
this.minute = this.minArr[val[1] || 0]
this.second = this.secondArr[val[2] || 0]
}
}
}
}
</script>
<style scoped>
.popup-content{
background-color: #FFFFFF;
}
.text1{
padding: 0 28rpx;
font-size: 34rpx;
line-height: 90rpx;
color: #888;
float: left;
}
.blue{
float: right;
color: #007aff;
}
.uni-page-head-title {
display: inline-block;
padding: 0 20rpx;
font-size: 26rpx;
height: 88rpx;
line-height: 88rpx;
color: #BEBEBE;
box-sizing: border-box;
}
.headBox{
display: flex;
justify-content: space-between;
border-bottom: 1px solid #e5e5e5;
}
picker-view {
width: 100%;
height: 600rpx;
margin-top:20rpx;
}
.item {
line-height: 100rpx;
height: 100rpx;
text-align: center;
font-size: 30rpx;
}
</style>

File diff suppressed because one or more lines are too long