const { withRecordAccess } = require('../../utils/record-access'); const { tryLoginRenewal } = require('../../utils/subscribe'); const { getLayout } = require('../../utils/layout'); // pages/period/period.js - 姨姨(美柚风格日历模式) const { api } = require('../../utils/request'); Page(withRecordAccess({ onLoginSubscribeTap: function() { return tryLoginRenewal(); }, onResize: function() { this.setData(getLayout()); }, data: { // 日历 currentYear: 0, currentMonth: 0, calendarDays: [], weekdays: ['日', '一', '二', '三', '四', '五', '六'], selectedDate: '', selectedDateLabel: '', selectedWeekday: '', // 统计数据 cycleDay: 0, predictedDays: 0, avgCycle: 0, periodStatusText: '暂无数据', periodStatusClass: '', // 每日记录 dailyRecord: {}, hasDetailContent: false, // 周期列表 cycles: [], savedCycles: [], // period_records 原始周期记录(用于姨姨设置弹窗预填,还原真实周期参数) dailyRecordsMap: {}, // key: dateStr -> record // 弹窗控制 showCycleModal: false, showFormula: false, showPeriodModal: false, // 经期详情表单(量、颜色、疼痛) periodForm: { amount: '', color: '', pain: '' }, periodAmountOptions: [ { value: '极少', label: '极少' }, { value: '少', label: '少' }, { value: '中等', label: '中等' }, { value: '多', label: '多' }, { value: '极多', label: '极多' } ], periodColorOptions: [ { value: '浅红', label: '浅红', dot: '#FFCDD2' }, { value: '鲜红', label: '鲜红', dot: '#FF1744' }, { value: '深红', label: '深红', dot: '#B71C1C' }, { value: '褐色', label: '褐色', dot: '#8D6E63' }, { value: '黑色', label: '黑色', dot: '#424242' } ], periodPainOptions: [ { value: '无', label: '无' }, { value: '轻微', label: '轻微' }, { value: '中微', label: '中微' }, { value: '剧烈', label: '剧烈' } ], cycleForm: { start_date: '', end_date: '', cycle_length: 28, period_length: 5, // 推算结果 predicted_start: '', predicted_end: '', predicted_ovulation_day: '', predicted_ovulation_start: '', predicted_ovulation_end: '' }, // 症状 showSymptomModal: false, selectedSymptoms: [], symptomMap: {}, symptomOptions: ['腹痛', '腰酸', '乏力', '头痛', '情绪波动', '乳房胀痛', '恶心', '腹泻', '畏寒', '失眠', '食欲增加', '无不适'], // 心情 showMoodModal: false, moodOptions: [ { emoji: '😊', label: '开心' }, { emoji: '😌', label: '平静' }, { emoji: '😔', label: '低落' }, { emoji: '😤', label: '烦躁' }, { emoji: '😰', label: '焦虑' }, { emoji: '🥱', label: '疲倦' }, { emoji: '😭', label: '难过' }, { emoji: '🥰', label: '甜蜜' } ], // 白带 showDischargeModal: false, dischargeForm: { status: '', flow: '', abnormal: [] }, dischargeAbnormalMap: {}, dischargeStatusOptions: [ { value: '干燥', label: '干燥' }, { value: '粘稠', label: '粘稠' }, { value: '稀糊状', label: '稀糊状' }, { value: '水状', label: '水状' }, { value: '蛋清状', label: '蛋清状' } ], dischargeFlowOptions: [ { value: '少量', label: '少量' }, { value: '适中', label: '适中' }, { value: '大量', label: '大量' } ], dischargeAbnormalOptions: [ { value: '带血丝', label: '带血丝' }, { value: '白带增多', label: '白带增多' }, { value: '泡沫状', label: '泡沫状' }, { value: '渣渣多', label: '渣渣多' }, { value: '偏黄', label: '偏黄' }, { value: '偏绿', label: '偏绿' }, { value: '异味', label: '异味' } ], // 体温 showTempModal: false, tempValue: '', // 体重 showWeightModal: false, weightValue: '', // 备注 showNoteModal: false, noteValue: '', // 权限设置 periodVisible: true, visibilitySaving: false, hasCouple: false, accessLoading: true, accessDenied: false, selectionRequired: false, myUserId: 0, partnerUserId: 0, myPreview: {}, partnerPreview: {}, // 爱爱记录(支持一天多条) showSexModal: false, sexRecords: [], // 当前选中日期的爱爱记录列表 sexRecordsMap: {}, // key: dateStr -> [{id, record_time, measure, measure_custom}] sexForm: { record_time: '', measure: '', remark: '' }, _lastFetchTime: 0, measureOptions: [ { value: '无措施', label: '无措施', icon: '🚫' }, { value: '避孕套', label: '避孕套', icon: '🛡️' }, { value: '短效避孕药', label: '短效避孕药', icon: '💊' }, { value: '长效避孕药', label: '长效避孕药', icon: '💊' }, { value: '紧急避孕药', label: '紧急避孕药', icon: '💊' }, { value: '自慰', label: '自慰', icon: '🖐️' }, { value: '体外排精', label: '体外排精', icon: '⚠️' }, { value: '未射精', label: '未射精', icon: '⏹️' }, { value: '节育环', label: '节育环', icon: '⭕' }, { value: '其他措施', label: '其他措施', icon: '📌' } ] }, onLoad: function() { this.setData(getLayout()); var now = new Date(); this.setData({ currentYear: now.getFullYear(), currentMonth: now.getMonth() + 1 }); // 权限必须先从服务端确认,避免缓存中的姨姨数据短暂泄露。 this.loadData(); }, onShow: function() { // 返回本页时重新确认伴侣是否更改了可见权限。 if (!this.data.accessLoading) this.loadData(); if (!this._permissionTimer) { var self = this; this._permissionTimer = setInterval(function() { api.periodVisibleGet().then(function(access) { var denied = access.can_access === false; if (denied !== self.data.accessDenied) { if (denied) self.setData({ accessDenied: true, cycles: [], savedCycles: [], dailyRecordsMap: {}, sexRecordsMap: {}, showCycleModal: false, showPeriodModal: false }); self.loadData(); } }).catch(function() {}); }, 10000); } }, onHide: function() { if (this._permissionTimer) clearInterval(this._permissionTimer); this._permissionTimer = null; }, onUnload: function() { if (this._permissionTimer) clearInterval(this._permissionTimer); this._permissionTimer = null; }, onPullDownRefresh: async function() { await this.loadData(); wx.stopPullDownRefresh(); }, // ========== 数据加载 ========== loadData: async function() { try { const res = await api.periodList(); var daily = res.daily || []; var sexRecords = res.sex_records || []; // 构建每日记录映射 var dailyMap = {}; for (var i = 0; i < daily.length; i++) { var item = daily[i]; // 解析经期详情 JSON(量/颜色/疼痛),供弹窗高亮 if (item.period_detail) { try { item.period_detail = JSON.parse(item.period_detail); } catch (e) { item.period_detail = null; } } dailyMap[item.record_date] = item; } // 构建爱爱记录映射(按日期分组) var sexMap = {}; for (var i = 0; i < sexRecords.length; i++) { var dateStr = sexRecords[i].record_date; if (!sexMap[dateStr]) sexMap[dateStr] = []; sexMap[dateStr].push(sexRecords[i]); } // 读取权限设置 var periodVisible = res.period_visible !== undefined ? !!res.period_visible : this.data.periodVisible; if (res.can_access === false) { try { } catch (e) {} this.setData({ cycles: [], savedCycles: [], dailyRecordsMap: {}, sexRecordsMap: {}, dailyRecord: {}, hasDetailContent: false, showCycleModal: false, showPeriodModal: false, showSymptomModal: false, showMoodModal: false, showDischargeModal: false, showTempModal: false, showWeightModal: false, showNoteModal: false, showSexModal: false, periodVisible: periodVisible, hasCouple: true, accessLoading: false, accessDenied: true, selectionRequired: false }); this.calculateStats([]); this.buildCalendar(); return; } if (res.selection_required) { this.setData({ cycles: [], savedCycles: [], dailyRecordsMap: {}, sexRecordsMap: {}, dailyRecord: {}, hasDetailContent: false, periodVisible: periodVisible, hasCouple: true, accessLoading: false, accessDenied: false, selectionRequired: true, myUserId: res.my_user_id, partnerUserId: res.partner_user_id, myPreview: res.my_preview || {}, partnerPreview: res.partner_preview || {} }); this.calculateStats([]); if (!this._selectionPromptShown) { this._selectionPromptShown = true; setTimeout(() => this.choosePeriodSource(), 0); } return; } var savedCycles = res.cycles || []; var cycles = this._buildDisplayCycles(dailyMap, savedCycles); this.setData({ cycles: cycles, savedCycles: savedCycles, dailyRecordsMap: dailyMap, sexRecordsMap: sexMap, periodVisible: periodVisible, hasCouple: !!res.has_couple, accessLoading: false, accessDenied: false, selectionRequired: false, _lastFetchTime: Date.now() }); // 写入本地缓存 this.calculateStats(cycles); this.buildCalendar(); // 如果选中日期存在,刷新详情 if (this.data.selectedDate) { this.refreshSelectedDayDetail(); } } catch(e) { console.error('从服务器加载数据失败:', e); this.setData({ accessLoading: false }); wx.showToast({ title: '姨姨数据加载失败', icon: 'none' }); } }, // 从 dailyRecordsMap 动态生成周期列表 // 将有月经记录(period > 0)的连续日期合并为一个周期 _buildCyclesFromDaily: function(dailyMap) { if (!dailyMap) return []; // 收集所有有月经记录的日期,按日期排序 var periodDates = []; for (var dateStr in dailyMap) { if (dailyMap.hasOwnProperty(dateStr)) { var record = dailyMap[dateStr]; if (record && parseInt(record.period) > 0) { periodDates.push(dateStr); } } } if (periodDates.length === 0) return []; periodDates.sort(); // 将连续日期合并为周期 var cycles = []; var currentStart = periodDates[0]; var currentEnd = periodDates[0]; var prevDate = periodDates[0]; for (var i = 1; i < periodDates.length; i++) { var curr = periodDates[i]; // 检查是否连续(相邻日期差1天) var prev = new Date(prevDate); var next = new Date(curr); var diff = Math.round((next - prev) / (1000 * 60 * 60 * 24)); if (diff === 1) { currentEnd = curr; } else { // 不连续,结束当前周期,开始新周期 cycles.push({ start_date: currentStart, end_date: currentEnd, cycle_length: 28, period_length: this._dayDiff(currentStart, currentEnd) + 1 }); currentStart = curr; currentEnd = curr; } prevDate = curr; } // 最后一个周期 cycles.push({ start_date: currentStart, end_date: currentEnd, cycle_length: 28, period_length: this._dayDiff(currentStart, currentEnd) + 1 }); // 按 start_date 降序(最新的在前面) cycles.sort(function(a, b) { return b.start_date.localeCompare(a.start_date); }); // 计算每个周期的 cycle_length(与前一个周期的间隔) for (var i = 0; i < cycles.length; i++) { if (i < cycles.length - 1) { // 当前周期与下一个(更早的)周期的间隔 var diff = this._dayDiff(cycles[i + 1].start_date, cycles[i].start_date); if (diff > 0) { cycles[i].cycle_length = diff; } } } return cycles; }, // 计算两个日期字符串之间的天数差(date2 - date1) _dayDiff: function(dateStr1, dateStr2) { var d1 = new Date(dateStr1); var d2 = new Date(dateStr2); return Math.round((d2 - d1) / (1000 * 60 * 60 * 24)); }, // ========== 统计数据计算 ========== calculateStats: function(cycles) { if (!cycles || cycles.length === 0) { this.setData({ cycleDay: 0, predictedDays: 0, avgCycle: 0, periodStatusText: '暂无数据', periodStatusClass: '' }); return; } // 排序副本,不修改原数组 var sorted = [].concat(cycles).sort(function(a, b) { return b.start_date.localeCompare(a.start_date); }); var now = new Date(); var today = this._formatDate(now); var last = sorted[0]; var lastStart = new Date(last.start_date); var lastEnd = new Date(last.end_date); // 平均周期 var totalCycle = 0; var cycleCount = 0; for (var i = 0; i < sorted.length; i++) { var c = parseInt(sorted[i].cycle_length); if (c > 0) { totalCycle += c; cycleCount++; } } var avgCycle = cycleCount > 0 ? Math.round(totalCycle / cycleCount) : 28; var predictionCycle = parseInt(last.cycle_length) || avgCycle; // 周期天数计算 // 如果今天在经期内:显示"经期第X天"(从开始日算) // 如果今天在经期后:显示"周期第X天"(从开始日算),同时显示"经期结束第Y天" var diffFromStart = Math.floor((now - lastStart) / (1000 * 60 * 60 * 24)); var cycleDay = diffFromStart + 1; if (cycleDay < 1) cycleDay = 1; // 预计下次:从开始日算起,经过 avgCycle 天为下次经期开始 // 剩余天数 = avgCycle - cycleDay var predictedDays = predictionCycle - cycleDay; if (predictedDays < 0) predictedDays = 0; // 状态 var statusText = ''; var statusClass = ''; if (today >= last.start_date && today <= last.end_date) { statusText = '生理期 · 留意身体状态,适当休息'; statusClass = 'period'; } else if (predictedDays <= 0) { statusText = '本次生理期已结束'; statusClass = 'safe'; } else if (predictedDays <= 7) { statusText = '预计 ' + predictedDays + ' 天后生理期开始'; statusClass = 'ovulation'; } else { statusText = '距离下次生理期还有 ' + predictedDays + ' 天'; statusClass = 'safe'; } this.setData({ cycleDay: cycleDay, predictedDays: predictedDays, avgCycle: predictionCycle, periodStatusText: statusText, periodStatusClass: statusClass }); }, // ========== 日历构建 ========== buildCalendar: function() { var year = this.data.currentYear; var month = this.data.currentMonth; var dailyMap = this.data.dailyRecordsMap; var daysInMonth = new Date(year, month, 0).getDate(); // 当月 1 号是星期几(0=周日,1=周一 ... 6=周六),用于月首补齐空白格,使日期对齐到正确的星期列 var firstWeekday = new Date(year, month - 1, 1).getDay(); var today = this._formatDate(new Date()); var selectedDate = this.data.selectedDate; var days = []; // 月首补齐空白占位格,让 1 号落在正确的星期列(修复星期与日期错位问题) for (var i = 0; i < firstWeekday; i++) { days.push({ day: '', dateStr: '', className: 'empty', periodLevel: 0, hasSex: false, sexCount: 0, hasRecord: false, isFuture: false, isToday: false }); } // 只显示当月日期,不补位上月和下月 for (var d = 1; d <= daysInMonth; d++) { var dateStr = year + '-' + this._pad(month) + '-' + this._pad(d); var dayInfo = this._buildDayInfo(d, dateStr, today, selectedDate, dailyMap); days.push(dayInfo); } this.setData({ calendarDays: days }); }, _buildDayInfo: function(dayNum, dateStr, today, selectedDate, dailyMap) { var className = ''; if (dateStr === today) className += 'today '; if (dateStr === selectedDate) className += 'selected '; // 标记未来日期(仅用于点击限制) var isFuture = dateStr > today; var record = dailyMap[dateStr]; var sexMap = this.data.sexRecordsMap; var sexRecords = sexMap[dateStr] || []; var periodLevel = 0; var hasSex = sexRecords.length > 0; var sexCount = sexRecords.length; var hasRecord = false; var phaseClass = ''; // 周期阶段:period/safe/ovulation if (record) { periodLevel = parseInt(record.period) || 0; hasRecord = record.symptoms || record.mood || record.discharge || record.temperature || record.weight || record.note; if (periodLevel > 0) className += 'period-day '; } // 计算周期阶段背景色 phaseClass = this._getCyclePhase(dateStr); if (phaseClass) className += phaseClass + ' '; return { day: dayNum, dateStr: dateStr, className: className, periodLevel: periodLevel, hasSex: hasSex, sexCount: sexCount, hasRecord: hasRecord, isFuture: isFuture, isToday: dateStr === today }; }, // 判断某日期属于哪个周期阶段 // 返回值:'phase-period' 已记录经期 | 'phase-predicted' 预测经期 | 'phase-ovulation-day' 排卵日 | 'phase-ovulation' 排卵期 | 'phase-safe' 安全期 _getCyclePhase: function(dateStr) { var cycles = this.data.cycles; if (!cycles || cycles.length === 0) return ''; // 排序副本,不修改原数组 var sorted = [].concat(cycles).sort(function(a, b) { return b.start_date.localeCompare(a.start_date); }); var last = sorted[0]; var avgCycle = parseInt(last.cycle_length) || this.data.avgCycle || 28; var periodLen = parseInt(last.period_length) || 5; var current = new Date(dateStr); var dateTime = current.getTime(); var lastStart = new Date(last.start_date); var lastEnd = new Date(last.end_date); // ----- 1. 检查是否在已记录的经期内(已知周期) ----- for (var i = 0; i < cycles.length; i++) { var cs = new Date(cycles[i].start_date); var ce = new Date(cycles[i].end_date); if (dateTime >= cs.getTime() && dateTime <= ce.getTime()) { return 'phase-period'; } } // ----- 2. 预测下一次经期(只预测下一次) ----- var nextPeriodStart = new Date(lastStart); nextPeriodStart.setDate(nextPeriodStart.getDate() + avgCycle); var nextPeriodEnd = new Date(nextPeriodStart); nextPeriodEnd.setDate(nextPeriodEnd.getDate() + periodLen - 1); if (dateTime >= nextPeriodStart.getTime() && dateTime <= nextPeriodEnd.getTime()) { return 'phase-predicted'; } // ----- 3. 计算排卵日 & 排卵期(基于下一次经期推算) ----- // 排卵日 = 下次经期前14天 var ovulationDay = new Date(nextPeriodStart); ovulationDay.setDate(ovulationDay.getDate() - 14); var ovDayTime = ovulationDay.getTime(); // 排卵期 = 排卵日前3天 ~ 排卵日后3天(共7天,排卵日居中) var ovStart = new Date(ovulationDay); ovStart.setDate(ovStart.getDate() - 3); var ovEnd = new Date(ovulationDay); ovEnd.setDate(ovEnd.getDate() + 3); if (dateTime >= ovStart.getTime() && dateTime <= ovEnd.getTime()) { // 如果是排卵日当天 if (dateStr === this._formatDate(ovulationDay)) { return 'phase-ovulation-day'; } return 'phase-ovulation'; } // ----- 4. 其他为安全期 ----- return 'phase-safe'; }, // ========== 日历导航 ========== prevMonth: function() { var y = this.data.currentYear; var m = this.data.currentMonth; if (m === 1) { y--; m = 12; } else { m--; } this.setData({ currentYear: y, currentMonth: m, selectedDate: '' }); this.buildCalendar(); }, nextMonth: function() { var y = this.data.currentYear; var m = this.data.currentMonth; if (m === 12) { y++; m = 1; } else { m++; } this.setData({ currentYear: y, currentMonth: m, selectedDate: '' }); this.buildCalendar(); }, // ========== 日期点击 ========== onDayTap: function(e) { var dateStr = e.currentTarget.dataset.date; var today = this._formatDate(new Date()); // 禁止选择未来日期 if (dateStr > today) { wx.showToast({ title: '无法记录未来的日期', icon: 'none' }); return; } var weekday = new Date(dateStr).getDay(); var weekNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; var parts = dateStr.split('-'); var label = parseInt(parts[0]) + '年' + parseInt(parts[1]) + '月' + parseInt(parts[2]) + '日'; this.setData({ selectedDate: dateStr, selectedDateLabel: label, selectedWeekday: weekNames[weekday] }); this.refreshSelectedDayDetail(); this.buildCalendar(); // 刷新选中状态 }, refreshSelectedDayDetail: function() { var dateStr = this.data.selectedDate; if (!dateStr) return; var dailyMap = this.data.dailyRecordsMap; var record = dailyMap[dateStr] || {}; var sexMap = this.data.sexRecordsMap; var sexRecords = sexMap[dateStr] || []; this.setData({ dailyRecord: record, sexRecords: sexRecords, hasDetailContent: !!(parseInt(record.period) > 0 || sexRecords.length > 0 || record.symptoms || record.mood || record.discharge || record.temperature || record.weight || record.note) }); }, // ========== 快捷事件操作 ========== // 通用:清空每日记录中的单个字段并保存 _clearField: function(field) { var dateStr = this.data.selectedDate; if (!dateStr) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var mapRecord = this.data.dailyRecordsMap[dateStr] || {}; var record = JSON.parse(JSON.stringify(mapRecord)); if (field === 'period') { record.period = '0'; record.period_detail = null; } else if (field === 'discharge') { record.discharge = ''; record.discharge_detail = null; } else if (field === 'temperature') { record.temperature = null; } else if (field === 'weight') { record.weight = null; } this._saveDailyRecord(record); }, // 月经 - 有记录时打开详情弹窗可查看/修改,无记录时也打开弹窗 onPeriodTap: function() { this.togglePeriod(); }, // 白带 - 有记录时打开详情弹窗可查看/修改,无记录时也打开弹窗 onDischargeTap: function() { this.showDischargePicker(); }, // 体温 - 有记录时点击直接清空,无记录时打开输入弹窗 onTempTap: function() { if (this.data.dailyRecord.temperature) { this._clearField('temperature'); } else { this.showTempInput(); } }, // 体重 - 有记录时点击直接清空,无记录时打开输入弹窗 onWeightTap: function() { if (this.data.dailyRecord.weight) { this._clearField('weight'); } else { this.showWeightInput(); } }, // 经期 - 显示详情弹窗(量、颜色、疼痛) togglePeriod: function() { var dateStr = this.data.selectedDate; if (!dateStr) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var today = this._formatDate(new Date()); if (dateStr > today) { wx.showToast({ title: '无法记录未来的日期', icon: 'none' }); return; } var record = this.data.dailyRecord || {}; // 从已有记录解析经期详情 var periodDetail = record.period_detail || {}; this.setData({ showPeriodModal: true, periodForm: { amount: periodDetail.amount || '', color: periodDetail.color || '', pain: periodDetail.pain || '' } }); }, // 点击选项只切换选中状态,点确定才保存 onPeriodAmount: function(e) { var val = e.currentTarget.dataset.value; var form = this.data.periodForm; if (form.amount === val) { form.amount = ''; } else { form.amount = val; } this.setData({ periodForm: form }); }, onPeriodColor: function(e) { var val = e.currentTarget.dataset.value; var form = this.data.periodForm; if (form.color === val) { form.color = ''; } else { form.color = val; } this.setData({ periodForm: form }); }, onPeriodPain: function(e) { var val = e.currentTarget.dataset.value; var form = this.data.periodForm; if (form.pain === val) { form.pain = ''; } else { form.pain = val; } this.setData({ periodForm: form }); }, confirmPeriodDetail: function() { var form = this.data.periodForm; var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var record = this._cloneRecord(mapRecord); if (form.amount || form.color || form.pain) { record.period = '1'; record.period_detail = { amount: form.amount, color: form.color, pain: form.pain }; } else { record.period = '0'; record.period_detail = null; } this.setData({ showPeriodModal: false }); this._saveDailyRecord(record); }, closePeriodModal: function() { this.setData({ showPeriodModal: false }); }, // 爱爱 - 显示爱爱记录弹窗 showSexRecords: function() { var dateStr = this.data.selectedDate; if (!dateStr) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var today = this._formatDate(new Date()); if (dateStr > today) { wx.showToast({ title: '无法记录未来的日期', icon: 'none' }); return; } var sexMap = this.data.sexRecordsMap; var records = sexMap[dateStr] || []; // 设置默认时间(当前时间,精确到分钟) var now = new Date(); var hh = this._pad(now.getHours()); var mm = this._pad(now.getMinutes()); var defaultTime = hh + ':' + mm; this.setData({ showSexModal: true, sexRecords: records, 'sexForm.record_time': defaultTime, 'sexForm.measure': '', 'sexForm.remark': '' }); }, // 爱爱 - 时间选择 onSexTimeChange: function(e) { this.setData({ 'sexForm.record_time': e.detail.value }); }, // 爱爱 - 措施选择 onSexMeasureSelect: function(e) { var measure = e.currentTarget.dataset.measure; this.setData({ 'sexForm.measure': measure }); }, // 爱爱 - 备注输入 onSexRemarkInput: function(e) { this.setData({ 'sexForm.remark': e.detail.value }); }, // 爱爱 - 确认添加记录 confirmSexAdd: async function() { var dateStr = this.data.selectedDate; var form = this.data.sexForm; if (!form.record_time) { wx.showToast({ title: '请选择时间', icon: 'none' }); return; } if (!form.measure) { wx.showToast({ title: '请选择措施', icon: 'none' }); return; } wx.showLoading({ title: '保存中...', mask: true }); try { await api.periodSexAdd({ record_date: dateStr, record_time: form.record_time, measure: form.measure, measure_custom: '', remark: form.remark || '' }); wx.hideLoading(); // 刷新本地数据 await this._refreshSexRecords(dateStr); // 添加成功后关闭弹窗 this.setData({ showSexModal: false }); wx.showToast({ title: '添加成功', icon: 'success' }); } catch(e) { wx.hideLoading(); console.error('添加爱爱记录失败:', e); wx.showToast({ title: '添加失败', icon: 'none' }); } }, // 删除每日记录中的单个字段(月经/白带/体温/体重) deletePeriodField: async function(e) { var field = e.currentTarget.dataset.field; var dateStr = this.data.selectedDate; if (!dateStr) return; var fieldLabel = { period: '月经记录', discharge: '白带记录', temperature: '体温记录', weight: '体重记录' }; var label = fieldLabel[field] || '该记录'; wx.showModal({ title: '确认删除', content: '确定删除' + label + '吗?', success: async (res) => { if (res.confirm) { var mapRecord = this.data.dailyRecordsMap[dateStr] || {}; var record = JSON.parse(JSON.stringify(mapRecord)); if (field === 'period') { record.period = '0'; record.period_detail = null; } else if (field === 'discharge') { record.discharge = ''; record.discharge_detail = null; } else if (field === 'temperature') { record.temperature = null; } else if (field === 'weight') { record.weight = null; } this._saveDailyRecord(record); } } }); }, // 爱爱 - 删除记录 deleteSexRecord: async function(e) { var id = e.currentTarget.dataset.id; var dateStr = this.data.selectedDate; wx.showModal({ title: '确认删除', content: '确定删除这条爱爱记录吗?', success: async (res) => { if (res.confirm) { wx.showLoading({ title: '删除中...', mask: true }); try { await api.periodSexDelete({ id: id }); wx.hideLoading(); await this._refreshSexRecords(dateStr); wx.showToast({ title: '已删除', icon: 'success' }); } catch(e) { wx.hideLoading(); console.error('删除爱爱记录失败:', e); wx.showToast({ title: '删除失败', icon: 'none' }); } } } }); }, // 爱爱 - 长按日历格子删除 onDayLongPress: function(e) { var dateStr = e.currentTarget.dataset.date; var sexMap = this.data.sexRecordsMap; var records = sexMap[dateStr] || []; if (records.length === 0) { wx.showToast({ title: '该日期没有爱爱记录', icon: 'none' }); return; } // 显示操作菜单选择要删除的记录 var itemList = records.map(function(r) { var label = r.record_time ? r.record_time.substring(0, 5) : '未知时间'; label += ' - ' + (r.measure || '未知'); return label; }); var self = this; wx.showActionSheet({ itemList: itemList, success: function(res) { var idx = res.tapIndex; var record = records[idx]; wx.showModal({ title: '确认删除', content: '确定删除这条爱爱记录吗?', success: async (modalRes) => { if (modalRes.confirm) { wx.showLoading({ title: '删除中...', mask: true }); try { await api.periodSexDelete({ id: record.id }); wx.hideLoading(); await self._refreshSexRecords(dateStr); wx.showToast({ title: '已删除', icon: 'success' }); } catch(e) { wx.hideLoading(); console.error('删除爱爱记录失败:', e); wx.showToast({ title: '删除失败', icon: 'none' }); } } } }); } }); }, // 爱爱 - 刷新某日期的爱爱记录 _refreshSexRecords: async function(dateStr) { try { var res = await api.periodSexList({ record_date: dateStr }); var records = Array.isArray(res) ? res : (res.records || []); var sexMap = this.data.sexRecordsMap; sexMap[dateStr] = records; this.setData({ sexRecords: records, sexRecordsMap: sexMap, 'sexForm.record_time': this._pad(new Date().getHours()) + ':' + this._pad(new Date().getMinutes()), 'sexForm.measure': '', 'sexForm.remark': '' }); // 重新生成周期并写入缓存 var newCycles = this._buildDisplayCycles(this.data.dailyRecordsMap, this.data.savedCycles); this.setData({ cycles: newCycles }); this.calculateStats(newCycles); // 刷新日历和详情 this.buildCalendar(); this.refreshSelectedDayDetail(); } catch(e) { console.error('刷新爱爱记录失败:', e); } }, // 症状 showSymptomPicker: function() { if (!this.data.selectedDate) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var current = mapRecord.symptoms || ''; var selected = current ? current.split(',') : []; var symptomMap = {}; for (var i = 0; i < selected.length; i++) { symptomMap[selected[i]] = true; } this.setData({ showSymptomModal: true, selectedSymptoms: selected, symptomMap: symptomMap }); }, toggleSymptom: function(e) { var symptom = e.currentTarget.dataset.symptom; var arr = [].concat(this.data.selectedSymptoms); var idx = arr.indexOf(symptom); if (symptom === '无不适') { this.setData({ selectedSymptoms: ['无不适'], symptomMap: { '无不适': true } }); return; } var noIdx = arr.indexOf('无不适'); if (noIdx > -1) arr.splice(noIdx, 1); if (idx > -1) { arr.splice(idx, 1); } else { arr.push(symptom); } var symptomMap = {}; for (var i = 0; i < arr.length; i++) { symptomMap[arr[i]] = true; } this.setData({ selectedSymptoms: arr, symptomMap: symptomMap }); }, confirmSymptoms: function() { var symptomsStr = this.data.selectedSymptoms.join(','); // 从完整 map 中获取记录,避免丢失已有字段 var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var record = this._cloneRecord(mapRecord); record.symptoms = symptomsStr; this.setData({ showSymptomModal: false }); this._saveDailyRecord(record); }, closeSymptomModal: function() { this.setData({ showSymptomModal: false }); }, // 心情 showMoodPicker: function() { this.setData({ showMoodModal: true }); }, selectMood: function(e) { var mood = e.currentTarget.dataset.mood; var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var record = this._cloneRecord(mapRecord); record.mood = record.mood === mood ? '' : mood; this.setData({ showMoodModal: false }); this._saveDailyRecord(record); }, closeMoodModal: function() { this.setData({ showMoodModal: false }); }, // 白带 showDischargePicker: function() { if (!this.data.selectedDate) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var record = this.data.dailyRecord || {}; var dischargeDetail = record.discharge_detail || {}; var abnormalArr = dischargeDetail.abnormal || []; var abnormalMap = {}; for (var i = 0; i < abnormalArr.length; i++) { abnormalMap[abnormalArr[i]] = true; } this.setData({ showDischargeModal: true, dischargeForm: { status: dischargeDetail.status || '', flow: dischargeDetail.flow || '', abnormal: abnormalArr }, dischargeAbnormalMap: abnormalMap }); }, // 点击选项只切换选中状态,点确定才保存 onDischargeStatus: function(e) { var val = e.currentTarget.dataset.value; var form = this.data.dischargeForm; if (form.status === val) { form.status = ''; } else { form.status = val; } this.setData({ dischargeForm: form }); }, onDischargeFlow: function(e) { var val = e.currentTarget.dataset.value; var form = this.data.dischargeForm; if (form.flow === val) { form.flow = ''; } else { form.flow = val; } this.setData({ dischargeForm: form }); }, onDischargeAbnormal: function(e) { var val = e.currentTarget.dataset.value; var form = this.data.dischargeForm; var arr = [].concat(form.abnormal); var idx = arr.indexOf(val); if (idx > -1) { arr.splice(idx, 1); } else { arr.push(val); } var abnormalMap = {}; for (var i = 0; i < arr.length; i++) { abnormalMap[arr[i]] = true; } this.setData({ dischargeForm: { status: form.status, flow: form.flow, abnormal: arr }, dischargeAbnormalMap: abnormalMap }); }, confirmDischargeDetail: function() { var form = this.data.dischargeForm; var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var record = this._cloneRecord(mapRecord); var parts = []; if (form.status) parts.push(form.status); if (form.flow) parts.push(form.flow); if (form.abnormal.length > 0) parts.push(form.abnormal.join('、')); record.discharge = parts.join(' · ') || ''; record.discharge_detail = { status: form.status, flow: form.flow, abnormal: form.abnormal }; this.setData({ showDischargeModal: false }); this._saveDailyRecord(record); }, closeDischargeModal: function() { this.setData({ showDischargeModal: false }); }, // 体温 showTempInput: function() { if (!this.data.selectedDate) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var val = mapRecord.temperature || ''; this.setData({ showTempModal: true, tempValue: String(val) }); }, onTempInput: function(e) { this.setData({ tempValue: e.detail.value }); }, confirmTemp: function() { var val = this.data.tempValue; var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var record = this._cloneRecord(mapRecord); // 已有体温记录且输入框为空或与原有值相同 → 清除 if (record.temperature && (val === '' || val === undefined || val === null || String(val) === String(record.temperature))) { record.temperature = null; this.setData({ showTempModal: false }); this._saveDailyRecord(record); return; } if (val === '' || val === undefined || val === null) { wx.showToast({ title: '请先输入体温', icon: 'none' }); return; } if (isNaN(parseFloat(val))) { wx.showToast({ title: '请输入有效体温', icon: 'none' }); return; } record.temperature = val; this.setData({ showTempModal: false }); this._saveDailyRecord(record); }, closeTempModal: function() { this.setData({ showTempModal: false }); }, // 体重 showWeightInput: function() { if (!this.data.selectedDate) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var val = mapRecord.weight || ''; this.setData({ showWeightModal: true, weightValue: String(val) }); }, onWeightInput: function(e) { this.setData({ weightValue: e.detail.value }); }, confirmWeight: function() { var val = this.data.weightValue; var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var record = this._cloneRecord(mapRecord); // 已有体重记录且输入框为空或与原有值相同 → 清除 if (record.weight && (val === '' || val === undefined || val === null || String(val) === String(record.weight))) { record.weight = null; this.setData({ showWeightModal: false }); this._saveDailyRecord(record); return; } if (val === '' || val === undefined || val === null) { wx.showToast({ title: '请先输入体重', icon: 'none' }); return; } if (isNaN(parseFloat(val))) { wx.showToast({ title: '请输入有效体重', icon: 'none' }); return; } record.weight = val; this.setData({ showWeightModal: false }); this._saveDailyRecord(record); }, closeWeightModal: function() { this.setData({ showWeightModal: false }); }, // 备注 showNoteInput: function() { if (!this.data.selectedDate) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var val = mapRecord.note || ''; this.setData({ showNoteModal: true, noteValue: val }); }, onNoteInput: function(e) { this.setData({ noteValue: e.detail.value }); }, confirmNote: function() { var mapRecord = this.data.dailyRecordsMap[this.data.selectedDate] || {}; var record = this._cloneRecord(mapRecord); record.note = this.data.noteValue; this.setData({ showNoteModal: false }); this._saveDailyRecord(record); }, closeNoteModal: function() { this.setData({ showNoteModal: false }); }, // ========== 保存每日记录 ========== _cloneRecord: function(record) { return JSON.parse(JSON.stringify(record)); }, _saveDailyRecord: async function(record) { var dateStr = this.data.selectedDate; if (!dateStr) { wx.showToast({ title: '请先选择日期', icon: 'none' }); return; } // 二次校验:禁止保存未来日期 var today = this._formatDate(new Date()); if (dateStr > today) { wx.showToast({ title: '无法记录未来的日期', icon: 'none' }); return; } record.record_date = dateStr; wx.showLoading({ title: '保存中...', mask: true }); try { // 确保 period 字段明确处理:'0'=无, '1'=少量, '2'=中量, '3'=大量 var periodVal = record.period; if (periodVal === undefined || periodVal === null || periodVal === '') { periodVal = '0'; } else { periodVal = String(periodVal); } await api.periodDailySave({ record_date: dateStr, period: periodVal, sex: '0', symptoms: record.symptoms || '', mood: record.mood || '', discharge: record.discharge || '', temperature: record.temperature !== undefined && record.temperature !== null && record.temperature !== '' ? String(record.temperature) : null, weight: record.weight !== undefined && record.weight !== null && record.weight !== '' ? String(record.weight) : null, note: record.note || '', period_detail: record.period_detail ? JSON.stringify(record.period_detail) : '' }); wx.hideLoading(); // 合并已有记录,避免覆盖丢失字段 var map = this.data.dailyRecordsMap; var existing = map[dateStr] || {}; var merged = {}; // 先复制已有记录的所有字段 for (var k in existing) { merged[k] = existing[k]; } // 再用新记录覆盖 for (var k in record) { merged[k] = record[k]; } // 确保 merged.period 是字符串类型 if (merged.period !== undefined && merged.period !== null) { merged.period = String(merged.period); } map[dateStr] = merged; var sexMap = this.data.sexRecordsMap; var sexRecords = sexMap[dateStr] || []; this.setData({ dailyRecord: merged, dailyRecordsMap: map, hasDetailContent: !!(parseInt(merged.period) > 0 || sexRecords.length > 0 || merged.symptoms || merged.mood || merged.discharge || merged.temperature || merged.weight || merged.note) }); // 重新生成周期并刷新统计 var newCycles = this._buildDisplayCycles(map, this.data.savedCycles); this.setData({ cycles: newCycles }); this.calculateStats(newCycles); // ★ 将更新后的数据写入本地缓存 this.buildCalendar(); wx.showToast({ title: '保存成功', icon: 'success' }); } catch(e) { wx.hideLoading(); console.error('保存每日记录失败:', e); wx.showToast({ title: '保存失败: ' + (e.error || e.errMsg || '未知错误'), icon: 'none' }); } }, // ========== 姨姨设置弹窗 ========== showPeriodSettings: async function() { // 优先读取用户已保存的周期记录(period_records),还原上次保存的开始/结束日期与周期参数 // 修复:保存后再次打开弹窗被重置为默认值的问题 var saved = this.data.savedCycles || []; var lastSaved = saved.length > 0 ? saved[0] : null; var startStr; var endStr; var cycleLength; var periodLength; if (lastSaved && lastSaved.start_date && lastSaved.end_date) { startStr = lastSaved.start_date; endStr = lastSaved.end_date; periodLength = parseInt(lastSaved.period_length) || 5; cycleLength = parseInt(lastSaved.cycle_length) || this.data.avgCycle || 28; } else { // 无历史记录时使用默认值:结束日为今天,开始日 = 结束日 - 持续天数 + 1 periodLength = 5; var endDate = new Date(); var startDate = new Date(); startDate.setDate(startDate.getDate() - periodLength + 1); startStr = this._formatDate(startDate); endStr = this._formatDate(endDate); cycleLength = this.data.avgCycle || 28; } var form = { start_date: startStr, end_date: endStr, cycle_length: cycleLength, period_length: periodLength, predicted_start: '', predicted_end: '', predicted_ovulation_day: '', predicted_ovulation_start: '', predicted_ovulation_end: '' }; // 立即计算推算结果 form = this._computePrediction(form); // 加载权限设置(先 await,确保保存时 periodVisible 已是最新值,避免误存 0) try { var pv = await api.periodVisibleGet(); if (pv && pv.period_visible !== undefined) { this.setData({ periodVisible: !!pv.period_visible, hasCouple: !!pv.has_couple }); } } catch (e) { console.error('加载权限设置失败:', e); } this.setData({ showCycleModal: true, showFormula: false, cycleForm: form }); }, onPeriodVisibleChange: function(e) { if (this.data.visibilitySaving) return; var visible = !!e.detail.value; var oldValue = this.data.periodVisible; this.setData({ periodVisible: visible, visibilitySaving: true }); api.periodVisibleSet({ period_visible: visible ? 1 : 0 }).then(() => { this.setData({ visibilitySaving: false }); }).catch(() => { this.setData({ periodVisible: oldValue, visibilitySaving: false }); wx.showToast({ title: '权限设置失败', icon: 'none' }); }); }, closeCycleModal: function() { this.setData({ showCycleModal: false, showFormula: false }); }, toggleFormula: function() { this.setData({ showFormula: !this.data.showFormula }); }, onCycleStartChange: function(e) { var startDate = e.detail.value; var form = this.data.cycleForm; form.start_date = startDate; // 根据开始日期和持续天数自动计算结束日期 var s = new Date(startDate); s.setDate(s.getDate() + parseInt(form.period_length) - 1); form.end_date = this._formatDate(s); // 更新推算 form = this._computePrediction(form); this.setData({ cycleForm: form }); }, onCycleEndChange: function(e) { var endDate = e.detail.value; var form = this.data.cycleForm; form.end_date = endDate; // 根据开始和结束日期自动计算持续天数 var s = new Date(form.start_date); var e = new Date(endDate); var diffDays = Math.round((e.getTime() - s.getTime()) / (1000 * 60 * 60 * 24)) + 1; if (diffDays > 0) { form.period_length = diffDays; } // 更新推算 form = this._computePrediction(form); this.setData({ cycleForm: form }); }, adjustCycle: function(e) { var dir = parseInt(e.currentTarget.dataset.dir); var form = this.data.cycleForm; form.cycle_length += dir; if (form.cycle_length < 15) form.cycle_length = 15; if (form.cycle_length > 60) form.cycle_length = 60; form = this._computePrediction(form); this.setData({ cycleForm: form }); }, adjustPeriod: function(e) { var dir = parseInt(e.currentTarget.dataset.dir); var form = this.data.cycleForm; form.period_length += dir; if (form.period_length < 1) form.period_length = 1; if (form.period_length > 15) form.period_length = 15; // 根据开始日期和新的持续天数计算结束日期 var s = new Date(form.start_date); s.setDate(s.getDate() + parseInt(form.period_length) - 1); form.end_date = this._formatDate(s); form = this._computePrediction(form); this.setData({ cycleForm: form }); }, // 推算下次姨妈信息 _computePrediction: function(form) { if (!form.start_date) return form; var start = new Date(form.start_date); var cycleLen = parseInt(form.cycle_length) || 28; var periodLen = parseInt(form.period_length) || 5; // 下次姨妈开始 = 上次来时 + 周期长度 var nextStart = new Date(start); nextStart.setDate(nextStart.getDate() + cycleLen); // 下次姨妈结束 = 下次开始 + 持续天数 - 1 var nextEnd = new Date(nextStart); nextEnd.setDate(nextEnd.getDate() + periodLen - 1); // 排卵日 = 下次姨妈开始前14天 var ovDay = new Date(nextStart); ovDay.setDate(ovDay.getDate() - 14); // 排卵期 = 排卵日前3天 ~ 排卵日后3天 var ovStart = new Date(ovDay); ovStart.setDate(ovStart.getDate() - 3); var ovEnd = new Date(ovDay); ovEnd.setDate(ovEnd.getDate() + 3); form.predicted_start = this._formatDate(nextStart); form.predicted_end = this._formatDate(nextEnd); form.predicted_ovulation_day = this._formatDate(ovDay); form.predicted_ovulation_start = this._formatDate(ovStart); form.predicted_ovulation_end = this._formatDate(ovEnd); return form; }, saveCycle: async function() { var fd = this.data.cycleForm; if (!fd.start_date || !fd.end_date) { wx.showToast({ title: '请选择日期', icon: 'none' }); return; } if (fd.start_date > fd.end_date) { wx.showToast({ title: '结束日期不能早于开始日期', icon: 'none' }); return; } wx.showLoading({ title: '保存中...' }); try { var saved = this.data.savedCycles || []; var lastSaved = saved.length > 0 ? saved[0] : null; var cycleData = { start_date: fd.start_date, end_date: fd.end_date, cycle_length: fd.cycle_length, period_length: fd.period_length }; if (lastSaved && lastSaved.id) { cycleData.id = lastSaved.id; cycleData.symptoms = lastSaved.symptoms || ''; cycleData.note = lastSaved.note || ''; await api.periodUpdate(cycleData); } else { await api.periodAdd(cycleData); } wx.hideLoading(); wx.showToast({ title: '保存成功', icon: 'success' }); this.closeCycleModal(); // 等待重新拉取数据并重算周期/安全期后再结束 await this.loadData(); } catch(e) { wx.hideLoading(); wx.showToast({ title: '保存失败', icon: 'none' }); } }, onCycleLongPress: function(e) { var dataset = e.currentTarget.dataset; var id = dataset.id; var startDate = dataset.start; var endDate = dataset.end; var self = this; wx.showModal({ title: '删除周期', content: '确定删除 ' + startDate + ' 至 ' + endDate + ' 的周期记录吗?', confirmText: '删除', confirmColor: '#e6437f', success: async function(res) { if (!res.confirm) return; wx.showLoading({ title: '删除中...', mask: true }); try { await api.periodDelete({ id: id || 0, start_date: startDate, end_date: endDate }); wx.hideLoading(); wx.showToast({ title: '已删除', icon: 'success' }); self.loadData(); } catch (err) { wx.hideLoading(); wx.showToast({ title: '删除失败', icon: 'none' }); } } }); }, closeSexModal: function() { this.setData({ showSexModal: false }); }, preventMove: function() {}, // ========== 工具 ========== _periodDetailText: function(detail) { if (!detail) return '有记录'; var parts = []; if (detail.amount) parts.push(detail.amount); if (detail.color) parts.push(detail.color); if (detail.pain) parts.push(detail.pain); return parts.join(' · ') || '有记录'; }, _dischargeDetailText: function(detail) { if (!detail) return ''; var parts = []; if (detail.status) parts.push(detail.status); if (detail.flow) parts.push(detail.flow); if (detail.abnormal && detail.abnormal.length > 0) parts.push(detail.abnormal.join('、')); return parts.join(' · ') || ''; }, _formatDate: function(date) { return date.getFullYear() + '-' + this._pad(date.getMonth() + 1) + '-' + this._pad(date.getDate()); }, _pad: function(n) { return n < 10 ? '0' + n : '' + n; }, // 已保存的周期设置是推算依据;逐日标记仅补充没有周期记录的手动输入。 _buildDisplayCycles: function(dailyMap, savedCycles) { var saved = (savedCycles || []).map(function(item) { return Object.assign({}, item, { cycle_length: parseInt(item.cycle_length) || 28, period_length: parseInt(item.period_length) || 5 }); }); saved.sort(function(a, b) { return b.start_date.localeCompare(a.start_date); }); var dailyCycles = this._buildCyclesFromDaily(dailyMap).filter(function(item) { return !saved.some(function(record) { return item.start_date <= record.end_date && item.end_date >= record.start_date; }); }); if (saved.length > 0) { dailyCycles.forEach(function(item) { if (item.start_date > saved[0].start_date) { item.cycle_length = saved[0].cycle_length; item.period_length = saved[0].period_length; } }); } return saved.concat(dailyCycles).sort(function(a, b) { return b.start_date.localeCompare(a.start_date); }); }, choosePeriodSource: function() { var self = this; function label(name, preview) { var count = preview.cycles || 0; var detail = count ? count + '次周期' : (preview.items || 0) + '条记录'; return name + '(' + detail + (preview.latest ? ',最近' + preview.latest : '') + ')'; } wx.showActionSheet({ itemList: [label('使用我的记录', this.data.myPreview), label('使用对方记录', this.data.partnerPreview)], success: function(choice) { var keepUserId = choice.tapIndex === 0 ? self.data.myUserId : self.data.partnerUserId; wx.showModal({ title: '确认选择', content: '选择后,另一方绑定前的姨姨记录会永久删除,无法恢复。确定继续吗?', confirmColor: '#FF6B9D', success: async function(result) { if (!result.confirm) return; wx.showLoading({ title: '处理中...', mask: true }); try { await api.periodSourceSelect({ user_id: keepUserId }); await self.loadData(); } catch (err) { wx.showToast({ title: '选择失败,请重试', icon: 'none' }); await self.loadData(); } finally { wx.hideLoading(); } } }); } }); }, // ========== 导航 ========== goBack: function() { wx.navigateBack(); }, goHome: function() { wx.redirectTo({ url: '/pages/index/index' }); }, goProfile: function() { wx.redirectTo({ url: '/pages/profile/profile' }); } }));