vue全家桶实现笔记本功能

一个通过vue实现的练手小项目,数据保存和导出通过node进行处理

成品截图:

安装vue-cli,webpack:

cnpm install webpack -g

cnpm install vue-cli -g

通过vue-cli搭建项目:

需要使用vuex管理数据,添加store文件夹,最终目录结构:

----vue_notes

|--components

|--router

|--store

编辑入口文件 main.js

//引入Vue
import Vue from ‘vue‘
//引入vuere-source,该组件为网络请求组件
import VueResource from "vue-resource"
//引入store,vuex对象
import store from ‘./store‘
//引入入口页面
import App from ‘./App‘
//使用vue-resource中间件挂载网络请求组件
Vue.use(VueResource);

/* 实例化vue项目 */
new Vue({
    el: ‘#app‘,
    store,
    ...App
})

vuex出口文件 store/index.js

import Vue from ‘vue‘
import Vuex from ‘vuex‘
import mutations from ‘./mutations‘
import actions from ‘./actions‘
import getters from ‘./getters‘

//添加vuex中间件
Vue.use(Vuex);

//使用数据结构
const state = {
    isAllList: true,
    notes: [],
    activeNote: {},
}

export default new Vuex.Store({
    state,
    mutations,
    actions,
    getters,
})

vuex方法声明 /store/mutation-types.js

//修改日记列表状态,是否查看全部
export const changeListStatus = "changeListStatus";
//新增日记
export const addNote = "addNote";
//编辑当前日记
export const editNote = "editNote";
//删除所选日记
export const deleteNote = "deleteNote";
//切换收藏状态
export const toggleFavorite = "toggleFavorite";
//切换当前日记
export const setActiveNote = "setActiveNote";
//初始化日记数据
export const initNotes = ‘initNotes‘;
//编辑当前日记标题
export const eidtNoteTitle = ‘eidtNoteTitle‘;

store/mutations.js 声明方法

import * as types from ‘./mutation-types‘

export default {
    [types.changeListStatus](state, bool) {
        state.isAllList = bool;
    },
    [types.addNote](state) {
        const newNote = {
            text: ‘New note‘,
            title: ‘New‘,
            favorite: !state.isAllList,
            _rm: Math.random(),
        }
        state.notes.push(newNote);
        state.activeNote = newNote;
    },
    [types.editNote](state, text) {
        state.activeNote.text = text;
    },
    [types.deleteNote](state) {
        let rm = state.activeNote[‘_rm‘];
        let index = state.notes.findIndex(function(v, i) {
            if(rm == v[‘_rm‘]) return true;
            return false;
        });
        if(index >= 0) state.notes.splice(index, 1);
        state.activeNote = state.notes[0] || {};
    },
    [types.toggleFavorite](state) {
        state.activeNote[‘favorite‘] = !state.activeNote[‘favorite‘]
    },
    [types.setActiveNote](state, note) {
        state.activeNote = note;
    },
    [types.initNotes](state, notes) {
        for(let i of notes.notes) {
            if(i._rm === notes.activeNote._rm) {
                notes.activeNote = i;
                break;
            }
        }
        state.isAllList = notes.isAllList;
        state.notes = notes.notes;
        state.activeNote = notes.activeNote;
        window.state = state;
    },
    [types.eidtNoteTitle](state, title) {
        state.activeNote.title = title;
    }
}

/store/actions.js 声明异步方法

import * as types from ‘./mutation-types‘

export default {
    [types.changeListStatus]({ commit }, { bool }) {
        commit(‘changeListStatus‘, bool);
    },
    [types.addNote]({ commit }) {
        commit(‘addNote‘);
    },
    [types.editNote]({ commit }, { text }) {
        commit(‘editNote‘, text);
    },
    [types.deleteNote]({ commit }) {
        commit(‘deleteNote‘);
    },
    [types.toggleFavorite]({ commit }) {
        commit(‘toggleFavorite‘);
    },
    [types.setActiveNote]({ commit }, { note }) {
        commit(‘setActiveNote‘, note);
    },
    [types.initNotes]({ commit }, { notes }) {
        commit(‘initNotes‘, notes);
    },
    [types.eidtNoteTitle]({ commit }, { title }) {
        commit(‘eidtNoteTitle‘, title);
    }
}

