diff --git a/package.json b/package.json index bc36d80..9ef1f6d 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@tarojs/runtime": "4.1.5", "@tarojs/shared": "4.1.5", "@tarojs/taro": "4.1.5", + "dayjs": "^1.11.13", "qqmap-wx-jssdk": "^1.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", diff --git a/src/app.ts b/src/app.ts index b9f6294..6ef979c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,18 +1,32 @@ import { Component, ReactNode } from 'react' import './app.scss' import './nutui-theme.scss' +import { useDictionaryStore } from './store/dictionaryStore' interface AppProps { children: ReactNode } class App extends Component { - componentDidMount() {} + componentDidMount() { + // 初始化字典数据 + this.initDictionaryData() + } componentDidShow() {} componentDidHide() {} + // 初始化字典数据 + private async initDictionaryData() { + try { + const { fetchDictionary } = useDictionaryStore.getState() + await fetchDictionary() + } catch (error) { + console.error('初始化字典数据失败:', error) + } + } + render() { // this.props.children 是将要会渲染的页面 return this.props.children diff --git a/src/components/DateTimePicker/DateTimePicker.tsx b/src/components/DateTimePicker/DateTimePicker.tsx new file mode 100644 index 0000000..9975fff --- /dev/null +++ b/src/components/DateTimePicker/DateTimePicker.tsx @@ -0,0 +1,115 @@ +import React, { useState, useEffect } from 'react' +import { View, Text } from '@tarojs/components' +import { Picker, Popup } from '@nutui/nutui-react-taro' +import styles from './index.module.scss' + +export interface DateTimePickerProps { + visible: boolean + onClose: () => void + onConfirm: (year: number, month: number) => void + defaultYear?: number + defaultMonth?: number + minYear?: number + maxYear?: number +} + +const DateTimePicker: React.FC = ({ + visible, + onClose, + onConfirm, + defaultYear = new Date().getFullYear(), + defaultMonth = new Date().getMonth() + 1, + minYear = 2020, + maxYear = 2030 +}) => { + const [selectedYear, setSelectedYear] = useState(defaultYear) + const [selectedMonth, setSelectedMonth] = useState(defaultMonth) + + // 生成年份选项 + const yearOptions = Array.from({ length: maxYear - minYear + 1 }, (_, index) => ({ + text: `${minYear + index}年`, + value: minYear + index + })) + + // 生成月份选项 + const monthOptions = Array.from({ length: 12 }, (_, index) => ({ + text: `${index + 1}月`, + value: index + 1 + })) + + useEffect(() => { + if (visible) { + setSelectedYear(defaultYear) + setSelectedMonth(defaultMonth) + } + }, [visible, defaultYear, defaultMonth]) + + const handleYearChange = (value: any) => { + setSelectedYear(value[0]) + } + + const handleMonthChange = (value: any) => { + setSelectedMonth(value[0]) + } + + const handleConfirm = () => { + onConfirm(selectedYear, selectedMonth) + onClose() + } + + const handleCancel = () => { + onClose() + } + + return ( + + {/* 拖拽手柄 */} + + + {/* 时间选择器 */} + + + {/* 年份选择 */} + + + + + + {/* 月份选择 */} + + + + + + + + {/* 操作按钮 */} + + + 取消 + + + 完成 + + + + ) +} + +export default DateTimePicker diff --git a/src/components/DateTimePicker/README.md b/src/components/DateTimePicker/README.md new file mode 100644 index 0000000..61754dc --- /dev/null +++ b/src/components/DateTimePicker/README.md @@ -0,0 +1,67 @@ +# DateTimePicker 年月选择器 + +一个基于 NutUI 的年月切换弹窗组件,支持自定义年份范围和默认值。 + +## 功能特性 + +- 🎯 年月分别选择,操作简单直观 +- 🎨 遵循设计稿样式,美观易用 +- 📱 支持移动端手势操作 +- ⚙️ 可自定义年份范围 +- �� 基于 NutUI 组件库,稳定可靠 + +## 使用方法 + +```tsx +import { DateTimePicker } from '@/components' + +const MyComponent = () => { + const [visible, setVisible] = useState(false) + + const handleConfirm = (year: number, month: number) => { + console.log('选择的年月:', year, month) + setVisible(false) + } + + return ( + setVisible(false)} + onConfirm={handleConfirm} + defaultYear={2025} + defaultMonth={11} + minYear={2020} + maxYear={2030} + /> + ) +} +``` + +## API 参数 + +| 参数 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| visible | boolean | - | 是否显示弹窗 | +| onClose | () => void | - | 关闭弹窗的回调 | +| onConfirm | (year: number, month: number) => void | - | 确认选择的回调 | +| defaultYear | number | 当前年份 | 默认选中的年份 | +| defaultMonth | number | 当前月份 | 默认选中的月份 | +| minYear | number | 2020 | 可选择的最小年份 | +| maxYear | number | 2030 | 可选择的最大年份 | + +## 样式定制 + +组件使用 CSS Modules,可以通过修改 `index.module.scss` 文件来自定义样式。 + +主要样式类: +- `.date-time-picker-popup` - 弹窗容器 +- `.picker-columns` - 选择器列容器 +- `.picker-column` - 单列选择器 +- `.action-buttons` - 操作按钮区域 + +## 注意事项 + +1. 组件基于 NutUI 的 Picker 和 Popup 组件 +2. 年份范围建议不要设置过大,以免影响性能 +3. 月份固定为 1-12 月 +4. 组件会自动处理边界情况 diff --git a/src/components/DateTimePicker/example.tsx b/src/components/DateTimePicker/example.tsx new file mode 100644 index 0000000..b9c44d8 --- /dev/null +++ b/src/components/DateTimePicker/example.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react' +import { View, Button } from '@tarojs/components' +import DateTimePicker from './DateTimePicker' + +const DateTimePickerExample: React.FC = () => { + const [visible, setVisible] = useState(false) + const [selectedDate, setSelectedDate] = useState('') + + const handleOpen = () => { + setVisible(true) + } + + const handleClose = () => { + setVisible(false) + } + + const handleConfirm = (year: number, month: number) => { + setSelectedDate(`${year}年${month}月`) + console.log('选择的日期:', year, month) + } + + return ( + + + + {selectedDate && ( + + 已选择: {selectedDate} + + )} + + + + ) +} + +export default DateTimePickerExample diff --git a/src/components/DateTimePicker/index.module.scss b/src/components/DateTimePicker/index.module.scss new file mode 100644 index 0000000..ea892ee --- /dev/null +++ b/src/components/DateTimePicker/index.module.scss @@ -0,0 +1,102 @@ +.date-time-picker-popup { + :global(.nut-popup) { + border-radius: 16px 16px 0 0; + background: #fff; + } +} + +.popup-handle { + width: 40px; + height: 4px; + background: #e5e5e5; + border-radius: 2px; + margin: 12px auto 0; +} + +.picker-container { + padding: 20px 0; +} + +.picker-columns { + display: flex; + justify-content: center; + align-items: center; + gap: 60px; +} + +.picker-column { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +.picker-label { + font-size: 14px; + color: #999; + font-weight: 400; +} + +.year-picker, +.month-picker { + :global(.nut-picker) { + width: 80px; + } + + :global(.nut-picker__content) { + height: 200px; + } + + :global(.nut-picker-item) { + height: 40px; + line-height: 40px; + font-size: 16px; + color: #333; + } + + :global(.nut-picker-item--selected) { + color: #000; + font-weight: 500; + } + + :global(.nut-picker-item--disabled) { + color: #ccc; + } +} + +.action-buttons { + display: flex; + padding: 0 20px 20px; + gap: 12px; +} + +.cancel-btn, +.confirm-btn { + flex: 1; + height: 44px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.cancel-btn { + background: #fff; + border: 1px solid #e5e5e5; +} + +.cancel-text { + color: #666; + font-size: 16px; +} + +.confirm-btn { + background: #000; + border: 1px solid #000; +} + +.confirm-text { + color: #fff; + font-size: 16px; +} diff --git a/src/components/DateTimePicker/index.ts b/src/components/DateTimePicker/index.ts new file mode 100644 index 0000000..4817df4 --- /dev/null +++ b/src/components/DateTimePicker/index.ts @@ -0,0 +1,2 @@ +import DateTimePicker from './DateTimePicker' +export default DateTimePicker diff --git a/src/components/FormBasicInfo/FormBasicInfo.tsx b/src/components/FormBasicInfo/FormBasicInfo.tsx deleted file mode 100644 index 176bb23..0000000 --- a/src/components/FormBasicInfo/FormBasicInfo.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react' -import { View, Text, Input, Image, Picker } from '@tarojs/components' -import { Stadium } from '../SelectStadium' -import img from '@/config/images'; -import './FormBasicInfo.scss' -import { FormFieldConfig } from '@/config/formSchema/publishBallFormSchema'; - -interface FormBasicInfoProps { - fee: string - location: string - gameplay: string - selectedStadium: Stadium | null - onFeeChange: (value: string) => void - onLocationChange: (value: string) => void - onGameplayChange: (value: string) => void - onStadiumSelect: () => void - children: FormFieldConfig[] -} - -const FormBasicInfo: React.FC = ({ - fee, - location, - gameplay, - selectedStadium, - onFeeChange, - onLocationChange, - onGameplayChange, - onStadiumSelect, - children -}) => { - const renderChildren = () => { - return children.map((child: any, index: number) => { - return - - - - { - index === 0 && ( - {child.label} - - onFeeChange(e.detail.value)} - /> - 元/每人 - - ) - } - { - index === 1 && ( - {child.label} - - - {selectedStadium ? selectedStadium.name : '请选择'} - - - - ) - } - { - index === 2 && ( - {child.label} - - - {gameplay ? gameplay : '请选择'} - - - - ) - } - - }) - } - return ( - - {/* 费用 */} - {renderChildren()} - - ) -} - -export default FormBasicInfo \ No newline at end of file diff --git a/src/components/FormBasicInfo/index.ts b/src/components/FormBasicInfo/index.ts deleted file mode 100644 index 97a6f31..0000000 --- a/src/components/FormBasicInfo/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './FormBasicInfo' -export type { FormBasicInfoProps } from './FormBasicInfo' \ No newline at end of file diff --git a/src/components/NumberInterval/NumberInterval.tsx b/src/components/NumberInterval/NumberInterval.tsx index 2a8f829..0a4ef3b 100644 --- a/src/components/NumberInterval/NumberInterval.tsx +++ b/src/components/NumberInterval/NumberInterval.tsx @@ -4,18 +4,18 @@ import './NumberInterval.scss' import { InputNumber } from '@nutui/nutui-react-taro' interface NumberIntervalProps { - minParticipants: number - maxParticipants: number - onMinParticipantsChange: (value: number) => void - onMaxParticipantsChange: (value: number) => void + value: [number, number] + onChange: (value: [number, number]) => void } const NumberInterval: React.FC = ({ - minParticipants, - maxParticipants, - onMinParticipantsChange, - onMaxParticipantsChange + value, + onChange }) => { + const [minParticipants, maxParticipants] = value || [1, 4] + const handleChange = (value: [number | string, number | string]) => { + onChange([Number(value[0]), Number(value[1])]) + } return ( @@ -23,9 +23,10 @@ const NumberInterval: React.FC = ({ handleChange([value, maxParticipants])} formatter={(value) => `${value}人`} /> @@ -35,9 +36,10 @@ const NumberInterval: React.FC = ({ handleChange([value, maxParticipants])} + min={minParticipants} + max={maxParticipants} formatter={(value) => `${value}人`} /> diff --git a/src/components/TextareaTag/TextareaTag.tsx b/src/components/TextareaTag/TextareaTag.tsx index f8f0d6e..4aa5e91 100644 --- a/src/components/TextareaTag/TextareaTag.tsx +++ b/src/components/TextareaTag/TextareaTag.tsx @@ -6,8 +6,8 @@ import { Checkbox } from '@nutui/nutui-react-taro' import './TextareaTag.scss' interface TextareaTagProps { - value: string - onChange: (value: string) => void + value: { description: string, description_tag: string[] } + onChange: (value: { description: string, description_tag: string[] }) => void title?: string showTitle?: boolean placeholder?: string @@ -22,27 +22,17 @@ const TextareaTag: React.FC = ({ maxLength = 500, options = [] }) => { - // 处理输入框变化 - const [tags, setTags] = useState([]) - const handleInputChange = useCallback((e: any) => { - onChange(e.detail.value) + // 处理文本输入变化 + const handleTextChange = useCallback((e: any) => { + onChange({...value, description: e.detail.value}) }, [onChange]) - // 选择预设选项 - const handleSelectOption = useCallback((option: string) => { - let newValue = '' - - if (value) { - // 如果已有内容,用分号分隔添加 - newValue = value + ';' + option - } else { - // 如果没有内容,直接添加 - newValue = option - } - - onChange(newValue) - }, [value, onChange]) + // 处理标签选择变化 + const handleTagChange = useCallback((selectedTags: string[]) => { + onChange({...value, description_tag: selectedTags}) + }, [onChange]) + console.log(options, 'options') return ( {/* 选择选项 */} @@ -52,8 +42,8 @@ const TextareaTag: React.FC = ({ setTags(value)} + value={value.description_tag} + onChange={handleTagChange} > { options?.map((option, index) => ( @@ -74,9 +64,9 @@ const TextareaTag: React.FC = ({