Vue 中如何实现仿印象笔记的页面设计?

介绍

印象笔记是一家非常受欢迎的记笔记工具,实现类似的页面设计可以让我们的应用更加用户友好。Vue 提供了很多方便的工具,让我们能够快速地构建现代化的应用程序。在这篇文章中,我们将探讨 Vue 中如何实现仿印象笔记的页面设计。

技术栈

在开始之前,我们需要先介绍一些技术栈:

Vue:一个用于构建用户界面的渐进式框架

Vuex:一个用于 Vue 应用程序的状态管理模式

Vue Router:一个用于 Vue 应用程序的路由管理器

ElementUI:一个基于 Vue 的组件库,提供了丰富的 UI 组件和功能

Moment.js:一个 JavaScript 日期处理库,用于格式化日期和时间

页面结构

印象笔记的页面结构可分为三个部分:侧边栏、笔记列表和笔记详情。我们将使用 Vue、Vuex 和 Vue Router 来实现这个结构。

侧边栏

我们将侧边栏放在应用程序的左侧,用于管理笔记本和标签。

// Sidebar.vue

<template>

<div class="sidebar">

<div class="sidebar-header">

<img src="./assets/logo.png" alt="logo">

<h1>我的笔记</h1>

</div>

<div class="sidebar-body">

<ul class="sidebar-nav">

<li v-for="notebook in notebooks" :key="notebook.id">

<router-link :to="'/notebook/' + notebook.id">

{{ notebook.name }}

</router-link>

</li>

</ul>

<hr>

<h2>标签</h2>

<ul class="sidebar-nav">

<li v-for="tag in tags" :key="tag.id">

<router-link :to="'/tag/' + tag.id">

{{ tag.name }}

</router-link>

</li>

</ul>

</div>

</div>

</template>

<script>

import { mapGetters } from 'vuex';

export default {

computed: {

...mapGetters(['notebooks', 'tags'])

}

};

</script>

<style scoped>

.sidebar {

width: 200px;

height: 100%;

background-color: #f7f7f7;

display: flex;

flex-direction: column;

}

.sidebar-header {

padding: 20px;

border-bottom: 1px solid #e7e7e7;

display: flex;

align-items: center;

}

.sidebar-header img {

margin-right: 10px;

width: 40px;

height: 40px;

}

.sidebar-header h1 {

font-size: 18px;

margin: 0;

}

.sidebar-body {

flex: 1;

padding: 20px;

overflow-y: auto;

}

.sidebar-nav {

list-style: none;

margin: 0;

padding: 0;

}

.sidebar-nav li {

margin: 10px 0;

}

</style>

在上面的代码中,我们使用了 Vuex 的 mapGetters 辅助函数来获取笔记本和标签的列表。

笔记列表

笔记列表位于侧边栏下方,用于显示笔记本或标签下的所有笔记。

// NoteList.vue

<template>

<div class="note-list">

<div class="note-list-header">

<h2>{{ title }}</h2>

</div>

<div class="note-list-body">

<div v-if="loading" class="note-list-loading">

加载中...

</div>

<div v-if="notes.length">

<ul class="note-list-items">

<li v-for="note in notes" :key="note.id">

<router-link :to="'/note/' + note.id">

<h3>{{ note.title }}</h3>

<div v-html="note.body"></div>

<p><strong>修改时间:</strong>{{ note.updatedAt }}</p>

</router-link>

</li>

</ul>

</div>

<div v-else-if="error" class="note-list-error">

加载出错,错误信息:{{ error }}

</div>

<div v-else class="note-list-empty">

没有笔记

</div>

</div>

</div>

</template>

<script>

import { mapGetters } from 'vuex';

import moment from 'moment';

export default {

props: ['type', 'id'],

computed: {

...mapGetters(['notes', 'loading', 'error']),

title() {

if (this.type === 'notebook') {

const notebook = this.notebooks.find(notebook => notebook.id === this.id);

return notebook ? notebook.name : '未知笔记本';

} else if (this.type === 'tag') {

const tag = this.tags.find(tag => tag.id === this.id);

return tag ? tag.name : '未知标签';

} else {

return '';

}

},

filteredNotes() {

if (this.type === 'notebook') {

return this.notes.filter(note => note.notebookId === this.id);

} else if (this.type === 'tag') {

return this.notes.filter(note => note.tags.includes(this.id));

} else {

return [];

}

}

},

filters: {

formatDate(value, format = 'YYYY-MM-DD HH:mm:ss') {

return moment(value).format(format);

}

}

};

</script>