/store/getters.js 声明获取数据方法

export default {
    favoriteNotes: state => {
        return state.notes.filter((v, i) => v[‘favorite‘]);
    }
}

App.vue 外层组件

<template>
    <div id="app">
        <toolbar></toolbar>
        <notes-list></notes-list>
        <notes-editor></notes-editor>
    </div>
</template>

<script>
    import Vue from ‘vue‘
    import { mapActions, mapState } from ‘vuex‘

    import Toolbar from "./components/Toolbar.vue";
    import NotesList from "./components/NotesList.vue";
    import NotesEditor from "./components/NotesEditor.vue";

    export default {
        name: ‘app‘,
        components: {
            Toolbar,
            NotesList,
            NotesEditor
        },
        computed: {
//引入vuex状态生成对应计算属性
            ...mapState({
                isAllList: state => state.isAllList,
                notes: state => state.notes,
                activeNote: state => state.activeNote,
            })
        },
//钩子方法,创建dom之前抓取数据进行初始化
        beforeCreate() {
            this.$http.get(‘/test.action‘).then(function(res) {
                return res.json();
            }).then((data) => this.initNotes({notes: data}));
        },
        methods: {
//引入vuex initNotes方法
            ...mapActions([‘initNotes‘]),
            save() {
                this.$http.post(‘/save.action‘, this.$store.state).then((res) => res.json()).then((data) => console.log(data));
            }
        },
//监听数据变化,如果出现变化,重新保存到后台
        watch: {
            ‘isAllList‘: function() {
                return this.save;
            },
            ‘notes‘: function() {
                return this.save;
            },
            ‘activeNote‘: {
                handler: function() {
                    return this.save;
                },
                deep: true
            },
        }
    }
</script>

Toolbar.vue 操作日记按钮组件

<template>
    <div id="toolbar">
        <i class="glyphicon glyphicon-plus" @click="addNote"></i>
        <i class="glyphicon glyphicon-star" :class="{starred: activeNote[‘favorite‘]}" @click="toggleFavorite"></i>
        <i class="glyphicon glyphicon-remove" @click="deleteNote"></i>
        <i class="glyphicon glyphicon-save" @click="down"></i>
    </div>
</template>

<script>
    import { mapState, mapActions } from "Vuex";

     export default {
        computed: {
            ...mapState({
                activeNote: state => state.activeNote,
            })
        },
        methods: {
            ...mapActions({
                addNote: ‘addNote‘,
                toggleFavorite: ‘toggleFavorite‘,
                deleteNote: ‘deleteNote‘
            }),
            down() {
                window.open(‘/down.action‘, ‘_blank‘);
            }
        }
    }
</script>

 NodeList.vue 日记列表组件

<template>
    <div id="notes-list">
        <div id="list-header">
            <h2>Notes | coligo</h2>
            <div class="btn-group btn-group-justified" role="group">
                <div class="btn-group" role="group">
                    <button type="button" class="btn btn-default" :class="{active:isAllList}" @click="changeStatus(‘isAll‘)"> All Notes </button>
                </div>
                <div class="btn-group" role="group">
                    <button type="button" class="btn btn-default" :class="{active:!isAllList}" @click="changeStatus(‘isFavorite‘)"> Favorites </button>
                </div>
            </div>
        </div>
        <div id="container">
            <div class="list-group">
                <a class="list-group-item" href="javascript:;" v-for="(v,k) in list" :class="{active: v[‘_rm‘]==activeNote[‘_rm‘]}" @click="setActiveNote({note:v})">
                    <h4 class="list-group-item-heading">{{ v[‘title‘].length>10 ? v[‘title‘].substring(0,10) + "..." : v[‘title‘] }}</h4>
                </a>
            </div>
        </div>
    </div>
</template>

