├── .babelrc ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── data ├── debug.log ├── note.db ├── note_book.db ├── note_category.db ├── plan.db ├── plan_category.db └── user.db ├── logo.png ├── logo.svg ├── package.json ├── pages ├── bg.jpg ├── logo-small.png ├── screen-shot.png ├── screen-shot2.png └── screen-shot3.png ├── script ├── build.js ├── dev.es.js ├── dev.js ├── pack.es.js └── pack.js ├── src ├── App.vue ├── assets │ ├── demo-files │ │ ├── demo.css │ │ └── demo.js │ ├── demo.html │ ├── fonts │ │ ├── icomoon.eot │ │ ├── icomoon.svg │ │ ├── icomoon.ttf │ │ └── icomoon.woff │ ├── style.css │ ├── tui-editor-contents.css │ ├── tui-editor.css │ ├── user.svg │ └── w-ui │ │ ├── themify.eot │ │ ├── themify.svg │ │ ├── themify.ttf │ │ └── themify.woff ├── auto-updater.js ├── components │ ├── Editor.vue │ ├── IconText.vue │ ├── Split │ │ ├── Split.vue │ │ ├── SplitPanel.vue │ │ └── emitter.js │ ├── Today.vue │ ├── editor.css │ ├── note │ │ └── NoteList.vue │ ├── plan │ │ ├── Day.vue │ │ ├── DayDetail.vue │ │ ├── DayDone.vue │ │ ├── Month.vue │ │ ├── MonthList.vue │ │ ├── Season.vue │ │ ├── SeasonList.vue │ │ ├── Tags.vue │ │ ├── Week.vue │ │ ├── WeekList.vue │ │ ├── Welcome.vue │ │ ├── Year.vue │ │ └── YearList.vue │ └── select │ │ ├── Item.vue │ │ ├── Select.js │ │ ├── index.js │ │ └── select.less ├── index.html ├── index.js ├── logo.png ├── logo.svg ├── main.js ├── models │ ├── BaseModel.js │ ├── Note.js │ ├── NoteBook.js │ ├── NoteCategory.js │ ├── Plan.js │ ├── db.js │ └── model.js ├── pages │ ├── Note.vue │ ├── NoteDesk.vue │ ├── Plan.vue │ ├── note.less │ ├── noteDesk.less │ └── plan.less ├── routers.js ├── services │ ├── NoteService.js │ ├── PlanService.js │ ├── data.js │ ├── dbUtil.js │ ├── index.js │ ├── note.js │ └── plan.js └── store │ └── index.js ├── yarn-error.log └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ] 11 | ], 12 | "plugins": [ 13 | "transform-vue-jsx", 14 | "transform-object-rest-spread" 15 | ] 16 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | pack/ 3 | dist/ 4 | .cache/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![image](./pages/logo-small.png) 2 | 3 | [![Github All Releases](https://img.shields.io/github/downloads/lin-xi/mark/total.svg)](https://github.com/lin-xi/mark/releases) 4 | ![GitHub issues](https://img.shields.io/github/issues/lin-xi/mark.svg) 5 | ![GitHub closed issues](https://img.shields.io/github/issues-closed/lin-xi/mark.svg) 6 | ![Gitter](https://img.shields.io/gitter/room/lin-xi/mark.svg) 7 | 8 | # [Mark](https://lin-xi.github.io/mark/) 2.0.2 9 | 10 | https://lin-xi.github.io/mark/ 11 | 12 | mark 是一个 mac 下的免费开源的 markdown 编辑程序,使用[tui.editor](https://nhnent.github.io/tui.editor/)和 electron 构建。 13 | 14 | tui.editor(TOAST UI Editor)是一款所见即所得的 Markdown 编辑器。TOAST UI Editor 提供 Markdown 模式和 WYSIWYG 模式。它的功能非常强大,你可以编辑表格,UML 图和图表等。 15 | 16 | ### TOAST UI Editor 的 Markdown 模式的特点有: 17 | 18 | - 所见即所得。你在编辑 Markdown 的同时,可以预览生成的 HTML 页面。 19 | 20 | - 异步滚动。可以在 Markdown 和预览之间进行异步滚动。 21 | 22 | - 列表自动缩进。 23 | 24 | - 语法高亮。 25 | 26 | ### TOAST UI Editor 的 WYSIWYG 模式的特点有: 27 | 28 | - 可以直接从浏览器,excel,powerpoint 等复制内容并进行粘贴。 29 | 30 | - 支持 170+种语言的语法高亮。 31 | 32 | - 支持表格。 33 | 34 | # 下载安装 35 | 36 | [Mark 2.0.2](https://github.com/lin-xi/mark/releases/download/2.0.2/Mark-2.0.2.dmg_02.zip) 37 | 38 | [历史版本](https://github.com/lin-xi/mark/releases) 39 | 40 | # 主要特色 41 | 42 | - 计划 todo 管理 43 | - 支持天,周,月,季,年级别的 todo 定制 44 | - note,笔记管理 45 | - category, 支持分类 46 | - save data on local disk,本地数据存储,支持导入导出数据文件 47 | 48 | ![image](./pages/screen-shot.png) 49 | ![image](./pages/screen-shot2.png) 50 | ![image](./pages/screen-shot3.png) 51 | 52 | Mark 致力于做好用的效率提升工具,欢迎交流并提出宝贵意见 53 | 54 | # 发布日志 Release Log 55 | 56 | ## 2.0.2 57 | 58 | - [x] 【功能】UI 简化 59 | - [x] 【功能】笔记页面,列表可拖动调整大小 60 | - [x] 【bug】修复 bug 61 | 62 | ## 2.0.0 63 | 64 | - [x] 【功能】新增笔记本管理 65 | - [x] 【功能】简化 todo 66 | 67 | ## 1.1.3 68 | 69 | - [x] 【功能】修改 note 页面分类默认折叠 70 | - [x] 【功能】优化笔记写作界面,写作空间更大 71 | - [x] 【功能】增加分类删除功能 72 | - [x] 【功能】优化可拖动区域 73 | - [x] 【功能】markdown 模式字体大小优化 74 | - [x] 【bug】添加新分类展示类 bug 修复 75 | 76 | ## 1.1.2 77 | 78 | - [x] 【功能】新增折叠列表功能 79 | - [x] 【bug】修复 markdown 模式下,同步滚动的问题 80 | - [x] 【功能】添加文字颜色编辑功能 81 | - [x] 【功能】窗口默认宽度修改为 1200 82 | 83 | ## 1.1.1 84 | 85 | - [x] 【bug】修复 logo 不透明 86 | 87 | ## 1.1.0 88 | 89 | bug 修复 90 | 91 | - [x] 【bug】列表区没有设置滚动区域 92 | - [x] 【bug】导出数据,点击取消报错 93 | - [x] 【bug】Note 页面,编辑器没处理 window resize 事件 94 | 95 | # 致谢 Special thanks 96 | 97 | [electron](https://github.com/electron/electron) 98 | 99 | [tui.editor](https://github.com/nhnent/tui.editor) 100 | 101 | [nedb](https://github.com/louischatriot/nedb) 102 | -------------------------------------------------------------------------------- /data/note.db: -------------------------------------------------------------------------------- 1 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"生命周期\n\n
\n
\n","createTime":1530524407093,"subject":"生命周期","_id":"EfkWQNqC4Ef8p2P3"} 2 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"6K5qYy04WliSR9P5","content":"模版解析","createTime":1530526407603,"subject":"模版解析","_id":"H6OHLk4ec3D6THpY"} 3 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"属性变更监听","createTime":1530524202772,"subject":"属性变更监听","_id":"R6CrVXma8YSAGw7N","updateTime":1530524732246} 4 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"6K5qYy04WliSR9P5","content":"属性变更监听","createTime":1530524725771,"subject":"属性变更监听","_id":"YUHs91lShsXu0XWy"} 5 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"模版解析","createTime":1530524392938,"subject":"模版解析","_id":"dutIOlhdI9iu8UT5","updateTime":1530879168405} 6 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"6K5qYy04WliSR9P5","content":"事件绑定","createTime":1530526431118,"subject":"事件绑定","_id":"jSPq2qB2tXRauywK"} 7 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"6K5qYy04WliSR9P5","content":"生命周期","createTime":1530526413634,"subject":"生命周期","_id":"kNxl7ZfwpgUOOXRT"} 8 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"KsupKLP6BQ1bnOhR","content":"MVVM 模式\n\n
\n
\n","createTime":1530523507343,"subject":"MVVM 模式","_id":"q3bkFL9PhktZIVKA","updateTime":1530784450294} 9 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"","content":"前言","createTime":1530676224853,"subject":"前言","_id":"sQygs5KQ5srb7YlB"} 10 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":"事件绑定","_id":"zWTB3SpWz6cNNWqv","updateTime":1530784382983} 11 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"事件绑定hahha\n\n事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":"事件绑定hahha","_id":"zWTB3SpWz6cNNWqv","updateTime":1595940346479} 12 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"事件绑定hahha\n\n事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":"事件绑定hahha","_id":"zWTB3SpWz6cNNWqv","updateTime":1595940348052} 13 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"### 事件绑定hahha\n
\n事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":"### 事件绑定hahha","_id":"zWTB3SpWz6cNNWqv","updateTime":1595940364894} 14 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"### 事件绑定hahha\n
\n事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":" 事件绑定hahha","_id":"zWTB3SpWz6cNNWqv","updateTime":1595940685982} 15 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"### 事件绑定\n
\n事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":" 事件绑定","_id":"zWTB3SpWz6cNNWqv","updateTime":1595940696096} 16 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"### 事件绑定\n
\n事件绑定\n事件绑定\n事件绑定\n事件绑定\n事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":" 事件绑定","_id":"zWTB3SpWz6cNNWqv","updateTime":1595940867830} 17 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"K55ge5fyWthqIFSj","content":"### 事件绑定\n
\n事件绑定\n\n> 事件绑定\n\n事件绑定\n事件绑定\n事件绑定\n\n
\n
\n
\n
\n","createTime":1530524434926,"subject":" 事件绑定","_id":"zWTB3SpWz6cNNWqv","updateTime":1595941015807} 18 | {"bookId":"X8VijvhMpzMhfQDs","categoryId":"","content":"","createTime":1595943078781,"subject":"","_id":"vHFCSH6DA4AlyOqu"} 19 | -------------------------------------------------------------------------------- /data/note_book.db: -------------------------------------------------------------------------------- 1 | {"createTime":1529853667577,"name":"编程","_id":"BOnyROnW0IihyuO4"} 2 | {"createTime":1529854951646,"name":"Vue高级编程","_id":"X8VijvhMpzMhfQDs"} 3 | -------------------------------------------------------------------------------- /data/note_category.db: -------------------------------------------------------------------------------- 1 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1530526966759,"name":"第八章 数据管理","_id":"4GpisVuPAvmS3n3n","updateTime":1530529754890} 2 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1530524689009,"name":"第三章 源码对比解析","_id":"6K5qYy04WliSR9P5","updateTime":1530529724774} 3 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1530525899771,"name":"第六章 虚拟DOM解析","_id":"7dO50yGRdd7E7LmP","updateTime":1530529790595} 4 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1529900900913,"name":"第二章 自制MVVM框架","_id":"K55ge5fyWthqIFSj","updateTime":1530526788620} 5 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1529900471898,"name":"第一章 MVVM设计模式","_id":"KsupKLP6BQ1bnOhR","updateTime":1595932404534} 6 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1530525992322,"name":"第七章 路由解析","_id":"imvoI3Cdr24p8buq","updateTime":1530529744512} 7 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1530525829872,"name":"第五章 编译机制","_id":"o7XjoV6VQ4KczlwP","updateTime":1530529734695} 8 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1530524887314,"name":"第四章 渲染机制","_id":"v8TiwLBfUBetAAqL","updateTime":1530529730098} 9 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1530527008360,"name":"第九章 编程优化","_id":"ykdlYSyYr7LFB6jy","updateTime":1530529759933} 10 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1595940898629,"name":"第十章 性能优化","_id":"FMPpsU7azsrwTgyM"} 11 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1595941050719,"name":"第十一张 案例实战","_id":"HEKLX8yGhuamXZUf"} 12 | {"bookId":"X8VijvhMpzMhfQDs","createTime":1595941307702,"name":"第十三章 附录","_id":"wwKpUCkAy5EXyB7F"} 13 | -------------------------------------------------------------------------------- /data/plan.db: -------------------------------------------------------------------------------- 1 | {"content":"","createTime":1595931280749,"status":1,"subject":"dadfda","tags":[],"_id":"56oYMHOBzYB8MvxL","updateTime":1595931428903} 2 | {"content":"","createTime":1530977037084,"status":true,"subject":"lll","tags":["测试"],"_id":"863mo6V24ahmEEIP","updateTime":1595913448485} 3 | {"content":"高级编程\n高级编程2","createTime":1530977188303,"status":1,"subject":"发发发","tags":["Vue高级编程"],"_id":"T2A9Ck7tA0CYQzcE","updateTime":1595928920588} 4 | {"content":"","createTime":1595913528875,"status":true,"subject":"ddddd","tags":[],"_id":"fJRZ0VvK2k72urZ5","updateTime":1595913534032} 5 | {"content":"","createTime":1530976998842,"status":true,"subject":"第一个任务","tags":["测试2"],"_id":"qIbO3Nua09S0uEET","updateTime":1595913542299} 6 | {"content":"","createTime":1530977013507,"status":false,"subject":"第二个任务","tags":[],"_id":"r0MnBG1vBEywT4Qp","updateTime":1595913539352} 7 | {"content":"江湖见","createTime":1595913735900,"status":1,"subject":"江湖见","tags":["江湖"],"_id":"z5WwQxKqIAC223MQ","updateTime":1595929461551} 8 | -------------------------------------------------------------------------------- /data/plan_category.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/data/plan_category.db -------------------------------------------------------------------------------- /data/user.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/data/user.db -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/logo.png -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mark", 3 | "version": "2.0.2", 4 | "description": "mark is a markdown editor for mac", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "parcel watch src/index.html", 8 | "build": "NODE_ENV=production parcel build src/index.html --public-url ./ --target electron --no-cache", 9 | "dev": "rimraf ./dist && node script/dev.js", 10 | "electron": "electron . --debug", 11 | "pack": "rimraf ./dist && rimraf ./pack && node script/pack.js", 12 | "unpack": "asar extract /Users/baidu/work/workspace/javascript/github/mark/pack/mac/Mark.app/Contents/Resources/app.asar app" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/lin-xi/mark.git" 17 | }, 18 | "keywords": [ 19 | "markdown", 20 | "editor", 21 | "note", 22 | "mac" 23 | ], 24 | "author": "lin-xi", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/lin-xi/mark/issues" 28 | }, 29 | "homepage": "https://github.com/lin-xi/mark#readme", 30 | "devDependencies": { 31 | "@vue/component-compiler-utils": "^2.1.0", 32 | "asar": "^0.14.2", 33 | "babel-cli": "^6.26.0", 34 | "css-loader": "^0.28.10", 35 | "electron": "9.1.1", 36 | "electron-builder": "^22.7.0", 37 | "electron-packager": "^15.0.0", 38 | "less": "^3.5.3", 39 | "opn": "^5.2.0", 40 | "parcel-bundler": "^1.9.4", 41 | "rimraf": "^2.6.2", 42 | "vue-hot-reload-api": "^2.3.0", 43 | "vue-template-compiler": "^2.5.13" 44 | }, 45 | "dependencies": { 46 | "@toast-ui/editor": "^2.3.1-alpha", 47 | "@toast-ui/editor-plugin-chart": "^1.0.0", 48 | "@toast-ui/editor-plugin-code-syntax-highlight": "^1.0.0", 49 | "@toast-ui/editor-plugin-color-syntax": "^1.0.1", 50 | "@toast-ui/editor-plugin-table-merged-cell": "^1.1.1", 51 | "@toast-ui/editor-plugin-uml": "^1.0.0", 52 | "adm-zip": "^0.4.7", 53 | "babel-core": "^6.26.0", 54 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 55 | "babel-plugin-syntax-jsx": "^6.18.0", 56 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 57 | "babel-plugin-transform-runtime": "^6.23.0", 58 | "babel-plugin-transform-vue-jsx": "^3.7.0", 59 | "babel-preset-env": "^1.6.1", 60 | "babel-register": "^6.26.0", 61 | "better-sqlite3": "^7.1.0", 62 | "codemirror": "^5.34.0", 63 | "electron-logger": "^0.0.3", 64 | "fs-extra": "^5.0.0", 65 | "highlight.js": "^9.12.0", 66 | "moment": "^2.20.1", 67 | "nedb": "^1.8.0", 68 | "node-fetch": "^2.0.0", 69 | "split.js": "^1.6.2", 70 | "tui-editor": "^1.4.10", 71 | "uuid": "^3.2.1", 72 | "vue": "^2.5.13", 73 | "vue-router": "^3.0.1", 74 | "w-ui": "^1.1.17" 75 | }, 76 | "prettier": { 77 | "singleQuote": false, 78 | "semi": true 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /pages/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/pages/bg.jpg -------------------------------------------------------------------------------- /pages/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/pages/logo-small.png -------------------------------------------------------------------------------- /pages/screen-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/pages/screen-shot.png -------------------------------------------------------------------------------- /pages/screen-shot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/pages/screen-shot2.png -------------------------------------------------------------------------------- /pages/screen-shot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/pages/screen-shot3.png -------------------------------------------------------------------------------- /script/build.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/script/build.js -------------------------------------------------------------------------------- /script/dev.es.js: -------------------------------------------------------------------------------- 1 | import Parcel from 'parcel-bundler' 2 | import path from 'path' 3 | import electron from 'electron' 4 | 5 | 6 | const option = { 7 | watch: true, 8 | // target: 'electron', 9 | publicUrl: './', 10 | cache: true, 11 | hmr: false 12 | } 13 | 14 | let index = new Parcel(path.join(__dirname, '../src/index.html'), option) 15 | index.bundle().then(() => { 16 | console.log('>> done') 17 | var proc = require('child_process') 18 | 19 | var child = proc.spawn(electron, ['.', '--debug'], { 20 | stdio: 'inherit' 21 | }) 22 | child.on('close', function (code) { 23 | process.exit(code) 24 | }) 25 | }) -------------------------------------------------------------------------------- /script/dev.js: -------------------------------------------------------------------------------- 1 | require("babel-register"); 2 | require("./dev.es.js"); 3 | -------------------------------------------------------------------------------- /script/pack.es.js: -------------------------------------------------------------------------------- 1 | import Parcel from 'parcel-bundler' 2 | import path from 'path' 3 | import fs from 'fs-extra' 4 | const builder = require('electron-builder') 5 | 6 | const option = { 7 | watch: false, 8 | cache: false, 9 | // target: 'electron', 10 | publicUrl: './', 11 | hmr: false 12 | } 13 | 14 | let index = new Parcel(path.join(__dirname, '../src/index.html'), option) 15 | index.bundle().then(() => { 16 | fs.copySync(path.join(__dirname, '../logo.png'), path.join(__dirname, '../dist/logo.png')) 17 | fs.copySync(path.join(__dirname, '../logo.svg'), path.join(__dirname, '../dist/logo.svg')) 18 | fs.copySync(path.join(__dirname, '../src/index.js'), path.join(__dirname, '../dist/index.js')) 19 | fs.copySync(path.join(__dirname, '../src/auto-updater.js'), path.join(__dirname, '../dist/auto-updater.js')) 20 | fs.copySync(path.join(__dirname, '../src/services'), path.join(__dirname, '../dist/services')) 21 | fs.copySync(path.join(__dirname, '../package.json'), path.join(__dirname, '../dist/package.json')) 22 | 23 | console.log('>> done') 24 | console.log('>> start pack...') 25 | 26 | builder.build({ 27 | config: { 28 | 'appId': 'com.mark.app', 29 | 'productName': 'Mark', 30 | 'directories': { 31 | 'output': path.join(__dirname, '../pack') 32 | }, 33 | 'mac': { 34 | 'target': [ 35 | { 36 | 'target': 'dmg' 37 | } 38 | ], 39 | 'icon': path.join(__dirname, '../dist/logo.png') 40 | } 41 | } 42 | }).then(() => { 43 | console.log('>> success packed') 44 | process.exit() 45 | }) 46 | .catch((error) => { 47 | console.error(error) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /script/pack.js: -------------------------------------------------------------------------------- 1 | require("babel-register"); 2 | require("./pack.es.js"); -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 146 | 147 | 290 | -------------------------------------------------------------------------------- /src/assets/demo-files/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | font-family: sans-serif; 5 | font-size: 1em; 6 | line-height: 1.5; 7 | color: #555; 8 | background: #fff; 9 | } 10 | h1 { 11 | font-size: 1.5em; 12 | font-weight: normal; 13 | } 14 | small { 15 | font-size: .66666667em; 16 | } 17 | a { 18 | color: #e74c3c; 19 | text-decoration: none; 20 | } 21 | a:hover, a:focus { 22 | box-shadow: 0 1px #e74c3c; 23 | } 24 | .bshadow0, input { 25 | box-shadow: inset 0 -2px #e7e7e7; 26 | } 27 | input:hover { 28 | box-shadow: inset 0 -2px #ccc; 29 | } 30 | input, fieldset { 31 | font-family: sans-serif; 32 | font-size: 1em; 33 | margin: 0; 34 | padding: 0; 35 | border: 0; 36 | } 37 | input { 38 | color: inherit; 39 | line-height: 1.5; 40 | height: 1.5em; 41 | padding: .25em 0; 42 | } 43 | input:focus { 44 | outline: none; 45 | box-shadow: inset 0 -2px #449fdb; 46 | } 47 | .glyph { 48 | font-size: 16px; 49 | width: 15em; 50 | padding-bottom: 1em; 51 | margin-right: 4em; 52 | margin-bottom: 1em; 53 | float: left; 54 | overflow: hidden; 55 | } 56 | .liga { 57 | width: 80%; 58 | width: calc(100% - 2.5em); 59 | } 60 | .talign-right { 61 | text-align: right; 62 | } 63 | .talign-center { 64 | text-align: center; 65 | } 66 | .bgc1 { 67 | background: #f1f1f1; 68 | } 69 | .fgc1 { 70 | color: #999; 71 | } 72 | .fgc0 { 73 | color: #000; 74 | } 75 | p { 76 | margin-top: 1em; 77 | margin-bottom: 1em; 78 | } 79 | .mvm { 80 | margin-top: .75em; 81 | margin-bottom: .75em; 82 | } 83 | .mtn { 84 | margin-top: 0; 85 | } 86 | .mtl, .mal { 87 | margin-top: 1.5em; 88 | } 89 | .mbl, .mal { 90 | margin-bottom: 1.5em; 91 | } 92 | .mal, .mhl { 93 | margin-left: 1.5em; 94 | margin-right: 1.5em; 95 | } 96 | .mhmm { 97 | margin-left: 1em; 98 | margin-right: 1em; 99 | } 100 | .mls { 101 | margin-left: .25em; 102 | } 103 | .ptl { 104 | padding-top: 1.5em; 105 | } 106 | .pbs, .pvs { 107 | padding-bottom: .25em; 108 | } 109 | .pvs, .pts { 110 | padding-top: .25em; 111 | } 112 | .unit { 113 | float: left; 114 | } 115 | .unitRight { 116 | float: right; 117 | } 118 | .size1of2 { 119 | width: 50%; 120 | } 121 | .size1of1 { 122 | width: 100%; 123 | } 124 | .clearfix:before, .clearfix:after { 125 | content: " "; 126 | display: table; 127 | } 128 | .clearfix:after { 129 | clear: both; 130 | } 131 | .hidden-true { 132 | display: none; 133 | } 134 | .textbox0 { 135 | width: 3em; 136 | background: #f1f1f1; 137 | padding: .25em .5em; 138 | line-height: 1.5; 139 | height: 1.5em; 140 | } 141 | #testDrive { 142 | display: block; 143 | padding-top: 24px; 144 | line-height: 1.5; 145 | } 146 | .fs0 { 147 | font-size: 16px; 148 | } 149 | .fs1 { 150 | font-size: 24px; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /src/assets/demo-files/demo.js: -------------------------------------------------------------------------------- 1 | if (!('boxShadow' in document.body.style)) { 2 | document.body.setAttribute('class', 'noBoxShadow'); 3 | } 4 | 5 | document.body.addEventListener("click", function(e) { 6 | var target = e.target; 7 | if (target.tagName === "INPUT" && 8 | target.getAttribute('class').indexOf('liga') === -1) { 9 | target.select(); 10 | } 11 | }); 12 | 13 | (function() { 14 | var fontSize = document.getElementById('fontSize'), 15 | testDrive = document.getElementById('testDrive'), 16 | testText = document.getElementById('testText'); 17 | function updateTest() { 18 | testDrive.innerHTML = testText.value || String.fromCharCode(160); 19 | if (window.icomoonLiga) { 20 | window.icomoonLiga(testDrive); 21 | } 22 | } 23 | function updateSize() { 24 | testDrive.style.fontSize = fontSize.value + 'px'; 25 | } 26 | fontSize.addEventListener('change', updateSize, false); 27 | testText.addEventListener('input', updateTest, false); 28 | testText.addEventListener('change', updateTest, false); 29 | updateSize(); 30 | }()); 31 | -------------------------------------------------------------------------------- /src/assets/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/src/assets/fonts/icomoon.eot -------------------------------------------------------------------------------- /src/assets/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/src/assets/fonts/icomoon.ttf -------------------------------------------------------------------------------- /src/assets/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/src/assets/fonts/icomoon.woff -------------------------------------------------------------------------------- /src/assets/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icomoon'; 3 | src: url('./fonts/icomoon.eot'); 4 | src: url('./fonts/icomoon.eot') format('embedded-opentype'), 5 | url('./fonts/icomoon.ttf') format('truetype'), 6 | url('./fonts/icomoon.woff') format('woff'), 7 | url('./fonts/icomoon.svg') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | [class^="icon-"], [class*=" icon-"] { 13 | /* use !important to prevent issues with browser extensions that change fonts */ 14 | font-family: 'icomoon' !important; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | 22 | /* Better Font Rendering =========== */ 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | .icon-activity:before { 28 | content: "\e900"; 29 | } 30 | .icon-airplay:before { 31 | content: "\e901"; 32 | } 33 | .icon-alert-circle:before { 34 | content: "\e902"; 35 | } 36 | .icon-alert-octagon:before { 37 | content: "\e903"; 38 | } 39 | .icon-alert-triangle:before { 40 | content: "\e904"; 41 | } 42 | .icon-align-center:before { 43 | content: "\e905"; 44 | } 45 | .icon-align-justify:before { 46 | content: "\e906"; 47 | } 48 | .icon-align-left:before { 49 | content: "\e907"; 50 | } 51 | .icon-align-right:before { 52 | content: "\e908"; 53 | } 54 | .icon-anchor:before { 55 | content: "\e909"; 56 | } 57 | .icon-aperture:before { 58 | content: "\e90a"; 59 | } 60 | .icon-archive:before { 61 | content: "\e9b0"; 62 | } 63 | .icon-arrow-down:before { 64 | content: "\e9b1"; 65 | } 66 | .icon-arrow-down-circle:before { 67 | content: "\e9b2"; 68 | } 69 | .icon-arrow-down-left:before { 70 | content: "\e9b3"; 71 | } 72 | .icon-arrow-down-right:before { 73 | content: "\e9b4"; 74 | } 75 | .icon-arrow-left:before { 76 | content: "\e9b5"; 77 | } 78 | .icon-arrow-left-circle:before { 79 | content: "\e90b"; 80 | } 81 | .icon-arrow-right:before { 82 | content: "\e90c"; 83 | } 84 | .icon-arrow-right-circle:before { 85 | content: "\e90d"; 86 | } 87 | .icon-arrow-up:before { 88 | content: "\e90e"; 89 | } 90 | .icon-arrow-up-circle:before { 91 | content: "\e90f"; 92 | } 93 | .icon-arrow-up-left:before { 94 | content: "\e910"; 95 | } 96 | .icon-arrow-up-right:before { 97 | content: "\e911"; 98 | } 99 | .icon-at-sign:before { 100 | content: "\e912"; 101 | } 102 | .icon-award:before { 103 | content: "\e913"; 104 | } 105 | .icon-bar-chart:before { 106 | content: "\e914"; 107 | } 108 | .icon-bar-chart-2:before { 109 | content: "\e915"; 110 | } 111 | .icon-battery:before { 112 | content: "\e9b6"; 113 | } 114 | .icon-battery-charging:before { 115 | content: "\e9b7"; 116 | } 117 | .icon-bell:before { 118 | content: "\e9b8"; 119 | } 120 | .icon-bell-off:before { 121 | content: "\e9b9"; 122 | } 123 | .icon-bluetooth:before { 124 | content: "\e9ba"; 125 | } 126 | .icon-bold:before { 127 | content: "\e9bb"; 128 | } 129 | .icon-book:before { 130 | content: "\e916"; 131 | } 132 | .icon-book-open:before { 133 | content: "\e917"; 134 | } 135 | .icon-bookmark:before { 136 | content: "\e918"; 137 | } 138 | .icon-box:before { 139 | content: "\e919"; 140 | } 141 | .icon-briefcase:before { 142 | content: "\e91a"; 143 | } 144 | .icon-calendar:before { 145 | content: "\e91b"; 146 | } 147 | .icon-camera:before { 148 | content: "\e91c"; 149 | } 150 | .icon-camera-off:before { 151 | content: "\e91d"; 152 | } 153 | .icon-cast:before { 154 | content: "\e91e"; 155 | } 156 | .icon-check:before { 157 | content: "\e91f"; 158 | } 159 | .icon-check-circle:before { 160 | content: "\e920"; 161 | } 162 | .icon-check-square:before { 163 | content: "\e9bc"; 164 | } 165 | .icon-chevron-down:before { 166 | content: "\e9bd"; 167 | } 168 | .icon-chevron-left:before { 169 | content: "\e9be"; 170 | } 171 | .icon-chevron-right:before { 172 | content: "\e9bf"; 173 | } 174 | .icon-chevron-up:before { 175 | content: "\e9c0"; 176 | } 177 | .icon-chevrons-down:before { 178 | content: "\e9c1"; 179 | } 180 | .icon-chevrons-left:before { 181 | content: "\e921"; 182 | } 183 | .icon-chevrons-right:before { 184 | content: "\e922"; 185 | } 186 | .icon-chevrons-up:before { 187 | content: "\e923"; 188 | } 189 | .icon-chrome:before { 190 | content: "\e924"; 191 | } 192 | .icon-circle:before { 193 | content: "\e925"; 194 | } 195 | .icon-clipboard:before { 196 | content: "\e926"; 197 | } 198 | .icon-clock:before { 199 | content: "\e927"; 200 | } 201 | .icon-cloud:before { 202 | content: "\e928"; 203 | } 204 | .icon-cloud-drizzle:before { 205 | content: "\e929"; 206 | } 207 | .icon-cloud-lightning:before { 208 | content: "\e92a"; 209 | } 210 | .icon-cloud-off:before { 211 | content: "\e92b"; 212 | } 213 | .icon-cloud-rain:before { 214 | content: "\e9c2"; 215 | } 216 | .icon-cloud-snow:before { 217 | content: "\e9c3"; 218 | } 219 | .icon-code:before { 220 | content: "\e9c4"; 221 | } 222 | .icon-codepen:before { 223 | content: "\e9c5"; 224 | } 225 | .icon-command:before { 226 | content: "\e9c6"; 227 | } 228 | .icon-compass:before { 229 | content: "\e9c7"; 230 | } 231 | .icon-copy:before { 232 | content: "\e92c"; 233 | } 234 | .icon-corner-down-left:before { 235 | content: "\e92d"; 236 | } 237 | .icon-corner-down-right:before { 238 | content: "\e92e"; 239 | } 240 | .icon-corner-left-down:before { 241 | content: "\e92f"; 242 | } 243 | .icon-corner-left-up:before { 244 | content: "\e930"; 245 | } 246 | .icon-corner-right-down:before { 247 | content: "\e931"; 248 | } 249 | .icon-corner-right-up:before { 250 | content: "\e932"; 251 | } 252 | .icon-corner-up-left:before { 253 | content: "\e933"; 254 | } 255 | .icon-corner-up-right:before { 256 | content: "\e934"; 257 | } 258 | .icon-cpu:before { 259 | content: "\e935"; 260 | } 261 | .icon-credit-card:before { 262 | content: "\e936"; 263 | } 264 | .icon-crop:before { 265 | content: "\e9c8"; 266 | } 267 | .icon-crosshair:before { 268 | content: "\e9c9"; 269 | } 270 | .icon-database:before { 271 | content: "\e9ca"; 272 | } 273 | .icon-delete:before { 274 | content: "\e9cb"; 275 | } 276 | .icon-disc:before { 277 | content: "\e9cc"; 278 | } 279 | .icon-dollar-sign:before { 280 | content: "\e9cd"; 281 | } 282 | .icon-download:before { 283 | content: "\e937"; 284 | } 285 | .icon-download-cloud:before { 286 | content: "\e938"; 287 | } 288 | .icon-droplet:before { 289 | content: "\e939"; 290 | } 291 | .icon-edit:before { 292 | content: "\e93a"; 293 | } 294 | .icon-edit-2:before { 295 | content: "\e93b"; 296 | } 297 | .icon-edit-3:before { 298 | content: "\e93c"; 299 | } 300 | .icon-external-link:before { 301 | content: "\e93d"; 302 | } 303 | .icon-eye:before { 304 | content: "\e93e"; 305 | } 306 | .icon-eye-off:before { 307 | content: "\e93f"; 308 | } 309 | .icon-facebook:before { 310 | content: "\e940"; 311 | } 312 | .icon-fast-forward:before { 313 | content: "\e941"; 314 | } 315 | .icon-feather:before { 316 | content: "\e9ce"; 317 | } 318 | .icon-file:before { 319 | content: "\e9cf"; 320 | } 321 | .icon-file-minus:before { 322 | content: "\e9d0"; 323 | } 324 | .icon-file-plus:before { 325 | content: "\e9d1"; 326 | } 327 | .icon-file-text:before { 328 | content: "\e9d2"; 329 | } 330 | .icon-film:before { 331 | content: "\e9d3"; 332 | } 333 | .icon-filter:before { 334 | content: "\e942"; 335 | } 336 | .icon-flag:before { 337 | content: "\e943"; 338 | } 339 | .icon-folder:before { 340 | content: "\e944"; 341 | } 342 | .icon-folder-minus:before { 343 | content: "\e945"; 344 | } 345 | .icon-folder-plus:before { 346 | content: "\e946"; 347 | } 348 | .icon-gift:before { 349 | content: "\e947"; 350 | } 351 | .icon-git-branch:before { 352 | content: "\e948"; 353 | } 354 | .icon-git-commit:before { 355 | content: "\e949"; 356 | } 357 | .icon-git-merge:before { 358 | content: "\e94a"; 359 | } 360 | .icon-git-pull-request:before { 361 | content: "\e94b"; 362 | } 363 | .icon-github:before { 364 | content: "\e94c"; 365 | } 366 | .icon-gitlab:before { 367 | content: "\e9d4"; 368 | } 369 | .icon-globe:before { 370 | content: "\e9d5"; 371 | } 372 | .icon-grid:before { 373 | content: "\e9d6"; 374 | } 375 | .icon-hard-drive:before { 376 | content: "\e9d7"; 377 | } 378 | .icon-hash:before { 379 | content: "\e9d8"; 380 | } 381 | .icon-headphones:before { 382 | content: "\e9d9"; 383 | } 384 | .icon-heart:before { 385 | content: "\e94d"; 386 | } 387 | .icon-help-circle:before { 388 | content: "\e94e"; 389 | } 390 | .icon-home:before { 391 | content: "\e94f"; 392 | } 393 | .icon-image:before { 394 | content: "\e950"; 395 | } 396 | .icon-inbox:before { 397 | content: "\e951"; 398 | } 399 | .icon-info:before { 400 | content: "\e952"; 401 | } 402 | .icon-instagram:before { 403 | content: "\e953"; 404 | } 405 | .icon-italic:before { 406 | content: "\e954"; 407 | } 408 | .icon-layers:before { 409 | content: "\e955"; 410 | } 411 | .icon-layout:before { 412 | content: "\e956"; 413 | } 414 | .icon-life-buoy:before { 415 | content: "\e957"; 416 | } 417 | .icon-link:before { 418 | content: "\e9da"; 419 | } 420 | .icon-link-2:before { 421 | content: "\e9db"; 422 | } 423 | .icon-linkedin:before { 424 | content: "\e9dc"; 425 | } 426 | .icon-list:before { 427 | content: "\e9dd"; 428 | } 429 | .icon-loader:before { 430 | content: "\e9de"; 431 | } 432 | .icon-lock:before { 433 | content: "\e9df"; 434 | } 435 | .icon-log-in:before { 436 | content: "\e958"; 437 | } 438 | .icon-log-out:before { 439 | content: "\e959"; 440 | } 441 | .icon-mail:before { 442 | content: "\e95a"; 443 | } 444 | .icon-map:before { 445 | content: "\e95b"; 446 | } 447 | .icon-map-pin:before { 448 | content: "\e95c"; 449 | } 450 | .icon-maximize:before { 451 | content: "\e95d"; 452 | } 453 | .icon-maximize-2:before { 454 | content: "\e95e"; 455 | } 456 | .icon-menu:before { 457 | content: "\e95f"; 458 | } 459 | .icon-message-circle:before { 460 | content: "\e960"; 461 | } 462 | .icon-message-square:before { 463 | content: "\e961"; 464 | } 465 | .icon-mic:before { 466 | content: "\e962"; 467 | } 468 | .icon-mic-off:before { 469 | content: "\e9e0"; 470 | } 471 | .icon-minimize:before { 472 | content: "\e9e1"; 473 | } 474 | .icon-minimize-2:before { 475 | content: "\e9e2"; 476 | } 477 | .icon-minus:before { 478 | content: "\e9e3"; 479 | } 480 | .icon-minus-circle:before { 481 | content: "\e9e4"; 482 | } 483 | .icon-minus-square:before { 484 | content: "\e9e5"; 485 | } 486 | .icon-monitor:before { 487 | content: "\e963"; 488 | } 489 | .icon-moon:before { 490 | content: "\e964"; 491 | } 492 | .icon-more-horizontal:before { 493 | content: "\e965"; 494 | } 495 | .icon-more-vertical:before { 496 | content: "\e966"; 497 | } 498 | .icon-move:before { 499 | content: "\e967"; 500 | } 501 | .icon-music:before { 502 | content: "\e968"; 503 | } 504 | .icon-navigation:before { 505 | content: "\e969"; 506 | } 507 | .icon-navigation-2:before { 508 | content: "\e96a"; 509 | } 510 | .icon-octagon:before { 511 | content: "\e96b"; 512 | } 513 | .icon-package:before { 514 | content: "\e96c"; 515 | } 516 | .icon-paperclip:before { 517 | content: "\e96d"; 518 | } 519 | .icon-pause:before { 520 | content: "\e9e6"; 521 | } 522 | .icon-pause-circle:before { 523 | content: "\e9e7"; 524 | } 525 | .icon-percent:before { 526 | content: "\e9e8"; 527 | } 528 | .icon-phone:before { 529 | content: "\e9e9"; 530 | } 531 | .icon-phone-call:before { 532 | content: "\e9ea"; 533 | } 534 | .icon-phone-forwarded:before { 535 | content: "\e9eb"; 536 | } 537 | .icon-phone-incoming:before { 538 | content: "\e96e"; 539 | } 540 | .icon-phone-missed:before { 541 | content: "\e96f"; 542 | } 543 | .icon-phone-off:before { 544 | content: "\e970"; 545 | } 546 | .icon-phone-outgoing:before { 547 | content: "\e971"; 548 | } 549 | .icon-pie-chart:before { 550 | content: "\e972"; 551 | } 552 | .icon-play:before { 553 | content: "\e973"; 554 | } 555 | .icon-play-circle:before { 556 | content: "\e974"; 557 | } 558 | .icon-plus:before { 559 | content: "\e975"; 560 | } 561 | .icon-plus-circle:before { 562 | content: "\e976"; 563 | } 564 | .icon-plus-square:before { 565 | content: "\e977"; 566 | } 567 | .icon-pocket:before { 568 | content: "\e978"; 569 | } 570 | .icon-power:before { 571 | content: "\e9ec"; 572 | } 573 | .icon-printer:before { 574 | content: "\e9ed"; 575 | } 576 | .icon-radio:before { 577 | content: "\e9ee"; 578 | } 579 | .icon-refresh-ccw:before { 580 | content: "\e9ef"; 581 | } 582 | .icon-refresh-cw:before { 583 | content: "\e9f0"; 584 | } 585 | .icon-repeat:before { 586 | content: "\e9f1"; 587 | } 588 | .icon-rewind:before { 589 | content: "\e979"; 590 | } 591 | .icon-rotate-ccw:before { 592 | content: "\e97a"; 593 | } 594 | .icon-rotate-cw:before { 595 | content: "\e97b"; 596 | } 597 | .icon-rss:before { 598 | content: "\e97c"; 599 | } 600 | .icon-save:before { 601 | content: "\e97d"; 602 | } 603 | .icon-scissors:before { 604 | content: "\e97e"; 605 | } 606 | .icon-search:before { 607 | content: "\e97f"; 608 | } 609 | .icon-send:before { 610 | content: "\e980"; 611 | } 612 | .icon-server:before { 613 | content: "\e981"; 614 | } 615 | .icon-settings:before { 616 | content: "\e982"; 617 | } 618 | .icon-share:before { 619 | content: "\e983"; 620 | } 621 | .icon-share-2:before { 622 | content: "\e9f2"; 623 | } 624 | .icon-shield:before { 625 | content: "\e9f3"; 626 | } 627 | .icon-shield-off:before { 628 | content: "\e9f4"; 629 | } 630 | .icon-shopping-bag:before { 631 | content: "\e9f5"; 632 | } 633 | .icon-shopping-cart:before { 634 | content: "\e9f6"; 635 | } 636 | .icon-shuffle:before { 637 | content: "\e9f7"; 638 | } 639 | .icon-sidebar:before { 640 | content: "\e984"; 641 | } 642 | .icon-skip-back:before { 643 | content: "\e985"; 644 | } 645 | .icon-skip-forward:before { 646 | content: "\e986"; 647 | } 648 | .icon-slack:before { 649 | content: "\e987"; 650 | } 651 | .icon-slash:before { 652 | content: "\e988"; 653 | } 654 | .icon-sliders:before { 655 | content: "\e989"; 656 | } 657 | .icon-smartphone:before { 658 | content: "\e98a"; 659 | } 660 | .icon-speaker:before { 661 | content: "\e98b"; 662 | } 663 | .icon-square:before { 664 | content: "\e98c"; 665 | } 666 | .icon-star:before { 667 | content: "\e98d"; 668 | } 669 | .icon-stop-circle:before { 670 | content: "\e98e"; 671 | } 672 | .icon-sun:before { 673 | content: "\e9f8"; 674 | } 675 | .icon-sunrise:before { 676 | content: "\e9f9"; 677 | } 678 | .icon-sunset:before { 679 | content: "\e9fa"; 680 | } 681 | .icon-tablet:before { 682 | content: "\e9fb"; 683 | } 684 | .icon-tag:before { 685 | content: "\e9fc"; 686 | } 687 | .icon-target:before { 688 | content: "\e9fd"; 689 | } 690 | .icon-terminal:before { 691 | content: "\e98f"; 692 | } 693 | .icon-thermometer:before { 694 | content: "\e990"; 695 | } 696 | .icon-thumbs-down:before { 697 | content: "\e991"; 698 | } 699 | .icon-thumbs-up:before { 700 | content: "\e992"; 701 | } 702 | .icon-toggle-left:before { 703 | content: "\e993"; 704 | } 705 | .icon-toggle-right:before { 706 | content: "\e994"; 707 | } 708 | .icon-trash:before { 709 | content: "\e995"; 710 | } 711 | .icon-trash-2:before { 712 | content: "\e996"; 713 | } 714 | .icon-trending-down:before { 715 | content: "\e997"; 716 | } 717 | .icon-trending-up:before { 718 | content: "\e998"; 719 | } 720 | .icon-triangle:before { 721 | content: "\e999"; 722 | } 723 | .icon-truck:before { 724 | content: "\e9fe"; 725 | } 726 | .icon-tv:before { 727 | content: "\e9ff"; 728 | } 729 | .icon-twitter:before { 730 | content: "\ea00"; 731 | } 732 | .icon-type:before { 733 | content: "\ea01"; 734 | } 735 | .icon-umbrella:before { 736 | content: "\ea02"; 737 | } 738 | .icon-underline:before { 739 | content: "\ea03"; 740 | } 741 | .icon-unlock:before { 742 | content: "\e99a"; 743 | } 744 | .icon-upload:before { 745 | content: "\e99b"; 746 | } 747 | .icon-upload-cloud:before { 748 | content: "\e99c"; 749 | } 750 | .icon-user:before { 751 | content: "\e99d"; 752 | } 753 | .icon-user-check:before { 754 | content: "\e99e"; 755 | } 756 | .icon-user-minus:before { 757 | content: "\e99f"; 758 | } 759 | .icon-user-plus:before { 760 | content: "\e9a0"; 761 | } 762 | .icon-user-x:before { 763 | content: "\e9a1"; 764 | } 765 | .icon-users:before { 766 | content: "\e9a2"; 767 | } 768 | .icon-video:before { 769 | content: "\e9a3"; 770 | } 771 | .icon-video-off:before { 772 | content: "\e9a4"; 773 | } 774 | .icon-voicemail:before { 775 | content: "\ea04"; 776 | } 777 | .icon-volume:before { 778 | content: "\ea05"; 779 | } 780 | .icon-volume-1:before { 781 | content: "\ea06"; 782 | } 783 | .icon-volume-2:before { 784 | content: "\ea07"; 785 | } 786 | .icon-volume-x:before { 787 | content: "\ea08"; 788 | } 789 | .icon-watch:before { 790 | content: "\ea09"; 791 | } 792 | .icon-wifi:before { 793 | content: "\e9a5"; 794 | } 795 | .icon-wifi-off:before { 796 | content: "\e9a6"; 797 | } 798 | .icon-wind:before { 799 | content: "\e9a7"; 800 | } 801 | .icon-x:before { 802 | content: "\e9a8"; 803 | } 804 | .icon-x-circle:before { 805 | content: "\e9a9"; 806 | } 807 | .icon-x-square:before { 808 | content: "\e9aa"; 809 | } 810 | .icon-youtube:before { 811 | content: "\e9ab"; 812 | } 813 | .icon-zap:before { 814 | content: "\e9ac"; 815 | } 816 | .icon-zap-off:before { 817 | content: "\e9ad"; 818 | } 819 | .icon-zoom-in:before { 820 | content: "\e9ae"; 821 | } 822 | .icon-zoom-out:before { 823 | content: "\e9af"; 824 | } 825 | -------------------------------------------------------------------------------- /src/assets/tui-editor-contents.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /** 3 | * @fileoverview style for content 4 | * @author NHN Ent. FE Development Lab 5 | */ 6 | 7 | .CodeMirror { 8 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | } 10 | 11 | .tui-editor-contents *:not(table) { 12 | line-height: 160%; 13 | box-sizing: content-box; 14 | } 15 | 16 | .tui-editor-contents i, 17 | .tui-editor-contents cite, 18 | .tui-editor-contents em, 19 | .tui-editor-contents var, 20 | .tui-editor-contents address, 21 | .tui-editor-contents dfn { 22 | font-style: italic; 23 | } 24 | 25 | .tui-editor-contents strong { 26 | font-weight: bold; 27 | } 28 | 29 | .tui-editor-contents p { 30 | margin: 10px 0; 31 | color: #555; 32 | } 33 | 34 | .tui-editor-contents > h1:first-of-type, 35 | .tui-editor-contents > div > div:first-of-type h1 { 36 | margin-top: 14px; 37 | } 38 | 39 | .tui-editor-contents h1, 40 | .tui-editor-contents h2, 41 | .tui-editor-contents h3, 42 | .tui-editor-contents h5 { 43 | font-weight: bold; 44 | } 45 | 46 | .tui-editor-contents h1 { 47 | font-size: 24px; 48 | line-height: 28px; 49 | border-bottom: 3px double #999; 50 | margin: 52px 0 15px 0; 51 | padding-bottom: 7px; 52 | color: #000; 53 | } 54 | 55 | .tui-editor-contents h2 { 56 | font-size: 20px; 57 | line-height: 23px; 58 | border-bottom: 1px solid #dbdbdb; 59 | margin: 30px 0 13px 0; 60 | padding-bottom: 7px; 61 | color: #333; 62 | } 63 | 64 | .tui-editor-contents h3, 65 | .tui-editor-contents h4 { 66 | font-size: 18px; 67 | line-height: 18px; 68 | margin: 20px 0 2px; 69 | color: #333; 70 | } 71 | 72 | .tui-editor-contents h5, 73 | .tui-editor-contents h6 { 74 | font-size: 16px; 75 | line-height: 17px; 76 | margin: 10px 0 -4px; 77 | color: #333; 78 | } 79 | 80 | .tui-editor-contents blockquote { 81 | margin: 15px 0; 82 | } 83 | 84 | .tui-editor-contents blockquote { 85 | border-left: 4px solid #dddddd; 86 | padding: 0 15px; 87 | color: #777777; 88 | } 89 | 90 | .tui-editor-contents blockquote > :first-child { 91 | margin-top: 0; 92 | } 93 | 94 | .tui-editor-contents blockquote > :last-child { 95 | margin-bottom: 0; 96 | } 97 | 98 | .tui-editor-contents pre, 99 | .tui-editor-contents code { 100 | font-family: Consolas, Courier, "Apple SD 산돌고딕 Neo", -apple-system, "Lucida Grande", "Apple SD Gothic Neo", "맑은 고딕", "Malgun Gothic", "Segoe UI", "돋움", dotum, sans-serif; 101 | border: 0; 102 | border-radius: 0; 103 | } 104 | 105 | .tui-editor-contents pre { 106 | margin: 2px 0 8px; 107 | padding: 18px; 108 | background-color: #f5f7f8; 109 | } 110 | 111 | .tui-editor-contents code { 112 | color: #c1788b; 113 | padding: 4px 4px 2px 0; 114 | letter-spacing: -0.3px; 115 | } 116 | 117 | .tui-editor-contents pre code { 118 | padding: 0; 119 | color: inherit; 120 | white-space: pre-wrap; 121 | background-color: transparent; 122 | } 123 | 124 | .tui-editor-contents pre.addon { 125 | border: 1px solid #e8ebed; 126 | background-color: #fff; 127 | } 128 | 129 | .tui-editor-contents img { 130 | margin: 4px 0 10px; 131 | box-sizing: border-box; 132 | vertical-align: top; 133 | max-width: 100%; 134 | } 135 | 136 | .tui-editor-contents table { 137 | margin: 2px 0 14px; 138 | color: #555; 139 | width: auto; 140 | border-collapse: collapse; 141 | box-sizing: border-box; 142 | } 143 | 144 | .tui-editor-contents table th, 145 | .tui-editor-contents table td { 146 | height: 32px; 147 | padding: 5px 14px 5px 12px; 148 | } 149 | 150 | .tui-editor-contents table td { 151 | border: 1px solid #eaeaea; 152 | } 153 | 154 | .tui-editor-contents table th { 155 | border: 1px solid #ddd; 156 | background-color: #f0f0f0; 157 | font-weight: bold; 158 | color: #333; 159 | padding-top: 6px; 160 | } 161 | 162 | .tui-editor-contents ul, 163 | .tui-editor-contents menu, 164 | .tui-editor-contents ol, 165 | .tui-editor-contents dir { 166 | display: block; 167 | list-style-type: disc; 168 | padding-left: 17px; 169 | margin: 6px 0 10px; 170 | color: #555; 171 | } 172 | 173 | .tui-editor-contents ol { 174 | list-style-type: decimal; 175 | } 176 | 177 | .tui-editor-contents ul ul, 178 | .tui-editor-contents ul ol, 179 | .tui-editor-contents ol ol, 180 | .tui-editor-contents ol ul { 181 | margin-top: 0 !important; 182 | margin-bottom: 0 !important; 183 | } 184 | 185 | .tui-editor-contents ul li { 186 | position: relative; 187 | } 188 | 189 | .tui-editor-contents ul p, ol p { 190 | margin: 0; 191 | } 192 | 193 | .tui-editor-contents ul li.task-list-item:before, 194 | .tui-editor-contents pre ul li:before { 195 | content: ""; 196 | } 197 | 198 | .tui-editor-contents hr { 199 | border-top: 1px solid #eee; 200 | margin: 16px 0; 201 | } 202 | 203 | .tui-editor-contents a { 204 | text-decoration: underline; 205 | color: #5286bc; 206 | } 207 | 208 | .tui-editor-contents a:hover { 209 | color: #007cff; 210 | } 211 | 212 | .tui-editor-contents { 213 | font-size: 13px; 214 | margin: 0; 215 | padding: 0; 216 | } 217 | 218 | .tui-editor-contents .task-list-item { 219 | border: 0; 220 | list-style: none; 221 | padding-left: 22px; 222 | margin-left: -22px; 223 | background-repeat: no-repeat; 224 | background-size: 16px 16px; 225 | background-position: 0 2px; 226 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAADdJREFUKBVjvHv37n8GMgALSI+SkhJJWu/du8fARJIOJMWjGpECA505GjjoIYLEB6dVUNojFQAA/1MJUFWet/4AAAAASUVORK5CYII='); 227 | } 228 | 229 | .tui-editor-contents .task-list-item.checked { 230 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAMpJREFUKBVjjJ/64D8DGYCJDD1gLbTVyM3OxJDiJMzAxcYIdyALnIWDAdJU7i/OICfCxsDMxMgwc88bwk5F1vTs/W+GFUffwY2H+1FBlI2hLliCQYCbGSyJrqlzwwuGj9//YWoMtRBgUBJnZ6gMEGeQFWaFOw9kE7omkG5GWDyCPF7mJ86gIMbO8P//fwZGRkYGXJpAGuFO/fbrP0PXppcMD179JKgJRSOIA9N8/NZXrM4DqYEBjOgAaYYFOUwRNhruVGyS+MTI1ggAx8NTGcUtFVQAAAAASUVORK5CYII='); 231 | } 232 | 233 | .tui-editor-contents .task-list-item input[type='checkbox'], 234 | .tui-editor-contents .task-list-item .task-list-item-checkbox { 235 | margin-left: -17px; 236 | margin-right: 3.8px; 237 | margin-top: 3px; 238 | } 239 | -------------------------------------------------------------------------------- /src/assets/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/w-ui/themify.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/src/assets/w-ui/themify.eot -------------------------------------------------------------------------------- /src/assets/w-ui/themify.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/src/assets/w-ui/themify.ttf -------------------------------------------------------------------------------- /src/assets/w-ui/themify.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/src/assets/w-ui/themify.woff -------------------------------------------------------------------------------- /src/auto-updater.js: -------------------------------------------------------------------------------- 1 | const {app, autoUpdater, Menu} = require('electron') 2 | const ChildProcess = require('child_process') 3 | const path = require('path') 4 | 5 | let state = 'checking' 6 | 7 | exports.initialize = () => { 8 | if (process.mas) return 9 | 10 | autoUpdater.on('checking-for-update', () => { 11 | state = 'checking' 12 | exports.updateMenu() 13 | }) 14 | 15 | autoUpdater.on('update-available', () => { 16 | state = 'checking' 17 | exports.updateMenu() 18 | }) 19 | 20 | autoUpdater.on('update-downloaded', () => { 21 | state = 'installed' 22 | exports.updateMenu() 23 | }) 24 | 25 | autoUpdater.on('update-not-available', () => { 26 | state = 'no-update' 27 | exports.updateMenu() 28 | }) 29 | 30 | autoUpdater.on('error', () => { 31 | state = 'no-update' 32 | exports.updateMenu() 33 | }) 34 | 35 | autoUpdater.setFeedURL(`https://electron-api-demos.githubapp.com/updates?version=${app.getVersion()}`) 36 | autoUpdater.checkForUpdates() 37 | } 38 | 39 | exports.updateMenu = () => { 40 | if (process.mas) return 41 | 42 | const menu = Menu.getApplicationMenu() 43 | if (!menu) return 44 | 45 | menu.items.forEach(item => { 46 | if (item.submenu) { 47 | item.submenu.items.forEach(item => { 48 | switch (item.key) { 49 | case 'checkForUpdate': 50 | item.visible = state === 'no-update' 51 | break 52 | case 'checkingForUpdate': 53 | item.visible = state === 'checking' 54 | break 55 | case 'restartToUpdate': 56 | item.visible = state === 'installed' 57 | break 58 | } 59 | }) 60 | } 61 | }) 62 | } 63 | 64 | exports.createShortcut = callback => { 65 | spawnUpdate([ 66 | '--createShortcut', 67 | path.basename(process.execPath), 68 | '--shortcut-locations', 69 | 'StartMenu' 70 | ], callback) 71 | } 72 | 73 | exports.removeShortcut = callback => { 74 | spawnUpdate([ 75 | '--removeShortcut', 76 | path.basename(process.execPath) 77 | ], callback) 78 | } 79 | 80 | function spawnUpdate (args, callback) { 81 | const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe') 82 | let stdout = '' 83 | let spawned = null 84 | 85 | try { 86 | spawned = ChildProcess.spawn(updateExe, args) 87 | } catch (error) { 88 | if (error && error.stdout == null) { 89 | error.stdout = stdout 90 | } 91 | process.nextTick(() => { callback(error) }) 92 | return 93 | } 94 | 95 | var error = null 96 | 97 | spawned.stdout.on('data', data => { 98 | stdout += data 99 | }) 100 | 101 | spawned.on('error', processError => { 102 | if (!error) error = processError 103 | }) 104 | 105 | spawned.on('close', (code, signal) => { 106 | if (!error && code !== 0) { 107 | error = new Error(`Command failed: ${code} ${signal}`) 108 | } 109 | if (error && error.code == null) error.code = code 110 | if (error && error.stdout == null) error.stdout = stdout 111 | callback(error) 112 | }) 113 | } 114 | -------------------------------------------------------------------------------- /src/components/Editor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 87 | 88 | 99 | -------------------------------------------------------------------------------- /src/components/IconText.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 36 | 37 | -------------------------------------------------------------------------------- /src/components/Split/Split.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 96 | 97 | 126 | -------------------------------------------------------------------------------- /src/components/Split/SplitPanel.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 38 | -------------------------------------------------------------------------------- /src/components/Split/emitter.js: -------------------------------------------------------------------------------- 1 | function broadcast(componentName, eventName, params) { 2 | this.$children.forEach(child => { 3 | const name = child.$options.name; 4 | 5 | if (name === componentName) { 6 | child.$emit.apply(child, [eventName, ...params]); 7 | } else { 8 | // Todo If params is an empty array, the received will be undefined 9 | broadcast.apply(child, [componentName, eventName, ...params]); 10 | } 11 | }); 12 | } 13 | export default { 14 | methods: { 15 | dispatch(componentName, eventName, params) { 16 | let parent = this.$parent || this.$root; 17 | let name = parent.$options.name; 18 | 19 | while (parent && (!name || name !== componentName)) { 20 | parent = parent.$parent; 21 | if (parent) { 22 | name = parent.$options.name; 23 | } 24 | } 25 | if (parent) { 26 | parent.$emit.apply(parent, [eventName, ...params]); 27 | } 28 | }, 29 | broadcast(componentName, eventName, params) { 30 | broadcast.call(this, componentName, eventName, params); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/Today.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | 29 | -------------------------------------------------------------------------------- /src/components/note/NoteList.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 175 | 176 | -------------------------------------------------------------------------------- /src/components/plan/Day.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 214 | 215 | -------------------------------------------------------------------------------- /src/components/plan/DayDetail.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 115 | 116 | 185 | -------------------------------------------------------------------------------- /src/components/plan/DayDone.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 154 | 155 | -------------------------------------------------------------------------------- /src/components/plan/Month.vue: -------------------------------------------------------------------------------- 1 | 2 | 35 | 36 | 186 | 187 | -------------------------------------------------------------------------------- /src/components/plan/MonthList.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 131 | 132 | -------------------------------------------------------------------------------- /src/components/plan/Season.vue: -------------------------------------------------------------------------------- 1 | 2 | 35 | 36 | 190 | 191 | 283 | -------------------------------------------------------------------------------- /src/components/plan/SeasonList.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 135 | 136 | -------------------------------------------------------------------------------- /src/components/plan/Tags.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 40 | 41 | -------------------------------------------------------------------------------- /src/components/plan/Week.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 178 | 179 | -------------------------------------------------------------------------------- /src/components/plan/WeekList.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 129 | 130 | -------------------------------------------------------------------------------- /src/components/plan/Welcome.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 151 | 152 | -------------------------------------------------------------------------------- /src/components/plan/Year.vue: -------------------------------------------------------------------------------- 1 | 2 | 35 | 36 | 182 | 183 | 275 | -------------------------------------------------------------------------------- /src/components/plan/YearList.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 135 | 136 | -------------------------------------------------------------------------------- /src/components/select/Item.vue: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/select/Select.js: -------------------------------------------------------------------------------- 1 | import './select.less' 2 | 3 | export default { 4 | name: 'pop-select', 5 | props: { 6 | mode: { 7 | type: String, 8 | default: 'select' 9 | }, 10 | value: { 11 | type: [Number, String, Boolean] 12 | }, 13 | position: { 14 | type: String, 15 | default: 'left' 16 | } 17 | }, 18 | data () { 19 | return { 20 | showMenu: false, 21 | displayName: '', 22 | options: [], 23 | selectedText: '', 24 | selectValue: '', 25 | dirty: false 26 | } 27 | }, 28 | methods: { 29 | doPop (e) { 30 | this.showMenu = !this.showMenu 31 | e.preventDefault() 32 | e.stopPropagation() 33 | }, 34 | doHide () { 35 | this.showMenu = !this.showMenu 36 | }, 37 | doSelect (item) { 38 | this.selectedText = item.text 39 | this.selectedValue = item.value 40 | this.$emit('input', this.selectedValue) 41 | this.$emit('change', item) 42 | }, 43 | update (val) { 44 | this.options = [] 45 | this.$slots.default.map(child => { 46 | let props = child.componentOptions ? child.componentOptions.propsData : undefined 47 | if (props) { 48 | this.options.push({ 49 | value: props.value, 50 | text: props.text, 51 | children: child.componentOptions.children 52 | }) 53 | } 54 | }) 55 | this.options.map(item => { 56 | if (item.value === val) { 57 | this.selectedValue = item.vlaue 58 | this.selectedText = item.text 59 | } 60 | }) 61 | } 62 | }, 63 | render (h) { 64 | let posClass = this.position === 'right' ? 'pop-option right' : 'pop-option' 65 | return
66 | { this.$slots.text ? this.$slots.text :
{this.selectedText}
} 67 | { 68 | this.showMenu 69 | ? 78 | : '' 79 | } 80 |
81 | }, 82 | watch: { 83 | value (newVal, oldVal) { 84 | // todo 85 | setTimeout(() => { 86 | this.update(newVal) 87 | }, 1000) 88 | } 89 | }, 90 | mounted () { 91 | this.update(this.value) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/components/select/index.js: -------------------------------------------------------------------------------- 1 | import Select from './Select.js' 2 | import SelectItem from './Item.vue' 3 | export {Select, SelectItem} 4 | -------------------------------------------------------------------------------- /src/components/select/select.less: -------------------------------------------------------------------------------- 1 | .pop-select{ 2 | display: inline-block; 3 | position: relative; 4 | 5 | .select-value{ 6 | padding: 2px 10px; 7 | background-color: #f0f0f0; 8 | border-radius: 4px; 9 | } 10 | .pop-option{ 11 | position: absolute; 12 | left: 0; 13 | top: 30px; 14 | width: 200px; 15 | max-height: 300px; 16 | overflow-x: hidden; 17 | background-color: #fff; 18 | box-shadow: 0 0 10px #ccc; 19 | z-index: 999; 20 | border-radius: 4px; 21 | 22 | li{ 23 | padding: 10px; 24 | } 25 | li:hover{ 26 | background-color: #f5f5f5; 27 | cursor: pointer; 28 | } 29 | li.active{ 30 | background-color: #f0f0f0; 31 | } 32 | } 33 | .pop-option.right{ 34 | left: auto; 35 | right: 0; 36 | } 37 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Mark 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | require("babel-register"); 2 | 3 | const path = require("path"); 4 | const fetch = require("node-fetch"); 5 | const logger = require("electron-logger"); 6 | const fse = require("fs-extra"); 7 | 8 | const { app, BrowserWindow, ipcMain, Menu, shell } = require("electron"); 9 | const Service = require("./services"); 10 | const Data = require("./services/data.js").default; 11 | const DBS = ["plan.db", "note.db", "note_book.db", "note_category.db"]; 12 | let willQuitApp = false; 13 | 14 | /** 15 | * 初始化 16 | */ 17 | function initialize() { 18 | app.requestSingleInstanceLock(); 19 | app.on("second-instance", (event, argv, cwd) => { 20 | if (global.win) { 21 | if (global.win.isMinimized()) { 22 | global.win.restore(); 23 | } 24 | global.win.focus(); 25 | } 26 | }); 27 | 28 | app.on("ready", () => { 29 | appReady(); 30 | }); 31 | 32 | app.on("window-all-closed", function() { 33 | if (process.platform !== "darwin") { 34 | app.quit(); 35 | } 36 | }); 37 | 38 | app.on("activate", () => { 39 | if (global.win === undefined) { 40 | createWindow(); 41 | } else { 42 | global.win.show(); 43 | } 44 | }); 45 | 46 | app.on("before-quit", () => { 47 | willQuitApp = true; 48 | }); 49 | } 50 | 51 | // 创建窗口 52 | function createWindow() { 53 | const windowOptions = { 54 | width: 1200, 55 | minWidth: 800, 56 | height: 700, 57 | title: app.getName(), 58 | titleBarStyle: "hiddenInset", 59 | webPreferences: { 60 | nodeIntegration: true 61 | } 62 | }; 63 | let win = new BrowserWindow(windowOptions); 64 | win.loadURL(path.join("file://", __dirname, "../dist/index.html")); 65 | 66 | const debug = /--debug/.test(process.argv[2]); 67 | if (debug) { 68 | win.webContents.openDevTools(); 69 | win.maximize(); 70 | } 71 | 72 | win.on("close", function(e) { 73 | if (!willQuitApp) { 74 | e.preventDefault(); 75 | win.hide(); 76 | } else { 77 | global.win = undefined; 78 | } 79 | }); 80 | 81 | global.win = win; 82 | } 83 | 84 | /** 85 | * 初始化全局配置 86 | */ 87 | function globalConfig() { 88 | // 数据目录初始化 89 | let ppath = path.join(__dirname, "../data"); 90 | if (process.env.NODE_ENV === "production") { 91 | ppath = path.join(app.getPath("userData"), "mark"); 92 | } 93 | if (!fse.ensureDirSync(ppath)) { 94 | fse.mkdirsSync(ppath); 95 | } 96 | // logger设置 97 | logger.setOutput({ file: path.join(ppath, "debug.log") }); 98 | logger.setLevel("info"); 99 | 100 | // db文件初始化 101 | DBS.forEach(item => { 102 | let dbPath = path.join(ppath, item); 103 | if (!fse.pathExistsSync(dbPath)) { 104 | fse.outputFileSync(dbPath, "", "utf8"); 105 | logger.info("create db files sucess"); 106 | } 107 | }); 108 | global.dataPath = ppath; 109 | global.logger = logger; 110 | global.app = app; 111 | } 112 | 113 | /** 114 | * 初始化进程通信管道 115 | */ 116 | function initIPC() { 117 | // db请求 118 | ipcMain.on("data-rpc", (event, args) => { 119 | let uuid = args[0]; 120 | let dbname = args[1]; 121 | let command = args[2]; 122 | let params = args.slice(3); 123 | 124 | global.daos[dbname][command](...params).then(data => { 125 | event.sender.send("data-rpc-response", { 126 | errNo: 0, 127 | errMsg: "", 128 | uuid: uuid, 129 | result: data 130 | }); 131 | }); 132 | }); 133 | 134 | // http请求 135 | ipcMain.on("data-request", (event, args) => { 136 | let uuid = args[0]; 137 | let url = args[1]; 138 | let params = args[2]; 139 | fetch(url, params) 140 | .then(res => { 141 | return res.json(); 142 | }) 143 | .then(json => { 144 | event.sender.send("data-request-response", { 145 | errNo: 0, 146 | errMsg: "", 147 | uuid: uuid, 148 | result: json 149 | }); 150 | }); 151 | }); 152 | } 153 | 154 | /** 155 | * 初始化DB实例 156 | */ 157 | function initDBInstance() { 158 | global.daos = {}; 159 | global.daos["note"] = Service.Note; 160 | global.daos["plan"] = Service.Plan; 161 | } 162 | 163 | /** 164 | * 初始化菜单 165 | */ 166 | function setMenu() { 167 | const template = [ 168 | { 169 | label: "Mark", 170 | submenu: [ 171 | { 172 | label: "偏好设置", 173 | accelerator: "Shift+Ctrl+P", 174 | click() { 175 | sendMenuCommand("setting"); 176 | } 177 | } 178 | ] 179 | }, 180 | { 181 | label: "文件", 182 | submenu: [ 183 | { label: "保存", accelerator: "CmdOrCtrl+S", selector: "save:" } 184 | ] 185 | }, 186 | { 187 | label: "编辑", 188 | submenu: [ 189 | { label: "撤销", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, 190 | { label: "重做", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" }, 191 | { type: "separator" }, 192 | { label: "剪切", accelerator: "CmdOrCtrl+X", selector: "cut:" }, 193 | { label: "复制", accelerator: "CmdOrCtrl+C", selector: "copy:" }, 194 | { label: "粘贴", accelerator: "CmdOrCtrl+V", selector: "paste:" }, 195 | { label: "全选", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }, 196 | { type: "separator" }, 197 | { 198 | label: "重新加载", 199 | accelerator: "CmdOrCtrl+R", 200 | click: function(item, focusedWindow) { 201 | if (global.win) global.win.reload(); 202 | } 203 | } 204 | ] 205 | }, 206 | { 207 | label: "数据管理", 208 | submenu: [ 209 | { 210 | label: "导出数据", 211 | accelerator: "Shift+Ctrl+E", 212 | click() { 213 | Data.exportData(); 214 | } 215 | }, 216 | { type: "separator" }, 217 | { 218 | label: "导入数据", 219 | accelerator: "Shift+Ctrl+I", 220 | click() { 221 | Data.importData(); 222 | } 223 | } 224 | ] 225 | }, 226 | { 227 | label: "反馈", 228 | submenu: [ 229 | { 230 | label: "Bug反馈", 231 | click() { 232 | shell.openExternal("https://github.com/lin-xi/mark/issues"); 233 | } 234 | }, 235 | { 236 | label: "需求反馈", 237 | click() { 238 | shell.openExternal("https://github.com/lin-xi/mark/issues"); 239 | } 240 | } 241 | ] 242 | }, 243 | { 244 | label: "关于", 245 | submenu: [ 246 | { 247 | label: "关于", 248 | click() { 249 | sendMenuCommand("about"); 250 | } 251 | }, 252 | { 253 | label: "源码", 254 | click() { 255 | shell.openExternal("https://github.com/lin-xi/mark"); 256 | } 257 | }, 258 | { type: "separator" }, 259 | { 260 | label: "捐赠", 261 | click() { 262 | shell.openExternal("https://github.com/lin-xi/mark"); 263 | } 264 | } 265 | ] 266 | } 267 | ]; 268 | Menu.setApplicationMenu(Menu.buildFromTemplate(template)); 269 | } 270 | 271 | /** 272 | * 发送菜单命令 273 | */ 274 | function sendMenuCommand(command) { 275 | global.win.webContents.send("menu-command", command); 276 | } 277 | 278 | /** 279 | * appReady 280 | */ 281 | function appReady() { 282 | console.log("userData>>>", app.getPath("userData")); 283 | // autoUpdater.initialize() //自动更新 284 | // 初始化全局配置 285 | globalConfig(); 286 | // 初始化DB实例 287 | initDBInstance(); 288 | // 初始化进程通信管道 289 | initIPC(); 290 | // 创建窗口 291 | createWindow(); 292 | setMenu(); 293 | } 294 | initialize(); 295 | -------------------------------------------------------------------------------- /src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lin-xi/mark/d266ff5157379002cad0f7beede97ae31e7c684b/src/logo.png -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './routers.js' 4 | Vue.component('App', App) 5 | 6 | Vue.prototype.$eventHub = new Vue() 7 | 8 | new Vue({ 9 | el: '#app', 10 | router, 11 | render: h => h(App) 12 | }) 13 | -------------------------------------------------------------------------------- /src/models/BaseModel.js: -------------------------------------------------------------------------------- 1 | import model from "./model.js"; 2 | 3 | class BaseModel { 4 | constructor(table) { 5 | this.table = table; 6 | } 7 | 8 | query(where, sort = { created_at: "DESC" }) { 9 | return model.query(this.table, where, sort); 10 | } 11 | 12 | queryOne(where, sort = { created_at: "DESC" }) { 13 | return model.queryOne(this.table, where, sort); 14 | } 15 | 16 | add(where, sort = { created_at: "DESC" }) { 17 | return model.query(this.table, where, sort); 18 | } 19 | 20 | update(data) { 21 | return model.add(this.table, data); 22 | } 23 | 24 | remove(data) { 25 | return model.add(this.table, data); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/models/Note.js: -------------------------------------------------------------------------------- 1 | import BaseModel from "./BaseModel.js"; 2 | 3 | class Note extends BaseModel { 4 | constructor() { 5 | super("note"); 6 | } 7 | } 8 | 9 | export default new Note(); 10 | -------------------------------------------------------------------------------- /src/models/NoteBook.js: -------------------------------------------------------------------------------- 1 | import BaseModel from "./BaseModel.js"; 2 | 3 | class NoteBook extends BaseModel { 4 | constructor() { 5 | super("note_book"); 6 | } 7 | } 8 | 9 | export default new NoteBook(); 10 | -------------------------------------------------------------------------------- /src/models/NoteCategory.js: -------------------------------------------------------------------------------- 1 | import BaseModel from "./BaseModel.js"; 2 | 3 | class NoteCategory extends BaseModel { 4 | constructor() { 5 | super("note_category"); 6 | } 7 | } 8 | 9 | export default new NoteCategory(); 10 | -------------------------------------------------------------------------------- /src/models/Plan.js: -------------------------------------------------------------------------------- 1 | import BaseModel from "./BaseModel.js"; 2 | 3 | class Plan extends BaseModel { 4 | constructor() { 5 | super("plan"); 6 | } 7 | } 8 | 9 | export default new Plan(); 10 | -------------------------------------------------------------------------------- /src/models/db.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import Database from "better-sqlite3"; 4 | import model from "./model.js"; 5 | 6 | const USER_DATA = global.app.getPath("userData"); 7 | let configPath = path.join(USER_DATA, "mark", "config.json"); 8 | let config = {}; 9 | if (fs.existsSync(configPath)) { 10 | let text = fs.readFileSync(configPath, "utf8"); 11 | if (text) { 12 | config = JSON.parse(text); 13 | } 14 | } 15 | let ppath = path.join(USER_DATA, "mark", "mark.db"); 16 | if (config.dataPath) { 17 | ppath = path.join(config.dataPath, "mark.db"); 18 | } 19 | 20 | const db = new Database(ppath, { verbose: console.log }); 21 | export { db }; 22 | -------------------------------------------------------------------------------- /src/models/model.js: -------------------------------------------------------------------------------- 1 | import db from "./db.js"; 2 | 3 | const DDL = { 4 | plan: `CREATE TABLE IF NOT EXISTS plan ( 5 | id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 6 | subject VARCHAR (512) NOT NULL, 7 | tags VARCHAR (256), 8 | content TEXT, 9 | user_id INT, 10 | created_at DATETIME, 11 | updated_at DATETIME, 12 | status INT (1) 13 | );`, 14 | note: `CREATE TABLE IF NOT EXISTS note ( 15 | id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 16 | subject VARCHAR (512) NOT NULL, 17 | content TEXT, 18 | book_id BIGINT, 19 | category_id BIGINT, 20 | created_at DATETIME, 21 | updated_at DATETIME, 22 | user_id INT 23 | );`, 24 | note_book: `CREATE TABLE IF NOT EXISTS note_book ( 25 | id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 26 | name VARCHAR (256) NOT NULL, 27 | created_at DATETIME, 28 | updated_at DATETIME, 29 | user_id INT 30 | );`, 31 | note_category: `CREATE TABLE note_category ( 32 | id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 33 | name VARCHAR (128) NOT NULL, 34 | book_id BIGINT, 35 | created_at DATETIME, 36 | updated_at DATETIME, 37 | user_id INT 38 | );` 39 | }; 40 | 41 | function initDB() { 42 | for (let key in DDL) { 43 | let sql = DDL[key]; 44 | let stm = db.prepare(sql); 45 | stm.run(); 46 | } 47 | } 48 | 49 | function add(table, data) { 50 | let fields = Object.keys(data); 51 | let sign = fields.map(item => "@" + item); 52 | const sql = `INSERT INTO ${table} (${fields.join(",")}) VALUES (${sign.join( 53 | "," 54 | )})`; 55 | let statement = db.prepare(sql); 56 | let record = statement.run(data); 57 | console.log("🖨 :", sql, data); 58 | return record; 59 | } 60 | 61 | function update(table, where, data) { 62 | let fieldStm = []; 63 | for (let field in data) { 64 | let val = wrapValue(data[field]); 65 | fieldStm.push(`${field}=${val}`); 66 | } 67 | let whereStm = []; 68 | for (let field in where) { 69 | let val = wrapValue(where[field]); 70 | whereStm.push(`${field}=${val}`); 71 | } 72 | let sql = `UPDATE ${table} SET ${fieldStm.join(",")} WHERE ${whereStm.join( 73 | " and " 74 | )})`; 75 | let statement = db.prepare(sql); 76 | statement.run(); 77 | console.log("🖨 :", sql); 78 | } 79 | 80 | function remove(table, where) { 81 | let whereStm = []; 82 | for (let field in where) { 83 | let val = wrapValue(where[field]); 84 | whereStm.push(`${field}=${val}`); 85 | } 86 | let sql = `DELETE from ${table} WHERE ${whereStm.join(" and ")})`; 87 | let statement = db.prepare(sql); 88 | statement.run(); 89 | console.log("🖨 :", sql); 90 | } 91 | 92 | function query(table, where = {}, sort = {}) { 93 | let sql = prepareQuery(table, where, sort); 94 | let statement = db.prepare(sql); 95 | const records = statement.all(); 96 | console.log("🖨 :", sql); 97 | return records; 98 | } 99 | 100 | function queryOne(table, where = {}, sort = {}) { 101 | let sql = prepareQuery(table, where, sort); 102 | let statement = db.prepare(sql); 103 | const record = statement.get(); 104 | console.log("🖨 :", sql); 105 | return record; 106 | } 107 | 108 | function execute(sql) { 109 | let statement = db.prepare(sql); 110 | const records = statement.run(); 111 | console.log("🖨 :", sql); 112 | return records; 113 | } 114 | 115 | function prepareQuery(table, where, sort) { 116 | let whereStm = []; 117 | for (let field in where) { 118 | let val = wrapValue(where[field]); 119 | whereStm.push(`${field}=${val}`); 120 | } 121 | let sortStm = []; 122 | for (let field in sort) { 123 | whereStm.push(`${field} ${sort[field]}`); 124 | } 125 | let order = ""; 126 | if (sortStm.length > 0) { 127 | order = ` ORDER BY ${sortStm.join(",")}`; 128 | } 129 | let condition = ""; 130 | if (whereStm.length > 0) { 131 | condition = ` WHERE ${whereStm.join(" and ")}`; 132 | } 133 | let sql = `SELECT * from ${table} ${condition} ${order}`; 134 | console.log(sql); 135 | return sql; 136 | } 137 | 138 | function wrapValue(val) { 139 | switch (typeof val) { 140 | case "number": 141 | case "boolean": 142 | break; 143 | default: 144 | val = `'${val}'`; 145 | break; 146 | } 147 | return val; 148 | } 149 | 150 | export default { initDB, add, update, remove, query, queryOne }; 151 | -------------------------------------------------------------------------------- /src/pages/Note.vue: -------------------------------------------------------------------------------- 1 | 108 | 109 | 383 | -------------------------------------------------------------------------------- /src/pages/NoteDesk.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | -------------------------------------------------------------------------------- /src/pages/Plan.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 65 | -------------------------------------------------------------------------------- /src/pages/note.less: -------------------------------------------------------------------------------- 1 | .page-note { 2 | width: 100%; 3 | height: 100%; 4 | font-size: 16px; 5 | 6 | .te-md-container .CodeMirror { 7 | font-size: 16px; 8 | } 9 | i { 10 | font-size: 18px; 11 | margin-right: 5px; 12 | cursor: pointer; 13 | color: #21cc0f; 14 | } 15 | .body-box { 16 | width: 100%; 17 | height: 100%; 18 | overflow: hidden; 19 | display: flex; 20 | .gutter.gutter-horizontal { 21 | background-image: none; 22 | } 23 | 24 | .com-note-list { 25 | height: 100%; 26 | overflow: hidden; 27 | display: flex; 28 | flex-direction: column; 29 | .date-area { 30 | display: flex; 31 | align-items: center; 32 | justify-content: space-between; 33 | border-bottom: 1px #f0f0f0 dashed; 34 | background-color: #f0f0f0; 35 | padding: 10px; 36 | user-select: none; 37 | -webkit-app-region: drag; 38 | input { 39 | border: 1px #e0e0e0 solid; 40 | height: 30px; 41 | width: 100px; 42 | border-radius: 15px; 43 | outline: none; 44 | font-size: 14px; 45 | text-indent: 10px; 46 | } 47 | span { 48 | padding-left: 5px; 49 | font-weight: bold; 50 | } 51 | .tool-bar { 52 | font-weight: normal; 53 | font-size: 14px; 54 | display: flex; 55 | align-items: center; 56 | justify-content: center; 57 | cursor: pointer; 58 | } 59 | .info { 60 | flex: 0 0 90px; 61 | } 62 | } 63 | .add-area { 64 | position: relative; 65 | padding: 10px; 66 | background-color: #e5e5e5; 67 | input { 68 | border: 1px #e0e0e0 solid; 69 | height: 40px; 70 | width: 100%; 71 | border-radius: 4px; 72 | outline: none; 73 | font-size: 14px; 74 | text-indent: 10px; 75 | } 76 | } 77 | .list-area { 78 | padding: 10px 0; 79 | overflow: auto; 80 | 81 | .note-item { 82 | padding: 10px 10px 10px 20px; 83 | color: #444; 84 | background-color: #fafbfc; 85 | } 86 | .wui-accordion .w-accordion-title > span { 87 | font-size: 16px; 88 | } 89 | .wui-accordion .w-accordion-title { 90 | padding: 16px; 91 | } 92 | .note-item.active { 93 | background-color: #f0f0ff; 94 | } 95 | .note-item:hover { 96 | background-color: #f5f5f5; 97 | cursor: pointer; 98 | } 99 | .title-bar { 100 | display: flex; 101 | align-items: center; 102 | input { 103 | border: 1px #e0e0e0 solid; 104 | height: 40px; 105 | width: 200px; 106 | border-radius: 4px; 107 | outline: none; 108 | font-size: 14px; 109 | text-indent: 10px; 110 | } 111 | .title-text { 112 | margin-right: 5px; 113 | } 114 | } 115 | .task-list { 116 | li { 117 | padding: 10px; 118 | font-size: 14px; 119 | margin-top: 5px; 120 | margin-bottom: 5px; 121 | background-color: #f6f7f8; 122 | cursor: pointer; 123 | color: #666; 124 | } 125 | li.active { 126 | background-color: #e3ecf7; 127 | } 128 | } 129 | } 130 | } 131 | .editor { 132 | width: 100%; 133 | height: 100%; 134 | padding: 5px 10px 10px 10px; 135 | border-left: 1px #e5e5e5 solid; 136 | .tui-editor-contents h1, 137 | .tui-editor-contents h2, 138 | .tui-editor-contents h3, 139 | .tui-editor-contents h5 { 140 | font-weight: bold; 141 | } 142 | .tui-editor-contents h1 { 143 | font-size: 24px; 144 | line-height: 28px; 145 | border-bottom: 3px double #999; 146 | margin: 52px 0 15px 0; 147 | padding-bottom: 7px; 148 | color: #000; 149 | } 150 | .tui-editor-contents h2 { 151 | font-size: 20px; 152 | line-height: 23px; 153 | border-bottom: 1px solid #dbdbdb; 154 | margin: 30px 0 13px 0; 155 | padding-bottom: 7px; 156 | color: #333; 157 | } 158 | .tui-editor-contents h3, 159 | .tui-editor-contents h4 { 160 | font-size: 18px; 161 | line-height: 18px; 162 | margin: 20px 0 2px; 163 | color: #333; 164 | } 165 | .tui-editor-contents h5, 166 | .tui-editor-contents h6 { 167 | font-size: 16px; 168 | line-height: 17px; 169 | margin: 10px 0 -4px; 170 | color: #333; 171 | } 172 | .tui-editor-contents { 173 | font-size: 16px; 174 | table th { 175 | border: 1px solid #eaeaea; 176 | background-color: #cafefe; 177 | font-weight: 300; 178 | color: #666; 179 | font-weight: bolder; 180 | padding-top: 6px; 181 | } 182 | } 183 | .cat-area { 184 | position: relative; 185 | padding: 2px 0px 15px 0; 186 | font-size: 16px; 187 | position: relative; 188 | display: flex; 189 | align-items: center; 190 | justify-content: space-between; 191 | user-select: none; 192 | -webkit-app-region: drag; 193 | 194 | .left-menu { 195 | display: flex; 196 | align-items: center; 197 | } 198 | 199 | .cat-label { 200 | padding: 2px 10px; 201 | border-radius: 4px; 202 | background-color: lightgreen; 203 | cursor: pointer; 204 | color: #666; 205 | } 206 | .cat-select { 207 | position: absolute; 208 | left: 40px; 209 | top: 30px; 210 | z-index: 10000; 211 | background-color: #fff; 212 | border: 1px #e5e5e5 solid; 213 | box-shadow: 0 0 10px #ccc; 214 | .cat-select-item { 215 | padding: 10px; 216 | } 217 | .cat-select-item:hover { 218 | background-color: #f0f0f0; 219 | cursor: pointer; 220 | } 221 | } 222 | .more-menu { 223 | position: relative; 224 | i { 225 | color: #000; 226 | } 227 | .tool-menu { 228 | position: absolute; 229 | right: 0; 230 | top: 30px; 231 | width: 150px; 232 | z-index: 10; 233 | background: #fff; 234 | box-shadow: 0 0 10px #ccc; 235 | color: #666; 236 | li { 237 | padding: 10px; 238 | i { 239 | color: #d0d0d0; 240 | } 241 | } 242 | li:hover { 243 | background-color: #f0f0f0; 244 | cursor: pointer; 245 | } 246 | } 247 | } 248 | } 249 | .save-status { 250 | position: absolute; 251 | left: 20px; 252 | bottom: 10px; 253 | font-size: 12px; 254 | color: #ccc; 255 | .unsave { 256 | display: inline-block; 257 | width: 10px; 258 | height: 10px; 259 | border-radius: 5px; 260 | background-color: red; 261 | margin-right: 4px; 262 | } 263 | } 264 | .tool { 265 | display: flex; 266 | border: 1px #e8e8e8 solid; 267 | margin-top: 10px; 268 | border-radius: 4px; 269 | span { 270 | flex: 1; 271 | border-right: 1px #e8e8e8 solid; 272 | font-size: 14px; 273 | padding: 5px; 274 | cursor: pointer; 275 | text-align: center; 276 | background-color: #f7f8f9; 277 | img { 278 | width: 20px; 279 | vertical-align: middle; 280 | margin-right: 5px; 281 | } 282 | } 283 | span:first-child { 284 | border-radius: 4px 0 0 4px; 285 | } 286 | span:last-child { 287 | border-right: none; 288 | border-radius: 0 4px 4px 0; 289 | } 290 | } 291 | } 292 | .welcome { 293 | height: 100%; 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/pages/noteDesk.less: -------------------------------------------------------------------------------- 1 | .page-note-desk { 2 | width: 100%; 3 | height: 100%; 4 | font-size: 16px; 5 | 6 | i { 7 | font-size: 18px; 8 | margin-right: 5px; 9 | cursor: pointer; 10 | color: #21cc0f; 11 | } 12 | 13 | .body-box { 14 | width: 100%; 15 | height: 100%; 16 | overflow: hidden; 17 | 18 | .com-note-list { 19 | width: 100%; 20 | height: 100%; 21 | overflow: hidden; 22 | display: flex; 23 | flex-direction: column; 24 | 25 | .date-area { 26 | display: flex; 27 | align-items: center; 28 | justify-content: space-between; 29 | border-bottom: 1px #f0f0f0 dashed; 30 | background-color: #f0f0f0; 31 | padding: 10px 0 10px 10px; 32 | user-select: none; 33 | -webkit-app-region: drag; 34 | 35 | input { 36 | border: 1px #e0e0e0 solid; 37 | height: 30px; 38 | width: 100px; 39 | border-radius: 15px; 40 | outline: none; 41 | font-size: 14px; 42 | text-indent: 10px; 43 | } 44 | span { 45 | padding-left: 5px; 46 | font-weight: bold; 47 | } 48 | .info { 49 | flex: 0 0 90px; 50 | } 51 | } 52 | 53 | .title-bar{ 54 | display: flex; 55 | align-items: center; 56 | justify-content: space-between; 57 | border: 1px #eee solid; 58 | padding: 10px; 59 | font-size: 14px; 60 | 61 | .title{ 62 | display: flex; 63 | align-items: center; 64 | } 65 | .tool-bar{ 66 | display: flex; 67 | align-items: center; 68 | justify-content: center; 69 | .space{ 70 | padding-right: 5px; 71 | } 72 | .wui-switch{ 73 | outline: none; 74 | } 75 | .wui-switch:checked { 76 | border-color: #ccc; 77 | background-color: #ccc; 78 | } 79 | } 80 | } 81 | 82 | .add-area { 83 | position: relative; 84 | padding: 10px; 85 | background-color: #e5e5e5; 86 | input { 87 | border: 1px #e0e0e0 solid; 88 | height: 40px; 89 | width: 100%; 90 | border-radius: 4px; 91 | outline: none; 92 | font-size: 14px; 93 | text-indent: 10px; 94 | } 95 | } 96 | 97 | .list-area { 98 | display: flex; 99 | flex-wrap: wrap; 100 | padding: 10px; 101 | overflow-x: none; 102 | overflow-y: auto; 103 | 104 | .list-item { 105 | flex: 0 0 120px; 106 | height: 120px; 107 | padding: 10px; 108 | position: relative; 109 | 110 | .remove-musk{ 111 | position: absolute; 112 | left: 0px; 113 | top: 0px; 114 | width: 30px; 115 | height: 30px; 116 | border-radius: 100%; 117 | background-color: rgba(0,0,0, 0.2); 118 | i{ 119 | font-size: 30px; 120 | color: #fff; 121 | } 122 | } 123 | .wrapper { 124 | height: 100%; 125 | background-color: #f5f5f5; 126 | border-radius: 4px; 127 | margin-bottom: 20px; 128 | box-shadow: 0 0 10px #d6d6d6; 129 | text-align: center; 130 | i { 131 | display: inline-block; 132 | width: 50px; 133 | height: 75px; 134 | line-height: 75px; 135 | font-size: 50px; 136 | margin-right: 0px; 137 | } 138 | .item-title{ 139 | width: 100%; 140 | overflow: hidden; 141 | color: #555; 142 | font-size: 14px; 143 | } 144 | .item-input{ 145 | border: 1px #e0e0e0 solid; 146 | height: 22px; 147 | width: 90%; 148 | border-radius: 4px; 149 | outline: none; 150 | font-size: 14px; 151 | text-indent: 5px; 152 | } 153 | } 154 | .wrapper:hover { 155 | background-color: #f0f0ff; 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | .add-icon { 163 | display: flex; 164 | align-items: center; 165 | justify-content: center; 166 | padding: 2px 10px; 167 | font-size: 14px; 168 | cursor: pointer; 169 | } 170 | } -------------------------------------------------------------------------------- /src/pages/plan.less: -------------------------------------------------------------------------------- 1 | .page-plan { 2 | width: 100%; 3 | height: 100%; 4 | font-size: 16px; 5 | 6 | li { 7 | list-style: none; 8 | } 9 | 10 | .body-box { 11 | width: 100%; 12 | height: 100%; 13 | overflow: hidden; 14 | display: flex; 15 | 16 | .wui-tab-nav-item { 17 | line-height: 40px; 18 | font-size: 16px; 19 | background: #f5f6f7; 20 | cursor: pointer; 21 | } 22 | .wui-tab-nav-item:not(:last-child):after { 23 | border: none; 24 | } 25 | .wui-tab-nav .wui-tab-active { 26 | background: #fff; 27 | color: #666; 28 | } 29 | .wui-tab-nav .wui-tab-active:before { 30 | width: 0; 31 | } 32 | 33 | .list { 34 | flex: 0 0 600px; 35 | height: 100%; 36 | overflow-x: hidden; 37 | overflow-y: auto; 38 | position: relative; 39 | border-right: 1px #eee solid; 40 | } 41 | 42 | .detail { 43 | flex: 1; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/routers.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | Vue.use(VueRouter) 4 | 5 | import Plan from './pages/Plan.vue' 6 | import NoteDesk from './pages/NoteDesk.vue' 7 | import Note from './pages/Note.vue' 8 | 9 | export default new VueRouter({ 10 | mode: 'hash', 11 | base: __dirname, 12 | routes: [ 13 | // {name: 'Index', path: '/', component: Index}, 14 | {name: 'Plan', path: '/plan', component: Plan}, 15 | {name: 'NoteDesk', path: '/noteDesk', props: true, component: NoteDesk}, 16 | {name: 'Note', path: '/note/:bookId', props: true, component: Note}, 17 | {name: 'NoteDetail', path: '/noteDetail/:noteId', props: true, component: Plan}, 18 | { path: '*', redirect: '/plan' } 19 | ], 20 | }) -------------------------------------------------------------------------------- /src/services/NoteService.js: -------------------------------------------------------------------------------- 1 | import note from "../models/Note.js"; 2 | import noteBook from "../models/NoteBook.js"; 3 | import noteCategory from "../models/NoteCategory.js"; 4 | 5 | export default class NoteService { 6 | constructor() {} 7 | 8 | queryNote(where, sort = { created_at: "DESC" }) { 9 | return note.query(where, sort); 10 | } 11 | 12 | queryBook(where, sort = { created_at: "DESC" }) { 13 | return noteBook.query(where, sort); 14 | } 15 | 16 | queryCategory(where, sort = { created_at: "DESC" }) { 17 | return noteCategory.query(where, sort); 18 | } 19 | 20 | addNote(data) { 21 | return note.add(data); 22 | } 23 | 24 | addBook(data) { 25 | return noteBook.add(data); 26 | } 27 | 28 | addCategory(data) { 29 | return noteCategory.add(this.table, data); 30 | } 31 | 32 | updateNote(where, data) { 33 | return note.update(where, data); 34 | } 35 | 36 | updateBook(where, data) { 37 | return noteBook.update(where, data); 38 | } 39 | 40 | updateCategory(where, data) { 41 | return noteCategory.update(where, data); 42 | } 43 | 44 | removeNote(where) { 45 | return note.remove(where); 46 | } 47 | 48 | removeBook(where) { 49 | return noteBook.remove(where); 50 | } 51 | 52 | removeCategory(where) { 53 | return noteCategory.remove(where); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/services/PlanService.js: -------------------------------------------------------------------------------- 1 | import plan from "../models/Plan.js"; 2 | 3 | export default class PlanService { 4 | constructor() {} 5 | 6 | queryPlan(where, sort = { created_at: "DESC" }) { 7 | return note.query(where, sort); 8 | } 9 | 10 | addPlan(data) { 11 | return note.add(data); 12 | } 13 | 14 | updatePlan(where, data) { 15 | return note.update(where, data); 16 | } 17 | 18 | removePlan(where) { 19 | return note.remove(where); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/services/data.js: -------------------------------------------------------------------------------- 1 | 2 | import moment from 'moment' 3 | import AdmZip from 'adm-zip' 4 | import fs from 'fs' 5 | import path from 'path' 6 | const { 7 | dialog, 8 | shell 9 | } = require('electron') 10 | 11 | export default { 12 | exportData () { 13 | let name = moment().format('YYYY-MM-DD_HH_mm_ss') + '_data.m' 14 | dialog.showSaveDialog(global.win, { 15 | title: '请选择保存位置', 16 | defaultPath: '~/Download/' + name 17 | }, (filename, bookmark) => { 18 | if (filename) { 19 | let zip = new AdmZip() 20 | zip.addLocalFile(global.dataPath + '/plan.db') 21 | zip.addLocalFile(global.dataPath + '/note.db') 22 | zip.addLocalFile(global.dataPath + '/note_book.db') 23 | zip.addLocalFile(global.dataPath + '/note_category.db') 24 | zip.writeZip(filename) 25 | 26 | shell.openItem(path.dirname(filename)) 27 | 28 | dialog.showMessageBox(global.win, { 29 | type: 'info', 30 | title: 'Mark', 31 | message: '导出数据成功' 32 | }) 33 | } 34 | }) 35 | }, 36 | 37 | importData () { 38 | dialog.showMessageBox(global.win, { 39 | type: 'question', 40 | title: 'Mark', 41 | message: '导入后,当前全部数据会被覆盖,你确定要覆盖相当数据吗?,', 42 | buttons: ['继续导入', '取消'] 43 | }, (response, checkboxChecked) => { 44 | if (response == 0) { 45 | dialog.showOpenDialog(global.win, { 46 | title: '请选择数据文件', 47 | defaultPath: '~/Download/', 48 | filters: [ 49 | {name: 'Mark Data File', extensions: ['m']} 50 | ] 51 | }, (filePaths, bookmark) => { 52 | if (filePaths.length == 0) return 53 | 54 | let zip = new AdmZip(filePaths[0]) 55 | var zipEntries = zip.getEntries() 56 | 57 | zipEntries.forEach(function (zipEntry) { 58 | // console.log(zipEntry.entryName) 59 | if (zipEntry.entryName == 'plan.db' || zipEntry.entryName == 'note.db') { 60 | // console.log(zipEntry.getData().toString('utf8')) 61 | fs.writeFileSync(global.dataPath + '/' + zipEntry.entryName, zipEntry.getData().toString('utf8'), 'utf8') 62 | } 63 | }) 64 | 65 | dialog.showMessageBox(global.win, { 66 | type: 'info', 67 | title: 'Mark', 68 | message: '导入数据成功' 69 | }) 70 | }) 71 | } 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/services/dbUtil.js: -------------------------------------------------------------------------------- 1 | import DataStore from 'nedb' 2 | import path from 'path' 3 | let cache = {} 4 | let callbacks = [] 5 | 6 | let getDB = name => { 7 | // let ppath = path.join(__dirname, '../../data/' + name + '.db') 8 | let ppath = path.join(global.app.getPath('userData'), 'mark', name + '.db') 9 | // if (process.env.NODE_ENV === 'production') { 10 | // ppath = path.join(global.app.getPath('userData'), 'mark', name + '.db') 11 | // } 12 | return new Promise(resolve => { 13 | if (!cache[name]) { 14 | cache[name] = 'waiting' 15 | let ds = new DataStore({ 16 | filename: ppath, 17 | autoload: true, 18 | onload(err) { 19 | if (err) { 20 | global.logger.info(err) 21 | console.log(err) 22 | } 23 | cache[name] = ds 24 | resolve(ds) 25 | while (callbacks.length > 0) { 26 | callbacks.pop().apply(null, ds) 27 | } 28 | } 29 | }) 30 | } else if (cache[name] === 'waiting') { 31 | callbacks.push(_ds => { 32 | resolve(_ds) 33 | }) 34 | } else { 35 | resolve(cache[name]) 36 | } 37 | }) 38 | } 39 | 40 | export default { 41 | getDB, 42 | 43 | add(db, doc) { 44 | return new Promise(resolve => { 45 | db.insert(doc, (err, newDoc) => { 46 | if (err) { 47 | console.error(err) 48 | } 49 | resolve(newDoc) 50 | }) 51 | }) 52 | }, 53 | 54 | update(db, doc, newDoc) { 55 | return new Promise(resolve => { 56 | db.update( 57 | doc, 58 | { 59 | $set: newDoc 60 | }, 61 | { 62 | multi: true 63 | }, 64 | (err, numReplaced) => { 65 | if (err) { 66 | console.error(err) 67 | } 68 | resolve(numReplaced) 69 | } 70 | ) 71 | }) 72 | }, 73 | 74 | remove(db, doc) { 75 | return new Promise(resolve => { 76 | db.remove(doc, (err, newDoc) => { 77 | if (err) { 78 | console.error(err) 79 | } 80 | resolve(newDoc) 81 | }) 82 | }) 83 | }, 84 | 85 | getDao(name) { 86 | return require(`./${name}.js`) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/services/index.js: -------------------------------------------------------------------------------- 1 | import Note from './note.js' 2 | import Plan from './plan.js' 3 | 4 | export { 5 | Note, 6 | Plan 7 | } 8 | -------------------------------------------------------------------------------- /src/services/note.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * NoteBook 4 | * { 5 | * _id: '', 6 | * name: '', 7 | * createTime: '', 8 | * updateTime: '' 9 | * } 10 | * 11 | * NoteCategory 12 | * { 13 | * _id: '', 14 | * name: '', 15 | * bookId: '', 16 | * createTime: '', 17 | * updateTime: '' 18 | * } 19 | * 20 | * Note 21 | * { 22 | * _id: '', 23 | * subject: '', 24 | * content: '', 25 | * createTime: '', 26 | * bookId: '', 27 | * categoryId: '', 28 | * updateTime: '' 29 | * } 30 | */ 31 | import DBUtil from './dbUtil' 32 | export default { 33 | queryNote (doc, sort = {createTime: -1}) { 34 | return new Promise(resolve => { 35 | DBUtil.getDB('note').then(db => { 36 | db.find(doc).sort(sort).exec((err, result) => { 37 | if (err) { 38 | console.error(err) 39 | } 40 | resolve(result) 41 | }) 42 | }) 43 | }) 44 | }, 45 | 46 | queryBook (doc, sort = {createTime: 1}) { 47 | return new Promise(resolve => { 48 | DBUtil.getDB('note_book').then(db => { 49 | db.find(doc).sort(sort).exec((err, result) => { 50 | if (err) { 51 | console.error(err) 52 | } 53 | resolve(result) 54 | }) 55 | }) 56 | }) 57 | }, 58 | 59 | queryCategory (doc, sort = {createTime: 1}) { 60 | return new Promise(resolve => { 61 | DBUtil.getDB('note_category').then(db => { 62 | db.find(doc).sort(sort).exec((err, result) => { 63 | if (err) { 64 | console.error(err) 65 | } 66 | resolve(result) 67 | }) 68 | }) 69 | }) 70 | }, 71 | 72 | addBook (doc) { 73 | return DBUtil.getDB('note_book').then(db => { 74 | return DBUtil.add(db, doc) 75 | }) 76 | }, 77 | 78 | addNote (doc) { 79 | return DBUtil.getDB('note').then(db => { 80 | return DBUtil.add(db, doc) 81 | }) 82 | }, 83 | 84 | addCategory (doc) { 85 | return DBUtil.getDB('note_category').then(db => { 86 | return DBUtil.add(db, doc) 87 | }) 88 | }, 89 | 90 | updateNote (doc, newDoc) { 91 | return DBUtil.getDB('note').then(db => { 92 | return DBUtil.update(db, doc, newDoc) 93 | }) 94 | }, 95 | 96 | updateBook (doc, newDoc) { 97 | return DBUtil.getDB('note_book').then(db => { 98 | return DBUtil.update(db, doc, newDoc) 99 | }) 100 | }, 101 | 102 | updateCategory (doc, newDoc) { 103 | return DBUtil.getDB('note_category').then(db => { 104 | return DBUtil.update(db, doc, newDoc) 105 | }) 106 | }, 107 | 108 | removeNote (doc) { 109 | return DBUtil.getDB('note').then(db => { 110 | return DBUtil.remove(db, doc) 111 | }) 112 | }, 113 | 114 | removeBook (doc) { 115 | return DBUtil.getDB('note_book').then(db => { 116 | return DBUtil.remove(db, doc) 117 | }) 118 | }, 119 | 120 | removeCategory (doc) { 121 | return DBUtil.getDB('note_category').then(db => { 122 | return DBUtil.remove(db, doc) 123 | }) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/services/plan.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Plan 4 | * { 5 | * _id: '', 6 | * subject: '', 7 | * content: '', 8 | * tags: [], 9 | * createTime: '', 10 | * updateTime: '', 11 | * status: 0 12 | * } 13 | */ 14 | import DBUtil from './dbUtil' 15 | export default { 16 | queryPlan (doc, sort = {createTime: -1}) { 17 | return new Promise(resolve => { 18 | DBUtil.getDB('plan').then(db => { 19 | db.find(doc).sort(sort).exec((err, result) => { 20 | if (err) { 21 | console.error(err) 22 | } 23 | resolve(result) 24 | }) 25 | }) 26 | }) 27 | }, 28 | 29 | addPlan (doc) { 30 | return DBUtil.getDB('plan').then(db => { 31 | return DBUtil.add(db, doc) 32 | }) 33 | }, 34 | 35 | updatePlan (doc, newDoc) { 36 | return DBUtil.getDB('plan').then(db => { 37 | return DBUtil.update(db, doc, newDoc) 38 | }) 39 | }, 40 | 41 | removePlan (doc) { 42 | return DBUtil.getDB('plan').then(db => { 43 | return DBUtil.remove(db, doc) 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | ipcRenderer 3 | } = window.require('electron') 4 | const uuidv4 = require('uuid/v4'); 5 | 6 | let events = {} 7 | 8 | ipcRenderer.on('data-rpc-response', (event, result) => { 9 | let uuid = result.uuid; 10 | if (events[uuid]) { 11 | events[uuid](result) 12 | } 13 | }) 14 | 15 | ipcRenderer.on('data-request-response', (event, result) => { 16 | let uuid = result.uuid; 17 | if (events[uuid]) { 18 | events[uuid](result) 19 | } 20 | }) 21 | 22 | export default { 23 | execute(...args) { 24 | let uuid = uuidv4() 25 | return new Promise(resolve => { 26 | events[uuid] = resolve 27 | ipcRenderer.send('data-rpc', [uuid, ...args]) 28 | }) 29 | }, 30 | 31 | request(...args) { 32 | let uuid = uuidv4() 33 | return new Promise(resolve => { 34 | events[uuid] = resolve 35 | ipcRenderer.send('data-request', [uuid, ...args]) 36 | }) 37 | } 38 | } --------------------------------------------------------------------------------