feat:重构模块
This commit is contained in:
186
src/views/period/calendar.vue
Normal file
186
src/views/period/calendar.vue
Normal 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>
|
||||
71
src/views/period/setting.vue
Normal file
71
src/views/period/setting.vue
Normal 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>
|
||||
Reference in New Issue
Block a user