<script>
    import { mapState, mapGetters, mapActions } from "Vuex";

    export default {
        data() {
            return {
                list: [],
            }
        },
        computed: {
            ...mapState({
                isAllList: state => state.isAllList,
                notes: state => state.notes,
                activeNote: state => state.activeNote,
            }),
            ...mapGetters({
                favoriteNotes: ‘favoriteNotes‘,
            }),
        },
        methods: {
            ...mapActions({
                setActiveNote: ‘setActiveNote‘,
                changeListStatus: ‘changeListStatus‘,
            }),
            changeStatus(s) {
                if(s == ‘isAll‘) {
                    this.changeListStatus({ bool: true });
                } else if(s == ‘isFavorite‘) {
                    this.changeListStatus({ bool: false });
                }
            },
            changeList() {
                if(this.isAllList) {
                    this.$data.list = this.notes;
                } else {
                    this.$data.list = this.favoriteNotes;
                }
            },
        },
        watch: {
            notes: function() {
                this.changeList();
            },
            isAllList: function() {
                this.changeList();
            },
        },
        mounted: function() {
//数据更新重新更新this.$data中数据再执行dom更新
            this.$nextTick(function() {
                this.$data.list = this.notes;
            });
        }
    }
</script>

<style>

</style>

NotesEditor 编辑框组件

<template>
    <div id="note-editor">
        <input type="text" v-model="textTitle" @change="eidtNoteTitle({title: textTitle})" />
        <textarea class="form-control" v-model="textVal" @change="editNote({text: textVal})"></textarea>
    </div>
</template>

<script>
    import { mapState, mapActions } from "Vuex";

    export default {
        data() {
            return {
                textVal: "",
                textTitle: ""
            }
        },
        computed: {
            ...mapState({
                activeNote: state => state.activeNote,
            })
        },
        methods: {
            ...mapActions({
                editNote: ‘editNote‘,
                eidtNoteTitle: ‘eidtNoteTitle‘
            }),
        },
        watch: {
            activeNote: function() {
                this.$data.textVal = this.activeNote[‘text‘];
                this.$data.textTitle = this.activeNote[‘title‘];
            },
        }
    }
</script>

服务端功能,服务端文件build/dev-server.js

//引入文件操作模块
var fs = require(‘fs‘);
//post解码模块
var bodyParser = require(‘body-parser‘);
// 创建 application/x-www-form-urlencoded 编码解析
var urlencodedParser = bodyParser.urlencoded({ extended: false });
//使用中间件解码
app.use(bodyParser.json());
app.use(urlencodedParser);
//查询数据
app.all(‘/test.action‘, function(req, res) {
    fs.readFile(‘./static/data/data.json‘, ‘utf-8‘, function(err, data) {
        res.json(JSON.parse(data));
    })
})
//保存数据
app.all(‘/save.action‘, function(req, res) {
    fs.writeFile(‘./static/data/data.json‘, JSON.stringify(req.body, null, ‘\t‘), function(err) {
        if(err) {
            console.log(err);
            res.json({satus: 400});
        } else {
            res.json({satus: 200});
        }
    })
})
//保存功能,将json文件读取解析为txt文件,然后发送到前端下载
app.all(‘/down.action‘, function(req, res) {
    fs.readFile(‘./static/data/data.json‘, ‘utf-8‘, function(err, data) {
        if(err) {
            res.json({status: 500});
        } else {
            let string = [];
            let jsonData = JSON.parse(data);
            for(let i of jsonData.notes) {
                string.push(`##${i.title}##\r\n${i.text}`)
            }
            fs.writeFile(‘./static/data/down.txt‘, string.join(`\r\n******************************************************\r\n`), function(err) {
                if(err) {
                    res.json({status: 500});
                } else {
                    res.download(‘./static/data/down.txt‘, ‘notes.txt‘);
                }
            })
        }
    })
})

代码已上传至github

时间: 2024-08-11 17:01:21

vue全家桶实现笔记本功能的相关文章

项目- Vue全家桶实战去哪网App

最近在学习Vue,花了几天时间跟着做了这个项目,算是对学习Vue入门的一个总结,欢迎同学们star 去哪网APP ?? 项目演示地址:http://118.25.39.84 基于 Vue 全家桶 (2.x) 制作的 去哪网APP项目,项目完整.功能完备.UI美观.交互一流. 点击查看效果 技术栈 [前端] Vue:构建用户界面的 MVVM 框架,核心是响应的数据绑定和组系统件 vue-router:为单页面应用提供的路由系统,项目上线前使用了路由懒加载技术,来异步加载路由优化性能 vuex:Vu

前端工程师晋升课程 Vue全家桶+SSR+Koa2全栈开发美团网

