【全栈实战】基于 Vue3 + Wot Design Uni 动手封装组件

news/2024/12/23 11:30:01 标签: 前端, vue, vue3, vue.js

😊你好,我是小航,一个正在变秃、变强的文艺倾年。

😊好久没有更新有关前端实战教程了,本文主要讲解【全栈实战】基于 Vue3 + Wot Design Uni 动手封装组件!

😊这个教程你将会学到技术正确的选型、Vue3基础语法实践、父子组件传参、移动端组件封装、组件源码阅读技巧、小程序适配技巧等,一起卷起来吧!

目录

  • 一、前言
    • Wot Design Uni
      • 技术选型
      • UI库选型
        • 开源热度对比
        • 多端支持情况
        • 组件数量
        • ts 支持情况
        • 发展趋势
    • Tyme
      • 语言支持情况
      • 活跃程度
      • 发展趋势
      • 使用教程
  • 二、封装
    • 思路
    • 父组件
    • 子组件
      • 初始化
      • 样式搭建
      • 工具封装
      • 常量定义
      • 逻辑设计
      • 解决微信小程序 Tab 动画异常
    • 效果展示
  • 三、完整代码
    • 代码
    • 下一步建议

一、前言

由于本人近期需要一个支持输入农历、公历的移动端组件,仅需要输入年、月、日、时,输入界面要求可以同时选择四列,且自由切换农历、公历。网上没找到现成的组件,只好自己封装一个了。下面先介绍一下自己的技术选型,本人开发采用的语言是Vue3 + Typescript

Wot Design Uni

在这里插入图片描述

技术选型

Uniapp的上手难度很低,而且Uniapp的语法风格与Vue保持高度一致,可以直接复用已有的Vue项目中的代码和经验。因此我这里使用的开发框架是Uniapp。

UI库选型

对于UI库的选型,我们常常从开源热度、多端支持情况、组件数量、ts支持情况、发展趋势来选择。

开源热度对比

截止到 2024-05-30 发表文章时的数据:

UI 框架uv-uiuview-pluswot-uiTuniaoUI
github stars568362492192
gitee stars55512635-
github forks1.1k15818820
gitee forks75430-

实到这里就一决高下了,github star 数uv-ui(568) > wot-ui(492) > uview-plus(362) > TuniaoUI(192),其中 uv-uiwot-ui 拔得头筹。

在这里插入图片描述

多端支持情况
UI 框架uv-uiuview-pluswot-uiTuniaoUI
h5
app(ios)
app(android)
微信小程序
支付宝小程序
QQ 小程序
百度小程序
头条小程序
组件数量
UI 框架uv-uiuview-pluswot-uiTuniaoUI
总数67677155
基础组件81185
表单组件16172014
数据组件134184
反馈组件810168
布局组件79-8
导航组件8895
其他组件78-5
内容组件---6

组件数:wot(71) > uv-ui(67) = uview-plus(67) > TuniaoUI(55)

ts 支持情况

查看 4 个组件库的源码,可以了解到:

  • uv-uiuView-plus 都是 js 写的,并非 ts,可以通过 ttou/uv-typings 提供类型支持。
  • wotTuniaoUI 都是 ts 写的,编码体验会好很多。

小知识:代码里如何辨别一个库是否有 ts 支持,写代码的时候按 ctrl + i (Mac 里 cmd + i),如果有提示就是有,啥都没有就是没有。

举个例子,编写 <xx-button type="" ...,在 type="" 双引号里面按 ctrl + i,看提示就知道了。

  • wot 有提示
    在这里插入图片描述
  • uv-ui 无提示
    在这里插入图片描述
发展趋势

wot-uiuv-ui皇城PK

在这里插入图片描述
目前 wot-ui 还是比不过 uv-ui 的,但是 wot-ui 有反超的势头。

这里我选择了wot-ui。wot-ui 全称 wot-design-uni,是 wot-design 的 uniapp 版本,文档地址:https://wot-design-uni.netlify.app/

