vue全家桶实现笔记本功能

Posted timmer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于vue全家桶实现笔记本功能的主要内容,如果未能解决你的问题,请参考以下文章

Vue全家桶之组件化开发

Vue全家桶

万字长文 Vue全家桶从入门到实战,超详细笔记整理 ( 三 ) (建议收藏)

vue全家桶

万字长文 Vue全家桶从入门到实战,超详细笔记整理 ( 二 ) (建议收藏)

Vue全家桶之组件化开发