第1章 课程导学这门课主讲以Vue SSR+Koa2全栈技术为目标,最终实现美团网项目.本章节旨在告诉大家我们会用到哪些技能.教学方法.课程内容分布.学习方法等.备注:我们会涉及Vue2.5.Nuxt.Koa2.element-ui.Mongodb等 1-1 课程导学第2章 Vue基础知识整个SSR部分都是用的Vue框架,需要给初级用户讲解Vue的基础语法,不会让他们在学习实战的时候感到迷茫,这个章节会通过vue-cli搭建一个简单的demo,让大家快速的掌握Vue的基础应用,即使他没有学习过.

【实战】Vue全家桶(vue + axios + vue-router + vuex)搭建移动端H5项目

使用Vue全家桶开发移动端页面. 本博文默认已安装node.js. github链接 一.准备工作 安装vue npm install vue 安装脚手架vue-cli npm install -g @vue/cli 创建webpack项目 vue init webpack my-app 运行 cd my-app npm run dev 按照提示,在浏览器打开http://localhost:8082/,效果如下: 安装状态管理vuex npm install vuex --save-dev 目

Vue全家桶高仿小米商城

大家好,我是河畔一角,时隔半年再次给大家带来一门重量级的实战课程:<Vue全家桶高仿小米商城>,现在很多公司都在参与到商城的构建体系当中,因此掌握一套商城的标准开发体系非常重要:商城的开始时非常复杂的,包括众多的页面设计和交互,以及非常丰富的知识点,所以一旦学会商城开发,其它系统完全不在话下. 下面给大家介绍一下小米商城包含的知识点和内容: 商城的页面流程: 商城组件部分: NavHeader(导航头组件).NavFooter(导航底部组件).ServiceBar(服务条组件).Product

Vue全家桶实战 从零独立开发企业级电商系统

Vue全家桶实战 从零独立开发企业级电商系统(免费升级Vue3.0)现阶段,电商系统随处可见,具有很大市场潜力:同时因为商城系统复杂,涉及到丰富的知识点,如果能进行电商系统的开发,其它各类型的前端系统也能掌握.本课程以Vue全家桶作为主要的技术体系,模拟小米商城,带大家从0开始开发网页和交互功能.你能进行完整的项目架构.体会页面开发的全流程.学习丰富的技术栈和各类组件知识,还能了解Git.动画.开发调试等方面的知识.同时项目本身具有很强的实用性,稍作修改,便能"为我所用".相信此课程能

从零开始系列之vue全家桶(3)安装使用vuex

什么是vuex? vuex:Vue提供的状态管理工具,用于同一管理我们项目中各种数据的交互和重用,存储我们需要用到数据对象. 即data中属性同时有一个或几个组件同时使用,就是data中共用的属性. 中大型单页应用必备. 小型单页应用完全可以不用. 安装: 1.cd 项目  (如cd my-vue)后,输入 cnpm install vuex --save 2.在src下新建一个名叫store的文件夹,与App.vue同级,并在文件夹下新建store.js文件.因为store.js是基于vue的

vue全家桶和react全家桶

vue全家桶:vue  +  vuex (状态管理)  + vue-router (路由) + vue-resource +axios +elementui react全家桶 : react + redux(状态管理) +react-router(路由) + axios + antd || antd-model 原文地址:https://www.cnblogs.com/aibabel/p/11827851.html

vue 全家桶介绍

Vue有著名的全家桶系列,包含了vue-router(http://router.vuejs.org),vuex(http://vuex.vuejs.org), vue-resource(https://github.com/pagekit/vue-resource).再加上构建工具vue-cli,sass样式,就是一个完整的vue项目的核心构成. 概括起来就是:.1.项目构建工具.2.路由.3.状态管理.4.http请求工具. 下面单独介绍 前言:Vue两大核心思想:组件化和数据驱动.组件化:

vue全家桶(Vue+Vue-router+Vuex+axios)(Vue+webpack项目实战系列之二)

Vue有多优秀搭配全家桶做项目有多好之类的咱就不谈了,直奔主题. 一.Vue 系列一已经用vue-cli搭建了Vue项目,此处就不赘述了. 二.Vue-router Vue的路由,先献上文档(https://router.vuejs.org/zh-cn/). 路由在全家桶里面定位是什么呢,创建单页应用!简单!我们知道Vuejs是一系列的组件组成应用,既然是组件那么就需要组合起来,将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们! 我们一般在