|
|
@@ -0,0 +1,385 @@
|
|
|
+<template>
|
|
|
+ <view v-if="visible" class="picker-mask" @click="handleMaskClick">
|
|
|
+ <view class="picker-container" @click.stop>
|
|
|
+ <!-- 头部 -->
|
|
|
+ <view class="picker-header">
|
|
|
+ <text class="picker-title">从业时间选择</text>
|
|
|
+ <text class="picker-cancel" @click="handleCancel">取消</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 日期选择列 -->
|
|
|
+ <view class="picker-content">
|
|
|
+ <picker-view
|
|
|
+ class="picker-view"
|
|
|
+ :value="pickerValue"
|
|
|
+ @change="handlePickerChange"
|
|
|
+ :indicator-style="indicatorStyle"
|
|
|
+ :mask-style="maskStyle"
|
|
|
+ >
|
|
|
+ <!-- 年列 -->
|
|
|
+ <picker-view-column>
|
|
|
+ <view
|
|
|
+ v-for="(year, index) in years"
|
|
|
+ :key="index"
|
|
|
+ class="picker-item"
|
|
|
+ :class="{ 'picker-item-selected': pickerValue[0] === index }"
|
|
|
+ >
|
|
|
+ {{ year }}年
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+
|
|
|
+ <!-- 月列 -->
|
|
|
+ <picker-view-column>
|
|
|
+ <view
|
|
|
+ v-for="(month, index) in months"
|
|
|
+ :key="index"
|
|
|
+ class="picker-item"
|
|
|
+ :class="{ 'picker-item-selected': pickerValue[1] === index }"
|
|
|
+ >
|
|
|
+ {{ month }}月
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+
|
|
|
+ <!-- 日列 -->
|
|
|
+ <picker-view-column>
|
|
|
+ <view
|
|
|
+ v-for="(day, index) in days"
|
|
|
+ :key="index"
|
|
|
+ class="picker-item"
|
|
|
+ :class="{ 'picker-item-selected': pickerValue[2] === index }"
|
|
|
+ >
|
|
|
+ {{ day }}日
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ </picker-view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 底部按钮 -->
|
|
|
+ <view class="picker-footer">
|
|
|
+ <view class="picker-btn reset-btn" @click="handleReset">重置</view>
|
|
|
+ <view class="picker-btn confirm-btn" @click="handleConfirm">确定</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ export default {
|
|
|
+ name: 'PracticeTimePicker',
|
|
|
+ props: {
|
|
|
+ visible: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ const now = new Date()
|
|
|
+ return {
|
|
|
+ pickerValue: [0, 0, 0],
|
|
|
+ selectedYear: now.getFullYear(),
|
|
|
+ selectedMonth: now.getMonth() + 1,
|
|
|
+ selectedDay: now.getDate(),
|
|
|
+ originalYear: now.getFullYear(),
|
|
|
+ originalMonth: now.getMonth() + 1,
|
|
|
+ originalDay: now.getDate(),
|
|
|
+ indicatorStyle: 'height: 50px; background-color: rgba(0, 122, 255, 0.15);',
|
|
|
+ maskStyle: 'background-color: rgba(255, 255, 255, 0.8);',
|
|
|
+ startYear: 1950,
|
|
|
+ endYear: now.getFullYear()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ years() {
|
|
|
+ const years = []
|
|
|
+ for (let i = this.startYear; i <= this.endYear; i++) {
|
|
|
+ years.push(i)
|
|
|
+ }
|
|
|
+ return years
|
|
|
+ },
|
|
|
+ months() {
|
|
|
+ return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
|
+ },
|
|
|
+ days() {
|
|
|
+ const daysInMonth = new Date(this.selectedYear, this.selectedMonth, 0).getDate()
|
|
|
+ const days = []
|
|
|
+ for (let i = 1; i <= daysInMonth; i++) {
|
|
|
+ days.push(i)
|
|
|
+ }
|
|
|
+ return days
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ visible(newVal) {
|
|
|
+ if (newVal) {
|
|
|
+ // 延迟初始化,确保 DOM 已渲染
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.initPicker()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ value(newVal) {
|
|
|
+ if (newVal && this.visible) {
|
|
|
+ this.parseValue(newVal)
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.updatePickerValue()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ selectedMonth() {
|
|
|
+ // 月份变化时,需要重新计算天数
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.updateDayIndex()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ selectedYear() {
|
|
|
+ // 年份变化时,需要重新计算天数(考虑闰年)
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.updateDayIndex()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ initPicker() {
|
|
|
+ if (this.value) {
|
|
|
+ this.parseValue(this.value)
|
|
|
+ } else {
|
|
|
+ const now = new Date()
|
|
|
+ this.selectedYear = now.getFullYear()
|
|
|
+ this.selectedMonth = now.getMonth() + 1
|
|
|
+ this.selectedDay = now.getDate()
|
|
|
+ }
|
|
|
+ this.originalYear = this.selectedYear
|
|
|
+ this.originalMonth = this.selectedMonth
|
|
|
+ this.originalDay = this.selectedDay
|
|
|
+ // 等待计算属性更新后再设置 pickerValue
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.updatePickerValue()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ parseValue(value) {
|
|
|
+ // 解析日期字符串,支持格式:YYYY-MM-DD 或 YYYY年MM月DD日
|
|
|
+ let year, month, day
|
|
|
+ if (value.includes('-')) {
|
|
|
+ const parts = value.split('-')
|
|
|
+ year = parseInt(parts[0])
|
|
|
+ month = parseInt(parts[1])
|
|
|
+ day = parseInt(parts[2])
|
|
|
+ } else if (value.includes('年')) {
|
|
|
+ const yearMatch = value.match(/(\d+)年/)
|
|
|
+ const monthMatch = value.match(/(\d+)月/)
|
|
|
+ const dayMatch = value.match(/(\d+)日/)
|
|
|
+ year = yearMatch ? parseInt(yearMatch[1]) : new Date().getFullYear()
|
|
|
+ month = monthMatch ? parseInt(monthMatch[1]) : new Date().getMonth() + 1
|
|
|
+ day = dayMatch ? parseInt(dayMatch[1]) : new Date().getDate()
|
|
|
+ } else {
|
|
|
+ const now = new Date()
|
|
|
+ year = now.getFullYear()
|
|
|
+ month = now.getMonth() + 1
|
|
|
+ day = now.getDate()
|
|
|
+ }
|
|
|
+ this.selectedYear = year
|
|
|
+ this.selectedMonth = month
|
|
|
+ this.selectedDay = day
|
|
|
+ },
|
|
|
+ updatePickerValue() {
|
|
|
+ try {
|
|
|
+ const yearIndex = this.years.indexOf(this.selectedYear)
|
|
|
+ const monthIndex = this.months.indexOf(this.selectedMonth)
|
|
|
+ const dayIndex = this.days.indexOf(this.selectedDay)
|
|
|
+
|
|
|
+ this.pickerValue = [
|
|
|
+ yearIndex >= 0 ? yearIndex : 0,
|
|
|
+ monthIndex >= 0 ? monthIndex : 0,
|
|
|
+ dayIndex >= 0 ? dayIndex : 0
|
|
|
+ ]
|
|
|
+ } catch (error) {
|
|
|
+ console.error('updatePickerValue error:', error)
|
|
|
+ // 如果出错,使用默认值
|
|
|
+ this.pickerValue = [0, 0, 0]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ updateDayIndex() {
|
|
|
+ // 如果当前选中的日期超过了新月份的天数,调整为该月最后一天
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const daysInMonth = new Date(this.selectedYear, this.selectedMonth, 0).getDate()
|
|
|
+ if (this.selectedDay > daysInMonth) {
|
|
|
+ this.selectedDay = daysInMonth
|
|
|
+ }
|
|
|
+ this.updatePickerValue()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handlePickerChange(e) {
|
|
|
+ const values = e.detail.value
|
|
|
+ if (!values || values.length < 3) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const newYear = this.years[values[0]]
|
|
|
+ const newMonth = this.months[values[1]]
|
|
|
+
|
|
|
+ // 如果年份或月份变化,需要重新计算天数
|
|
|
+ if (newYear !== this.selectedYear || newMonth !== this.selectedMonth) {
|
|
|
+ this.selectedYear = newYear
|
|
|
+ this.selectedMonth = newMonth
|
|
|
+
|
|
|
+ // 重新计算天数,确保日期不超过当月最大天数
|
|
|
+ const daysInMonth = new Date(this.selectedYear, this.selectedMonth, 0).getDate()
|
|
|
+ if (this.selectedDay > daysInMonth) {
|
|
|
+ this.selectedDay = daysInMonth
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新日期索引
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const dayIndex = this.days.indexOf(this.selectedDay)
|
|
|
+ this.pickerValue = [values[0], values[1], dayIndex >= 0 ? dayIndex : 0]
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 只有日期变化,需要确保索引有效
|
|
|
+ const dayIndex = Math.min(values[2], this.days.length - 1)
|
|
|
+ this.selectedDay = this.days[dayIndex] || 1
|
|
|
+ this.pickerValue = [values[0], values[1], dayIndex]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleMaskClick() {
|
|
|
+ this.handleCancel()
|
|
|
+ },
|
|
|
+ handleCancel() {
|
|
|
+ // 恢复原始值
|
|
|
+ this.selectedYear = this.originalYear
|
|
|
+ this.selectedMonth = this.originalMonth
|
|
|
+ this.selectedDay = this.originalDay
|
|
|
+ this.updatePickerValue()
|
|
|
+ this.$emit('close')
|
|
|
+ },
|
|
|
+ handleReset() {
|
|
|
+ const now = new Date()
|
|
|
+ this.selectedYear = now.getFullYear()
|
|
|
+ this.selectedMonth = now.getMonth() + 1
|
|
|
+ this.selectedDay = now.getDate()
|
|
|
+ this.updatePickerValue()
|
|
|
+ },
|
|
|
+ handleConfirm() {
|
|
|
+ // 格式化日期字符串,确保月份和日期是两位数
|
|
|
+ const monthStr = this.selectedMonth < 10 ? `0${this.selectedMonth}` : `${this.selectedMonth}`
|
|
|
+ const dayStr = this.selectedDay < 10 ? `0${this.selectedDay}` : `${this.selectedDay}`
|
|
|
+ const dateStr = `${this.selectedYear}-${monthStr}-${dayStr}`
|
|
|
+ this.$emit('confirm', dateStr)
|
|
|
+ this.$emit('close')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .picker-mask {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
+ // z-index: 1000;
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-end;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-container {
|
|
|
+ width: 100%;
|
|
|
+ background-color: #ffffff;
|
|
|
+ border-radius: 32rpx 32rpx 0 0;
|
|
|
+ overflow: hidden;
|
|
|
+ animation: slideUp 0.3s ease-out;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes slideUp {
|
|
|
+ from {
|
|
|
+ transform: translateY(100%);
|
|
|
+ }
|
|
|
+ to {
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 88rpx;
|
|
|
+ padding: 0 60rpx;
|
|
|
+ position: relative;
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-title {
|
|
|
+ font-size: 36rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #000000;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-cancel {
|
|
|
+ position: absolute;
|
|
|
+ right: 60rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #999999;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-content {
|
|
|
+ height: 400rpx;
|
|
|
+ padding: 20rpx 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-view {
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-item {
|
|
|
+ height: 50px;
|
|
|
+ line-height: 50px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #999999;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-item-selected {
|
|
|
+ color: #2652C2 !important;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-footer {
|
|
|
+ display: flex;
|
|
|
+ padding: 30rpx;
|
|
|
+ gap: 20rpx;
|
|
|
+ border-top: 1rpx solid #f0f0f0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-btn {
|
|
|
+ flex: 1;
|
|
|
+ height: 88rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 400;
|
|
|
+ }
|
|
|
+
|
|
|
+ .reset-btn {
|
|
|
+ background-color: #ffffff;
|
|
|
+ border: 2rpx solid #007AFF;
|
|
|
+ color: #000000;
|
|
|
+ }
|
|
|
+
|
|
|
+ .confirm-btn {
|
|
|
+ background-color: #007AFF;
|
|
|
+ color: #ffffff;
|
|
|
+ }
|
|
|
+</style>
|
|
|
+
|