Tyme

Tyme是一个非常强大的日历工具库,可以看作 Lunar 的升级版,拥有更优的设计和扩展性,支持公历和农历、星座、干支、生肖、节气、法定假日等。这有助与我们进行农历和公历之间的转化。

开源地址:https://github.com/6tail/tyme4ts

语言支持情况

看着就很棒,支持了市面了主流的语言。
在这里插入图片描述

活跃程度

近两周刚更新源代码,说明出了问题能解决。我们在采用别人封装好的库时候,一定要多看看仓库活跃程度,如果这个仓库都好久没有更新维护了,不建议使用。

在这里插入图片描述

发展趋势

没毛病,就它了!
在这里插入图片描述

使用教程

// install
npm init -y
npm i typescript -D
npm i ts-node -D
npm i tyme4ts
 
// test.ts
import {SolarDay} from 'tyme4ts';
 
const solar: SolarDay = SolarDay.fromYmd(1986, 5, 29);
 
// 1986年5月29日
console.log(solar.toString());

// 农历丙寅年四月廿一
console.log(solar.getLunarDay().toString());
 
// run
ts-node test.ts

二、封装

思路

在这里插入图片描述
示例数据:

// 公历2024年12月22日13时
birthdayValue: [2024, 12, 22, 13, 1]

用到的组件:

Input、Picker、Popup

父组件

<birthdayPicker
    v-model="state.userInfo.birthdayValue"
    prop="birthdayValue"
    :rules="[
      {
        required: false,
        message: '请输入生辰',
      },
    ]"
 ></birthdayPicker>

// 定义变量
const state = reactive({
  // 用户信息数据
  userInfo: {
    birthdayValue: null,
  },
})

子组件

初始化

Tips:便于调试,推荐onload加入debugger,可以直接看到小程序源代码是否刷新,小程序经常不刷新代码,经常误认为自己的编码问题,也是比较恼火。

onLoad(() => {
  debugger
})

定义组件名称

defineOptions({
  name: 'birthdayPicker',
})

定义接收父组件传值的接口

type bithdayPickerProps = {
  modelValue: number[] // 生日值
  disabled?: boolean // 是否禁用
  prop: string // 表单域 model 字段名,在使用表单校验功能的情况下,该属性是必填的
  rules: FormItemRule[] // 表单校验规则
}
const props = defineProps<bithdayPickerProps>()
const emit = defineEmits(['update:modelValue'])

定义用到的状态变量

const state = reactive({
  displayColumns: [],
  popupShow: false,
  birthdayArr: ['农历', '公历'],
  birthday: [],
  birthdayIndex: 0,
  pickerViewLoaded: false,
})

// 计算Picker展示值
const displayBirthday = computed(() => {
  const birthday = state.birthday
  // 避免空数组调用join函数
  if (isNullBirthdayArray(birthday, 4)) return null
  // 公历2024年12月22日13时
  return (
    state.birthdayArr[state.birthdayIndex] +
    birthday[0] +
    '年' +
    findValueInCoulums(birthday[1], state.displayColumns[1]) +
    findValueInCoulums(birthday[2], state.displayColumns[2]) +
    birthday[3] +
    '时'
  )
})

在这里插入图片描述

这里有个常见的面试考点:

如何在 ref() 与 reactive() 之间做正确选择?
(1)ref() 和 reactive() 用于跟踪其参数的更改。当使用它们初始化变量时,是向 Vue 提供信息:“嘿,每次这些变量发生更改时,请重新构建或重新运行依赖于它们的所有内容”。
(2)ref() 函数可以接受原始类型(最常见的是布尔值、字符串和数字)以及对象作为参数,而 reactive() 函数只能接受对象作为参数。
(3)ref() 有一个 .value 属性,你必须使用 .value 属性获取内容,但是使用 reactive() 的话可以直接访问。
(4)使用 ref() 函数可以替换整个对象实例,但是在使用 reactive() 函数时就不行。
(5)如果你喜欢组件内部状态仅有一个变量,那么 reactive() 就是适合你的正确工具。本人更喜欢使用reactive,看着比较舒服。
在这里插入图片描述

