//index.js const app = getApp() const util = require('../../utils/util.js') Page({ data: { userInfo:{}, hasUserInfo:false, clockShow: false, clockHeight: 0, time: '25', mTime: 1500000, timeStr: '25:00', rate: '', timer: null, cateArr: [ { icon: 'work', text: '工作' }, { icon: 'study', text: "学习" }, { icon: 'think', text: '思考' }, { icon: 'write', text: '写作' }, { icon: 'sport', text: '运动' }, { icon: 'read', text: "阅读" } ], cateActive: null, okShow: false, pauseShow: true, continueCancleShow: false, customTask: '', showCustomInput: false, plans: [], selectedPlan: null, editMode: false, editPlanId: null, currentTask: '', userInfo: null, isLoggedIn: false, theme: {}, containerStyle: '', buttonStyle: '', // 按钮动态样式 timerStyle: '', // 计时器动态样式 progressStyle: '', // 进度条动态样式 photoPath: '', isTimerRunning: false, // 新增:计时器是否正在运行 startTime: 0, // 新增:开始时间戳 endTime: 0, // 新增:结束时间戳 remainingTime: 0 // 新增:剩余时间(毫秒) }, onLoad: function () { const res = wx.getSystemInfoSync(); const rate = 750 / res.windowWidth; this.setData({ rate: rate, clockHeight: res.windowHeight * 2, time: '25', mTime: 25 * 60 * 1000, timeStr: '25:00' }); const plans = wx.getStorageSync('plans') || []; this.setData({ plans }); if (app.globalData.userInfo) { this.setData({ userInfo: app.globalData.userInfo, hasUserInfo: true, isLoggedIn: true }); } else { // 没有用户信息,引导用户授权 wx.showModal({ title: '授权提示', content: '需要获取您的用户信息以提供更好的服务,请授权登录', success: res => { if (res.confirm) { wx.getUserProfile({ desc: '用于完善用户资料', success: res => { app.globalData.userInfo = res.userInfo; this.setData({ userInfo: res.userInfo, hasUserInfo: true, isLoggedIn: true }); } }); } } }); } if (app.eventBus) { app.eventBus.on('userInfoChange', (userInfo) => { this.setData({ userInfo: userInfo, isLoggedIn: !!userInfo }); }); app.eventBus.on('themeChange', (theme) => { this.setData({ theme }); this.updateThemeStyles(); }); } this.setData({ userInfo: app.globalData.userInfo, isLoggedIn: !!app.globalData.userInfo, theme: app.globalData.theme }); this.updateThemeStyles(); }, onShow: function() { this.updateNavigationBarColor(); }, onUnload: function() { if (this.data.timer) { clearInterval(this.data.timer); this.setData({ timer: null }); } if (app.eventBus) { app.eventBus.off('userInfoChange'); app.eventBus.off('themeChange'); } }, updateNavigationBarColor: function() { const { theme } = this.data; if (theme && theme.primaryColor && theme.textColor) { wx.setNavigationBarColor({ frontColor: theme.textColor === '#333' ? '#000000' : '#ffffff', backgroundColor: theme.primaryColor, animation: { duration: 300, timingFunc: 'easeIn' } }); } }, updateThemeStyles: function () { const { theme } = this.data; const { bgColor, textColor, primaryColor } = theme; this.setData({ containerStyle: `background-color: ${bgColor}; color: ${textColor};`, buttonStyle: `background-color: ${primaryColor}; color: ${textColor};`, timerStyle: `color: ${primaryColor};`, progressStyle: primaryColor, sliderStyle: primaryColor // 新增滑块主题色 }); this.updateNavigationBarColor(); }, slideChange: function (e) { const minutes = e.detail.value; this.setData({ time: minutes.toString(), timeStr: minutes >= 10 ? minutes + ':00' : '0' + minutes + ':00', mTime: minutes * 60 * 1000 }); }, clickCate: function (e) { this.setData({ cateActive: e.currentTarget.dataset.index, showCustomInput: false, selectedPlan: null, editMode: false }); }, showCustomTaskInput: function() { this.setData({ showCustomInput: true, cateActive: null, selectedPlan: null, customTask: '', editMode: false }); }, inputCustomTask: function(e) { this.setData({ customTask: e.detail.value }); }, savePlan: function() { if (!this.data.customTask.trim()) { wx.showToast({ title: '请输入任务内容', icon: 'none' }); return; } let updatedPlans = []; if (this.data.editMode) { updatedPlans = this.data.plans.map(plan => { if (plan.id === this.data.editPlanId) { return { ...plan, content: this.data.customTask, time: this.data.time }; } return plan; }); wx.showToast({ title: '计划更新成功', icon: 'success' }); } else { const newPlan = { id: Date.now(), content: this.data.customTask, time: this.data.time, createdAt: new Date().toISOString() }; updatedPlans = [...this.data.plans, newPlan]; wx.showToast({ title: '计划添加成功', icon: 'success' }); } this.setData({ plans: updatedPlans, customTask: '', showCustomInput: false, editMode: false, editPlanId: null }); wx.setStorageSync('plans', updatedPlans); }, selectPlan: function(e) { const { index } = e.currentTarget.dataset; const selectedPlan = this.data.plans[index]; this.setData({ time: selectedPlan.time, customTask: selectedPlan.content, cateActive: null, showCustomInput: false, selectedPlan: selectedPlan, editMode: false }); }, editPlan: function(e) { const { index } = e.currentTarget.dataset; const planToEdit = this.data.plans[index]; this.setData({ time: planToEdit.time, customTask: planToEdit.content, showCustomInput: true, editMode: true, editPlanId: planToEdit.id, selectedPlan: null }); }, deletePlan: function(e) { const { index } = e.currentTarget.dataset; const planToDelete = this.data.plans[index]; wx.showModal({ title: '确认删除', content: `确定要删除计划"${planToDelete.content}"吗?`, success: (res) => { if (res.confirm) { const updatedPlans = this.data.plans.filter((_, i) => i !== index); this.setData({ plans: updatedPlans, selectedPlan: null }); wx.setStorageSync('plans', updatedPlans); wx.showToast({ title: '计划已删除', icon: 'success' }); } } }); }, start: function () { let taskContent = ''; let time = parseInt(this.data.time); let categoryIndex = this.data.cateActive; if (this.data.selectedPlan) { taskContent = this.data.selectedPlan.content; time = parseInt(this.data.selectedPlan.time); } else if (this.data.showCustomInput && this.data.customTask) { taskContent = this.data.customTask; categoryIndex = null; } else if (this.data.cateActive !== null) { taskContent = this.data.cateArr[this.data.cateActive].text; } else { wx.showToast({ title: '请选择一个任务', icon: 'none' }); return; } this.setData({ clockShow: true, mTime: time * 60 * 1000, timeStr: time >= 10 ? time + ':00' : '0' + time + ':00', time: time.toString(), currentTask: taskContent, cateActive: categoryIndex }); this.drawBg(); this.drawActive(); this.showPhotoConfirm(); }, drawBg: function () { const lineWidth = 6 / this.data.rate; const ctx = wx.createCanvasContext('progress_bg'); const center = 250 / this.data.rate; const radius = center - 2 * lineWidth; ctx.setLineWidth(lineWidth); ctx.setStrokeStyle('#f0f0f0'); ctx.setLineCap('round'); ctx.beginPath(); ctx.arc(center, center, radius, 0, 2 * Math.PI, false); ctx.stroke(); ctx.draw(); }, drawActive: function () { const _this = this; const lineWidth = 6 / this.data.rate; const center = 250 / this.data.rate; const radius = center - 2 * lineWidth; const timer = setInterval(function () { const angle = 1.5 + 2 * (_this.data.time * 60 * 1000 - _this.data.mTime) / (_this.data.time * 60 * 1000); const currentTime = _this.data.mTime - 100; _this.setData({ mTime: currentTime }); if (angle < 3.5) { if (currentTime % 1000 == 0) { let timeStr1 = currentTime / 1000; let timeStr2 = parseInt(timeStr1 / 60); let timeStr3 = (timeStr1 - timeStr2 * 60) >= 10 ? (timeStr1 - timeStr2 * 60) : '0' + (timeStr1 - timeStr2 * 60); timeStr2 = timeStr2 >= 10 ? timeStr2 : '0' + timeStr2; _this.setData({ timeStr: timeStr2 + ':' + timeStr3 }); } const ctx = wx.createCanvasContext('progress_active'); ctx.setLineWidth(lineWidth); ctx.setStrokeStyle(_this.data.theme.primaryColor); ctx.setLineCap('round'); ctx.beginPath(); ctx.arc(center, center, radius, 1.5 * Math.PI, angle * Math.PI, false); ctx.stroke(); ctx.draw(); } else { const logs = wx.getStorageSync('logs') || []; logs.unshift({ date: util.formatTime(new Date), task: _this.data.currentTask, cate: _this.data.cateActive, time: _this.data.time }); wx.setStorageSync('logs', logs); _this.setData({ timeStr: '00:00', okShow: true, pauseShow: false, continueCancleShow: false }); clearInterval(timer); } }, 100); _this.setData({ timer: timer }); }, showPhotoConfirm: function() { wx.showModal({ title: '打卡拍照', content: '开始专注前需要拍照打卡确认', confirmText: '拍照', cancelText: '暂不', success: (res) => { if (res.confirm) { this.checkCameraPermission(); } else { // 取消拍照则重置界面 this.resetTimerUI(); wx.showToast({ title: '拍照打卡已取消', icon: 'none' }); } } }); }, checkCameraPermission: function() { wx.getSetting({ success: (res) => { if (!res.authSetting['scope.camera']) { wx.authorize({ scope: 'scope.camera', success: () => this.takePhoto(), fail: () => { wx.showModal({ title: '权限申请', content: '需要授予相机权限才能拍照打卡', success: (res) => { if (res.confirm) wx.openSetting(); else this.resetTimerUI(); // 拒绝权限则重置界面 } }); } }); } else { this.takePhoto(); } } }); }, takePhoto: function () { wx.chooseImage({ count: 1, sourceType: ['camera'], success: (res) => { const photoPath = res.tempFilePaths[0]; this.setData({ photoPath }); wx.setStorageSync('checkInPhoto', photoPath); wx.showToast({ title: '拍照成功' }); // 拍照成功后才开始计时 this.startTimer(); }, fail: (err) => { console.error('拍照失败', err); wx.showToast({ title: '拍照失败', icon: 'none' }); this.resetTimerUI(); // 拍照失败则重置界面 } }); }, startTimer: function() { // 记录开始时间和预计结束时间 const now = Date.now(); this.setData({ isTimerRunning: true, startTime: now, endTime: now + this.data.remainingTime }); // 更新计时器显示 this.updateTimer(); }, updateTimer: function() { if (!this.data.isTimerRunning) return; const now = Date.now(); let remaining = this.data.endTime - now; // 处理计时结束 if (remaining <= 0) { this.finishTimer(); return; } // 更新剩余时间显示 const minutes = Math.floor(remaining / 60000); const seconds = Math.floor((remaining % 60000) / 1000); const timeStr = `${minutes >= 10 ? minutes : '0' + minutes}:${seconds >= 10 ? seconds : '0' + seconds}`; this.setData({ remainingTime: remaining, timeStr: timeStr }); // 更新进度环 this.drawActive(); // 每秒更新一次 this.timerInterval = setTimeout(() => { this.updateTimer(); }, 1000); }, // 重置计时器UI resetTimerUI: function() { this.setData({ clockShow: false, isTimerRunning: false, photoPath: '' }); clearTimeout(this.timerInterval); }, // 完成计时 finishTimer: function() { this.setData({ isTimerRunning: false, timeStr: '00:00' }); clearTimeout(this.timerInterval); // 显示完成提示 wx.showToast({ title: '专注完成!', icon: 'success', duration: 2000 }); }, pause: function () { clearInterval(this.data.timer); this.setData({ pauseShow: false, continueCancleShow: true, okShow: false }); }, continue: function () { this.drawActive(); this.setData({ pauseShow: true, continueCancleShow: false, okShow: false }); }, cancle: function () { clearInterval(this.data.timer); this.setData({ pauseShow: true, continueCancleShow: false, okShow: false, clockShow: false }); }, ok: function () { clearInterval(this.data.timer); this.setData({ pauseShow: true, continueCancleShow: false, okShow: false, clockShow: false }); } })