修复日程提醒功能

This commit is contained in:
2026-07-20 11:41:39 +08:00
parent 7ab6958538
commit f00ba9f9cc
19 changed files with 1735 additions and 1725 deletions
+18 -68
View File
@@ -1,17 +1,8 @@
<template>
<view class="page">
<view class="header">
<view class="header-row">
<view class="user-info">
<text class="greeting">{{ greeting }}</text>
<text class="username">{{ user?.nickname || '云泽用户' }}</text>
</view>
<view class="avatar">
<text class="avatar-text">{{ avatarText }}</text>
</view>
</view>
<view class="search-bar">
<FaIcon name="magnifying-glass" color="#909399" :size="16" />
<FaIcon name="magnifying-glass" color="#ffffff" :size="16" />
<text class="search-text">搜索功能数据...</text>
</view>
</view>
@@ -84,18 +75,6 @@ import AppTabbar from '@/components/AppTabbar.vue'
const user = ref(null)
const greeting = computed(() => {
const hour = new Date().getHours()
if (hour < 12) return '早上好'
if (hour < 18) return '下午好'
return '晚上好'
})
const avatarText = computed(() => {
const name = user.value?.nickname || '云'
return name.charAt(0).toUpperCase()
})
const noteCount = ref(0)
const scheduleCount = ref(0)
const schedulePending = ref(0)
@@ -199,60 +178,25 @@ function onActivityTap(item) {
.header {
flex-shrink: 0;
padding: calc(var(--status-bar-height, 44px) + 20rpx) $page-padding-x 24rpx;
background: $color-card;
box-shadow: $shadow-header;
}
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 28rpx;
}
.greeting {
display: block;
font-size: 26rpx;
color: $color-text-muted;
}
.username {
display: block;
font-size: 40rpx;
font-weight: 600;
color: $color-text;
margin-top: 4rpx;
}
.avatar {
width: 88rpx;
height: 88rpx;
border-radius: 50%;
background: $color-primary-bg;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-text {
font-size: 34rpx;
font-weight: 600;
color: $color-primary;
padding: calc(var(--status-bar-height, 44px) + 20rpx) $page-padding-x 20rpx;
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
box-shadow: 0 4rpx 16rpx rgba(79, 172, 254, 0.15);
position: relative;
}
.search-bar {
display: flex;
align-items: center;
gap: 16rpx;
background: $color-bg-page;
border-radius: $radius-md;
padding: 20rpx 24rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: $radius-full;
padding: 16rpx 24rpx;
border: 1rpx solid rgba(255, 255, 255, 0.3);
}
.search-text {
font-size: 28rpx;
color: $color-text-muted;
color: rgba(255, 255, 255, 0.7);
}
.main-scroll {
@@ -344,8 +288,14 @@ function onActivityTap(item) {
}
.quick-icon {
@include icon-box(88rpx);
border-radius: $radius-lg;
width: 88rpx;
height: 88rpx;
border-radius: 12rpx;
background: linear-gradient(135deg, rgba(79, 172, 254, 0.15) 0%, rgba(0, 242, 254, 0.15) 100%);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.quick-name {
+3 -3
View File
@@ -242,7 +242,7 @@ function onItemTap(item) {
transform: translateY(-50%);
width: 6rpx;
height: 36rpx;
background: $color-primary;
background: linear-gradient(180deg, #4facfe 0%, #00f2fe 100%);
border-radius: 0 4rpx 4rpx 0;
}
}
@@ -270,10 +270,10 @@ function onItemTap(item) {
border-radius: $radius-md;
&.active {
background: $color-primary-bg;
background: linear-gradient(135deg, rgba(79, 172, 254, 0.08) 0%, rgba(0, 242, 254, 0.08) 100%);
.item-name {
color: $color-primary;
color: #4facfe;
}
}
+77 -21
View File
@@ -22,7 +22,7 @@
</view>
</view>
<view class="main-scroll">
<view class="main-scroll" style="margin-top:10rpx">
<view class="menu-group" v-for="(group, gIndex) in menuGroups" :key="gIndex">
<view
class="menu-item"
@@ -167,26 +167,55 @@ function handleLogout() {
.profile-header {
flex-shrink: 0;
padding: calc(var(--status-bar-height, 44px) + 20rpx) $page-padding-x 36rpx;
background: $color-primary;
padding: calc(var(--status-bar-height, 44px) + 24rpx) $page-padding-x 40rpx;
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
position: relative;
overflow: hidden;
}
.profile-header::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 400rpx;
height: 400rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
pointer-events: none;
}
.profile-header::after {
content: '';
position: absolute;
bottom: -30%;
left: -20%;
width: 350rpx;
height: 350rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
pointer-events: none;
}
.profile-info {
display: flex;
align-items: center;
gap: 24rpx;
position: relative;
z-index: 1;
}
.profile-avatar {
width: 112rpx;
height: 112rpx;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
border: 4rpx solid rgba(255, 255, 255, 0.35);
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.15) 100%);
border: 3rpx solid rgba(255, 255, 255, 0.4);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.15);
}
.avatar-text {
@@ -231,9 +260,11 @@ function handleLogout() {
.profile-stats {
display: flex;
justify-content: space-around;
margin-top: 36rpx;
padding-top: 28rpx;
margin-top: 40rpx;
padding-top: 32rpx;
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
}
.profile-stat {
@@ -263,13 +294,21 @@ function handleLogout() {
@include card;
margin-bottom: 24rpx;
overflow: hidden;
border-radius: 16rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
transition: box-shadow 0.3s ease;
}
.menu-group:active {
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
}
.menu-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 24rpx;
padding: 32rpx 28rpx;
transition: background 0.2s ease, transform 0.2s ease;
& + & {
border-top: 1rpx solid $color-divider;
@@ -277,6 +316,7 @@ function handleLogout() {
&:active {
background: $color-bg-page;
transform: translateX(4rpx);
}
}
@@ -289,11 +329,19 @@ function handleLogout() {
}
.menu-icon {
@include icon-box(64rpx);
width: 56rpx;
height: 56rpx;
border-radius: 12rpx;
background: linear-gradient(135deg, rgba(79, 172, 254, 0.1) 0%, rgba(0, 242, 254, 0.1) 100%);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.menu-title {
font-size: 28rpx;
font-size: 30rpx;
font-weight: 500;
color: $color-text;
}
@@ -306,31 +354,39 @@ function handleLogout() {
.menu-badge {
font-size: 20rpx;
font-weight: 600;
color: #ffffff;
background: $color-primary;
padding: 4rpx 12rpx;
border-radius: $radius-full;
min-width: 32rpx;
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
padding: 6rpx 14rpx;
border-radius: 12rpx;
min-width: 36rpx;
text-align: center;
line-height: 1.4;
box-shadow: 0 4rpx 12rpx rgba(79, 172, 254, 0.3);
}
.logout-btn {
@include card;
height: 96rpx;
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8rpx;
margin-bottom: 12rpx;
border-radius: 16rpx;
background: linear-gradient(135deg, rgba(245, 108, 108, 0.1) 0%, rgba(245, 108, 108, 0.05) 100%);
border: 1rpx solid rgba(245, 108, 108, 0.2);
transition: all 0.3s ease;
}
.logout-btn-hover {
background: $color-bg-page !important;
background: linear-gradient(135deg, rgba(245, 108, 108, 0.2) 0%, rgba(245, 108, 108, 0.1) 100%) !important;
box-shadow: 0 4rpx 16rpx rgba(245, 108, 108, 0.15) !important;
}
.logout-text {
font-size: 30rpx;
color: $color-text-secondary;
font-size: 32rpx;
font-weight: 600;
color: #f56c6c;
pointer-events: none;
}
+18 -87
View File
@@ -15,29 +15,14 @@
<view class="field content-field">
<text class="field-label">内容</text>
<view class="editor-wrap">
<editor
id="noteEditor"
class="note-editor"
:placeholder="'记录你的想法...'"
:value="form.content"
@ready="onEditorReady"
@input="onEditorInput"
@focus="editorFocused = true"
@blur="editorFocused = false"
/>
<view class="editor-toolbar">
<view
v-for="btn in toolbarBtns"
:key="btn.name"
class="toolbar-btn"
:class="{ active: btn.active }"
@tap="execCommand(btn)"
>
<text>{{ btn.label }}</text>
</view>
</view>
</view>
<editor
id="noteEditor"
class="note-editor"
:placeholder="'记录你的想法...'"
:value="form.content"
@ready="onEditorReady"
@input="onEditorInput"
/>
</view>
<view class="switch-row" @tap="form.pinned = !form.pinned">
@@ -71,7 +56,6 @@ import { formatDate } from '@/utils/date.js'
const noteId = ref('')
const saving = ref(false)
const editorCtx = ref(null)
const editorFocused = ref(false)
const form = reactive({
title: '',
content: '',
@@ -81,28 +65,6 @@ const meta = reactive({
updatedAt: 0
})
const toolbarBtns = reactive([
{ name: 'bold', label: 'B', active: false, value: 'bold' },
{ name: 'italic', label: 'I', active: false, value: 'italic' },
{ name: 'underline', label: 'U', active: false, value: 'underline' },
{ name: 'strike', label: 'S', active: false, value: 'strikeThrough' },
{ name: 'header', label: 'H', active: false, value: 'header' },
{ name: 'list', label: '•', active: false, value: 'insertUnorderedList' },
{ name: 'indent', label: '→', active: false, value: 'indent' },
{ name: 'outdent', label: '←', active: false, value: 'outdent' },
{ name: 'divider', label: '—', active: false, value: 'insertHorizontalRule' },
])
onLoad(async (query) => {
if (query?.id) {
noteId.value = query.id
uni.setNavigationBarTitle({ title: '编辑笔记' })
await loadNote(query.id)
} else {
uni.setNavigationBarTitle({ title: '新建笔记' })
}
})
function onEditorReady() {
uni.createSelectorQuery().select('#noteEditor').context((res) => {
if (res && res.context) {
@@ -118,14 +80,15 @@ function onEditorInput(e) {
form.content = e.detail.html || ''
}
function execCommand(btn) {
if (!editorCtx.value) return
if (btn.name === 'header') {
editorCtx.value.format('header', 'H2')
onLoad(async (query) => {
if (query?.id) {
noteId.value = query.id
uni.setNavigationBarTitle({ title: '编辑笔记' })
await loadNote(query.id)
} else {
editorCtx.value.format(btn.value)
uni.setNavigationBarTitle({ title: '新建笔记' })
}
}
})
async function loadNote(id) {
try {
@@ -246,17 +209,9 @@ function onDelete() {
flex: 1;
display: flex;
flex-direction: column;
padding-bottom: 0;
padding: 24rpx 28rpx 0 28rpx;
overflow: hidden;
}
.editor-wrap {
background: $color-bg-page;
border-radius: $radius-md;
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
border-top: 1rpx solid $color-divider;
}
.note-editor {
@@ -268,32 +223,8 @@ function onDelete() {
color: $color-text;
box-sizing: border-box;
overflow-y: auto;
}
.editor-toolbar {
display: flex;
flex-wrap: wrap;
gap: 8rpx;
padding: 16rpx 20rpx;
border-top: 1rpx solid $color-divider;
background: $color-card;
}
.toolbar-btn {
padding: 10rpx 20rpx;
border-radius: $radius-sm;
font-size: 24rpx;
color: $color-text-secondary;
background: $color-bg-page;
&.active {
color: $color-primary;
background: $color-primary-bg;
}
&:active {
opacity: 0.7;
}
border-radius: $radius-md;
}
.placeholder {
+95 -3
View File
@@ -65,6 +65,38 @@
</view>
</picker>
</view>
<view class="field">
<text class="field-label">重复提醒间隔分钟</text>
<view class="number-input">
<button class="number-btn" @tap="form.repeatIntervalMinutes = Math.max(0, form.repeatIntervalMinutes - 1)" :disabled="isCompleted"></button>
<input
v-model.number="form.repeatIntervalMinutes"
type="number"
class="number-field"
:disabled="isCompleted"
@blur="form.repeatIntervalMinutes = Math.max(0, Math.min(1440, form.repeatIntervalMinutes))"
/>
<button class="number-btn" @tap="form.repeatIntervalMinutes = Math.min(1440, form.repeatIntervalMinutes + 1)" :disabled="isCompleted">+</button>
</view>
<text class="form-tip">0表示不重复仅发送一次</text>
</view>
<view class="field">
<text class="field-label">最多提醒次数</text>
<view class="number-input">
<button class="number-btn" @tap="form.maxSendCount = Math.max(1, form.maxSendCount - 1)" :disabled="isCompleted"></button>
<input
v-model.number="form.maxSendCount"
type="number"
class="number-field"
:disabled="isCompleted"
@blur="form.maxSendCount = Math.max(1, Math.min(100, form.maxSendCount))"
/>
<button class="number-btn" @tap="form.maxSendCount = Math.min(100, form.maxSendCount + 1)" :disabled="isCompleted">+</button>
</view>
<text class="form-tip">最多发送的提醒次数</text>
</view>
</view>
<view v-if="!isCompleted" class="footer">
@@ -99,7 +131,9 @@ const form = reactive({
date: '',
time: '',
remindChannels: ['app'],
remindMinutes: 15
remindMinutes: 0,
repeatIntervalMinutes: 0,
maxSendCount: 1
})
const remindIndex = computed(() => {
@@ -134,7 +168,9 @@ async function loadSchedule(id) {
form.date = formatDate(d)
form.time = `${pad(d.getHours())}:${pad(d.getMinutes())}`
form.remindChannels = [...(item.remindChannels || [])]
form.remindMinutes = item.remindMinutes ?? 15
form.remindMinutes = item.remindMinutes ?? 0
form.repeatIntervalMinutes = item.repeatIntervalMinutes ?? 0
form.maxSendCount = item.maxSendCount ?? 1
isCompleted.value = !!item.completed
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
@@ -200,7 +236,9 @@ async function saveData(datetime) {
content: form.content,
datetime,
remindChannels: [...form.remindChannels],
remindMinutes: form.remindMinutes
remindMinutes: form.remindMinutes,
repeatIntervalMinutes: form.repeatIntervalMinutes,
maxSendCount: form.maxSendCount
}
try {
if (scheduleId.value) {
@@ -334,6 +372,60 @@ async function saveData(datetime) {
}
}
.number-input {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 12rpx;
}
.number-btn {
width: 60rpx;
height: 60rpx;
border-radius: $radius-md;
background: $color-bg-page;
border: 1rpx solid $color-divider;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: $color-text;
flex-shrink: 0;
&:active:not(:disabled) {
background: $color-primary-bg;
color: $color-primary;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.number-field {
flex: 1;
height: 60rpx;
padding: 0 16rpx;
background: $color-bg-page;
border: 1rpx solid $color-divider;
border-radius: $radius-md;
font-size: 28rpx;
color: $color-text;
text-align: center;
&:disabled {
opacity: 0.5;
}
}
.form-tip {
display: block;
font-size: 22rpx;
color: $color-text-muted;
margin-top: 8rpx;
}
.footer {
position: fixed;
left: 0;