样式搭建

编写组件样式:

<template>
  <wd-popup
    v-model="state.popupShow"
    position="bottom"
    custom-style="border-radius:32rpx;height: 300px;"
    :close-on-click-modal="false"
    @close="closeBirthdayPicker"
    closable
    @after-enter="handleOpened"
  >
    <wd-tabs v-model="state.birthdayIndex" animated swipeable ref="tabsRef" @change="onChangeTab">
      <wd-tab v-for="(item, index) in state.birthdayArr" :key="index" :title="item">
        <wd-picker-view
          :columns="state.displayColumns"
          v-model="state.birthday"
          :column-change="onChangeColumn"
          ref="birthdayPicker"
        />
      </wd-tab>
    </wd-tabs>
  </wd-popup>
  <div @click="openBirthdayPicker">
    <wd-input
      :prop="props.prop"
      :rules="props.rules"
      type="text"
      label="生辰"
      v-model="displayBirthday"
      placeholder="请选择"
      readonly
      required
      placeholder-style="color:#bfbfbf"
    >
      <template #suffix>
        <!--箭头-->
        <wd-icon name="arrow-right" color="rgba(0, 0, 0, 0.25)" size="18px" />
      </template>
    </wd-input>
  </div>
</template>

在这里插入图片描述
基础的打开、关闭

// 打开生日选择器
const openBirthdayPicker = () => {
  if (props.disabled) return
  state.popupShow = true
  const value = props.modelValue
  if (isNullBirthdayArray(value, 5)) {
    state.birthday = [1940, 1, 1, 1]
    state.birthdayIndex = 0
    return
  }
  state.birthday = [value[0], value[1], value[2], value[3]]
  state.birthdayIndex = value[4]
}

// 关闭生日选择器
const closeBirthdayPicker = () => {
  emit('update:modelValue', [...state.birthday, state.birthdayIndex])
  state.popupShow = false
}

工具封装

由于我这里会经常对bithday数组进行判空,判断依据有:是否有空值、长度是否满足,是否是数组等,这里我们封装成一个函数

const isNullBirthdayArray = (birthday: number[], validLength: number = birthday.length) => {
  return (
    !birthday ||
    (isArray(birthday) &&
      (!birthday.length ||
        birthday.length !== validLength ||
        birthday.every((item) => item === null)))
  )
}

Picker值展示时需要根据birthday的值进行与当前columns映射

const findValueInCoulums = (value: number, columns: any[]) => {
  let res = ''
  columns.forEach((item) => {
    if (item.value === value) res = item.label
  })
  return res
}

常量定义

// 定义农历月份和日期的汉字表示
const chineseMonths = [
  '一月',
  '二月',
  '三月',
  '四月',
  '五月',
  '六月',
  '七月',
  '八月',
  '九月',
  '十月',
  '十一月',
  '十二月',
]
const chineseDays = [
  '初一',
  '初二',
  '初三',
  '初四',
  '初五',
  '初六',
  '初七',
  '初八',
  '初九',
  '初十',
  '十一',
  '十二',
  '十三',
  '十四',
  '十五',
  '十六',
  '十七',
  '十八',
  '十九',
  '二十',
  '廿一',
  '廿二',
  '廿三',
  '廿四',
  '廿五',
  '廿六',
  '廿七',
  '廿八',
  '廿九',
  '三十',
]

根据常量来更新展示列信息:

const yearColumns = Array.from({ length: new Date().getFullYear() - 1940 + 1 }, (_, i) => {
  return { label: `${1940 + i}`, value: 1940 + i }
})
const hourColumns = Array.from({ length: 24 }, (_, i) => ({
  label: `${i + 1}`,
  value: i + 1,
}))
const initDays = (len: number, type: 'lunar' | 'gregorian') =>
  Array.from({ length: len }, (_, i) => {
    return {
      label: type === 'lunar' ? `${chineseDays[i]}` : `${i + 1}`,
      value: i + 1,
    }
  })