<style scoped>

.note-list {

width: 100%;

height: 100%;

display: flex;

flex-direction: column;

}

.note-list-header {

padding: 20px;

border-bottom: 1px solid #e7e7e7;

}

.note-list-header h2 {

margin: 0;

font-size: 18px;

}

.note-list-body {

flex: 1;

padding: 20px;

overflow-y: auto;

}

.note-list-loading,

.note-list-error,

.note-list-empty {

text-align: center;

}

.note-list-items {

list-style: none;

margin: 0;

padding: 0;

}

.note-list-items li {

margin: 20px 0;

}

.note-list-items h3 {

font-size: 18px;

margin: 0;

}

.note-list-items p {

font-size: 12px;

margin: 10px 0;

}

.note-list-items p strong {

font-weight: 600;

}

</style>

在上面的代码中,我们使用了 props 接收父组件传递的笔记本或标签的 ID 和类型。然后,我们使用 Vuex 的 mapGetters 辅助函数获取笔记列表、加载状态和错误信息。在 computed 属性中,我们定义了一个 title 方法返回标题,一个 filteredNotes 方法返回过滤后的笔记列表。在 filters 中,我们定义了 formatDate 方法来格式化日期。

笔记详情

笔记详情位于笔记列表的右侧,用于显示选中的笔记的详细信息。

// NoteDetail.vue

<template>

<div class="note-detail">

<div class="note-detail-header">

<h1>{{ note.title }}</h1>

<p><strong>创建时间:</strong>{{ note.createdAt | formatDate }}</p>

<p><strong>修改时间:</strong>{{ note.updatedAt | formatDate }}</p>

</div>

<div v-html="note.body" class="note-detail-body"></div>

</div>

</template>

<script>

import { mapGetters } from 'vuex';

import moment from 'moment';

export default {

computed: {

...mapGetters(['note']),

},

filters: {

formatDate(value, format = 'YYYY-MM-DD HH:mm:ss') {

return moment(value).format(format);

}

}

};

</script>

<style scoped>

.note-detail {

width: 100%;

height: 100%;

display: flex;

flex-direction: column;

}

.note-detail-header {

padding: 20px;

border-bottom: 1px solid #e7e7e7;

}

.note-detail-header h1 {

margin: 0;

font-weight: 600;

font-size: 24px;

}

.note-detail-header p {

font-size: 12px;

margin: 10px 0;

}

.note-detail-header p strong {

font-weight: 600;

}

.note-detail-body {

flex: 1;

padding: 20px;

overflow-y: auto;

}

</style>

在上面的代码中,我们使用了 Vuex 的 mapGetters 辅助函数获取选中的笔记。在 filters 中,我们定义了 formatDate 方法来格式化日期。

应用程序

现在,我们已经将侧边栏、笔记列表和笔记详情都创建好了,下面是如何将它们组合到一起的。

// App.vue

<template>

<div id="app">

<sidebar />

<div class="main">

<router-view/>

</div>

</div>

</template>

<script>

import Sidebar from './components/Sidebar.vue';

export default {

name: 'App',

components: {

Sidebar

}

};

</script>

<style scoped>

#app {

height: 100%;

display: flex;

}

.main {

flex: 1;

height: 100%;

overflow-y: auto;

}

</style>

在上面代码中,我们在 App.vue 中放置了 Sidebar 组件,并在 .main div 中使用了 标签用于显示笔记列表和笔记详情。在下面的代码中,我们定义了路由表。

// router.js

import Vue from 'vue';

import VueRouter from 'vue-router';

import NoteList from './components/NoteList.vue';

import NoteDetail from './components/NoteDetail.vue';

Vue.use(VueRouter);

const routes = [

{ path: '/', redirect: '/notebook/1' },

{ path: '/notebook/:id', component: NoteList, props: { type: 'notebook' } },

{ path: '/tag/:id', component: NoteList, props: { type: 'tag' } },

{ path: '/note/:id', component: NoteDetail },

];

const router = new VueRouter({

routes,

});

export default router;

在上面的代码中,我们定义了三个路由:一个用于显示笔记本下的笔记,一个用于显示标签下的笔记,一个用于显示笔记详情。我们使用了 props 属性传递了笔记本或标签的 ID 和类型。

总结

在本文中,我们学习了如何使用 Vue、Vuex 和 Vue Router 实现仿印象笔记的页面设计。我们创建了侧边栏、笔记列表和笔记详情三个组件,并在 App.vue 中使用了这些组件。最后,我们定义了路由表以将组件集成到应用程序中。