修复日程提醒功能
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user