const updateColumns = (birthday: number[]) => {
  let monthColumns = []
  let dayColumns = []
  const [_year, _month, _day, _hour, birthdayIndex] = birthday
  // 1.农历列
  if (birthdayIndex === 0) {
    const lunarYear: LunarYear = LunarYear.fromYear(_year)
    monthColumns = lunarYear.getMonths().map((month) => ({
      label: month.isLeap()
        ? `${chineseMonths[month.getMonth() - 1]}`
        : chineseMonths[month.getMonth() - 1],
      value: month.getMonthWithLeap(),
    }))
    const lunarMonth: LunarMonth = LunarMonth.fromYm(_year, _month)
    const dayCount: number = lunarMonth.getDayCount()
    dayColumns = initDays(dayCount, 'lunar')
  }
  // 2.公历列
  if (birthdayIndex === 1) {
    monthColumns = Array.from({ length: 12 }, (_, i) => {
      return { label: `${chineseMonths[i]}`, value: i + 1 }
    })
    const solarMonth: SolarMonth = SolarMonth.fromYm(_year, _month)
    const dayCount: number = solarMonth.getDayCount()
    dayColumns = initDays(dayCount, 'gregorian')
  }
  state.displayColumns = [yearColumns, monthColumns, dayColumns, hourColumns]
}

逻辑设计

初始化列

// 1.初始化
onMounted(() => {
  handleShowValueUpdate(props.modelValue)
})

// 2.监听父组件的值变化
watch(
  () => props.modelValue,
  (newValue) => {
    handleShowValueUpdate(newValue)
  },
)

// 3.值变更时更新显示内容
const handleShowValueUpdate = (value: number[]) => {
  // 0.校验
  if (isNullBirthdayArray(value, 5)) {
    // 更新为初始值,状态值保持不变
    updateColumns([1940, 1, 1, 1, 0])
    return
  }
  // 1.获取选中项
  state.birthday = [value[0], value[1], value[2], value[3]]
  state.birthdayIndex = value[4]
  // 2.更新列
  updateColumns(value)
}

// 4.当农历、公历切换时:
const onChangeTab = (index) => {
  state.pickerViewLoaded = true
  const [_year, _month, _day, _hour] = state.birthday
  // 1.农历转公历
  if (index.name === 1) {
    console.log('农历转公历前', index.name, state.birthday)
    const lunarHour = LunarHour.fromYmdHms(_year, _month, _day, _hour, 0, 0)
    const solarTime: SolarTime = lunarHour.getSolarTime()
    state.birthday = [
      solarTime.getYear(),
      solarTime.getMonth(),
      solarTime.getDay(),
      solarTime.getHour(),
    ]
    console.log('农历转公历后', index.name, state.birthday)
  }
  // 2.公历转农历
  if (index.name === 0) {
    console.log('公历转农历前', index.name, state.birthday)
    const solarTime = SolarTime.fromYmdHms(_year, _month, _day, _hour, 0, 0)
    const lunarHour = solarTime.getLunarHour()
    state.birthday = [
      lunarHour.getYear(),
      lunarHour.getMonth(),
      lunarHour.getDay(),
      lunarHour.getHour(),
    ]
    console.log('公历转农历后', index.name, state.birthday)
  }
  // 3.更新Columns列,注意此时state.birthdayIndex还未更新
  updateColumns([...state.birthday, index.name])
}

