feat(medications): 添加药品结束日期选择功能
- 新增药品结束日期选择器,支持设置服药周期 - 优化日期显示格式,从"开始日期"改为"服药周期" - 添加日期验证逻辑,确保开始日期不早于今天且结束日期不早于开始日期 - 改进添加药品页面的日期选择UI,采用并排布局 - 调整InfoCard组件样式,移除图标背景色并减小字体大小
This commit is contained in:
@@ -128,7 +128,7 @@ export default function AddMedicationScreen() {
|
||||
const { upload, uploading } = useCosUpload({ prefix: 'images/medications' });
|
||||
// 获取登录验证相关的功能
|
||||
const { ensureLoggedIn } = useAuthGuard();
|
||||
const softBorderColor = useMemo(() => withAlpha(colors.border, 0.45), [colors.border]);
|
||||
const softBorderColor = useMemo(() => withAlpha(colors.border, 0.25), [colors.border]);
|
||||
const fadedBorderFill = useMemo(() => withAlpha('#ffffff', 1), [colors.border]);
|
||||
const glassPrimaryTint = useMemo(() => withAlpha(colors.primary, theme === 'dark' ? 0.55 : 0.45), [colors.primary, theme]);
|
||||
const glassDisabledTint = useMemo(() => withAlpha(colors.border, theme === 'dark' ? 0.45 : 0.6), [colors.border, theme]);
|
||||
@@ -146,8 +146,12 @@ export default function AddMedicationScreen() {
|
||||
const [timesPickerVisible, setTimesPickerVisible] = useState(false);
|
||||
const [timesPickerValue, setTimesPickerValue] = useState(1);
|
||||
const [startDate, setStartDate] = useState<Date>(new Date());
|
||||
const [endDate, setEndDate] = useState<Date | null>(null);
|
||||
const [datePickerVisible, setDatePickerVisible] = useState(false);
|
||||
const [datePickerValue, setDatePickerValue] = useState<Date>(new Date());
|
||||
const [endDatePickerVisible, setEndDatePickerVisible] = useState(false);
|
||||
const [endDatePickerValue, setEndDatePickerValue] = useState<Date>(new Date());
|
||||
const [datePickerMode, setDatePickerMode] = useState<'start' | 'end'>('start');
|
||||
const [medicationTimes, setMedicationTimes] = useState<string[]>([DEFAULT_TIME_PRESETS[0]]);
|
||||
const [timePickerVisible, setTimePickerVisible] = useState(false);
|
||||
const [timePickerDate, setTimePickerDate] = useState<Date>(createDateFromTime(DEFAULT_TIME_PRESETS[0]));
|
||||
@@ -276,6 +280,7 @@ export default function AddMedicationScreen() {
|
||||
timesPerDay: timesPerDay,
|
||||
medicationTimes: medicationTimes,
|
||||
startDate: dayjs(startDate).startOf('day').toISOString(), // ISO 8601 格式
|
||||
endDate: endDate ? dayjs(endDate).endOf('day').toISOString() : undefined, // 如果有结束日期,设置为当天结束时间
|
||||
repeatPattern: 'daily' as RepeatPattern,
|
||||
note: note.trim() || undefined,
|
||||
};
|
||||
@@ -332,6 +337,7 @@ export default function AddMedicationScreen() {
|
||||
dosageUnit,
|
||||
medicationTimes,
|
||||
startDate,
|
||||
endDate,
|
||||
note,
|
||||
dispatch,
|
||||
ensureLoggedIn,
|
||||
@@ -469,15 +475,47 @@ export default function AddMedicationScreen() {
|
||||
setPhotoUrl(null);
|
||||
}, []);
|
||||
|
||||
const openDatePicker = useCallback(() => {
|
||||
const openStartDatePicker = useCallback(() => {
|
||||
setDatePickerValue(startDate);
|
||||
setDatePickerVisible(true);
|
||||
}, [startDate]);
|
||||
|
||||
const openEndDatePicker = useCallback(() => {
|
||||
setEndDatePickerValue(endDate || new Date());
|
||||
setEndDatePickerVisible(true);
|
||||
}, [endDate]);
|
||||
|
||||
const confirmStartDate = useCallback((date: Date) => {
|
||||
// 验证开始日期不能早于今天
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const selectedDate = new Date(date);
|
||||
selectedDate.setHours(0, 0, 0, 0);
|
||||
|
||||
if (selectedDate < today) {
|
||||
Alert.alert('日期无效', '开始日期不能早于今天');
|
||||
return;
|
||||
}
|
||||
|
||||
setStartDate(date);
|
||||
setDatePickerVisible(false);
|
||||
}, []);
|
||||
|
||||
// 如果结束日期早于新的开始日期,清空结束日期
|
||||
if (endDate && endDate < date) {
|
||||
setEndDate(null);
|
||||
}
|
||||
}, [endDate]);
|
||||
|
||||
const confirmEndDate = useCallback((date: Date) => {
|
||||
// 验证结束日期不能早于开始日期
|
||||
if (date < startDate) {
|
||||
Alert.alert('日期无效', '结束日期不能早于开始日期');
|
||||
return;
|
||||
}
|
||||
|
||||
setEndDate(date);
|
||||
setEndDatePickerVisible(false);
|
||||
}, [startDate]);
|
||||
|
||||
const openTimePicker = useCallback(
|
||||
(index?: number) => {
|
||||
@@ -718,7 +756,6 @@ export default function AddMedicationScreen() {
|
||||
case 2:
|
||||
return (
|
||||
<View style={styles.stepSection}>
|
||||
|
||||
<View style={styles.inputGroup}>
|
||||
<ThemedText style={[styles.groupLabel, { color: colors.textSecondary }]}>每日次数</ThemedText>
|
||||
<TouchableOpacity
|
||||
@@ -736,6 +773,57 @@ export default function AddMedicationScreen() {
|
||||
<Ionicons name="chevron-down" size={18} color={colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.inputGroup}>
|
||||
<View style={styles.periodHeader}>
|
||||
<ThemedText style={[styles.groupLabel, { color: colors.textSecondary }]}>用药周期</ThemedText>
|
||||
</View>
|
||||
<View style={styles.dateRowContainer}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.85}
|
||||
style={[
|
||||
styles.dateRow,
|
||||
styles.dateRowHalf,
|
||||
{
|
||||
borderColor: softBorderColor,
|
||||
backgroundColor: colors.surface,
|
||||
},
|
||||
]}
|
||||
onPress={openStartDatePicker}
|
||||
>
|
||||
<View style={styles.dateLeft}>
|
||||
<Ionicons name="calendar" size={16} color={colors.textSecondary} />
|
||||
<ThemedText style={[styles.dateLabel, { color: colors.textMuted }]}>开始</ThemedText>
|
||||
<ThemedText style={[styles.dateValue, { color: colors.text }]}>
|
||||
{dayjs(startDate).format('MM/DD')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={16} color={colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.85}
|
||||
style={[
|
||||
styles.dateRow,
|
||||
styles.dateRowHalf,
|
||||
{
|
||||
borderColor: softBorderColor,
|
||||
backgroundColor: colors.surface,
|
||||
},
|
||||
]}
|
||||
onPress={openEndDatePicker}
|
||||
>
|
||||
<View style={styles.dateLeft}>
|
||||
<Ionicons name="calendar-outline" size={16} color={colors.textSecondary} />
|
||||
<ThemedText style={[styles.dateLabel, { color: colors.textMuted }]}>结束</ThemedText>
|
||||
<ThemedText style={[styles.dateValue, { color: colors.text }]}>
|
||||
{endDate ? dayjs(endDate).format('MM/DD') : '长期'}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={16} color={colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
case 3:
|
||||
@@ -895,30 +983,6 @@ export default function AddMedicationScreen() {
|
||||
<View style={styles.contentContainer}>{renderStepContent()}</View>
|
||||
|
||||
<View style={styles.footer}>
|
||||
{showDateField && (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.85}
|
||||
style={[
|
||||
styles.startDateRow,
|
||||
{
|
||||
borderColor: softBorderColor,
|
||||
backgroundColor: colors.surface,
|
||||
},
|
||||
]}
|
||||
onPress={openDatePicker}
|
||||
>
|
||||
<View style={styles.startDateLeft}>
|
||||
<Ionicons name="calendar" size={18} color={colors.textSecondary} />
|
||||
<View>
|
||||
<ThemedText style={[styles.startDateLabel, { color: colors.textMuted }]}>开始日期</ThemedText>
|
||||
<ThemedText style={[styles.startDateValue, { color: colors.text }]}>
|
||||
{dayjs(startDate).format('YYYY 年 MM 月 DD 日')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={18} color={colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
<View style={styles.footerButtons}>
|
||||
{currentStep > 0 && (
|
||||
@@ -1010,6 +1074,7 @@ export default function AddMedicationScreen() {
|
||||
<Pressable style={styles.pickerBackdrop} onPress={() => setDatePickerVisible(false)} />
|
||||
<View style={[styles.pickerSheet, { backgroundColor: colors.surface }]}
|
||||
>
|
||||
<ThemedText style={[styles.modalTitle, { color: colors.text }]}>选择开始日期</ThemedText>
|
||||
<DateTimePicker
|
||||
value={datePickerValue}
|
||||
mode="date"
|
||||
@@ -1045,6 +1110,51 @@ export default function AddMedicationScreen() {
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
visible={endDatePickerVisible}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={() => setEndDatePickerVisible(false)}
|
||||
>
|
||||
<Pressable style={styles.pickerBackdrop} onPress={() => setEndDatePickerVisible(false)} />
|
||||
<View style={[styles.pickerSheet, { backgroundColor: colors.surface }]}
|
||||
>
|
||||
<ThemedText style={[styles.modalTitle, { color: colors.text }]}>选择结束日期</ThemedText>
|
||||
<DateTimePicker
|
||||
value={endDatePickerValue}
|
||||
mode="date"
|
||||
display={Platform.OS === 'ios' ? 'inline' : 'calendar'}
|
||||
onChange={(event, date) => {
|
||||
if (Platform.OS === 'ios') {
|
||||
if (date) setEndDatePickerValue(date);
|
||||
} else {
|
||||
if (event.type === 'set' && date) {
|
||||
confirmEndDate(date);
|
||||
} else {
|
||||
setEndDatePickerVisible(false);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{Platform.OS === 'ios' && (
|
||||
<View style={styles.modalActions}>
|
||||
<Pressable
|
||||
onPress={() => setEndDatePickerVisible(false)}
|
||||
style={[styles.modalBtn, { borderColor: softBorderColor }]}
|
||||
>
|
||||
<ThemedText style={[styles.modalBtnText, { color: colors.textSecondary }]}>取消</ThemedText>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
onPress={() => confirmEndDate(endDatePickerValue)}
|
||||
style={[styles.modalBtn, styles.modalBtnPrimary, { backgroundColor: colors.primary }]}
|
||||
>
|
||||
<ThemedText style={[styles.modalBtnText, { color: colors.onPrimary }]}>确定</ThemedText>
|
||||
</Pressable>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
visible={timePickerVisible}
|
||||
transparent
|
||||
@@ -1500,6 +1610,39 @@ const styles = StyleSheet.create({
|
||||
footer: {
|
||||
gap: 12,
|
||||
},
|
||||
periodHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
dateRowContainer: {
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
},
|
||||
dateRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
borderWidth: 1,
|
||||
borderRadius: 18,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
dateRowHalf: {
|
||||
flex: 1,
|
||||
},
|
||||
dateLeft: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
dateLabel: {
|
||||
fontSize: 11,
|
||||
},
|
||||
dateValue: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
},
|
||||
startDateRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
||||
Reference in New Issue
Block a user