一个通过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全家桶实现笔记本功能的更多相关文章

  1. 使用vue全家桶制作博客网站

    前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用vue全家桶制作的博客网站 概述 该项目是基于vue全家桶(vue.vue-router.vuex.v ...

  2. 转载: 使用vue全家桶制作博客网站 HTML5 移动网站制作的好教程

    使用vue全家桶制作博客网站   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用vue全家桶制作的博客网站 概述 该项目是基于vue全家桶(vue. ...

  3. Vue全家桶介绍

    一直不清楚全家桶是什么玩意,上网搜了一下,才知道就是平时项目中使用的几个依赖包,下面分享一下 Vue 全家桶介绍 Vue有著名的全家桶系列,包含了vue-router(http://router.vu ...

  4. 一个简单的假vue全家桶(vue+vue-router+require)

    首先说明我觉得这是一个比较好理解的vue全家桶(虽然是假的),模块化也是用require来做的,而且如果后期有必要压缩我也会用gulp来做 1.依赖个个本地模块,require只是用来载入page,这 ...

  5. Vue 全家桶 + Electron 开发的一个跨三端的应用

    代码地址如下:http://www.demodashi.com/demo/11738.html GitHub Repo:vue-objccn Follow: halfrost · GitHub 利用 ...

  6. 升级vue全家桶过程记录

    背景 如果你使用了element-ui的el-tabs组件,并且想要单独升级element-ui至2.10.0,你会发现,使用了el-tabs组件的页面只要打开就卡死.原因是element-ui~2. ...

  7. [在线+源码]vue全家桶+Typescript开发一款习惯养成APP

    # vue-ts-daily 基于Vue.js的2.5.13版本和TypeScript编写的模仿原生应用的WebApp. [源码地址](https://github.com/xiaomuzhu/vue ...

  8. Vue全家桶高仿小米商城

    大家好,我是河畔一角,时隔半年再次给大家带来一门重量级的实战课程:<Vue全家桶高仿小米商城>,现在很多公司都在参与到商城的构建体系当中,因此掌握一套商城的标准开发体系非常重要:商城的开始 ...

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

    使用Vue全家桶开发移动端页面. 本博文默认已安装node.js. github链接 一.准备工作 安装vue npm install vue 安装脚手架vue-cli npm install -g ...

随机推荐

  1. Ubbeditor的使用

    简单介绍: 作为一种放在客户端文本编辑器,此时不能支持将JS.Html代码直接发送给服务器,这样将会给服务器带来极大的危险,比如UMEditor(富文本编辑器),它的使用需要关闭服务器端的代码检查的, ...

  2. HDU 4745 Two Rabbits(区间DP,最长非连续回文子串)

    Two Rabbits Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) Total ...

  3. NSString 属性为啥用copy 不用strong

     copy不能修改,strong可以修改,防止字符串被意外修改.demo: ——————————————————code 你要的 demo—————————————————— @property (n ...

  4. 小米范工具系列之一:小米范 web查找器

    最新版本1.5,下载地址:http://pan.baidu.com/s/1c1NDSVe  文件名web finder,请使用java1.8运行 小米范 web查找器主要功能为快速端口扫描,并识别we ...

  5. day11(jsp入门&Cookie&HttpSession&一次性图片校验码)

    day11 JSP入门   1 JSP概述 1.1 什么是JSP JSP(Java Server Pages)是JavaWeb服务器端的动态资源.它与html页面的作用是相同的,显示数据和获取数据. ...

  6. go-003-基础语法

    1.行分隔符 一行代表一个语句结束. 如果一行多个,使用“;”分割,不推荐使用,建议使用默认一行一个语句 2.标识符 标识符用来命名变量.类型等程序实体.一个标识符实际上就是一个或是多个字母(A~Z和 ...

  7. Zabbix设置自定义监控

    [zabbix]自定义监控项key值   说明: zabbix自带的默认模版里包括了很多监控项,有时候为了满足业务需求,需要根据自己的监控项目自定义监控项,这里介绍一种自定义监控项的方式. 1,首先编 ...

  8. Linux相关知识总结

    查看CPU使用情况 查看内存 ps命令显示所有运行中的进程等命令 top 命令用来显示CPU的使用情况free命令用来显示内存的使用情况 select和epoll区别select,poll,epoll ...

  9. 手把手教你学node.js之使用 superagent 与 cheerio 完成简单爬虫

    使用 superagent 与 cheerio 完成简单爬虫 目标 建立一个 lesson 3 项目,在其中编写代码. 当在浏览器中访问 http://localhost:3000/ 时,输出 CNo ...

  10. CodeForces - 366C Dima and Salad (01背包)

    题意:n件东西,有属性a和属性b.要选取若干件东西,使得\(\frac{\sum a_j}{\sum b_j} = k\).在这个条件下,问\(\sum a_j\)最大是多少. 分析:可以将其转化为0 ...