// 5.更新列
const updateColumns = (birthday: number[]) => {
  let monthColumns = []
  let dayColumns = []
  const [_year, _month, _day, _hour, birthdayIndex] = birthday
  // 1.农历列
  if (birthdayIndex === 0) {
    const lunarYear: LunarYear = LunarYear.fromYear(_year)
    monthColumns = lunarYear.getMonths().map((month) => ({
      label: month.isLeap()
        ? `${chineseMonths[month.getMonth() - 1]}`
        : chineseMonths[month.getMonth() - 1],
      value: month.getMonthWithLeap(),
    }))
    const lunarMonth: LunarMonth = LunarMonth.fromYm(_year, _month)
    const dayCount: number = lunarMonth.getDayCount()
    dayColumns = initDays(dayCount, 'lunar')
  }
  // 2.公历列
  if (birthdayIndex === 1) {
    monthColumns = Array.from({ length: 12 }, (_, i) => {
      return { label: `${chineseMonths[i]}`, value: i + 1 }
    })
    const solarMonth: SolarMonth = SolarMonth.fromYm(_year, _month)
    const dayCount: number = solarMonth.getDayCount()
    dayColumns = initDays(dayCount, 'gregorian')
  }
  state.displayColumns = [yearColumns, monthColumns, dayColumns, hourColumns]
}

当用户滑动列后进行更新picker

const onChangeColumn = (pickerView, value, columnIndex, resolve) => {
  // debugger
  // 1.更新birthday值
  const item = value[columnIndex]
  state.birthday[columnIndex] = Number(item.value)
  // 2.滑动列后重置后面的列
  for (let i = columnIndex + 1; i < 4; i++) {
    state.birthday[i] = 1
  }
  // 3.更新Columns列
  updateColumns([...state.birthday, state.birthdayIndex])
  // 4.更新pickerView
  if (columnIndex === 0) {
    pickerView.setColumnData(1, state.displayColumns[1])
    pickerView.setColumnData(2, state.displayColumns[2])
    pickerView.setColumnData(3, state.displayColumns[3])
  } else if (columnIndex === 1) {
    pickerView.setColumnData(2, state.displayColumns[2])
    pickerView.setColumnData(3, state.displayColumns[3])
  } else if (columnIndex === 2) {
    pickerView.setColumnData(3, state.displayColumns[3])
  }
  // 5.更新pickerView组件
  resolve()
}

这里我进行了后列重置,避免出现后面的日期不存在于当月,造成列Index错误。

在这里插入图片描述

解决微信小程序 Tab 动画异常

// 解决微信小程序Tabs表现异常
const tabsRef = ref<TabsInstance>()
function handleOpened() {
  tabsRef.value?.updateLineStyle(false)
}

在这里插入图片描述

效果展示

选择农历后:

在这里插入图片描述
农历切换到公里后:

在这里插入图片描述
测试闰月:

在这里插入图片描述
测试校验规则

在这里插入图片描述

三、完整代码

代码

<template>
  <wd-popup
    v-model="state.popupShow"
    position="bottom"
    custom-style="border-radius:32rpx;height: 300px;"
    :close-on-click-modal="false"
    @close="closeBirthdayPicker"
    closable
    @after-enter="handleOpened"
  >
    <wd-tabs v-model="state.birthdayIndex" animated swipeable ref="tabsRef" @change="onChangeTab">
      <wd-tab v-for="(item, index) in state.birthdayArr" :key="index" :title="item">
        <wd-picker-view
          :columns="state.displayColumns"
          v-model="state.birthday"
          :column-change="onChangeColumn"
          ref="birthdayPicker"
        />
      </wd-tab>
    </wd-tabs>
  </wd-popup>
  <div @click="openBirthdayPicker">
    <wd-input
      :prop="props.prop"
      :rules="props.rules"
      type="text"
      label="生辰"
      v-model="displayBirthday"
      placeholder="请选择"
      readonly
      required
      placeholder-style="color:#bfbfbf"
    >
      <template #suffix>
        <!--箭头-->
        <wd-icon name="arrow-right" color="rgba(0, 0, 0, 0.25)" size="18px" />
      </template>
    </wd-input>
  </div>
</template>

