feat:重构模块

This commit is contained in:
2025-10-08 22:06:39 +08:00
parent f60d78fe8b
commit 2e81e57774
222 changed files with 2267 additions and 3059 deletions

View File

@@ -0,0 +1,186 @@
<template>
<div class="mobile-period-view common-mobile-view">
<v-calendar ref="calendarRef" :attributes='dailyInfo.attrs'
@dayclick="dateClick" @did-move="changeMonthClick"
:is-dark="appStore.isDark"
class="period-calendar card-item"/>
<period-legend />
<period-form />
</div>
</template>
<script lang="ts" setup>
import PeriodLegend from '@/components/Period/PeriodLegend.vue'
import PeriodForm from '@/components/Period/PeriodForm.vue'
import { CalendarDay } from 'v-calendar/dist/types/src/utils/page'
import { formatDate, getNextDate } from 'vue3-common/utils/dateUtil'
import { onMounted, reactive, watch } from 'vue'
import { usePeriodStore } from '@/store/period.ts'
import { useAppStore } from '@/store/app.ts'
import { IPeriodDay } from '@/types/period.ts'
import dayjs, { Dayjs } from 'dayjs'
const periodStore = usePeriodStore()
const appStore = useAppStore()
const dailyInfo = reactive({
calendarDate: '',
menstruateDateList: [],
sexDateList: [],
attrs: [] as any[]
})
watch(() => periodStore.isRefresh, async () => {
dailyInfo.menstruateDateList = []
dailyInfo.sexDateList = []
dailyInfo.attrs = []
updatePeriodDay()
if (periodStore.periodSetting.isCalculate) {
const { cycleLength, duration } = periodStore.periodSetting
// 如果当月存在月经期
if (dailyInfo.menstruateDateList[0]) {
calculatePeriodDay(dailyInfo.menstruateDateList[0], cycleLength, duration)
} else {
// 否则获取上个月的
await periodStore.getMenstruateFirstDate(getLastMonth(periodStore.queryInfo.month))
const predictMenstruateDate = getNextDate(periodStore.menstruateFirstDate, cycleLength, 'days')
if (predictMenstruateDate) {
calculatePeriodDay(predictMenstruateDate, cycleLength, duration)
}
}
}
})
onMounted(async () => {
await periodStore.refreshInfo()
periodStore.currentPeriodDay = periodStore.getPeriodDay(formatDate(new Date()))
})
const updatePeriodDay = () => {
periodStore.periodDayList.forEach((item: IPeriodDay) => {
if (item.isMenstruate) {
dailyInfo.attrs.push({
highlight: 'red',
dates: item.date
})
dailyInfo.menstruateDateList.push(item.date)
}
if (item.sexActivity !== '') {
dailyInfo.sexDateList.push(item.date)
dailyInfo.attrs.push({
highlight: 'green',
dates: item.date
})
}
if (item.isDrug) {
dailyInfo.attrs.push({
dot: 'red',
dates: item.date
})
}
})
}
const getAllDatesInMonth = (dateStr: string) => {
const start = dayjs(dateStr).startOf('month')
const daysInMonth = start.daysInMonth()
const dates = []
for (let i = 0; i < daysInMonth; i++) {
dates.push(formatDate(start.add(i, 'day')))
}
return dates
}
const calculatePeriodDay = (date: string, cycleLength = 28, duration = 5) => {
const startDate = dayjs(date)
// 排卵日假设是周期的第 14 天,但对于短周期的情况,排卵日可以在周期的中间附近
const ovulationDate = startDate.add(cycleLength - 14, 'day') // 基于周期长度来计算排卵日
const fertileStart = ovulationDate.subtract(5, 'day') // 易孕期为排卵日前5天
const fertileEnd = ovulationDate.add(4, 'day') // 易孕期为排卵日后4天
const allDates = getAllDatesInMonth(date)
for (const dateStr of allDates) {
const currentDate = dayjs(dateStr)
// 判断月经期,跳过
if (currentDate.isSame(startDate, 'day') || checkIsMenstruateDate(currentDate, startDate, duration)) {
if (!dailyInfo.menstruateDateList.includes(dateStr)) {
dailyInfo.attrs.push({
content: 'red',
dates: formatDate(currentDate)
})
}
continue
}
if (dailyInfo.sexDateList.includes(formatDate(currentDate))) {
continue
}
// 判断排卵日
if (currentDate.isSame(ovulationDate, 'day')) {
dailyInfo.attrs.push({
highlight: 'purple',
dates: dateStr
})
} else if (currentDate.isAfter(fertileStart) && currentDate.isBefore(fertileEnd)) {
// 添加易孕期
dailyInfo.attrs.push({
content: 'purple',
dates: dateStr
})
} else {
// 添加安全期
dailyInfo.attrs.push({
content: 'green',
dates: dateStr
})
}
}
}
const checkIsMenstruateDate = (currentDate: Dayjs, startDate: Dayjs, duration: number) => {
return currentDate.isAfter(startDate) && currentDate.diff(startDate, 'day') < duration
}
const dateClick = (day: CalendarDay) => {
periodStore.selectDate = formatDate(day.date)
periodStore.currentPeriodDay = periodStore.getPeriodDay(formatDate(day.date))
}
/**
* 选择月份事件
*/
const changeMonthClick = async (value) => {
periodStore.queryInfo.month = value[0].id
await periodStore.refreshInfo()
}
const getLastMonth = (current: string): string => {
const previousMonth = dayjs(current, 'YYYY-MM').subtract(1, 'month')
return previousMonth.format('YYYY-MM')
}
</script>
<style lang="scss">
.mobile-period-view {
height: 100%;
display: flex;
flex-direction: column;
gap: 1vh;
.period-calendar {
justify-self: center;
align-self: center;
}
}
</style>

View File

@@ -0,0 +1,71 @@
<template>
<div class="mobile-period-setting common-mobile-view">
<el-form ref="formRef" :model="periodStore.periodSetting"
label-width="auto">
<el-form-item label="周期天数">
<el-input-number v-model="periodStore.periodSetting.cycleLength" autocomplete="off"
placeholder="请输入周期天数" clearable>
</el-input-number>
</el-form-item>
<el-form-item label="持续天数">
<el-input-number v-model="periodStore.periodSetting.duration" autocomplete="off"
placeholder="请输入持续天数" clearable>
</el-input-number>
</el-form-item>
<el-form-item label="是否计算">
<el-switch
v-model="periodStore.periodSetting.isCalculate"
active-text=""
inactive-text=""
/>
</el-form-item>
</el-form>
<div class="button-container">
<el-button type="primary" @click="confirmClick">确认</el-button>
<el-button type="info" @click="cancelClick">取消</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { usePeriodStore } from '@/store/period.ts'
import { useAppStore } from '@/store/app.ts'
import { onMounted, ref } from 'vue'
const formRef = ref()
const periodStore = usePeriodStore()
const appStore = useAppStore()
onMounted(async () => {
appStore.isShowMobileBack = true
})
/**
* 点击确认按钮
*/
const confirmClick = () => {
formRef.value.validate().then(async () => {
await periodStore.updatePeriodSetting()
history.back()
})
}
const cancelClick = () => {
history.back()
}
</script>
<style scoped lang="scss">
.mobile-period-setting {
display: flex;
flex-direction: column;
gap: 10px;
.button-container {
align-self: center;
}
}
</style>