封装一个Taro小程序的PickerDateTime组件

588 阅读3分钟

近期老板看我们太闲了,上个厕所都占不到坑了,于是,老板:你们把后台的功能往飞书小程序迁移吧,嘿嘿,看我不给你写点防御性代码,后面再整个后台app版,主打的一个工作稳定。

那么好,开整呗,谁让咱是打工的呢!首先就是基建,这个这边就不多废话了(这边也是提取了一个飞书小程序模版,回头闲一点再优化优化丢到github上)。

那么直接到写业务阶段,这边我们小程序ui库使用的taro-ui,我们后台的ui库是ant-design,那么问题就来了,抛开组件抛出来的数据结构不一致不说,tm好多组件直接没有,气冲冲的跑到老板办公室,得加钱,这是另外的价钱,于是于是就开始画饼了。

这块后台最常用的而taroui没有的组件:

  1. 日期选择器

ant-design

image.png

taroui

image.png

2.选择器

ant-design

image.png

taroui

image.png

这块就举个例子,就以上两个组件taro可以说是实现不了后台相同的功能的,只能自己手撸一个,本文是日期选择器所以以下内容就针对日期时间选择器,选择器这快也是大概按照飞书内部的选择器封装了一个,后续也会出相关的文章,

这块还有一个就是taro的日期选择和时间选择是分开的,而我们后台是一个字段,好吧开整,

功能

首先分析一波,需要做到的功能:

  • 详情回显
  • 选择日期时间后抛给父组件
  • 首次默认是当前时间

PickerDateTime

import { Text, PickerView, PickerViewColumn, View } from "@tarojs/components";
import React, { useMemo, useRef, useState } from "react";
import { AtFloatLayout } from "taro-ui";
import dayjs from "dayjs";

export interface PickerDateTimeProps {
    type: "date" | "datetime";
    value: string;
    onChange: (val: any) => void;
}

const findDateTimeIndex = (arr: Array<number | string>, val: number | string) => {
    const index = arr.map(Number).indexOf(+val);
    return index === -1 ? 0 : index;
};

const addZero = (num: number | string) => {
    return +num <= 9 ? `0${+num}` : `${+num}`;
};

const getDateTimeConfig = () => {
    function createDateTimeByStartAndEnd(start, end) {
        const arr = Array.from({ length: end - start + 1 }, (_, i) => addZero(start + i));
        return arr;
    }
    return [
        createDateTimeByStartAndEnd(1990, 2024),
        createDateTimeByStartAndEnd(1, 12),
        createDateTimeByStartAndEnd(1, 31),
        createDateTimeByStartAndEnd(0, 23),
        createDateTimeByStartAndEnd(0, 59),
        createDateTimeByStartAndEnd(0, 59)
    ];
};

const PickerDateTime: React.FC<PickerDateTimeProps> = props => {
    const { value, onChange } = props;
    const dates = getDateTimeConfig();
    const [years, months, days, hours, minutes, seconds] = dates;
    const [openSelector, setOpenSelector] = useState(true);
    const selectIndexs = useRef<number[]>([]);

    const generateEchoIndexByValue = useMemo(() => {
        let indexs: number[] = [];
        if (value) {
            const [date, time] = value.split(" ");
            const [year, month, day] = date.split("-");
            const [hour, minute, second] = time.split(":");
            indexs = [
                findDateTimeIndex(years, year),
                findDateTimeIndex(months, addZero(month)),
                findDateTimeIndex(days, addZero(day)),
                findDateTimeIndex(hours, addZero(hour)),
                findDateTimeIndex(minutes, addZero(minute)),
                findDateTimeIndex(seconds, addZero(second))
            ];
        } else {
            const now = dayjs();
            indexs = [
                findDateTimeIndex(years, now.year()),
                findDateTimeIndex(months, addZero(now.month() + 1)),
                findDateTimeIndex(days, addZero(now.date())),
                findDateTimeIndex(hours, addZero(now.hour())),
                findDateTimeIndex(minutes, addZero(now.minute())),
                findDateTimeIndex(seconds, addZero(now.second()))
            ];
        }
        selectIndexs.current = indexs;
        return indexs;
    }, [value, years, months, days, hours, minutes, seconds]);

    /**
     * 根据选择的时间下标数组生成日期时间
     * @param indexs
     * @returns
     */
    const generateDateTimeStrByIndexs = (indexs: number[]) => {
        const datesArr = indexs.map((item, i) => dates[i][item]);
        const date = datesArr.slice(0, 3).join("-");
        const time = datesArr.slice(3).join(":");
        return date + " " + time;
    };

    const onConfirm = () => {
        const dateTime = generateDateTimeStrByIndexs(selectIndexs.current);
        onChange(dateTime);
        setOpenSelector(false);
    };

    return (
        <View>
            <View className="flex items-center justify-between text-xs text-gray-900">
                <Text className="text-sm font-semibold">日期时间</Text>
                <Text
                    onClick={() => {
                        setOpenSelector(true);
                    }}>
                    {value}
                </Text>
            </View>
            <View>
                <AtFloatLayout isOpened={openSelector}>
                    <View>
                        <Text className="block w-20 h-1 bg-slate-500 rounded-full mx-auto"></Text>
                        <View className="mt-3 mb-6 flex items-center justify-center pb-3 border-b-.5 border-gray-200 relative">
                            <Text className="text-base font-semibold text-gray-900">请选择日期</Text>
                            <Text className="text-xs text-42-blue absolute right-0 top-2" onClick={onConfirm}>
                                确认
                            </Text>
                        </View>
                    </View>
                    <View className=" grid grid-cols-6">
                        {["年", "月", "日", "时", "分", "秒"].map(head => {
                            return <Text className=" text-center col-span-1 text-xs font-semibold text-gray-900">{head}</Text>;
                        })}
                    </View>
                    <PickerView
                        indicatorStyle="height: 50px;"
                        style="width: 100%; height: 300px;"
                        value={generateEchoIndexByValue}
                        onChange={e => {
                            selectIndexs.current = e.detail.value;
                        }}>
                        <PickerViewColumn>
                            {years.map(item => {
                                return <Text className="flex items-center justify-center text-xs text-gray-900">{item}</Text>;
                            })}
                        </PickerViewColumn>
                        <PickerViewColumn>
                            {months.map(item => {
                                return <Text className="flex items-center justify-center text-xs text-gray-900">{item}</Text>;
                            })}
                        </PickerViewColumn>
                        <PickerViewColumn>
                            {days.map(item => {
                                return <Text className="flex items-center justify-center text-xs text-gray-900">{item}</Text>;
                            })}
                        </PickerViewColumn>
                        <PickerViewColumn>
                            {hours.map(item => {
                                return <Text className="flex items-center justify-center text-xs text-gray-900">{item}</Text>;
                            })}
                        </PickerViewColumn>
                        <PickerViewColumn>
                            {minutes.map(item => {
                                return <Text className="flex items-center justify-center text-xs text-gray-900">{item}</Text>;
                            })}
                        </PickerViewColumn>
                        <PickerViewColumn>
                            {seconds.map(item => {
                                return <Text className="flex items-center justify-center text-xs text-gray-900">{item}</Text>;
                            })}
                        </PickerViewColumn>
                    </PickerView>
                </AtFloatLayout>
            </View>
        </View>
    );
};
export default PickerDateTime;

看看最终效果吧

7Hz6ylR8KT.gif

以上就是该组件完整功能和代码了,希望这篇博客能帮助你更好地理解和使用这个组件,如果觉得这篇文章还有点屌用的,点赞收藏支持一下,谢谢🐶!