| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- <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: #333333;
- text-align: center;
- }
- .picker-item-selected {
- color: #007AFF !important;
- font-weight: 600;
- }
- .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>
|