<script lang="ts" setup>
import { LunarYear, LunarMonth, LunarHour, SolarTime, SolarMonth } from 'tyme4ts'
import { isArray } from 'wot-design-uni/components/common/util'
import { reactive, computed, watch, ref, defineProps, defineEmits, defineOptions } from 'vue'
import { FormItemRule } from 'wot-design-uni/components/wd-form/types'

defineOptions({
  name: 'birthdayPicker',
})

onLoad(() => {
  debugger
})

// 解决微信小程序Tabs表现异常
const tabsRef = ref<TabsInstance>()
function handleOpened() {
  tabsRef.value?.updateLineStyle(false)
}

// 定义农历月份和日期的汉字表示
const chineseMonths = [
  '一月',
  '二月',
  '三月',
  '四月',
  '五月',
  '六月',
  '七月',
  '八月',
  '九月',
  '十月',
  '十一月',
  '十二月',
]
const chineseDays = [
  '初一',
  '初二',
  '初三',
  '初四',
  '初五',
  '初六',
  '初七',
  '初八',
  '初九',
  '初十',
  '十一',
  '十二',
  '十三',
  '十四',
  '十五',
  '十六',
  '十七',
  '十八',
  '十九',
  '二十',
  '廿一',
  '廿二',
  '廿三',
  '廿四',
  '廿五',
  '廿六',
  '廿七',
  '廿八',
  '廿九',
  '三十',
]

// 1.状态管理
const state = reactive({
  displayColumns: [],
  popupShow: false,
  birthdayArr: ['农历', '公历'],
  birthday: [],
  birthdayIndex: 0,
  pickerViewLoaded: false,
})

// 2.定义组件接收值
type bithdayPickerProps = {
  modelValue: number[] // 生日值
  disabled?: boolean // 是否禁用
  prop: string // 表单域 model 字段名,在使用表单校验功能的情况下,该属性是必填的
  rules: FormItemRule[] // 表单校验规则
}
const props = defineProps<bithdayPickerProps>()
const emit = defineEmits(['update:modelValue'])

// 3.计算Picker展示值
const findValueInCoulums = (value: number, columns: any[]) => {
  let res = ''
  columns.forEach((item) => {
    if (item.value === value) res = item.label
  })
  return res
}
const displayBirthday = computed(() => {
  const birthday = state.birthday
  // 避免空数组调用join函数
  if (isNullBirthdayArray(birthday, 4)) return null
  // 公历2024年12月22日13时
  return (
    state.birthdayArr[state.birthdayIndex] +
    birthday[0] +
    '年' +
    findValueInCoulums(birthday[1], state.displayColumns[1]) +
    findValueInCoulums(birthday[2], state.displayColumns[2]) +
    birthday[3] +
    '时'
  )
})
const isNullBirthdayArray = (birthday: number[], validLength: number = birthday.length) => {
  return (
    !birthday ||
    (isArray(birthday) &&
      (!birthday.length ||
        birthday.length !== validLength ||
        birthday.every((item) => item === null)))
  )
}
// 4.初始化
onMounted(() => {
  handleShowValueUpdate(props.modelValue)
})

// 5.监听父组件的值变化
watch(
  () => props.modelValue,
  (newValue) => {
    handleShowValueUpdate(newValue)
  },
)

// 6.值变更时更新显示内容
const handleShowValueUpdate = (value: number[]) => {
  // 0.校验
  if (isNullBirthdayArray(value, 5)) {
    // 更新为初始值,状态值保持不变
    updateColumns([1940, 1, 1, 1, 0])
    return
  }
  // 1.获取选中项
  state.birthday = [value[0], value[1], value[2], value[3]]
  state.birthdayIndex = value[4]
  // 2.更新列
  updateColumns(value)
}

