add:redux
parent
2a78e35d93
commit
9ca194b3a0
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
|||||||
|
import { Component } from 'react'
|
||||||
|
|
||||||
|
const objectToString = (style: object | string): string => {
|
||||||
|
if (style && typeof style === 'object') {
|
||||||
|
let styleStr = ''
|
||||||
|
Object.keys(style).forEach(key => {
|
||||||
|
const lowerCaseKey = key.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||||
|
styleStr += `${lowerCaseKey}:${style[key]};`
|
||||||
|
})
|
||||||
|
return styleStr
|
||||||
|
} else if (style && typeof style === 'string') {
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AtComponent<P, S> extends Component<P, S> {
|
||||||
|
/**
|
||||||
|
* 合并 style
|
||||||
|
* @param {Object|String} style1
|
||||||
|
* @param {Object|String} style2
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
mergeStyle(
|
||||||
|
style1: object | string,
|
||||||
|
style2: object | string
|
||||||
|
): object | string {
|
||||||
|
if (
|
||||||
|
style1 &&
|
||||||
|
typeof style1 === 'object' &&
|
||||||
|
style2 &&
|
||||||
|
typeof style2 === 'object'
|
||||||
|
) {
|
||||||
|
return Object.assign({}, style1, style2)
|
||||||
|
}
|
||||||
|
return objectToString(style1) + objectToString(style2)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
// 使用 HOC 以方便为各个页面复用这段逻辑
|
||||||
|
function createErrorBoundary(Page) {
|
||||||
|
return class ErrorBoundary extends Component {
|
||||||
|
el = React.createRef();
|
||||||
|
state = {
|
||||||
|
hasError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
static getDerivedStateFromError() {
|
||||||
|
return {
|
||||||
|
hasError: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error, errorInfo) {
|
||||||
|
console.log(error, errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start 需要手动调用页面组件上的生命周期方法 **/
|
||||||
|
componentDidShow() {
|
||||||
|
return this.el.current?.componentDidShow?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidHide() {
|
||||||
|
return this.el.current?.componentDidHide?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
onShareAppMessage() {
|
||||||
|
return this.el.current?.onShareAppMessage?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
//...
|
||||||
|
|
||||||
|
/* End 需要手动调用页面组件上的生命周期方法 **/
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.state.hasError ? (
|
||||||
|
<View>Something went wrong.</View>
|
||||||
|
) : (
|
||||||
|
<Page ref={this.el} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createErrorBoundary;
|
||||||
@ -0,0 +1,489 @@
|
|||||||
|
import classnames from "classnames";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import React from "react";
|
||||||
|
import Taro, { Events } from "@tarojs/taro";
|
||||||
|
|
||||||
|
import { Swiper, SwiperItem, View, Image } from "@tarojs/components";
|
||||||
|
import {
|
||||||
|
BaseEventOrig,
|
||||||
|
ITouch,
|
||||||
|
ITouchEvent,
|
||||||
|
} from "@tarojs/components/types/common";
|
||||||
|
import {
|
||||||
|
AtCalendarBodyListGroup,
|
||||||
|
AtCalendarBodyProps,
|
||||||
|
AtCalendarBodyState,
|
||||||
|
Calendar,
|
||||||
|
} from "../../../../types/calendar";
|
||||||
|
import { delayQuerySelector } from "../../../common/utils";
|
||||||
|
import generateCalendarGroup from "../common/helper";
|
||||||
|
import AtCalendarDateList from "../ui/date-list/index";
|
||||||
|
import AtCalendarDayList from "../ui/day-list/index";
|
||||||
|
|
||||||
|
import "./isFolding.less";
|
||||||
|
|
||||||
|
const ANIMTE_DURATION = 300;
|
||||||
|
|
||||||
|
const defaultProps: Partial<AtCalendarBodyProps> = {
|
||||||
|
marks: [],
|
||||||
|
selectedDate: {
|
||||||
|
end: Date.now(),
|
||||||
|
start: Date.now(),
|
||||||
|
},
|
||||||
|
format: "YYYY-MM-DD",
|
||||||
|
generateDate: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// const events = new Events();
|
||||||
|
|
||||||
|
export default class AtCalendarBody extends React.Component<
|
||||||
|
AtCalendarBodyProps,
|
||||||
|
Readonly<AtCalendarBodyState>
|
||||||
|
> {
|
||||||
|
static defaultProps: Partial<AtCalendarBodyProps> = defaultProps;
|
||||||
|
|
||||||
|
public constructor(props: AtCalendarBodyProps) {
|
||||||
|
super(props);
|
||||||
|
const {
|
||||||
|
validDates,
|
||||||
|
marks,
|
||||||
|
format,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
generateDate,
|
||||||
|
selectedDate,
|
||||||
|
selectedDates,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
this.generateFunc = generateCalendarGroup({
|
||||||
|
validDates,
|
||||||
|
format,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
marks,
|
||||||
|
selectedDates,
|
||||||
|
});
|
||||||
|
const listGroup = this.getGroups(generateDate, selectedDate);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
listGroup,
|
||||||
|
offsetSize: 0,
|
||||||
|
isAnimate: false,
|
||||||
|
isFolding: false,
|
||||||
|
temporaryCalendar: [],
|
||||||
|
temporaryCalendarWeek: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
delayQuerySelector(".at-calendar-slider__main").then((res: any) => {
|
||||||
|
if (res.length) {
|
||||||
|
this.maxWidth = res[0]?.width;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onChangeFoldingCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UNSAFE_componentWillReceiveProps(
|
||||||
|
nextProps: AtCalendarBodyProps
|
||||||
|
): void {
|
||||||
|
const {
|
||||||
|
validDates,
|
||||||
|
marks,
|
||||||
|
format,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
generateDate,
|
||||||
|
selectedDate,
|
||||||
|
selectedDates,
|
||||||
|
} = nextProps;
|
||||||
|
|
||||||
|
this.generateFunc = generateCalendarGroup({
|
||||||
|
validDates,
|
||||||
|
format,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
marks,
|
||||||
|
selectedDates,
|
||||||
|
});
|
||||||
|
const listGroup = this.getGroups(generateDate, selectedDate);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
offsetSize: 0,
|
||||||
|
listGroup,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private changeCount = 0;
|
||||||
|
private currentSwiperIndex = 1;
|
||||||
|
private startX = 0;
|
||||||
|
private swipeStartPoint = 0;
|
||||||
|
private isPreMonth = false;
|
||||||
|
private maxWidth = 0;
|
||||||
|
private isTouching = false;
|
||||||
|
|
||||||
|
private generateFunc: (
|
||||||
|
generateDate: number,
|
||||||
|
selectedDate: Calendar.SelectedDate,
|
||||||
|
isShowStatus?: boolean
|
||||||
|
) => Calendar.ListInfo<Calendar.Item>;
|
||||||
|
|
||||||
|
private getGroups = (
|
||||||
|
generateDate: number,
|
||||||
|
selectedDate: Calendar.SelectedDate
|
||||||
|
): AtCalendarBodyListGroup => {
|
||||||
|
const dayjsDate = dayjs(generateDate);
|
||||||
|
const arr: AtCalendarBodyListGroup = [];
|
||||||
|
const preList: Calendar.ListInfo<Calendar.Item> = this.generateFunc(
|
||||||
|
dayjsDate.subtract(1, "month").valueOf(),
|
||||||
|
selectedDate
|
||||||
|
);
|
||||||
|
|
||||||
|
const nowList: Calendar.ListInfo<Calendar.Item> = this.generateFunc(
|
||||||
|
generateDate,
|
||||||
|
selectedDate,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextList: Calendar.ListInfo<Calendar.Item> = this.generateFunc(
|
||||||
|
dayjsDate.add(1, "month").valueOf(),
|
||||||
|
selectedDate
|
||||||
|
);
|
||||||
|
|
||||||
|
const preListIndex =
|
||||||
|
this.currentSwiperIndex === 0 ? 2 : this.currentSwiperIndex - 1;
|
||||||
|
const nextListIndex =
|
||||||
|
this.currentSwiperIndex === 2 ? 0 : this.currentSwiperIndex + 1;
|
||||||
|
|
||||||
|
arr[preListIndex] = preList;
|
||||||
|
arr[nextListIndex] = nextList;
|
||||||
|
arr[this.currentSwiperIndex] = nowList;
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTouchStart = (e: ITouchEvent): void => {
|
||||||
|
if (!this.props.isSwiper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isTouching = true;
|
||||||
|
this.startX = e.touches[0].clientX;
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTouchMove = (e: ITouchEvent): void => {
|
||||||
|
if (!this.props.isSwiper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isTouching) return;
|
||||||
|
|
||||||
|
const { clientX } = e.touches[0];
|
||||||
|
const offsetSize = clientX - this.startX;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
offsetSize,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private animateMoveSlide = (offset: number, callback?: () => void): void => {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
isAnimate: true,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.setState({
|
||||||
|
offsetSize: offset,
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
isAnimate: false,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
callback?.();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, ANIMTE_DURATION);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTouchEnd = (): void => {
|
||||||
|
if (!this.props.isSwiper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { offsetSize } = this.state;
|
||||||
|
|
||||||
|
this.isTouching = false;
|
||||||
|
const isRight = offsetSize > 0;
|
||||||
|
|
||||||
|
const breakpoint = this.maxWidth / 2;
|
||||||
|
const absOffsetSize = Math.abs(offsetSize);
|
||||||
|
|
||||||
|
if (absOffsetSize > breakpoint) {
|
||||||
|
const res = isRight ? this.maxWidth : -this.maxWidth;
|
||||||
|
return this.animateMoveSlide(res, () => {
|
||||||
|
this.props.onSwipeMonth(isRight ? -1 : 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.animateMoveSlide(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleChange = (
|
||||||
|
e: BaseEventOrig<{
|
||||||
|
current: number;
|
||||||
|
source: string;
|
||||||
|
}>
|
||||||
|
): void => {
|
||||||
|
const { current, source } = e.detail;
|
||||||
|
|
||||||
|
if (source === "touch") {
|
||||||
|
this.currentSwiperIndex = current;
|
||||||
|
this.changeCount += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleAnimateFinish = (): void => {
|
||||||
|
if (this.changeCount > 0) {
|
||||||
|
this.props.onSwipeMonth(
|
||||||
|
this.isPreMonth ? -this.changeCount : this.changeCount
|
||||||
|
);
|
||||||
|
this.changeCount = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSwipeTouchStart = (
|
||||||
|
e: ITouchEvent & { changedTouches: Array<ITouch> }
|
||||||
|
): void => {
|
||||||
|
const { clientY, clientX } = e.changedTouches[0];
|
||||||
|
this.swipeStartPoint = this.props.isVertical ? clientY : clientX;
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSwipeTouchEnd = (
|
||||||
|
e: ITouchEvent & { changedTouches: Array<ITouch> }
|
||||||
|
): void => {
|
||||||
|
const { clientY, clientX } = e.changedTouches[0];
|
||||||
|
this.isPreMonth = this.props.isVertical
|
||||||
|
? clientY - this.swipeStartPoint > 0
|
||||||
|
: clientX - this.swipeStartPoint > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private onChangeFoldingCalendar = () => {
|
||||||
|
let { isFolding, listGroup } = this.state;
|
||||||
|
isFolding = !isFolding;
|
||||||
|
if (isFolding) {
|
||||||
|
let toDay = dayjs().format("YYYY-MM-DD");
|
||||||
|
let selectedDay = listGroup[1].list.find(
|
||||||
|
(item) => item.isSelected
|
||||||
|
)?.value;
|
||||||
|
let startWeekDay = dayjs(selectedDay)
|
||||||
|
.startOf("week")
|
||||||
|
.add(1, "day")
|
||||||
|
.format("YYYY-MM-DD");
|
||||||
|
let temporaryCalendar = [0, 1, 2, 3, 4, 5, 6].map((dayIndex) => {
|
||||||
|
let currentItemDay = dayjs(startWeekDay)
|
||||||
|
.add(dayIndex, "day")
|
||||||
|
.format("YYYY-MM-DD");
|
||||||
|
return {
|
||||||
|
isDisabled: false,
|
||||||
|
isSelected: currentItemDay === selectedDay,
|
||||||
|
isSelectedHead: currentItemDay === selectedDay,
|
||||||
|
isSelectedTail: currentItemDay === selectedDay,
|
||||||
|
isToday: toDay === currentItemDay,
|
||||||
|
marks: [],
|
||||||
|
text: Number(dayjs(currentItemDay).format("DD")),
|
||||||
|
type: 0,
|
||||||
|
value: currentItemDay,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.setState({ temporaryCalendar });
|
||||||
|
} else {
|
||||||
|
let temporaryCalendar = listGroup[1].list;
|
||||||
|
this.setState({ temporaryCalendar });
|
||||||
|
}
|
||||||
|
this.setState({ isFolding });
|
||||||
|
};
|
||||||
|
|
||||||
|
onDayClick(item: any) {
|
||||||
|
let { temporaryCalendar } = this.state;
|
||||||
|
let oldValueItem = temporaryCalendar.findIndex((ele) => ele.isSelected);
|
||||||
|
let newValueItem = temporaryCalendar.findIndex(
|
||||||
|
(ele) => ele.value === item.value
|
||||||
|
);
|
||||||
|
|
||||||
|
temporaryCalendar[oldValueItem].isSelected = false;
|
||||||
|
temporaryCalendar[oldValueItem].isSelectedHead = false;
|
||||||
|
temporaryCalendar[oldValueItem].isSelectedTail = false;
|
||||||
|
|
||||||
|
temporaryCalendar[newValueItem].isSelected = true;
|
||||||
|
temporaryCalendar[newValueItem].isSelectedHead = true;
|
||||||
|
temporaryCalendar[newValueItem].isSelectedTail = true;
|
||||||
|
|
||||||
|
this.setState({ temporaryCalendar });
|
||||||
|
this.props.onDayClick(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectDataChange(value: any) {
|
||||||
|
let { temporaryCalendar } = this.state;
|
||||||
|
let oldValueItem = temporaryCalendar.findIndex((ele) => ele.isSelected);
|
||||||
|
let newValueItem = temporaryCalendar.findIndex(
|
||||||
|
(ele) => ele.value === value
|
||||||
|
);
|
||||||
|
|
||||||
|
temporaryCalendar[oldValueItem].isSelected = false;
|
||||||
|
temporaryCalendar[oldValueItem].isSelectedHead = false;
|
||||||
|
temporaryCalendar[oldValueItem].isSelectedTail = false;
|
||||||
|
|
||||||
|
temporaryCalendar[newValueItem].isSelected = true;
|
||||||
|
temporaryCalendar[newValueItem].isSelectedHead = true;
|
||||||
|
temporaryCalendar[newValueItem].isSelectedTail = true;
|
||||||
|
|
||||||
|
this.setState({ temporaryCalendar });
|
||||||
|
this.props.onDayClick(temporaryCalendar[newValueItem]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const { isSwiper } = this.props;
|
||||||
|
const { isFolding, isAnimate, offsetSize, listGroup, temporaryCalendar } =
|
||||||
|
this.state;
|
||||||
|
|
||||||
|
Taro.eventCenter.on(
|
||||||
|
"onSelectDataChange",
|
||||||
|
this.onSelectDataChange.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isSwiper) {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={classnames(
|
||||||
|
"main",
|
||||||
|
"at-calendar-slider__main",
|
||||||
|
`at-calendar-slider__main--${process.env.TARO_ENV}`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<AtCalendarDayList />
|
||||||
|
<View
|
||||||
|
className={classnames("main__body", "body", {
|
||||||
|
"main__body--isFolding": isFolding,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<View className="body__slider body__slider--now">
|
||||||
|
{/* <AtCalendarDateList
|
||||||
|
list={temporaryCalendar}
|
||||||
|
onClick={this.onDayClick.bind(this)}
|
||||||
|
onLongClick={this.props.onLongClick}
|
||||||
|
/> */}
|
||||||
|
<AtCalendarDateList list={temporaryCalendar} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="arrow_box" onClick={this.onChangeFoldingCalendar}>
|
||||||
|
<Image
|
||||||
|
className={classnames("arrow", {
|
||||||
|
"arrow-rotate": isFolding,
|
||||||
|
})}
|
||||||
|
src={require("../../../img/index/arrow-down.png")}
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 需要 Taro 组件库维护 Swiper 使 小程序 和 H5 的表现保持一致 */
|
||||||
|
if (process.env.TARO_ENV === "h5") {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={classnames(
|
||||||
|
"main",
|
||||||
|
"at-calendar-slider__main",
|
||||||
|
`at-calendar-slider__main--${process.env.TARO_ENV}`
|
||||||
|
)}
|
||||||
|
onTouchEnd={this.handleTouchEnd}
|
||||||
|
onTouchMove={this.handleTouchMove}
|
||||||
|
onTouchStart={this.handleTouchStart}
|
||||||
|
>
|
||||||
|
<AtCalendarDayList />
|
||||||
|
<View
|
||||||
|
className={classnames("main__body body", {
|
||||||
|
"main__body--slider": isSwiper,
|
||||||
|
"main__body--animate": isAnimate,
|
||||||
|
})}
|
||||||
|
style={{
|
||||||
|
transform: isSwiper
|
||||||
|
? `translateX(-100%) translate3d(${offsetSize},0,0)`
|
||||||
|
: "",
|
||||||
|
WebkitTransform: isSwiper
|
||||||
|
? `translateX(-100%) translate3d(${offsetSize}px,0,0)`
|
||||||
|
: "",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View className="body__slider body__slider--pre">
|
||||||
|
<AtCalendarDateList list={listGroup[0].list} />
|
||||||
|
</View>
|
||||||
|
<View className="body__slider body__slider--now">
|
||||||
|
<AtCalendarDateList
|
||||||
|
list={temporaryCalendar}
|
||||||
|
onClick={this.props.onDayClick}
|
||||||
|
onLongClick={this.props.onLongClick}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View className="body__slider body__slider--next">
|
||||||
|
<AtCalendarDateList list={listGroup[2].list} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={classnames(
|
||||||
|
"main",
|
||||||
|
"at-calendar-slider__main",
|
||||||
|
`at-calendar-slider__main--${process.env.TARO_ENV}`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<AtCalendarDayList />
|
||||||
|
<Swiper
|
||||||
|
circular
|
||||||
|
current={1}
|
||||||
|
skipHiddenItemLayout
|
||||||
|
className={classnames("main__body", {
|
||||||
|
"main__body--isFolding": isFolding,
|
||||||
|
})}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
vertical={this.props.isVertical}
|
||||||
|
onAnimationFinish={this.handleAnimateFinish}
|
||||||
|
onTouchEnd={this.handleSwipeTouchEnd}
|
||||||
|
onTouchStart={this.handleSwipeTouchStart}
|
||||||
|
>
|
||||||
|
{listGroup.map((item, key) => (
|
||||||
|
<SwiperItem key={key} itemId={key.toString()}>
|
||||||
|
<AtCalendarDateList
|
||||||
|
list={key === 1 ? temporaryCalendar : item.list}
|
||||||
|
onClick={this.onDayClick.bind(this)}
|
||||||
|
onLongClick={this.props.onLongClick}
|
||||||
|
/>
|
||||||
|
{/* <AtCalendarDateList
|
||||||
|
list={item.list}
|
||||||
|
onClick={this.props.onDayClick}
|
||||||
|
onLongClick={this.props.onLongClick}
|
||||||
|
/> */}
|
||||||
|
</SwiperItem>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
|
||||||
|
<View className="arrow_box" onClick={this.onChangeFoldingCalendar}>
|
||||||
|
<Image
|
||||||
|
className={classnames("arrow", {
|
||||||
|
"arrow-rotate": isFolding,
|
||||||
|
})}
|
||||||
|
src={require("../../../img/index/arrow-down.png")}
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
.at-calendar-slider__main--weapp,
|
||||||
|
.at-calendar-slider__main--swan {
|
||||||
|
.main__body--isFolding {
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 样式定制 */
|
||||||
|
.at-calendar {
|
||||||
|
// 仅缩放宽度,防止上下抖动
|
||||||
|
// transform: scale(0.9, 1);
|
||||||
|
width: 90%;
|
||||||
|
margin: 0 auto;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.at-calendar__header .header__flex-item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
.at-calendar__header .header__flex,
|
||||||
|
.at-calendar__list.flex .flex__item-container .container-text {
|
||||||
|
// color: #333;
|
||||||
|
}
|
||||||
|
.at-calendar__list.flex .flex__item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
.at-calendar__list.flex .flex__item--today {
|
||||||
|
color: #333;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.at-calendar__list.flex
|
||||||
|
.flex__item--selected-head.flex__item--selected-tail
|
||||||
|
.flex__item-container {
|
||||||
|
background: linear-gradient(0deg, #fff0da, #ffe4c0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-calendar__list.flex .flex__item-container {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow_box {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
margin: 0 auto;
|
||||||
|
// margin-bottom: ;
|
||||||
|
// padding: 40rpx 80rpx 14rpx;
|
||||||
|
}
|
||||||
|
.arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
width: 24rpx;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-rotate {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
/* 样式定制 */
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export const TYPE_PRE_MONTH = -1
|
||||||
|
|
||||||
|
export const TYPE_NOW_MONTH = 0
|
||||||
|
|
||||||
|
export const TYPE_NEXT_MONTH = 1
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
import dayjs, { Dayjs } from 'dayjs'
|
||||||
|
import _flow from 'lodash/flow'
|
||||||
|
import { Calendar } from '../../../../types/calendar'
|
||||||
|
import * as constant from './constant'
|
||||||
|
import plugins from './plugins'
|
||||||
|
|
||||||
|
const TOTAL = 7 * 6
|
||||||
|
|
||||||
|
function getFullItem(
|
||||||
|
item: Partial<Calendar.Item>,
|
||||||
|
options: Calendar.GroupOptions,
|
||||||
|
selectedDate: Calendar.SelectedDate,
|
||||||
|
isShowStatus?: boolean
|
||||||
|
): any {
|
||||||
|
if (options.marks.find(x => x.value === item.value)) {
|
||||||
|
item.marks = [
|
||||||
|
{
|
||||||
|
value: item.value as string
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (!isShowStatus) return item
|
||||||
|
|
||||||
|
const bindedPlugins = plugins.map(fn =>
|
||||||
|
fn.bind(null, {
|
||||||
|
options,
|
||||||
|
selectedDate
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return _flow(bindedPlugins)(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function generateCalendarGroup(
|
||||||
|
options: Calendar.GroupOptions
|
||||||
|
): (
|
||||||
|
generateDate: number,
|
||||||
|
selectedDate: Calendar.SelectedDate,
|
||||||
|
isShowStatus?: boolean
|
||||||
|
) => Calendar.ListInfo<Calendar.Item> {
|
||||||
|
return function (
|
||||||
|
generateDate: number,
|
||||||
|
selectedDate: Calendar.SelectedDate,
|
||||||
|
isShowStatus?: boolean
|
||||||
|
): Calendar.ListInfo<Calendar.Item> {
|
||||||
|
const date = dayjs(generateDate)
|
||||||
|
|
||||||
|
const { format } = options
|
||||||
|
|
||||||
|
// 获取生成日期的第一天 和 最后一天
|
||||||
|
const firstDate = date.startOf('month')
|
||||||
|
const lastDate = date.endOf('month')
|
||||||
|
|
||||||
|
const preMonthDate = date.subtract(1, 'month')
|
||||||
|
|
||||||
|
const list: Calendar.List<Calendar.Item> = []
|
||||||
|
|
||||||
|
const nowMonthDays: number = date.daysInMonth()// 获取这个月有多少天
|
||||||
|
|
||||||
|
// 因为把周日放到了最后,这里要减一天
|
||||||
|
const preMonthLastDay = preMonthDate.endOf('month').subtract(1, 'day').day() // 获取上个月最后一天是周几
|
||||||
|
|
||||||
|
// 生成上个月的日期
|
||||||
|
for (let i = 1; i <= preMonthLastDay + 1; i++) {
|
||||||
|
const thisDate = firstDate.subtract(i, 'day').startOf('day')
|
||||||
|
|
||||||
|
let item = {
|
||||||
|
marks: [],
|
||||||
|
_value: thisDate,
|
||||||
|
text: thisDate.date(),
|
||||||
|
type: constant.TYPE_PRE_MONTH,
|
||||||
|
value: thisDate.format(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
item = getFullItem(item, options, selectedDate, isShowStatus)
|
||||||
|
|
||||||
|
list.push(item)
|
||||||
|
}
|
||||||
|
list.reverse()
|
||||||
|
|
||||||
|
// 生成这个月的日期
|
||||||
|
for (let i = 0; i < nowMonthDays; i++) {
|
||||||
|
const thisDate = firstDate.add(i, 'day').startOf('day')
|
||||||
|
let item = {
|
||||||
|
marks: [],
|
||||||
|
_value: thisDate,
|
||||||
|
text: thisDate.date(),
|
||||||
|
type: constant.TYPE_NOW_MONTH,
|
||||||
|
value: thisDate.format(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
item = getFullItem(item, options, selectedDate, isShowStatus)
|
||||||
|
|
||||||
|
list.push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成下个月的日期
|
||||||
|
let i = 1
|
||||||
|
while (list.length < TOTAL) {
|
||||||
|
const thisDate = lastDate.add(i++, 'day').startOf('day')
|
||||||
|
let item = {
|
||||||
|
marks: [],
|
||||||
|
_value: thisDate,
|
||||||
|
text: thisDate.date(),
|
||||||
|
type: constant.TYPE_NEXT_MONTH,
|
||||||
|
value: thisDate.format(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
item = getFullItem(item, options, selectedDate, isShowStatus)
|
||||||
|
|
||||||
|
list.push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
value: generateDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGenerateDate(date: Calendar.DateArg | undefined): Dayjs {
|
||||||
|
// return dayjs(date)
|
||||||
|
return dayjs(date).startOf('month')
|
||||||
|
}
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
import dayjs from 'dayjs'
|
||||||
|
import _isEmpty from 'lodash/isEmpty'
|
||||||
|
import { Calendar } from '../../../../types/calendar'
|
||||||
|
|
||||||
|
interface PluginArg {
|
||||||
|
options: Calendar.GroupOptions
|
||||||
|
|
||||||
|
selectedDate: Calendar.SelectedDate
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleActive(
|
||||||
|
args: PluginArg,
|
||||||
|
item: Calendar.Item
|
||||||
|
): Calendar.Item {
|
||||||
|
const { selectedDate } = args
|
||||||
|
const { _value } = item
|
||||||
|
|
||||||
|
const { start, end } = selectedDate
|
||||||
|
|
||||||
|
const dayjsEnd = dayjs(end)
|
||||||
|
const dayjsStart = start ? dayjs(start) : dayjsEnd
|
||||||
|
|
||||||
|
item.isSelected =
|
||||||
|
_value?.isSame(dayjsEnd) ||
|
||||||
|
_value?.isSame(dayjsStart) ||
|
||||||
|
(_value?.isAfter(dayjsStart) && _value?.isBefore(dayjsEnd))
|
||||||
|
|
||||||
|
item.isSelectedHead = _value?.isSame(dayjsStart)
|
||||||
|
item.isSelectedTail = _value?.isSame(dayjsEnd)
|
||||||
|
|
||||||
|
item.isToday = _value?.diff(dayjs(Date.now()).startOf('day'), 'day') === 0
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleMarks(
|
||||||
|
args: PluginArg,
|
||||||
|
item: Calendar.Item
|
||||||
|
): Calendar.Item {
|
||||||
|
const { options } = args
|
||||||
|
const { _value } = item
|
||||||
|
const { marks } = options
|
||||||
|
|
||||||
|
const markList = marks.filter(mark =>
|
||||||
|
_value ? dayjs(mark.value).startOf('day').isSame(_value) : false
|
||||||
|
)
|
||||||
|
|
||||||
|
item.marks = markList.slice(0, 1)
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
// export function handleSelectedDates (args: PluginArg): Calendar.Item {
|
||||||
|
// const { item, options } = args
|
||||||
|
// const { _value } = item
|
||||||
|
// const { selectedDates } = options
|
||||||
|
|
||||||
|
// if (selectedDates.length === 0) return args
|
||||||
|
|
||||||
|
// _forEach(selectedDates, date => {
|
||||||
|
// const { isSelected, isHead, isTail } = item
|
||||||
|
|
||||||
|
// // 如果当前 Item 已经具备了 三种状态下 无需继续判断 跳出循环
|
||||||
|
// if (isSelected) {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const { start, end } = date
|
||||||
|
|
||||||
|
// const dayjsEnd = dayjs(end).startOf('day')
|
||||||
|
// const dayjsStart = dayjs(start).startOf('day')
|
||||||
|
|
||||||
|
// item.isSelected =
|
||||||
|
// item.isSelected ||
|
||||||
|
// (_value.isAfter(dayjsStart) && _value.isBefore(dayjsEnd))
|
||||||
|
|
||||||
|
// item.isHead = item.isHead || _value.isSame(dayjsStart)
|
||||||
|
|
||||||
|
// item.isTail = item.isTail || _value.isSame(dayjsEnd)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// return item
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function handleDisabled(
|
||||||
|
args: PluginArg,
|
||||||
|
item: Calendar.Item
|
||||||
|
): Calendar.Item {
|
||||||
|
const { options } = args
|
||||||
|
const { _value } = item
|
||||||
|
const { minDate, maxDate } = options
|
||||||
|
|
||||||
|
const dayjsMinDate = dayjs(minDate)
|
||||||
|
const dayjsMaxDate = dayjs(maxDate)
|
||||||
|
|
||||||
|
item.isDisabled =
|
||||||
|
!!(minDate && _value?.isBefore(dayjsMinDate)) ||
|
||||||
|
!!(maxDate && _value?.isAfter(dayjsMaxDate))
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleValid(
|
||||||
|
args: PluginArg,
|
||||||
|
item: Calendar.Item
|
||||||
|
): Calendar.Item {
|
||||||
|
const { options } = args
|
||||||
|
const { _value } = item
|
||||||
|
const { validDates } = options
|
||||||
|
|
||||||
|
if (!_isEmpty(validDates)) {
|
||||||
|
const isInclude = validDates.some(date =>
|
||||||
|
_value ? dayjs(date.value).startOf('day').isSame(_value) : false
|
||||||
|
)
|
||||||
|
|
||||||
|
item.isDisabled = !isInclude
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item._value
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
export default [handleActive, handleMarks, handleDisabled, handleValid]
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
import classnames from "classnames";
|
||||||
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
|
import React from "react";
|
||||||
|
import { Picker, Text, View } from "@tarojs/components";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AtCalendarControllerProps,
|
||||||
|
AtCalendarControllerState,
|
||||||
|
} from "../../../../types/calendar";
|
||||||
|
|
||||||
|
export default class AtCalendarController extends React.Component<
|
||||||
|
AtCalendarControllerProps,
|
||||||
|
AtCalendarControllerState
|
||||||
|
> {
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const { generateDate, minDate, maxDate, monthFormat, hideArrow } =
|
||||||
|
this.props;
|
||||||
|
const dayjsDate: Dayjs = dayjs(generateDate);
|
||||||
|
const dayjsMinDate: Dayjs | boolean = !!minDate && dayjs(minDate);
|
||||||
|
const dayjsMaxDate: Dayjs | boolean = !!maxDate && dayjs(maxDate);
|
||||||
|
|
||||||
|
const isMinMonth: boolean =
|
||||||
|
dayjsMinDate && dayjsMinDate.startOf("month").isSame(dayjsDate);
|
||||||
|
|
||||||
|
const isMaxMonth: boolean =
|
||||||
|
dayjsMaxDate && dayjsMaxDate.startOf("month").isSame(dayjsDate);
|
||||||
|
|
||||||
|
const minDateValue: string = dayjsMinDate
|
||||||
|
? dayjsMinDate.format("YYYY-MM")
|
||||||
|
: "";
|
||||||
|
const maxDateValue: string = dayjsMaxDate
|
||||||
|
? dayjsMaxDate.format("YYYY-MM")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="at-calendar__controller controller flex-justify-sb">
|
||||||
|
{hideArrow ? null : (
|
||||||
|
<View
|
||||||
|
className={classnames("controller__arrow controller__arrow--left", {
|
||||||
|
"controller__arrow--disabled": isMinMonth,
|
||||||
|
})}
|
||||||
|
onClick={this.props.onPreMonth.bind(this, isMinMonth)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<View style="display: flex;align-items: center;">
|
||||||
|
<Picker
|
||||||
|
mode="date"
|
||||||
|
fields="day"
|
||||||
|
end={maxDateValue}
|
||||||
|
start={minDateValue}
|
||||||
|
onChange={this.props.onSelectDate}
|
||||||
|
value={dayjsDate.format("YYYY-MM-DD")}
|
||||||
|
>
|
||||||
|
{/* <Text className="controller__info"></Text> */}
|
||||||
|
{/* <Text>{dayjsDate.format(monthFormat)}</Text> */}
|
||||||
|
<Text>{dayjsDate.format("YYYY.MM.DD")}</Text>
|
||||||
|
</Picker>
|
||||||
|
<View
|
||||||
|
style="margin-left:10rpx"
|
||||||
|
className="at-icon at-icon-calendar"
|
||||||
|
></View>
|
||||||
|
</View>
|
||||||
|
{hideArrow ? null : (
|
||||||
|
<View
|
||||||
|
className={classnames(
|
||||||
|
"controller__arrow controller__arrow--right",
|
||||||
|
{
|
||||||
|
"controller__arrow--disabled": isMaxMonth,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
onClick={this.props.onNextMonth.bind(this, isMaxMonth)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,337 @@
|
|||||||
|
import classnames from "classnames";
|
||||||
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
|
import React from "react";
|
||||||
|
import Taro, { Events } from "@tarojs/taro";
|
||||||
|
import { View } from "@tarojs/components";
|
||||||
|
import { BaseEventOrig } from "@tarojs/components/types/common";
|
||||||
|
import {
|
||||||
|
AtCalendarDefaultProps,
|
||||||
|
AtCalendarProps,
|
||||||
|
AtCalendarPropsWithDefaults,
|
||||||
|
AtCalendarState,
|
||||||
|
Calendar,
|
||||||
|
} from "../../../types/calendar";
|
||||||
|
import AtCalendarBody from "./body/index";
|
||||||
|
import AtCalendarController from "./controller/index";
|
||||||
|
|
||||||
|
// const events = new Events();
|
||||||
|
|
||||||
|
const defaultProps: AtCalendarDefaultProps = {
|
||||||
|
validDates: [],
|
||||||
|
marks: [],
|
||||||
|
isSwiper: true,
|
||||||
|
hideArrow: false,
|
||||||
|
isVertical: false,
|
||||||
|
selectedDates: [],
|
||||||
|
isMultiSelect: false,
|
||||||
|
format: "YYYY-MM-DD",
|
||||||
|
currentDate: Date.now(),
|
||||||
|
// monthFormat: "YYYY年MM月",
|
||||||
|
monthFormat: "YYYY-MM-DD",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class AtCalendar extends React.Component<
|
||||||
|
AtCalendarProps,
|
||||||
|
Readonly<AtCalendarState>
|
||||||
|
> {
|
||||||
|
static defaultProps: AtCalendarDefaultProps = defaultProps;
|
||||||
|
|
||||||
|
public constructor(props: AtCalendarProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const { currentDate, isMultiSelect } = props as AtCalendarPropsWithDefaults;
|
||||||
|
|
||||||
|
this.state = this.getInitializeState(currentDate, isMultiSelect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UNSAFE_componentWillReceiveProps(nextProps: AtCalendarProps): void {
|
||||||
|
const { currentDate, isMultiSelect } = nextProps;
|
||||||
|
if (!currentDate || currentDate === this.props.currentDate) return;
|
||||||
|
|
||||||
|
if (isMultiSelect && this.props.isMultiSelect) {
|
||||||
|
const { start, end } = currentDate as Calendar.SelectedDate;
|
||||||
|
const { start: preStart, end: preEnd } = this.props
|
||||||
|
.currentDate as Calendar.SelectedDate;
|
||||||
|
|
||||||
|
if (start === preStart && preEnd === end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateValue: AtCalendarState = this.getInitializeState(
|
||||||
|
currentDate,
|
||||||
|
isMultiSelect
|
||||||
|
);
|
||||||
|
|
||||||
|
this.setState(stateValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSingleSelectdState = (value: Dayjs): Partial<AtCalendarState> => {
|
||||||
|
const { generateDate } = this.state;
|
||||||
|
console.log("generateDate", generateDate);
|
||||||
|
const stateValue: Partial<AtCalendarState> = {
|
||||||
|
selectedDate: this.getSelectedDate(value.valueOf()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// const dayjsGenerateDate: Dayjs = value.startOf("month"); // 重置到月份第一天
|
||||||
|
const dayjsGenerateDate: Dayjs = value;
|
||||||
|
const generateDateValue: number = dayjsGenerateDate.valueOf();
|
||||||
|
if (generateDateValue !== generateDate) {
|
||||||
|
this.triggerChangeDate(dayjsGenerateDate);
|
||||||
|
stateValue.generateDate = generateDateValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
private getMultiSelectedState = (
|
||||||
|
value: Dayjs
|
||||||
|
): Pick<AtCalendarState, "selectedDate"> => {
|
||||||
|
const { selectedDate } = this.state;
|
||||||
|
const { end, start } = selectedDate;
|
||||||
|
|
||||||
|
const valueUnix: number = value.valueOf();
|
||||||
|
const state: Pick<AtCalendarState, "selectedDate"> = {
|
||||||
|
selectedDate,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
state.selectedDate = this.getSelectedDate(valueUnix, 0);
|
||||||
|
} else {
|
||||||
|
state.selectedDate.end = Math.max(valueUnix, +start);
|
||||||
|
state.selectedDate.start = Math.min(valueUnix, +start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
private getSelectedDate = (
|
||||||
|
start: number,
|
||||||
|
end?: number
|
||||||
|
): Calendar.SelectedDate => {
|
||||||
|
const stateValue: Calendar.SelectedDate = {
|
||||||
|
start,
|
||||||
|
end: start,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof end !== "undefined") {
|
||||||
|
stateValue.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
private getInitializeState(
|
||||||
|
currentDate: Calendar.DateArg | Calendar.SelectedDate,
|
||||||
|
isMultiSelect?: boolean
|
||||||
|
): AtCalendarState {
|
||||||
|
let end: number;
|
||||||
|
let start: number;
|
||||||
|
let generateDateValue: number;
|
||||||
|
|
||||||
|
if (!currentDate) {
|
||||||
|
const dayjsStart = dayjs();
|
||||||
|
start = dayjsStart.startOf("day").valueOf();
|
||||||
|
// generateDateValue = dayjsStart.startOf("month").valueOf(); // 初始化选择器时间为每月第一天
|
||||||
|
generateDateValue = dayjsStart.startOf("day").valueOf();
|
||||||
|
return {
|
||||||
|
generateDate: generateDateValue,
|
||||||
|
selectedDate: {
|
||||||
|
start: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMultiSelect) {
|
||||||
|
const { start: cStart, end: cEnd } = currentDate as Calendar.SelectedDate;
|
||||||
|
|
||||||
|
const dayjsStart = dayjs(cStart);
|
||||||
|
|
||||||
|
start = dayjsStart.startOf("day").valueOf();
|
||||||
|
generateDateValue = dayjsStart.startOf("month").valueOf();
|
||||||
|
|
||||||
|
end = cEnd ? dayjs(cEnd).startOf("day").valueOf() : start;
|
||||||
|
} else {
|
||||||
|
const dayjsStart = dayjs(currentDate as Calendar.DateArg);
|
||||||
|
|
||||||
|
start = dayjsStart.startOf("day").valueOf();
|
||||||
|
// generateDateValue = dayjsStart.startOf("month").valueOf(); // 初始化选择器时间为每月第一天
|
||||||
|
generateDateValue = dayjsStart.startOf("day").valueOf();
|
||||||
|
|
||||||
|
end = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
generateDate: generateDateValue,
|
||||||
|
selectedDate: this.getSelectedDate(start, end),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private triggerChangeDate = (value: Dayjs): void => {
|
||||||
|
const { format } = this.props;
|
||||||
|
|
||||||
|
if (typeof this.props.onMonthChange !== "function") return;
|
||||||
|
|
||||||
|
this.props.onMonthChange(value.format(format));
|
||||||
|
};
|
||||||
|
|
||||||
|
private setMonth = (vectorCount: number): void => {
|
||||||
|
const { format } = this.props;
|
||||||
|
const { generateDate } = this.state;
|
||||||
|
|
||||||
|
const _generateDate: Dayjs = dayjs(generateDate).add(vectorCount, "month");
|
||||||
|
this.setState({
|
||||||
|
generateDate: _generateDate.valueOf(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (vectorCount && typeof this.props.onMonthChange === "function") {
|
||||||
|
this.props.onMonthChange(_generateDate.format(format));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleClickPreMonth = (isMinMonth?: boolean): void => {
|
||||||
|
if (isMinMonth === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setMonth(-1);
|
||||||
|
|
||||||
|
if (typeof this.props.onClickPreMonth === "function") {
|
||||||
|
this.props.onClickPreMonth();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleClickNextMonth = (isMaxMonth?: boolean): void => {
|
||||||
|
if (isMaxMonth === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setMonth(1);
|
||||||
|
|
||||||
|
if (typeof this.props.onClickNextMonth === "function") {
|
||||||
|
this.props.onClickNextMonth();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// picker 选择时间改变时触发
|
||||||
|
private handleSelectDate = (e: BaseEventOrig<{ value: string }>): void => {
|
||||||
|
const { value } = e.detail;
|
||||||
|
|
||||||
|
const _generateDate: Dayjs = dayjs(value);
|
||||||
|
const _generateDateValue: number = _generateDate.valueOf();
|
||||||
|
|
||||||
|
if (this.state.generateDate === _generateDateValue) return;
|
||||||
|
|
||||||
|
this.triggerChangeDate(_generateDate);
|
||||||
|
this.setState({
|
||||||
|
generateDate: _generateDateValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
Taro.eventCenter.trigger(
|
||||||
|
"onSelectDataChange",
|
||||||
|
dayjs(_generateDateValue).format("YYYY-MM-DD")
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("this.state", this.state);
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleDayClick = (item: Calendar.Item): void => {
|
||||||
|
console.log("handleDayClick", item);
|
||||||
|
const { isMultiSelect } = this.props;
|
||||||
|
const { isDisabled, value } = item;
|
||||||
|
|
||||||
|
if (isDisabled) return;
|
||||||
|
|
||||||
|
const dayjsDate: Dayjs = dayjs(value);
|
||||||
|
|
||||||
|
let stateValue: Partial<AtCalendarState> = {};
|
||||||
|
|
||||||
|
if (isMultiSelect) {
|
||||||
|
stateValue = this.getMultiSelectedState(dayjsDate);
|
||||||
|
} else {
|
||||||
|
stateValue = this.getSingleSelectdState(dayjsDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(stateValue as AtCalendarState, () => {
|
||||||
|
this.handleSelectedDate();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof this.props.onDayClick === "function") {
|
||||||
|
this.props.onDayClick({ value: item.value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSelectedDate = (): void => {
|
||||||
|
const selectDate = this.state.selectedDate;
|
||||||
|
if (typeof this.props.onSelectDate === "function") {
|
||||||
|
const info: Calendar.SelectedDate = {
|
||||||
|
start: dayjs(selectDate.start).format(this.props.format),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (selectDate.end) {
|
||||||
|
info.end = dayjs(selectDate.end).format(this.props.format);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.onSelectDate({
|
||||||
|
value: info,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleDayLongClick = (item: Calendar.Item): void => {
|
||||||
|
if (typeof this.props.onDayLongClick === "function") {
|
||||||
|
this.props.onDayLongClick({ value: item.value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const { generateDate, selectedDate } = this.state;
|
||||||
|
console.log(
|
||||||
|
"render generateDate",
|
||||||
|
dayjs(generateDate).format("YYYY-MM-DD")
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
validDates,
|
||||||
|
marks,
|
||||||
|
format,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
isSwiper,
|
||||||
|
className,
|
||||||
|
hideArrow,
|
||||||
|
isVertical,
|
||||||
|
monthFormat,
|
||||||
|
selectedDates,
|
||||||
|
} = this.props as AtCalendarPropsWithDefaults;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className={classnames("at-calendar", className)}>
|
||||||
|
<AtCalendarController
|
||||||
|
minDate={minDate}
|
||||||
|
maxDate={maxDate}
|
||||||
|
hideArrow={hideArrow}
|
||||||
|
monthFormat={monthFormat}
|
||||||
|
generateDate={generateDate}
|
||||||
|
onPreMonth={this.handleClickPreMonth}
|
||||||
|
onNextMonth={this.handleClickNextMonth}
|
||||||
|
onSelectDate={this.handleSelectDate}
|
||||||
|
/>
|
||||||
|
<AtCalendarBody
|
||||||
|
validDates={validDates}
|
||||||
|
marks={marks}
|
||||||
|
format={format}
|
||||||
|
minDate={minDate}
|
||||||
|
maxDate={maxDate}
|
||||||
|
isSwiper={isSwiper}
|
||||||
|
isVertical={isVertical}
|
||||||
|
selectedDate={selectedDate}
|
||||||
|
selectedDates={selectedDates}
|
||||||
|
generateDate={generateDate}
|
||||||
|
onDayClick={this.handleDayClick}
|
||||||
|
onSwipeMonth={this.setMonth}
|
||||||
|
onLongClick={this.handleDayLongClick}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import classnames from 'classnames'
|
||||||
|
import React from 'react'
|
||||||
|
import { Text, View } from '@tarojs/components'
|
||||||
|
import { Calendar } from '../../../../../types/calendar'
|
||||||
|
import * as constant from '../../common/constant'
|
||||||
|
|
||||||
|
const MAP: { [key: number]: string } = {
|
||||||
|
[constant.TYPE_PRE_MONTH]: 'pre',
|
||||||
|
[constant.TYPE_NOW_MONTH]: 'now',
|
||||||
|
[constant.TYPE_NEXT_MONTH]: 'next'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
list: Calendar.List<Calendar.Item>
|
||||||
|
|
||||||
|
onClick?: (item: Calendar.Item) => void
|
||||||
|
|
||||||
|
onLongClick?: (item: Calendar.Item) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AtCalendarList extends React.Component<Props> {
|
||||||
|
private handleClick = (item: Calendar.Item): void => {
|
||||||
|
if (typeof this.props.onClick === 'function') {
|
||||||
|
this.props.onClick(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleLongClick = (item: Calendar.Item): void => {
|
||||||
|
if (typeof this.props.onLongClick === 'function') {
|
||||||
|
this.props.onLongClick(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element | null {
|
||||||
|
const { list } = this.props
|
||||||
|
if (!list || list.length === 0) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className='at-calendar__list flex'>
|
||||||
|
{list.map((item: Calendar.Item) => (
|
||||||
|
<View
|
||||||
|
key={`list-item-${item.value}`}
|
||||||
|
onClick={this.handleClick.bind(this, item)}
|
||||||
|
onLongPress={this.handleLongClick.bind(this, item)}
|
||||||
|
className={classnames(
|
||||||
|
'flex__item',
|
||||||
|
`flex__item--${MAP[item.type]}`,
|
||||||
|
{
|
||||||
|
'flex__item--today': item.isToday,
|
||||||
|
'flex__item--active': item.isActive,
|
||||||
|
'flex__item--selected': item.isSelected,
|
||||||
|
'flex__item--selected-head': item.isSelectedHead,
|
||||||
|
'flex__item--selected-tail': item.isSelectedTail,
|
||||||
|
'flex__item--blur':
|
||||||
|
item.isDisabled ||
|
||||||
|
item.type === constant.TYPE_PRE_MONTH ||
|
||||||
|
item.type === constant.TYPE_NEXT_MONTH
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<View className='flex__item-container'>
|
||||||
|
<View className='container-text'>{item.text}</View>
|
||||||
|
</View>
|
||||||
|
<View className='flex__item-extra extra'>
|
||||||
|
{item.marks && item.marks.length > 0 ? (
|
||||||
|
<View className='extra-marks'>
|
||||||
|
{item.marks.map((mark, key) => (
|
||||||
|
<Text key={key} className='mark'>
|
||||||
|
{mark.value}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { View } from "@tarojs/components";
|
||||||
|
|
||||||
|
export default class AtCalendarHeader extends React.Component {
|
||||||
|
public render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<View className="at-calendar__header header">
|
||||||
|
<View className="header__flex">
|
||||||
|
<View className="header__flex-item">一</View>
|
||||||
|
<View className="header__flex-item">二</View>
|
||||||
|
<View className="header__flex-item">三</View>
|
||||||
|
<View className="header__flex-item">四</View>
|
||||||
|
<View className="header__flex-item">五</View>
|
||||||
|
<View className="header__flex-item">六</View>
|
||||||
|
<View className="header__flex-item">日</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
component: true,
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
@import "~taro-ui/dist/style/components/modal.scss";
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
component: true,
|
||||||
|
};
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
.tab-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100px;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
pointer-events: auto;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar-border {
|
||||||
|
background-color: rgba(0, 0, 0, 0.33);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
transform: scaleY(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar-item cover-image {
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar-item cover-view {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
import { Component } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import { CoverView, CoverImage } from "@tarojs/components";
|
||||||
|
|
||||||
|
import "./index.less";
|
||||||
|
|
||||||
|
export default class Index extends Component {
|
||||||
|
state = {
|
||||||
|
selected: 2,
|
||||||
|
color: "#999",
|
||||||
|
selectedColor: "#000",
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
borderStyle: "black",
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
pagePath: "pages/activity/activity",
|
||||||
|
text: "活动",
|
||||||
|
iconPath: "/img/tabar/1.png",
|
||||||
|
selectedIconPath: "/img/tabar/11.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: "pages/detect/detect",
|
||||||
|
text: "发现",
|
||||||
|
iconPath: "/img/tabar/2.png",
|
||||||
|
selectedIconPath: "/img/tabar/22.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: "pages/index/index",
|
||||||
|
text: "主页",
|
||||||
|
iconPath: "/img/tabar/3.png",
|
||||||
|
selectedIconPath: "/img/tabar/33.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: "pages/shop/shop",
|
||||||
|
text: "商城",
|
||||||
|
iconPath: "/img/tabar/4.png",
|
||||||
|
selectedIconPath: "/img/tabar/44.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: "pages/user/user",
|
||||||
|
text: "我的",
|
||||||
|
iconPath: "/img/tabar/5.png",
|
||||||
|
selectedIconPath: "/img/tabar/55.png",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
switchTab(index, url) {
|
||||||
|
this.setSelected(index);
|
||||||
|
Taro.switchTab({ url: "/" + url });
|
||||||
|
// setTimeout(() => this.setSelected(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelected(idx: number) {
|
||||||
|
console.log("setSelected", idx);
|
||||||
|
this.setState({
|
||||||
|
selected: idx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { list, selected, color, selectedColor } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CoverView className="tab-bar">
|
||||||
|
<CoverView className="tab-bar-border"></CoverView>
|
||||||
|
{list.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<CoverView
|
||||||
|
key={item.pagePath}
|
||||||
|
className="tab-bar-item"
|
||||||
|
onClick={this.switchTab.bind(this, index, item.pagePath)}
|
||||||
|
>
|
||||||
|
<CoverImage
|
||||||
|
src={selected === index ? item.selectedIconPath : item.iconPath}
|
||||||
|
/>
|
||||||
|
<CoverView
|
||||||
|
style={{
|
||||||
|
color: selected === index ? selectedColor : color,
|
||||||
|
fontWeight: selected === index ? "500" : "bold",
|
||||||
|
fontSize: "11px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.text}
|
||||||
|
</CoverView>
|
||||||
|
</CoverView>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</CoverView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: "模板页",
|
||||||
|
});
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { MpSplashDetail, WCUserLogin } from "../../utils/Interface";
|
||||||
|
import { Component, PropsWithChildren, useEffect, useState } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
// 引入 Swiper, SwiperItem 组件
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Swiper,
|
||||||
|
SwiperItem,
|
||||||
|
} from "@tarojs/components";
|
||||||
|
|
||||||
|
import "taro-ui/dist/style/components/button.scss"; // 按需引入
|
||||||
|
import "./activity.less";
|
||||||
|
|
||||||
|
const app = Taro.getApp();
|
||||||
|
|
||||||
|
import type CustomTabBar from "../../custom-tab-bar";
|
||||||
|
export default class Activity extends Component<any, any> {
|
||||||
|
pageCtx = Taro.getCurrentInstance().page;
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
name: "activity",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
console.log("app", app);
|
||||||
|
}
|
||||||
|
componentDidMount() {}
|
||||||
|
|
||||||
|
componentWillUnmount() {}
|
||||||
|
|
||||||
|
componentDidShow() {
|
||||||
|
const tabbar = Taro.getTabBar<CustomTabBar>(this.pageCtx);
|
||||||
|
tabbar?.setSelected(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidHide() {}
|
||||||
|
|
||||||
|
async initData() {}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { name } = this.state;
|
||||||
|
return <View>{name}</View>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: "模板页",
|
||||||
|
});
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { MpSplashDetail, WCUserLogin } from "../../utils/Interface";
|
||||||
|
import { Component, PropsWithChildren, useEffect, useState } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
// 引入 Swiper, SwiperItem 组件
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Swiper,
|
||||||
|
SwiperItem,
|
||||||
|
} from "@tarojs/components";
|
||||||
|
|
||||||
|
import "taro-ui/dist/style/components/button.scss"; // 按需引入
|
||||||
|
import "./detect.less";
|
||||||
|
|
||||||
|
const app = Taro.getApp();
|
||||||
|
|
||||||
|
import type CustomTabBar from "../../custom-tab-bar";
|
||||||
|
export default class Detect extends Component<any, any> {
|
||||||
|
pageCtx = Taro.getCurrentInstance().page;
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
name: "detect",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
console.log("app", app);
|
||||||
|
}
|
||||||
|
componentDidMount() {}
|
||||||
|
|
||||||
|
componentWillUnmount() {}
|
||||||
|
|
||||||
|
componentDidShow() {
|
||||||
|
const tabbar = Taro.getTabBar<CustomTabBar>(this.pageCtx);
|
||||||
|
tabbar?.setSelected(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidHide() {}
|
||||||
|
|
||||||
|
async initData() {}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { name } = this.state;
|
||||||
|
return <View>{name}</View>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
/* ----------------------------------------------
|
||||||
|
* Generated by Animista on 2023-12-8 17:46:25
|
||||||
|
* Licensed under FreeBSD License.
|
||||||
|
* See http://animista.net/license for more info.
|
||||||
|
* w: http://animista.net, t: @cssanimista
|
||||||
|
* ---------------------------------------------- */
|
||||||
|
|
||||||
|
@-webkit-keyframes slide-left {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateX(300rpx);
|
||||||
|
transform: translateX(300rpx);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateX(0);
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes slide-left {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateX(300rpx);
|
||||||
|
transform: translateX(300rpx);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateX(0);
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧滑入动画 */
|
||||||
|
.slide-left {
|
||||||
|
-webkit-animation: slide-left 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||||
|
animation: slide-left 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
.FadeOutFrame {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.FadeOutFrame.fadeout {
|
||||||
|
-webkit-transition: all 1.5s;
|
||||||
|
-moz-transition: all 1.5s;
|
||||||
|
-ms-transition: all 1.5s;
|
||||||
|
-o-transition: all 1.5s;
|
||||||
|
transition: all 1.5s;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.FadeInFrame {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.FadeInFrame.fadein {
|
||||||
|
-webkit-transition: all 1.5s;
|
||||||
|
-moz-transition: all 1.5s;
|
||||||
|
-ms-transition: all 1.5s;
|
||||||
|
-o-transition: all 1.5s;
|
||||||
|
transition: all 1.5s;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
错误类型收集
|
||||||
|
MiniProgramError 页面跳转错误
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: "模板页",
|
||||||
|
});
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { MpSplashDetail, WCUserLogin } from "../../utils/Interface";
|
||||||
|
import { Component, PropsWithChildren, useEffect, useState } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
// 引入 Swiper, SwiperItem 组件
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Swiper,
|
||||||
|
SwiperItem,
|
||||||
|
} from "@tarojs/components";
|
||||||
|
|
||||||
|
import "taro-ui/dist/style/components/button.scss"; // 按需引入
|
||||||
|
import "./template.less";
|
||||||
|
|
||||||
|
const app = Taro.getApp();
|
||||||
|
|
||||||
|
export default class Index extends Component<any, any> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
name: "template模板页",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
console.log("app", app);
|
||||||
|
}
|
||||||
|
componentDidMount() {}
|
||||||
|
|
||||||
|
componentWillUnmount() {}
|
||||||
|
|
||||||
|
componentDidShow() {}
|
||||||
|
|
||||||
|
componentDidHide() {}
|
||||||
|
|
||||||
|
async initData() {}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { name } = this.state;
|
||||||
|
return <View>{name}</View>;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: "注册",
|
||||||
|
});
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: "模板页",
|
||||||
|
});
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { MpSplashDetail, WCUserLogin } from "../../utils/Interface";
|
||||||
|
import { Component, PropsWithChildren, useEffect, useState } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
// 引入 Swiper, SwiperItem 组件
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Swiper,
|
||||||
|
SwiperItem,
|
||||||
|
} from "@tarojs/components";
|
||||||
|
|
||||||
|
import "taro-ui/dist/style/components/button.scss"; // 按需引入
|
||||||
|
import "./shop.less";
|
||||||
|
|
||||||
|
const app = Taro.getApp();
|
||||||
|
|
||||||
|
import type CustomTabBar from "../../custom-tab-bar";
|
||||||
|
export default class Shop extends Component<any, any> {
|
||||||
|
pageCtx = Taro.getCurrentInstance().page;
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
name: "shop",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
console.log("app", app);
|
||||||
|
}
|
||||||
|
componentDidMount() {}
|
||||||
|
|
||||||
|
componentWillUnmount() {}
|
||||||
|
|
||||||
|
componentDidShow() {
|
||||||
|
const tabbar = Taro.getTabBar<CustomTabBar>(this.pageCtx);
|
||||||
|
tabbar?.setSelected(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidHide() {}
|
||||||
|
|
||||||
|
async initData() {}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { name } = this.state;
|
||||||
|
return <View>{name}</View>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: "我的",
|
||||||
|
});
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { MpSplashDetail, WCUserLogin } from "../../utils/Interface";
|
||||||
|
import { Component, PropsWithChildren, useEffect, useState } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
// 引入 Swiper, SwiperItem 组件
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Swiper,
|
||||||
|
SwiperItem,
|
||||||
|
} from "@tarojs/components";
|
||||||
|
|
||||||
|
import "taro-ui/dist/style/components/button.scss"; // 按需引入
|
||||||
|
import "./user.less";
|
||||||
|
|
||||||
|
const app = Taro.getApp();
|
||||||
|
|
||||||
|
import type CustomTabBar from "../../custom-tab-bar";
|
||||||
|
export default class User extends Component<any, any> {
|
||||||
|
pageCtx = Taro.getCurrentInstance().page;
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
name: "user",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
console.log("app", app);
|
||||||
|
}
|
||||||
|
componentDidMount() {}
|
||||||
|
|
||||||
|
componentWillUnmount() {}
|
||||||
|
|
||||||
|
componentDidShow() {
|
||||||
|
const tabbar = Taro.getTabBar<CustomTabBar>(this.pageCtx);
|
||||||
|
tabbar?.setSelected(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidHide() {}
|
||||||
|
|
||||||
|
async initData() {}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { name } = this.state;
|
||||||
|
return <View>{name}</View>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
|
// 导入counter模块下的reducer函数
|
||||||
|
import counterReducer from "./features/counter";
|
||||||
|
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: {
|
||||||
|
// 每个reducer都可以理解成一个子仓库
|
||||||
|
counter: counterReducer,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default store;
|
||||||
@ -0,0 +1,231 @@
|
|||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { BaseEvent } from '@tarojs/components/types/common'
|
||||||
|
|
||||||
|
// #region Calendar
|
||||||
|
declare namespace Calendar {
|
||||||
|
export type DateArg = string | number
|
||||||
|
|
||||||
|
export type classNameType =
|
||||||
|
| string
|
||||||
|
| Array<string>
|
||||||
|
| { [key: string]: boolean }
|
||||||
|
|
||||||
|
export interface Mark {
|
||||||
|
value: DateArg
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ValidDate {
|
||||||
|
value: DateArg
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Item {
|
||||||
|
value: string
|
||||||
|
|
||||||
|
_value?: dayjs.Dayjs
|
||||||
|
|
||||||
|
text: number
|
||||||
|
|
||||||
|
type: number
|
||||||
|
|
||||||
|
marks: Array<Mark>
|
||||||
|
|
||||||
|
isActive?: boolean
|
||||||
|
|
||||||
|
isToday?: boolean
|
||||||
|
|
||||||
|
isBeforeMin?: boolean
|
||||||
|
|
||||||
|
isAfterMax?: boolean
|
||||||
|
|
||||||
|
isDisabled?: boolean
|
||||||
|
|
||||||
|
isSelected?: boolean
|
||||||
|
|
||||||
|
isSelectedHead?: boolean
|
||||||
|
|
||||||
|
isSelectedTail?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedDate {
|
||||||
|
end?: Calendar.DateArg
|
||||||
|
|
||||||
|
start: Calendar.DateArg
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupOptions {
|
||||||
|
validDates: Array<ValidDate>
|
||||||
|
|
||||||
|
marks: Array<Mark>
|
||||||
|
|
||||||
|
format: string
|
||||||
|
|
||||||
|
selectedDates: Array<SelectedDate>
|
||||||
|
|
||||||
|
minDate?: DateArg
|
||||||
|
|
||||||
|
maxDate?: DateArg
|
||||||
|
}
|
||||||
|
|
||||||
|
export type List<T> = Array<T>
|
||||||
|
|
||||||
|
export type ListInfo<T> = {
|
||||||
|
value: number
|
||||||
|
|
||||||
|
list: List<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Calendar
|
||||||
|
export { Calendar }
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region AtCalendar
|
||||||
|
export interface AtCalendarPropsBase {
|
||||||
|
format?: string
|
||||||
|
|
||||||
|
validDates?: Array<Calendar.ValidDate>
|
||||||
|
|
||||||
|
minDate?: Calendar.DateArg
|
||||||
|
|
||||||
|
maxDate?: Calendar.DateArg
|
||||||
|
|
||||||
|
isSwiper?: boolean
|
||||||
|
|
||||||
|
marks?: Array<Calendar.Mark>
|
||||||
|
|
||||||
|
monthFormat?: string
|
||||||
|
|
||||||
|
hideArrow?: boolean
|
||||||
|
|
||||||
|
isVertical?: boolean
|
||||||
|
|
||||||
|
className?: Calendar.classNameType
|
||||||
|
|
||||||
|
onClickPreMonth?: () => void
|
||||||
|
|
||||||
|
onClickNextMonth?: () => void
|
||||||
|
|
||||||
|
onSelectDate?: (item: { value: Calendar.SelectedDate }) => void
|
||||||
|
|
||||||
|
onDayClick?: (item: { value: string }) => void
|
||||||
|
|
||||||
|
onDayLongClick?: (item: { value: string }) => void
|
||||||
|
|
||||||
|
onMonthChange?: (value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AtCalendarSingleSelectedProps extends AtCalendarPropsBase {
|
||||||
|
isMultiSelect?: false
|
||||||
|
|
||||||
|
currentDate?: Calendar.DateArg
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AtCalendarMutilSelectedProps extends AtCalendarPropsBase {
|
||||||
|
isMultiSelect?: true
|
||||||
|
|
||||||
|
currentDate?: Calendar.SelectedDate
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AtCalendarProps =
|
||||||
|
| AtCalendarSingleSelectedProps
|
||||||
|
| AtCalendarMutilSelectedProps
|
||||||
|
|
||||||
|
export interface AtCalendarDefaultProps {
|
||||||
|
format: string
|
||||||
|
|
||||||
|
isSwiper: boolean
|
||||||
|
|
||||||
|
validDates: Array<Calendar.ValidDate>
|
||||||
|
|
||||||
|
marks: Array<Calendar.Mark>
|
||||||
|
|
||||||
|
currentDate: Calendar.DateArg | Calendar.SelectedDate
|
||||||
|
|
||||||
|
monthFormat: string
|
||||||
|
|
||||||
|
hideArrow: boolean
|
||||||
|
|
||||||
|
isVertical: boolean
|
||||||
|
|
||||||
|
isMultiSelect: boolean
|
||||||
|
|
||||||
|
selectedDates: Array<Calendar.SelectedDate>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AtCalendarState {
|
||||||
|
generateDate: number
|
||||||
|
|
||||||
|
selectedDate: Calendar.SelectedDate
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AtCalendarPropsWithDefaults = AtCalendarProps &
|
||||||
|
AtCalendarDefaultProps
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region AtCalendarController
|
||||||
|
export interface AtCalendarControllerProps {
|
||||||
|
generateDate: Calendar.DateArg
|
||||||
|
|
||||||
|
minDate?: Calendar.DateArg
|
||||||
|
|
||||||
|
maxDate?: Calendar.DateArg
|
||||||
|
|
||||||
|
hideArrow: boolean
|
||||||
|
|
||||||
|
monthFormat: string
|
||||||
|
|
||||||
|
onPreMonth: () => void
|
||||||
|
|
||||||
|
onNextMonth: () => void
|
||||||
|
|
||||||
|
onSelectDate: (e: BaseEvent) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AtCalendarControllerState { }
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region AtCalendarBody
|
||||||
|
export type AtCalendarBodyListGroup = Array<Calendar.ListInfo<Calendar.Item>>
|
||||||
|
|
||||||
|
export interface AtCalendarBodyProps {
|
||||||
|
format: string
|
||||||
|
|
||||||
|
validDates: Array<Calendar.ValidDate>
|
||||||
|
|
||||||
|
marks: Array<Calendar.Mark>
|
||||||
|
|
||||||
|
isSwiper: boolean
|
||||||
|
|
||||||
|
minDate?: Calendar.DateArg
|
||||||
|
|
||||||
|
maxDate?: Calendar.DateArg
|
||||||
|
|
||||||
|
isVertical: boolean
|
||||||
|
|
||||||
|
generateDate: number
|
||||||
|
|
||||||
|
selectedDate: Calendar.SelectedDate
|
||||||
|
|
||||||
|
selectedDates: Array<Calendar.SelectedDate> | []
|
||||||
|
|
||||||
|
onDayClick: (item: Calendar.Item) => void
|
||||||
|
|
||||||
|
onSwipeMonth: (vectorCount: number) => void
|
||||||
|
|
||||||
|
onLongClick: (item: Calendar.Item) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AtCalendarBodyState {
|
||||||
|
|
||||||
|
temporaryCalendarWeek: any
|
||||||
|
temporaryCalendar: any
|
||||||
|
|
||||||
|
isFolding: boolean
|
||||||
|
|
||||||
|
isAnimate: boolean
|
||||||
|
|
||||||
|
offsetSize: number
|
||||||
|
|
||||||
|
listGroup: AtCalendarBodyListGroup
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
Loading…
Reference in New Issue