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

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

😊好久没有更新有关前端实战教程了,本文主要讲解【全栈实战】基于 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
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/940809.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ajax中的axios

既然提到Ajax&#xff0c;那就先来说一说什么是Ajax吧 关于Ajax Ajax的定义 Asynchronous JavaScript And XML&#xff1a;异步的JavaScript和XML。 反正就是一句话总结&#xff1a; 使用XML HttpRequest 对象与服务器进行通讯。 AJAX 是一种在无需重新加载整个网页的情况下&…

60.基于SSM的个人网站的设计与实现(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;个人网站是在MySQL中建立数据表保存信息&#xff0c;运用SSMVue框架和Java语言编写。并按照软件设计开发流程进行设计实现充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SSM的网…

聊聊Flink:Flink的状态管理

一、Flink的状态是什么&#xff1f; 我们知道&#xff0c;Flink的一个算子可能会有多个子任务&#xff0c;每个子任务可能分布在不同的实例&#xff08;即slot&#xff09;上&#xff0c;我们可以把Flink的状态理解为某个算子的子任务在其当前实例上的一个变量&#xff0c;该变…

Idea 将多个module显示在同一个project

Idea 将多个maven项目显示在同一个project下 1、选择菜单 File-》New -》Module from Existing Sources -> 2、在弹出的界面选中对应的Module的pom.xml,然后点击OK按钮就行了 (弹出框上面也提示了Eclipse 项目选中.project文件&#xff1b;Maven 项目选中pom.xml; ) 最终显…

文件解析漏洞中间件(iis和Apache)

IIS解析漏洞 IIS6.X #环境 Windows Server 2003 在iis6.x中&#xff0c;.asp文件夹中的任意文件都会被当做asp文件去执行 在默认网站里创建一个a.asp文件夹并创建一个1.jpg写进我们的asp代码 <%now()%> #asp一句话 <%eval request("h")%> 单独创建一…

gitee别人仓库再上传自己仓库

一、新建一个自己的Git仓库 如果没有注册账号的朋友&#xff0c;可以先去注册一个Gitee的账号&#xff0c;用于管理自己的代码特别好用&#xff01;&#xff01;&#xff01; 接下来就是在gitee上新建一个自己的仓库&#xff0c;如下图所示 二、右建 Git Bush Here删除.git文件…

STM32F407 | Embedded IDE01 - vscode搭建Embedded IDE开发环境(支持JLINK、STLINK、DAPLINK)

导言 Embedded IDE官网:https://em-ide.com/docs/intro 我猜肯定有部分人使用SI Keil开发STM32项目&#xff0c;也有vscode Keil开发STM32程序。SI或vscode编写代码&#xff0c;然后切换Keil编译、下载、调试程序。有一段时间&#xff0c;我也是这么干的。但是&#xff0c;程…

算法的学习笔记—扑克牌顺子(牛客JZ61)

&#x1f600;前言 扑克牌顺子问题是一道趣味性与逻辑性兼备的题目&#xff0c;要求判断五张牌是否能组成顺子&#xff0c;其中大小王&#xff08;癞子&#xff09;可作为任意牌面。癞子的特殊性增加了问题的复杂度&#xff0c;也为解题提供了更多的可能性。通过这一问题&#…

记录遇到的一个新的变种JS加密

源 逻辑分析 混淆代码的目的是隐藏实际逻辑&#xff0c;增加逆向工程的难度。以下是对代码的逐步分析和解读。 第一部分&#xff1a;立即调用的函数表达式 (IIFE) (function () {var _K [...]; // 存储大量字符串的数组 })();​ 1. 目的&#xff1a;这个 IIFE 是整个代码运行…

LeetCode:104.二叉树的最大深度

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;104.二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节…

Fastjson <= 1.2.47 反序列化漏洞复现

0x01 前言 Fastjson 是一个 Java 语言编写的高性能功能完善的 JSON 库&#xff0c;可以将 Java 对象转换为 JSON 格式&#xff0c;也可以将 JSON 字符串转换为 Java 对象&#xff0c;在中国和美国使用较为广泛。 0x02 漏洞成因 Fastjson < 1.2.68 版本在处理反序列化对象时…

python:函数

一、嵌套函数 1.1概念 嵌套函数是定义在另一个函数作用域内部的函数。外部函数可以访问其内部声明的嵌套函数&#xff0c;而嵌套函数则可以访问其外部函数的作用域&#xff08;包括参数和局部变量&#xff09;。 1.2实例 一般情况下&#xff0c;我们是这样书写嵌套函数的&a…

Linux 下的 GPT 和 MBR 分区表详解

文章目录 Linux 下的 GPT 和 MBR 分区表详解一、分区表的作用二、MBR&#xff08;Master Boot Record&#xff09;1. **特点**2. **优点**3. **缺点**4. **适用场景** 三、GPT&#xff08;GUID Partition Table&#xff09;1. **特点**2. **优点**3. **缺点**4. **适用场景** 四…

基于单片机的智能婴儿床监护系统多功能婴儿床摇篮系统

功能介绍 以STM32单片机为控制核心蓝牙传输控制可以进行哭闹检测、尿床检测、音乐播放、语音提醒、哭闹时可以进行摇床有不同的模式自动模式和睡眠模式 实物可做&#xff0c;其他功能也可以 电路图 PCB 源代码 u8 Temperature_High; //室内温度高阈值 u8 Temperature_…

人工智能在VR展览中扮演什么角色?

人工智能&#xff08;AI&#xff09;在VR展览中扮演着多重关键角色&#xff0c;这些角色不仅增强了用户体验&#xff0c;还为展览的组织者提供了强大的工具。 接下来&#xff0c;由专业从事VR展览制作的圆桌3D云展厅平台为大家介绍AI在VR展览中的一些主要作用&#xff1a; 个性…

JVM和数据库面试知识点

JVM内存结构 主要有几部分&#xff1a;堆、栈、方法区和程序计数器 堆是JVM中最大的一块内存区域&#xff0c;用于存储对象实例&#xff0c;一般通过new创建的对象都存放在堆中。堆被所有的线程共享&#xff0c;但是它的访问时线程不安全的&#xff0c;通常通过锁的机制来保证线…

flask-admin+Flask-WTF 实现实现增删改查

背景&#xff1a; flask-adminflask-wtf在网上可以搜索到很多资料&#xff0c;但有价值的很少&#xff0c;或许是太简单&#xff0c;或者是很少人这么用&#xff0c;或者。。。&#xff0c;本文将作者近礼拜摸索到的一点经验分享出来&#xff0c;给自己做个记录。 材料&#…

C++简明教程(文章要求学过一点C语言)(3)

一、编程工具大揭秘——IDE 当我们准备踏入 C 编程的奇妙世界时&#xff0c;首先要认识一个重要的“魔法盒子”——集成开发环境&#xff08;IDE&#xff09;。IDE 就像是一个全能的编程工作室&#xff0c;它把我们写代码所需要的各种工具都整合到了一起&#xff0c;让编程这件…

STM32-笔记5-按键点灯(中断方法)

1、复制03-流水灯项目&#xff0c;重命名06-按键点灯&#xff08;中断法&#xff09; 在\Drivers\BSP目录下创建一个文件夹exti&#xff0c;在该文件夹下&#xff0c;创建两个文件exti.c和exti.h文件&#xff0c;并且把这两个文件加载到项目中&#xff0c;打开项目工程文件 加载…

实现 WebSocket 接入文心一言

目录 什么是 WebSocket&#xff1f; 为什么需要 WebSocket&#xff1f; HTTP 的局限性 WebSocket 的优势 总结&#xff1a;HTTP 和 WebSocket 的区别 WebSocket 的劣势 WebSocket 常见应用场景 WebSocket 握手过程 WebSocket 事件处理和生命周期 WebSocket 心跳机制 …