You've already forked SmartisanNote.Remake
"优化页面切换动画效果,实现无缝滑动过渡"
This commit is contained in:
33
IFLOW.md
33
IFLOW.md
@@ -14,6 +14,8 @@
|
||||
* **PWA 支持**: vite-plugin-pwa
|
||||
* **本地存储**: IndexedDB (通过 `src/utils/indexedDBStorage.js` 封装)
|
||||
* **CSS 预处理器**: Less
|
||||
* **动画库**: @oku-ui/motion
|
||||
* **拖拽库**: vue-draggable-plus
|
||||
|
||||
## 项目结构
|
||||
|
||||
@@ -21,17 +23,41 @@
|
||||
.
|
||||
├── android/ # Capacitor Android 项目文件
|
||||
├── public/ # 静态资源目录 (图标等)
|
||||
│ ├── assets/ # 应用资源文件
|
||||
│ │ └── icons/ # 图标文件
|
||||
│ │ ├── drawable-xxhdpi/ # Android 图标资源
|
||||
│ │ └── drawable-xxxhdpi/ # Android 图标资源
|
||||
│ └── icons/ # PWA 图标
|
||||
│ ├── icon-192.png
|
||||
│ └── icon-512.png
|
||||
├── src/ # 源代码目录
|
||||
│ ├── App.vue # 根组件
|
||||
│ ├── main.js # 应用入口文件
|
||||
│ ├── common/ # 通用样式
|
||||
│ │ └── base.css # 基础样式文件
|
||||
│ ├── components/ # 可复用的 UI 组件
|
||||
│ │ ├── FolderItem.vue # 文件夹项组件
|
||||
│ │ ├── FolderManage.vue # 文件夹管理组件
|
||||
│ │ ├── Header.vue # 头部组件
|
||||
│ │ ├── Modal.vue # 模态框组件
|
||||
│ │ ├── NoteItem.vue # 便签项组件
|
||||
│ │ ├── RichTextEditor.vue # 富文本编辑器组件
|
||||
│ │ ├── Search.vue # 搜索组件
|
||||
│ │ ├── SettingGroup.vue # 设置组组件
|
||||
│ │ └── SwitchButton.vue # 开关按钮组件
|
||||
│ ├── pages/ # 页面组件
|
||||
│ │ ├── FolderPage.vue # 文件夹页面
|
||||
│ │ ├── NoteEditorPage.vue # 便签编辑页面
|
||||
│ │ ├── NoteListPage.vue # 便签列表页面
|
||||
│ │ └── SettingsPage.vue # 设置页面
|
||||
│ ├── stores/ # Pinia 状态管理
|
||||
│ │ ├── useAppStore.js # 应用状态管理
|
||||
│ │ └── useAppStore.test.js # 应用状态管理测试
|
||||
│ └── utils/ # 工具函数
|
||||
│ ├── dateUtils.js # 日期工具函数
|
||||
│ └── indexedDBStorage.js # IndexedDB 存储封装
|
||||
├── index.html # 应用入口 HTML 文件
|
||||
├── .nvmdrc # node.js 版本
|
||||
├── update.txt # 更新日志
|
||||
├── package.json # 项目依赖和脚本
|
||||
├── vite.config.js # Vite 配置文件
|
||||
└── capacitor.config.json # Capacitor 配置文件
|
||||
@@ -46,6 +72,9 @@
|
||||
* **多种排序方式**: 按更新时间、标题、星标状态排序。
|
||||
* **PWA 支持**: 可安装为独立应用,支持离线使用。
|
||||
* **本地存储**: 所有数据存储在浏览器的 `IndexedDB` 中。
|
||||
* **富文本编辑**: 支持标题、待办事项、列表、加粗、引用、图片等多种格式。
|
||||
* **动画效果**: 使用 Oku Motion 实现流畅的动画效果。
|
||||
* **拖拽排序**: 支持图片拖拽排序。
|
||||
* **深色模式**: (计划中) 支持切换深色/浅色主题。
|
||||
* **云同步**: (计划中) 支持多设备间数据同步。
|
||||
|
||||
@@ -123,6 +152,8 @@ npm run android
|
||||
* **CSS 命名**: 使用 BEM 命名规范,部分使用原子化 CSS 类名(以 `code-fun-` 开头)
|
||||
* **响应式设计**: 使用 viewport 单位 (vw/vh) 和 CSS 变量实现响应式布局
|
||||
* **图标**: 使用 PNG 图片作为图标,存储在 `public/assets/icons/` 目录下
|
||||
* **动画**: 使用 Oku Motion 实现动画效果
|
||||
* **拖拽**: 使用 vue-draggable-plus 实现拖拽功能
|
||||
|
||||
### 命名规范
|
||||
|
||||
|
||||
109
README.md
Normal file
109
README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 锤子便签(重制版)
|
||||
|
||||
这是一个基于 Vue 3、Vite 和 Pinia 的移动端现代化 Web 应用,旨在重现并改进经典的锤子便签应用体验。该项目采用 PWA(渐进式 Web 应用)技术,支持离线使用和安装到主屏幕。
|
||||
|
||||
## 主要特性
|
||||
|
||||
- **便签管理**: 创建、编辑、删除、置顶、加星便签
|
||||
- **文件夹管理**: 将便签分类到不同的文件夹中
|
||||
- **搜索功能**: 按标题或内容搜索便签
|
||||
- **回收站**: 临时存储已删除的便签,支持彻底删除
|
||||
- **多种排序方式**: 按更新时间、标题、星标状态排序
|
||||
- **PWA 支持**: 可安装为独立应用,支持离线使用
|
||||
- **本地存储**: 所有数据存储在浏览器的 IndexedDB 中
|
||||
- **富文本编辑**: 支持标题、待办事项、列表、加粗、引用、图片等多种格式
|
||||
- **动画效果**: 使用 Oku Motion 实现流畅的动画效果
|
||||
- **拖拽排序**: 支持图片拖拽排序
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**: Vue 3 (Composition API)
|
||||
- **构建工具**: Vite
|
||||
- **状态管理**: Pinia
|
||||
- **路由**: Vue Router
|
||||
- **UI 组件库**: Ionic Vue (部分使用)
|
||||
- **PWA 支持**: vite-plugin-pwa
|
||||
- **本地存储**: IndexedDB (通过 `src/utils/indexedDBStorage.js` 封装)
|
||||
- **CSS 预处理器**: Less
|
||||
- **动画库**: @oku-ui/motion
|
||||
- **拖拽库**: vue-draggable-plus
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
.
|
||||
├── android/ # Capacitor Android 项目文件
|
||||
├── public/ # 静态资源目录 (图标等)
|
||||
├── src/ # 源代码目录
|
||||
│ ├── App.vue # 根组件
|
||||
│ ├── main.js # 应用入口文件
|
||||
│ ├── common/ # 通用样式
|
||||
│ ├── components/ # 可复用的 UI 组件
|
||||
│ ├── pages/ # 页面组件
|
||||
│ ├── stores/ # Pinia 状态管理
|
||||
│ └── utils/ # 工具函数
|
||||
├── index.html # 应用入口 HTML 文件
|
||||
├── package.json # 项目依赖和脚本
|
||||
├── vite.config.js # Vite 配置文件
|
||||
└── capacitor.config.json # Capacitor 配置文件
|
||||
```
|
||||
|
||||
## 开发与构建
|
||||
|
||||
### 前置条件
|
||||
|
||||
确保已安装 Node.js (>=14) 和 npm。
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 开发
|
||||
|
||||
启动开发服务器:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
这将在 `http://localhost:3000` 启动应用。
|
||||
|
||||
### 构建
|
||||
|
||||
构建标准 Web 应用:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
构建 PWA 应用:
|
||||
|
||||
```bash
|
||||
npm run build:pwa
|
||||
```
|
||||
|
||||
构建所有版本 (标准 + PWA):
|
||||
|
||||
```bash
|
||||
npm run build:all
|
||||
```
|
||||
|
||||
### 部署 PWA
|
||||
|
||||
构建 PWA 并上传到服务器:
|
||||
|
||||
```bash
|
||||
npm run deploy:pwa
|
||||
```
|
||||
|
||||
这将执行 `vite build --mode pwa` 并运行 `upload-pwa.js` 脚本。
|
||||
|
||||
### Android 应用
|
||||
|
||||
运行 Android 应用:
|
||||
|
||||
```bash
|
||||
npm run android
|
||||
```
|
||||
@@ -19,6 +19,7 @@
|
||||
"@capacitor/core": "^5.7.2",
|
||||
"@capacitor/ios": "^5.7.2",
|
||||
"@ionic/vue": "^8.7.6",
|
||||
"@oku-ui/motion": "^0.4.3",
|
||||
"@vue/cli-service": "^5.0.9",
|
||||
"@vue/compiler-sfc": "^3.5.22",
|
||||
"basic-ftp": "^5.0.5",
|
||||
|
||||
104
src/App.vue
104
src/App.vue
@@ -1,7 +1,97 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import '@/common/base.css'
|
||||
</script>
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition :name="transitionName">
|
||||
<component :is="Component" :key="route.path" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import '@/common/base.css'
|
||||
|
||||
const route = useRoute()
|
||||
const transitionName = ref('slide-left')
|
||||
|
||||
// 监听路由变化,动态设置过渡动画方向
|
||||
watch(
|
||||
() => route.path,
|
||||
(toPath, fromPath) => {
|
||||
// 特殊处理:从编辑页返回列表页的情况
|
||||
if ((fromPath.startsWith('/editor') || fromPath.startsWith('/notes/')) && toPath === '/notes') {
|
||||
transitionName.value = 'slide-right'
|
||||
return
|
||||
}
|
||||
|
||||
// 判断导航方向
|
||||
const toDepth = toPath.split('/').length
|
||||
const fromDepth = fromPath.split('/').length
|
||||
|
||||
// 如果是进入更深的页面(如从列表页进入编辑页),使用左滑动画
|
||||
if (toDepth > fromDepth) {
|
||||
transitionName.value = 'slide-left'
|
||||
}
|
||||
// 如果是返回上层页面(如从编辑页返回列表页),使用右滑动画
|
||||
else if (toDepth < fromDepth) {
|
||||
transitionName.value = 'slide-right'
|
||||
}
|
||||
// 同级页面切换使用默认的左滑动画
|
||||
else {
|
||||
transitionName.value = 'slide-left'
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.app-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #f5f5f5; // 设置默认背景色,防止闪烁
|
||||
}
|
||||
|
||||
// 左滑进入(从右到左)
|
||||
.slide-left-enter-active,
|
||||
.slide-left-leave-active,
|
||||
.slide-right-enter-active,
|
||||
.slide-right-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.slide-left-enter-from {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.slide-left-leave-to {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.slide-left-enter-to,
|
||||
.slide-left-leave-from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
// 右滑进入(从左到右)
|
||||
.slide-right-enter-from {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.slide-right-leave-to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.slide-right-enter-to,
|
||||
.slide-right-leave-from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
</style>
|
||||
|
||||
131
src/main.js
131
src/main.js
@@ -1,63 +1,68 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
|
||||
// 导入页面组件
|
||||
// 便签列表页面
|
||||
import NoteListPage from './pages/NoteListPage.vue'
|
||||
// 便签编辑页面(用于新建和编辑便签)
|
||||
import NoteEditorPage from './pages/NoteEditorPage.vue'
|
||||
// 文件夹管理页面
|
||||
import FolderPage from './pages/FolderPage.vue'
|
||||
// 设置页面
|
||||
import SettingsPage from './pages/SettingsPage.vue'
|
||||
|
||||
// 导入数据库初始化函数
|
||||
import { initDB } from './utils/indexedDBStorage'
|
||||
|
||||
// 配置路由规则
|
||||
// 定义应用的所有路由路径和对应的组件
|
||||
const routes = [
|
||||
// 根路径重定向到便签列表页面
|
||||
{ path: '/', redirect: '/notes' },
|
||||
// 便签列表页面路由
|
||||
{ path: '/notes', component: NoteListPage },
|
||||
// 编辑便签页面路由(带便签ID参数)
|
||||
{ path: '/notes/:id', component: NoteEditorPage, props: true },
|
||||
// 新建便签页面路由
|
||||
{ path: '/editor', component: NoteEditorPage },
|
||||
// 编辑便签页面路由(带便签ID参数)
|
||||
{ path: '/editor/:id', component: NoteEditorPage, props: true },
|
||||
// 文件夹管理页面路由
|
||||
{ path: '/folders', component: FolderPage },
|
||||
// 设置页面路由
|
||||
{ path: '/settings', component: SettingsPage }
|
||||
]
|
||||
|
||||
// 创建路由实例
|
||||
// 使用Hash模式以支持静态文件部署
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
// 创建并挂载Vue应用实例
|
||||
// 配置Pinia状态管理和Vue Router路由
|
||||
const app = createApp(App)
|
||||
|
||||
// 初始化数据库
|
||||
initDB().then(() => {
|
||||
console.log('数据库初始化成功')
|
||||
}).catch(error => {
|
||||
console.error('数据库初始化失败:', error)
|
||||
// 即使数据库初始化失败,也继续启动应用
|
||||
// 这样可以确保应用在没有IndexedDB支持的环境中仍然可以运行
|
||||
})
|
||||
|
||||
// 使用Pinia进行状态管理
|
||||
app.use(createPinia())
|
||||
// 使用Vue Router进行路由管理
|
||||
app.use(router)
|
||||
// 挂载应用到DOM元素
|
||||
app.mount('#app')
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { createPinia } from 'pinia'
|
||||
import { motionPlugin } from '@oku-ui/motion'
|
||||
import App from './App.vue'
|
||||
|
||||
// 导入页面组件
|
||||
// 便签列表页面
|
||||
import NoteListPage from './pages/NoteListPage.vue'
|
||||
// 便签编辑页面(用于新建和编辑便签)
|
||||
import NoteEditorPage from './pages/NoteEditorPage.vue'
|
||||
// 文件夹管理页面
|
||||
import FolderPage from './pages/FolderPage.vue'
|
||||
// 设置页面
|
||||
import SettingsPage from './pages/SettingsPage.vue'
|
||||
|
||||
// 导入数据库初始化函数
|
||||
import { initDB } from './utils/indexedDBStorage'
|
||||
|
||||
// 配置路由规则
|
||||
// 定义应用的所有路由路径和对应的组件
|
||||
const routes = [
|
||||
// 根路径重定向到便签列表页面
|
||||
{ path: '/', redirect: '/notes' },
|
||||
// 便签列表页面路由
|
||||
{ path: '/notes', component: NoteListPage },
|
||||
// 编辑便签页面路由(带便签ID参数)
|
||||
{ path: '/notes/:id', component: NoteEditorPage, props: true },
|
||||
// 新建便签页面路由
|
||||
{ path: '/editor', component: NoteEditorPage },
|
||||
// 编辑便签页面路由(带便签ID参数)
|
||||
{ path: '/editor/:id', component: NoteEditorPage, props: true },
|
||||
// 文件夹管理页面路由
|
||||
{ path: '/folders', component: FolderPage },
|
||||
// 设置页面路由
|
||||
{ path: '/settings', component: SettingsPage },
|
||||
]
|
||||
|
||||
// 创建路由实例
|
||||
// 使用Hash模式以支持静态文件部署
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
})
|
||||
|
||||
// 创建并挂载Vue应用实例
|
||||
// 配置Pinia状态管理和Vue Router路由
|
||||
const app = createApp(App)
|
||||
|
||||
// 初始化数据库
|
||||
initDB()
|
||||
.then(() => {
|
||||
console.log('数据库初始化成功')
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('数据库初始化失败:', error)
|
||||
// 即使数据库初始化失败,也继续启动应用
|
||||
// 这样可以确保应用在没有IndexedDB支持的环境中仍然可以运行
|
||||
})
|
||||
|
||||
// 使用Pinia进行状态管理
|
||||
app.use(createPinia())
|
||||
// 使用Vue Router进行路由管理
|
||||
app.use(router)
|
||||
// 使用Oku Motion插件
|
||||
app.use(motionPlugin)
|
||||
// 挂载应用到DOM元素
|
||||
app.mount('#app')
|
||||
|
||||
Reference in New Issue
Block a user