// 7.更新Columns:年份列、月份列、天数列、小时列
const yearColumns = Array.from({ length: new Date().getFullYear() - 1940 + 1 }, (_, i) => {
  return { label: `${1940 + i}`, value: 1940 + i }
})
const hourColumns = Array.from({ length: 24 }, (_, i) => ({
  label: `${i + 1}`,
  value: i + 1,
}))
const initDays = (len: number, type: 'lunar' | 'gregorian') =>
  Array.from({ length: len }, (_, i) => {
    return {
      label: type === 'lunar' ? `${chineseDays[i]}` : `${i + 1}`,
      value: i + 1,
    }
  })

const updateColumns = (birthday: number[]) => {
  let monthColumns = []
  let dayColumns = []
  const [_year, _month, _day, _hour, birthdayIndex] = birthday
  // 1.农历列
  if (birthdayIndex === 0) {
    const lunarYear: LunarYear = LunarYear.fromYear(_year)
    monthColumns = lunarYear.getMonths().map((month) => ({
      label: month.isLeap()
        ? `${chineseMonths[month.getMonth() - 1]}`
        : chineseMonths[month.getMonth() - 1],
      value: month.getMonthWithLeap(),
    }))
    const lunarMonth: LunarMonth = LunarMonth.fromYm(_year, _month)
    const dayCount: number = lunarMonth.getDayCount()
    dayColumns = initDays(dayCount, 'lunar')
  }
  // 2.公历列
  if (birthdayIndex === 1) {
    monthColumns = Array.from({ length: 12 }, (_, i) => {
      return { label: `${chineseMonths[i]}`, value: i + 1 }
    })
    const solarMonth: SolarMonth = SolarMonth.fromYm(_year, _month)
    const dayCount: number = solarMonth.getDayCount()
    dayColumns = initDays(dayCount, 'gregorian')
  }
  state.displayColumns = [yearColumns, monthColumns, dayColumns, hourColumns]
}

// 8.当用户滑动列后触发
const onChangeColumn = (pickerView, value, columnIndex, resolve) => {
  // debugger
  // 1.更新birthday值
  const item = value[columnIndex]
  state.birthday[columnIndex] = Number(item.value)
  // 2.滑动列后重置后面的列
  for (let i = columnIndex + 1; i < 4; i++) {
    state.birthday[i] = 1
  }
  // 3.更新Columns列
  updateColumns([...state.birthday, state.birthdayIndex])
  // 4.更新pickerView
  if (columnIndex === 0) {
    pickerView.setColumnData(1, state.displayColumns[1])
    pickerView.setColumnData(2, state.displayColumns[2])
    pickerView.setColumnData(3, state.displayColumns[3])
  } else if (columnIndex === 1) {
    pickerView.setColumnData(2, state.displayColumns[2])
    pickerView.setColumnData(3, state.displayColumns[3])
  } else if (columnIndex === 2) {
    pickerView.setColumnData(3, state.displayColumns[3])
  }
  // 5.更新pickerView组件
  resolve()
}

// 9.当农历、公历切换时:
const onChangeTab = (index) => {
  state.pickerViewLoaded = true
  const [_year, _month, _day, _hour] = state.birthday
  // 1.农历转公历
  if (index.name === 1) {
    console.log('农历转公历前', index.name, state.birthday)
    const lunarHour = LunarHour.fromYmdHms(_year, _month, _day, _hour, 0, 0)
    const solarTime: SolarTime = lunarHour.getSolarTime()
    state.birthday = [
      solarTime.getYear(),
      solarTime.getMonth(),
      solarTime.getDay(),
      solarTime.getHour(),
    ]
    console.log('农历转公历后', index.name, state.birthday)
  }
  // 2.公历转农历
  if (index.name === 0) {
    console.log('公历转农历前', index.name, state.birthday)
    const solarTime = SolarTime.fromYmdHms(_year, _month, _day, _hour, 0, 0)
    const lunarHour = solarTime.getLunarHour()
    state.birthday = [
      lunarHour.getYear(),
      lunarHour.getMonth(),
      lunarHour.getDay(),
      lunarHour.getHour(),
    ]
    console.log('公历转农历后', index.name, state.birthday)
  }
  // 3.更新Columns列,注意此时state.birthdayIndex还未更新
  updateColumns([...state.birthday, index.name])
}

// 打开生日选择器
const openBirthdayPicker = () => {
  if (props.disabled) return
  state.popupShow = true
  const value = props.modelValue
  if (isNullBirthdayArray(value, 5)) {
    state.birthday = [1940, 1, 1, 1]
    state.birthdayIndex = 0
    return
  }
  state.birthday = [value[0], value[1], value[2], value[3]]
  state.birthdayIndex = value[4]
}

// 关闭生日选择器
const closeBirthdayPicker = () => {
  emit('update:modelValue', [...state.birthday, state.birthdayIndex])
  state.popupShow = false
}
</script>

<style lang="scss" scoped></style>

下一步建议

近期本人时间有限,暂不做改进,大佬们可以拿去做完善。

  1. 缺少 取消和确认 的操作。
  2. 组件不够通用,需要暴漏常用的函数和变量。
📌 [ 笔者 ]   文艺倾年
📃 [ 更新 ]   2024.12.22
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

在这里插入图片描述


http://www.niftyadmin.cn/n/5796574.html

相关文章

ModbusTCP从站转Profinet主站案例

一. 案例背景 在复杂的工业自动化场景中&#xff0c;企业常常会采用不同品牌的设备来构建生产系统。西门子SINAMICS G120变频器以其高性能、高精度的速度和转矩控制功能&#xff0c;在电机驱动领域应用广泛。施耐德M580可编程逻辑控制器则以强大的逻辑控制和数据处理能力著称&…

Java基于SSM框架的无中介租房系统小程序【附源码、文档】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

3. Kafka入门—安装与基本命令

Kafka基础操作 一. 章节简介二. kafka简介三. Kafka安装1. 准备工作2. Zookeeper安装2.1 配置文件2.2 启动相关命令3. Kafka安装3.1 配置文件3.2 启动相关命令-------------------------------------------------------------------------------------------------------------…

Linux快速入门-Linux的常用命令

Linux的常用命令 1. Linux的终端与工作区1.1 终端概述1.2 切换终端 2. Shell语言解释器2.1 Shell概述 3. 用户登录与身份切换3.1 su 命令3.2 sudo 命令 4. 文件、目录操作命令4.1 pwd 命令4.2 cd 命令4.3 ls 命令4.3.1 ls 指令叠加使用 4.4 mkdir 命令4.5 rmdir 命令4.6 cp 命令…

《算法》题目

多项选择题 2023年2月,美国国家标准与技术研究院(NIST)将 Ascon算法确立为轻量级加密(LWC)标准,关于该算法和标准的说法,正确的是( )。 A.该标准属于国际标准 B.该标准旨在保护物联网(IoT)创建和传输的信息 C.通过法律法规规范标准化机构的职责与权限,可以起到推动技…

TCP与UDP的端口连通性

注意&#xff1a; 本文内容于 2024-12-21 02:34:15 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;TCP与UDP的端口连通性。感谢您的关注与支持&#xff01; 常规情况下&#xff0c;我们验证跟对方…

蓝队HW初级面试题总结

更多大厂面试经验的视频经验分享看主页 目录&#xff1a; 基础漏洞 安全设备部署 内网渗透 所面试的公司&#xff1a;深蓝&#xff0c;360&#xff0c;长亭&#xff0c;深信服&#xff08;问的问题都差不多&#xff0c;所以我混合起来总结了一下&#xff09; 面试职位&am…

分布式协同 - 分布式事务_TCC解决方案

文章目录 导图Pre流程图2PC VS 3PC VS TCC2PC&#xff08;Two-Phase Commit&#xff0c;二阶段提交&#xff09;3PC&#xff08;Three-Phase Commit&#xff0c;三阶段提交&#xff09;TCC&#xff08;Try-Confirm-Cancel&#xff09;2PC、3PC与TCC的区别2PC、3PC与TCC的联系 导…