├── .babelrc
├── .electron-vue
├── build.js
├── dev-client.js
├── dev-runner.js
├── webpack.main.config.js
├── webpack.renderer.config.js
└── webpack.web.config.js
├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── appveyor.yml
├── assets
├── alipay.png
├── commitMessageStandard.md
├── home.jpg
├── login.png
├── logo.png
├── tree.txt
├── treeNodeCli.md
├── vueDevtools.md
└── wechatpay.png
├── build
└── icons
│ ├── 256x256.png
│ ├── icon.icns
│ └── icon.ico
├── dist
├── electron
│ └── .gitkeep
└── web
│ └── .gitkeep
├── docs
├── .nojekyll
├── README.md
├── _coverpage.md
├── _navbar.md
├── _sidebar.md
├── assets
│ ├── icon.ico
│ └── logo.png
├── changelog.md
├── en
│ ├── README.md
│ └── _sidebar.md
├── faq.md
├── index.html
└── sw.js
├── package-lock.json
├── package.json
├── screenshot-app.png
├── screenshot-desktop.png
├── screenshot-frontend.png
├── screenshot-home-online.png
├── screenshot-web.png
├── sentry-symbols.js
├── sentry.properties
├── snap
└── snapcraft.yaml
├── src
├── api
│ ├── assets.js
│ ├── category.js
│ ├── incomeAndExpenditure.js
│ └── user.js
├── datastore
│ ├── README.md
│ └── index.js
├── index.ejs
├── main
│ ├── index.dev.js
│ ├── index.js
│ └── startOnBoot.js
└── renderer
│ ├── App.vue
│ ├── assets
│ ├── .gitkeep
│ └── logo.png
│ ├── components
│ ├── Layout
│ │ └── Layout.vue
│ └── add
│ │ └── add.vue
│ ├── main.js
│ ├── router
│ └── index.js
│ ├── store
│ ├── index.js
│ ├── modules
│ │ ├── Counter.js
│ │ ├── dialogAdd.js
│ │ ├── index.js
│ │ └── todo.js
│ └── plugins
│ │ └── todo.js
│ └── views
│ ├── Assets
│ └── Assets.vue
│ ├── Category
│ └── Category.vue
│ ├── Help
│ └── Help.vue
│ ├── Home
│ ├── Home.vue
│ └── components
│ │ ├── add
│ │ └── add.vue
│ │ ├── history
│ │ └── history.vue
│ │ ├── statsCurve
│ │ └── statsCurve.vue
│ │ ├── statsIcon
│ │ └── statsIcon.vue
│ │ └── todo
│ │ ├── todo.vue
│ │ └── todoItem.vue
│ ├── IncomeAndExpenditure
│ └── IncomeAndExpenditure.vue
│ ├── Login
│ └── Login.vue
│ ├── Notes
│ └── Notes.vue
│ ├── Password
│ └── Password.vue
│ └── Settings
│ └── Settings.vue
├── static
├── .gitkeep
├── icon.ico
├── iconMessage.ico
├── iconMessage.png
├── iconMessageNotWin.png
├── iconMessageNotWin@2x.png
├── iconMessageNotWin@3x.png
├── iconTransparent.ico
├── iconTransparent.png
├── iconTransparentNotWin.png
├── iconTransparentNotWin@2x.png
├── iconTransparentNotWin@3x.png
├── logo.ico
├── logo.png
├── logoNotWin.png
├── logoNotWin@2x.png
└── logoNotWin@3x.png
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "comments": false,
3 | "env": {
4 | "main": {
5 | "presets": [
6 | [
7 | "env",
8 | {
9 | "targets": {
10 | "node": 7
11 | }
12 | }
13 | ],
14 | "stage-0"
15 | ]
16 | },
17 | "renderer": {
18 | "presets": [
19 | [
20 | "env",
21 | {
22 | "modules": false
23 | }
24 | ],
25 | "stage-0"
26 | ]
27 | },
28 | "web": {
29 | "presets": [
30 | [
31 | "env",
32 | {
33 | "modules": false
34 | }
35 | ],
36 | "stage-0"
37 | ]
38 | }
39 | },
40 | "plugins": [
41 | "transform-runtime",
42 | [
43 | "component",
44 | [
45 | {
46 | "libraryName": "element-ui",
47 | "styleLibraryName": "theme-chalk"
48 | }
49 | ]
50 | ]
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/.electron-vue/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.NODE_ENV = 'production'
4 |
5 | const { say } = require('cfonts')
6 | const chalk = require('chalk')
7 | const del = require('del')
8 | const { spawn } = require('child_process')
9 | const webpack = require('webpack')
10 | const Multispinner = require('multispinner')
11 |
12 |
13 | const mainConfig = require('./webpack.main.config')
14 | const rendererConfig = require('./webpack.renderer.config')
15 | const webConfig = require('./webpack.web.config')
16 |
17 | const doneLog = chalk.bgGreen.white(' DONE ') + ' '
18 | const errorLog = chalk.bgRed.white(' ERROR ') + ' '
19 | const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
20 | const isCI = process.env.CI || false
21 |
22 | if (process.env.BUILD_TARGET === 'clean') clean()
23 | else if (process.env.BUILD_TARGET === 'web') web()
24 | else build()
25 |
26 | function clean () {
27 | del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
28 | console.log(`\n${doneLog}\n`)
29 | process.exit()
30 | }
31 |
32 | function build () {
33 | greeting()
34 |
35 | del.sync(['dist/electron/*', '!.gitkeep'])
36 |
37 | const tasks = ['main', 'renderer']
38 | const m = new Multispinner(tasks, {
39 | preText: 'building',
40 | postText: 'process'
41 | })
42 |
43 | let results = ''
44 |
45 | m.on('success', () => {
46 | process.stdout.write('\x1B[2J\x1B[0f')
47 | console.log(`\n\n${results}`)
48 | console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
49 | process.exit()
50 | })
51 |
52 | pack(mainConfig).then(result => {
53 | results += result + '\n\n'
54 | m.success('main')
55 | }).catch(err => {
56 | m.error('main')
57 | console.log(`\n ${errorLog}failed to build main process`)
58 | console.error(`\n${err}\n`)
59 | process.exit(1)
60 | })
61 |
62 | pack(rendererConfig).then(result => {
63 | results += result + '\n\n'
64 | m.success('renderer')
65 | }).catch(err => {
66 | m.error('renderer')
67 | console.log(`\n ${errorLog}failed to build renderer process`)
68 | console.error(`\n${err}\n`)
69 | process.exit(1)
70 | })
71 | }
72 |
73 | function pack (config) {
74 | return new Promise((resolve, reject) => {
75 | config.mode = 'production'
76 | webpack(config, (err, stats) => {
77 | if (err) reject(err.stack || err)
78 | else if (stats.hasErrors()) {
79 | let err = ''
80 |
81 | stats.toString({
82 | chunks: false,
83 | colors: true
84 | })
85 | .split(/\r?\n/)
86 | .forEach(line => {
87 | err += ` ${line}\n`
88 | })
89 |
90 | reject(err)
91 | } else {
92 | resolve(stats.toString({
93 | chunks: false,
94 | colors: true
95 | }))
96 | }
97 | })
98 | })
99 | }
100 |
101 | function web () {
102 | del.sync(['dist/web/*', '!.gitkeep'])
103 | webConfig.mode = 'production'
104 | webpack(webConfig, (err, stats) => {
105 | if (err || stats.hasErrors()) console.log(err)
106 |
107 | console.log(stats.toString({
108 | chunks: false,
109 | colors: true
110 | }))
111 |
112 | process.exit()
113 | })
114 | }
115 |
116 | function greeting () {
117 | const cols = process.stdout.columns
118 | let text = ''
119 |
120 | if (cols > 85) text = 'lets-build'
121 | else if (cols > 60) text = 'lets-|build'
122 | else text = false
123 |
124 | if (text && !isCI) {
125 | say(text, {
126 | colors: ['yellow'],
127 | font: 'simple3d',
128 | space: false
129 | })
130 | } else console.log(chalk.yellow.bold('\n lets-build'))
131 | console.log()
132 | }
--------------------------------------------------------------------------------
/.electron-vue/dev-client.js:
--------------------------------------------------------------------------------
1 | const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
2 |
3 | hotClient.subscribe(event => {
4 | /**
5 | * Reload browser when HTMLWebpackPlugin emits a new index.html
6 | *
7 | * Currently disabled until jantimon/html-webpack-plugin#680 is resolved.
8 | * https://github.com/SimulatedGREG/electron-vue/issues/437
9 | * https://github.com/jantimon/html-webpack-plugin/issues/680
10 | */
11 | // if (event.action === 'reload') {
12 | // window.location.reload()
13 | // }
14 |
15 | /**
16 | * Notify `mainWindow` when `main` process is compiling,
17 | * giving notice for an expected reload of the `electron` process
18 | */
19 | if (event.action === 'compiling') {
20 | document.body.innerHTML += `
21 |
34 |
35 |
36 | Compiling Main Process...
37 |
38 | `
39 | }
40 | })
41 |
--------------------------------------------------------------------------------
/.electron-vue/dev-runner.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const chalk = require('chalk')
4 | const electron = require('electron')
5 | const path = require('path')
6 | const { say } = require('cfonts')
7 | const { spawn } = require('child_process')
8 | const webpack = require('webpack')
9 | const WebpackDevServer = require('webpack-dev-server')
10 | const webpackHotMiddleware = require('webpack-hot-middleware')
11 |
12 | const mainConfig = require('./webpack.main.config')
13 | const rendererConfig = require('./webpack.renderer.config')
14 |
15 | let electronProcess = null
16 | let manualRestart = false
17 | let hotMiddleware
18 |
19 | function logStats (proc, data) {
20 | let log = ''
21 |
22 | log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`)
23 | log += '\n\n'
24 |
25 | if (typeof data === 'object') {
26 | data.toString({
27 | colors: true,
28 | chunks: false
29 | }).split(/\r?\n/).forEach(line => {
30 | log += ' ' + line + '\n'
31 | })
32 | } else {
33 | log += ` ${data}\n`
34 | }
35 |
36 | log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n'
37 |
38 | console.log(log)
39 | }
40 |
41 | function startRenderer () {
42 | return new Promise((resolve, reject) => {
43 | rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
44 | rendererConfig.mode = 'development'
45 | const compiler = webpack(rendererConfig)
46 | hotMiddleware = webpackHotMiddleware(compiler, {
47 | log: false,
48 | heartbeat: 2500
49 | })
50 |
51 | compiler.hooks.compilation.tap('compilation', compilation => {
52 | compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
53 | hotMiddleware.publish({ action: 'reload' })
54 | cb()
55 | })
56 | })
57 |
58 | compiler.hooks.done.tap('done', stats => {
59 | logStats('Renderer', stats)
60 | })
61 |
62 | const server = new WebpackDevServer(
63 | compiler,
64 | {
65 | contentBase: path.join(__dirname, '../'),
66 | quiet: true,
67 | before (app, ctx) {
68 | app.use(hotMiddleware)
69 | ctx.middleware.waitUntilValid(() => {
70 | resolve()
71 | })
72 | }
73 | }
74 | )
75 |
76 | server.listen(9080)
77 | })
78 | }
79 |
80 | function startMain () {
81 | return new Promise((resolve, reject) => {
82 | mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
83 | mainConfig.mode = 'development'
84 | const compiler = webpack(mainConfig)
85 |
86 | compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
87 | logStats('Main', chalk.white.bold('compiling...'))
88 | hotMiddleware.publish({ action: 'compiling' })
89 | done()
90 | })
91 |
92 | compiler.watch({}, (err, stats) => {
93 | if (err) {
94 | console.log(err)
95 | return
96 | }
97 |
98 | logStats('Main', stats)
99 |
100 | if (electronProcess && electronProcess.kill) {
101 | manualRestart = true
102 | process.kill(electronProcess.pid)
103 | electronProcess = null
104 | startElectron()
105 |
106 | setTimeout(() => {
107 | manualRestart = false
108 | }, 5000)
109 | }
110 |
111 | resolve()
112 | })
113 | })
114 | }
115 |
116 | function startElectron () {
117 | var args = [
118 | '--inspect=5858',
119 | // 为了方便调试chrome://inspect,本地调试用,此代码不能提交
120 | // '--inspect-brk=5858',
121 | path.join(__dirname, '../dist/electron/main.js')
122 | ]
123 |
124 | // detect yarn or npm and process commandline args accordingly
125 | if (process.env.npm_execpath.endsWith('yarn.js')) {
126 | args = args.concat(process.argv.slice(3))
127 | } else if (process.env.npm_execpath.endsWith('npm-cli.js')) {
128 | args = args.concat(process.argv.slice(2))
129 | }
130 |
131 | electronProcess = spawn(electron, args)
132 |
133 | electronProcess.stdout.on('data', data => {
134 | electronLog(data, 'blue')
135 | })
136 | electronProcess.stderr.on('data', data => {
137 | electronLog(data, 'red')
138 | })
139 |
140 | electronProcess.on('close', () => {
141 | if (!manualRestart) process.exit()
142 | })
143 | }
144 |
145 | function electronLog (data, color) {
146 | let log = ''
147 | data = data.toString().split(/\r?\n/)
148 | data.forEach(line => {
149 | log += ` ${line}\n`
150 | })
151 | if (/[0-9A-z]+/.test(log)) {
152 | console.log(
153 | chalk[color].bold('┏ Electron -------------------') +
154 | '\n\n' +
155 | log +
156 | chalk[color].bold('┗ ----------------------------') +
157 | '\n'
158 | )
159 | }
160 | }
161 |
162 | function greeting () {
163 | const cols = process.stdout.columns
164 | let text = ''
165 |
166 | if (cols > 104) text = 'electron-vue'
167 | else if (cols > 76) text = 'electron-|vue'
168 | else text = false
169 |
170 | if (text) {
171 | say(text, {
172 | colors: ['yellow'],
173 | font: 'simple3d',
174 | space: false
175 | })
176 | } else console.log(chalk.yellow.bold('\n electron-vue'))
177 | console.log(chalk.blue(' getting ready...') + '\n')
178 | }
179 |
180 | function init () {
181 | greeting()
182 |
183 | Promise.all([startRenderer(), startMain()])
184 | .then(() => {
185 | startElectron()
186 | })
187 | .catch(err => {
188 | console.error(err)
189 | })
190 | }
191 |
192 | init()
193 |
--------------------------------------------------------------------------------
/.electron-vue/webpack.main.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.BABEL_ENV = 'main'
4 |
5 | const path = require('path')
6 | const { dependencies } = require('../package.json')
7 | const webpack = require('webpack')
8 |
9 | // const BabiliWebpackPlugin = require('babili-webpack-plugin')
10 | const BabiliWebpackPlugin = require('babel-minify-webpack-plugin')
11 |
12 | let mainConfig = {
13 | entry: {
14 | main: path.join(__dirname, '../src/main/index.js')
15 | },
16 | externals: [
17 | ...Object.keys(dependencies || {})
18 | ],
19 | // 为了方便调试chrome://inspect,本地调试用,此代码不能提交
20 | // devtool:`inline-source-map`,
21 | module: {
22 | rules: [
23 | {
24 | test: /\.js$/,
25 | use: 'babel-loader',
26 | exclude: /node_modules/
27 | },
28 | {
29 | test: /\.node$/,
30 | use: 'node-loader'
31 | }
32 | ]
33 | },
34 | node: {
35 | __dirname: process.env.NODE_ENV !== 'production',
36 | __filename: process.env.NODE_ENV !== 'production'
37 | },
38 | output: {
39 | filename: '[name].js',
40 | libraryTarget: 'commonjs2',
41 | path: path.join(__dirname, '../dist/electron')
42 | },
43 | plugins: [
44 | new webpack.NoEmitOnErrorsPlugin()
45 | ],
46 | resolve: {
47 | extensions: ['.js', '.json', '.node']
48 | },
49 | target: 'electron-main'
50 | }
51 |
52 | /**
53 | * Adjust mainConfig for development settings
54 | */
55 | if (process.env.NODE_ENV !== 'production') {
56 | mainConfig.plugins.push(
57 | new webpack.DefinePlugin({
58 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
59 | })
60 | )
61 | }
62 |
63 | /**
64 | * Adjust mainConfig for production settings
65 | */
66 | if (process.env.NODE_ENV === 'production') {
67 | mainConfig.plugins.push(
68 | new BabiliWebpackPlugin(),
69 | new webpack.DefinePlugin({
70 | 'process.env.NODE_ENV': '"production"'
71 | })
72 | )
73 | }
74 |
75 | module.exports = mainConfig
76 |
--------------------------------------------------------------------------------
/.electron-vue/webpack.renderer.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.BABEL_ENV = 'renderer'
4 |
5 | const path = require('path')
6 | const { dependencies } = require('../package.json')
7 | const webpack = require('webpack')
8 |
9 | // const BabiliWebpackPlugin = require('babili-webpack-plugin')
10 | const BabiliWebpackPlugin = require('babel-minify-webpack-plugin')
11 | const CopyWebpackPlugin = require('copy-webpack-plugin')
12 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
13 | const HtmlWebpackPlugin = require('html-webpack-plugin')
14 | const { VueLoaderPlugin } = require('vue-loader')
15 |
16 | /**
17 | * List of node_modules to include in webpack bundle
18 | *
19 | * Required for specific packages like Vue UI libraries
20 | * that provide pure *.vue files that need compiling
21 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
22 | */
23 | let whiteListedModules = ['vue', 'vuetify']
24 |
25 | let rendererConfig = {
26 | devtool: '#cheap-module-eval-source-map',
27 | entry: {
28 | renderer: path.join(__dirname, '../src/renderer/main.js')
29 | },
30 | externals: [
31 | ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
32 | ],
33 | module: {
34 | rules: [
35 | {
36 | test: /\.scss$/,
37 | use: ['vue-style-loader', 'css-loader', 'sass-loader']
38 | },
39 | {
40 | test: /\.sass$/,
41 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax']
42 | },
43 | {
44 | test: /\.less$/,
45 | use: ['vue-style-loader', 'css-loader', 'less-loader']
46 | },
47 | {
48 | test: /\.css$/,
49 | use: ['vue-style-loader', 'css-loader']
50 | },
51 | {
52 | test: /\.html$/,
53 | use: 'vue-html-loader'
54 | },
55 | {
56 | test: /\.js$/,
57 | use: 'babel-loader',
58 | exclude: /node_modules/
59 | },
60 | {
61 | test: /\.node$/,
62 | use: 'node-loader'
63 | },
64 | {
65 | test: /\.vue$/,
66 | use: {
67 | loader: 'vue-loader',
68 | options: {
69 | extractCSS: process.env.NODE_ENV === 'production',
70 | loaders: {
71 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
72 | scss: 'vue-style-loader!css-loader!sass-loader',
73 | less: 'vue-style-loader!css-loader!less-loader'
74 | }
75 | }
76 | }
77 | },
78 | {
79 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
80 | use: {
81 | loader: 'url-loader',
82 | query: {
83 | limit: 10000,
84 | name: 'imgs/[name]--[folder].[ext]'
85 | }
86 | }
87 | },
88 | {
89 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
90 | loader: 'url-loader',
91 | options: {
92 | limit: 10000,
93 | name: 'media/[name]--[folder].[ext]'
94 | }
95 | },
96 | {
97 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
98 | use: {
99 | loader: 'url-loader',
100 | query: {
101 | limit: 10000,
102 | name: 'fonts/[name]--[folder].[ext]'
103 | }
104 | }
105 | }
106 | ]
107 | },
108 | node: {
109 | __dirname: process.env.NODE_ENV !== 'production',
110 | __filename: process.env.NODE_ENV !== 'production'
111 | },
112 | plugins: [
113 | new VueLoaderPlugin(),
114 | new MiniCssExtractPlugin({filename: 'styles.css'}),
115 | // new HtmlWebpackPlugin({
116 | // filename: 'index.html',
117 | // template: path.resolve(__dirname, '../src/index.ejs'),
118 | // minify: {
119 | // collapseWhitespace: true,
120 | // removeAttributeQuotes: true,
121 | // removeComments: true
122 | // },
123 | // nodeModules: process.env.NODE_ENV !== 'production'
124 | // ? path.resolve(__dirname, '../node_modules')
125 | // : false
126 | // }),
127 | new HtmlWebpackPlugin({
128 | filename: 'index.html',
129 | template: path.resolve(__dirname, '../src/index.ejs'),
130 | templateParameters(compilation, assets, options) {
131 | return {
132 | compilation: compilation,
133 | webpack: compilation.getStats().toJson(),
134 | webpackConfig: compilation.options,
135 | htmlWebpackPlugin: {
136 | files: assets,
137 | options: options
138 | },
139 | process,
140 | };
141 | },
142 | minify: {
143 | collapseWhitespace: true,
144 | removeAttributeQuotes: true,
145 | removeComments: true
146 | },
147 | nodeModules: process.env.NODE_ENV !== 'production'
148 | ? path.resolve(__dirname, '../node_modules')
149 | : false
150 | }),
151 | new webpack.HotModuleReplacementPlugin(),
152 | new webpack.NoEmitOnErrorsPlugin()
153 | ],
154 | output: {
155 | filename: '[name].js',
156 | libraryTarget: 'commonjs2',
157 | path: path.join(__dirname, '../dist/electron')
158 | },
159 | resolve: {
160 | alias: {
161 | '@': path.join(__dirname, '../src/renderer'),
162 | 'vue$': 'vue/dist/vue.esm.js',
163 | '~': path.join(__dirname, '../src'),
164 | },
165 | extensions: ['.js', '.vue', '.json', '.css', '.node']
166 | },
167 | target: 'electron-renderer'
168 | }
169 |
170 | /**
171 | * Adjust rendererConfig for development settings
172 | */
173 | if (process.env.NODE_ENV !== 'production') {
174 | rendererConfig.plugins.push(
175 | new webpack.DefinePlugin({
176 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
177 | })
178 | )
179 | }
180 |
181 | /**
182 | * Adjust rendererConfig for production settings
183 | */
184 | if (process.env.NODE_ENV === 'production') {
185 | rendererConfig.devtool = ''
186 |
187 | rendererConfig.plugins.push(
188 | new BabiliWebpackPlugin(),
189 | new CopyWebpackPlugin([
190 | {
191 | from: path.join(__dirname, '../static'),
192 | to: path.join(__dirname, '../dist/electron/static'),
193 | ignore: ['.*']
194 | }
195 | ]),
196 | new webpack.DefinePlugin({
197 | 'process.env.NODE_ENV': '"production"'
198 | }),
199 | new webpack.LoaderOptionsPlugin({
200 | minimize: true
201 | })
202 | )
203 | }
204 |
205 | module.exports = rendererConfig
206 |
--------------------------------------------------------------------------------
/.electron-vue/webpack.web.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.BABEL_ENV = 'web'
4 |
5 | const path = require('path')
6 | const webpack = require('webpack')
7 |
8 | // const BabiliWebpackPlugin = require('babili-webpack-plugin')
9 | const BabiliWebpackPlugin = require('babel-minify-webpack-plugin')
10 | const CopyWebpackPlugin = require('copy-webpack-plugin')
11 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
12 | const HtmlWebpackPlugin = require('html-webpack-plugin')
13 | const { VueLoaderPlugin } = require('vue-loader')
14 |
15 | let webConfig = {
16 | devtool: '#cheap-module-eval-source-map',
17 | entry: {
18 | web: path.join(__dirname, '../src/renderer/main.js')
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.scss$/,
24 | use: ['vue-style-loader', 'css-loader', 'sass-loader']
25 | },
26 | {
27 | test: /\.sass$/,
28 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax']
29 | },
30 | {
31 | test: /\.less$/,
32 | use: ['vue-style-loader', 'css-loader', 'less-loader']
33 | },
34 | {
35 | test: /\.css$/,
36 | use: ['vue-style-loader', 'css-loader']
37 | },
38 | {
39 | test: /\.html$/,
40 | use: 'vue-html-loader'
41 | },
42 | {
43 | test: /\.js$/,
44 | use: 'babel-loader',
45 | include: [ path.resolve(__dirname, '../src/renderer') ],
46 | exclude: /node_modules/
47 | },
48 | {
49 | test: /\.vue$/,
50 | use: {
51 | loader: 'vue-loader',
52 | options: {
53 | extractCSS: true,
54 | loaders: {
55 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
56 | scss: 'vue-style-loader!css-loader!sass-loader',
57 | less: 'vue-style-loader!css-loader!less-loader'
58 | }
59 | }
60 | }
61 | },
62 | {
63 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
64 | use: {
65 | loader: 'url-loader',
66 | query: {
67 | limit: 10000,
68 | name: 'imgs/[name].[ext]'
69 | }
70 | }
71 | },
72 | {
73 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
74 | use: {
75 | loader: 'url-loader',
76 | query: {
77 | limit: 10000,
78 | name: 'fonts/[name].[ext]'
79 | }
80 | }
81 | }
82 | ]
83 | },
84 | plugins: [
85 | new VueLoaderPlugin(),
86 | new MiniCssExtractPlugin({filename: 'styles.css'}),
87 | // new HtmlWebpackPlugin({
88 | // filename: 'index.html',
89 | // template: path.resolve(__dirname, '../src/index.ejs'),
90 | // minify: {
91 | // collapseWhitespace: true,
92 | // removeAttributeQuotes: true,
93 | // removeComments: true
94 | // },
95 | // nodeModules: false
96 | // }),
97 | new HtmlWebpackPlugin({
98 | filename: 'index.html',
99 | template: path.resolve(__dirname, '../src/index.ejs'),
100 | templateParameters(compilation, assets, options) {
101 | return {
102 | compilation: compilation,
103 | webpack: compilation.getStats().toJson(),
104 | webpackConfig: compilation.options,
105 | htmlWebpackPlugin: {
106 | files: assets,
107 | options: options
108 | },
109 | process,
110 | };
111 | },
112 | minify: {
113 | collapseWhitespace: true,
114 | removeAttributeQuotes: true,
115 | removeComments: true
116 | },
117 | nodeModules: false
118 | }),
119 | new webpack.DefinePlugin({
120 | 'process.env.IS_WEB': 'true'
121 | }),
122 | new webpack.HotModuleReplacementPlugin(),
123 | new webpack.NoEmitOnErrorsPlugin()
124 | ],
125 | output: {
126 | filename: '[name].js',
127 | path: path.join(__dirname, '../dist/web')
128 | },
129 | resolve: {
130 | alias: {
131 | '@': path.join(__dirname, '../src/renderer'),
132 | 'vue$': 'vue/dist/vue.esm.js'
133 | },
134 | extensions: ['.js', '.vue', '.json', '.css']
135 | },
136 | target: 'web'
137 | }
138 |
139 | /**
140 | * Adjust webConfig for production settings
141 | */
142 | if (process.env.NODE_ENV === 'production') {
143 | webConfig.devtool = ''
144 |
145 | webConfig.plugins.push(
146 | new BabiliWebpackPlugin(),
147 | new CopyWebpackPlugin([
148 | {
149 | from: path.join(__dirname, '../static'),
150 | to: path.join(__dirname, '../dist/web/static'),
151 | ignore: ['.*']
152 | }
153 | ]),
154 | new webpack.DefinePlugin({
155 | 'process.env.NODE_ENV': '"production"'
156 | }),
157 | new webpack.LoaderOptionsPlugin({
158 | minimize: true
159 | })
160 | )
161 | }
162 |
163 | module.exports = webConfig
164 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | dist/electron/*
3 | dist/web/*
4 | build/*
5 | !build/icons
6 | node_modules/
7 | npm-debug.log
8 | npm-debug.log.*
9 | thumbs.db
10 | !.gitkeep
11 | .idea/
12 | yarn-error.log
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | matrix:
2 | include:
3 | - os: osx
4 | osx_image: xcode10.2
5 | language: node_js
6 | node_js: "10"
7 | env:
8 | - ELECTRON_CACHE=$HOME/.cache/electron
9 | - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
10 |
11 | - os: linux
12 | services: docker
13 | language: generic
14 |
15 | cache:
16 | directories:
17 | - node_modules
18 | - $HOME/.cache/electron
19 | - $HOME/.cache/electron-builder
20 |
21 | script:
22 | - |
23 | if [ "$TRAVIS_OS_NAME" == "linux" ]; then
24 | docker run --rm \
25 | --env-file <(env | grep -vE '\r|\n' | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS_TAG|TRAVIS|TRAVIS_REPO_|TRAVIS_BUILD_|TRAVIS_BRANCH|TRAVIS_PULL_REQUEST_|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_') \
26 | -v ${PWD}:/project \
27 | -v ~/.cache/electron:/root/.cache/electron \
28 | -v ~/.cache/electron-builder:/root/.cache/electron-builder \
29 | electronuserland/builder:wine \
30 | /bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn release --linux --win"
31 | else
32 | yarn release
33 | fi
34 | before_cache:
35 | - rm -rf $HOME/.cache/electron-builder/wine
36 |
37 | branches:
38 | except:
39 | - "/^v\\d+\\.\\d+\\.\\d+$/"
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Lan Miao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > 特别提醒
2 | - *online* 分支是网络版,基于 [electron-forge](https://github.com/electron-userland/electron-forge) 开发,需要配合我的另一个开源项目 [招财猫](https://github.com/hilanmiao/smart-miao) 进行开发
3 | - *master* 分支是离线版,基于 [elctron-vue](https://github.com/SimulatedGREG/electron-vue) 开发
4 |
5 | 
6 |
7 | 招财猫系列应用截图:
8 |
9 |
10 |
11 | 网页端 |
12 | 移动端 |
13 | 桌面端 |
14 | 营销网站 |
15 |
16 |
17 |
18 |
19 | |
20 |
21 |
22 | |
23 |
24 |
25 | |
26 |
27 |
28 | |
29 |
30 |
31 |
32 | ***
33 |
34 | # PocketBook
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ## 概述
49 |
50 | **使用Electron开发一套记账软件,几乎每一个实现都会有一片博文说明。**
51 |
52 |
53 | ## 下载安装
54 |
55 | macOS用户请下载最新版本的`dmg`文件,windows用户请下载最新版本的`exe`文件,linux用户请下载`AppImage`文件。
56 |
57 | 点击此处下载[应用](https://github.com/hilanmiao/LanMiaoDesktop/releases)。
58 |
59 | 安装完毕,点击此处[尝试打开](PocketBook:?a=1&b=2)。
60 |
61 | [PocketBook:?a=1&b=2](PocketBook:?a=1&b=2 "PocketBook:?a=1&b=2")
62 |
63 | ## 应用截图
64 |
65 | 
66 |
67 | 
68 |
69 | ## 开发说明
70 |
71 | > 目前仅针对Windows。Mac、Linux平台并未测试。
72 |
73 | ``` bash
74 | # install dependencies
75 | npm install
76 |
77 | # serve with hot reload at localhost:9080
78 | npm run dev
79 |
80 | # build electron application for production
81 | npm run build
82 |
83 | # lint all JS/Vue component files in `src/`
84 | npm run lint
85 | ```
86 |
87 |
88 | ## 其他相关
89 |
90 | - [NSIS-UI](https://github.com/hilanmiao/NSIS-UI) windows exe安装包美化程序。
91 |
92 |
93 | ## 赞助
94 |
95 | 如果你喜欢它,不妨给它点个star或者请我喝杯咖啡
96 |
97 |
98 |
99 | 支付宝 |
100 | 微信 |
101 |
102 |
103 |
104 |
105 | |
106 |
107 |
108 | |
109 |
110 |
111 |
112 | ## 开源协议
113 |
114 | 本项目基于 [MIT](http://opensource.org/licenses/MIT) 协议,请自由地享受和参与开源。
115 |
116 |
117 | ## 贡献
118 |
119 | 如果你有好的意见或建议,欢迎给我们提 [issue] 或 [PR],为优化 [LanMiaoDesktop] 贡献力量
120 |
121 | [PR]: https://github.com/hilanmiao/LanMiaoDesktop/pulls
122 |
123 | [issue]: https://github.com/hilanmiao/LanMiaoDesktop/issues
124 |
125 | [LanMiaoDesktop]: https://github.com/youzan/vant-weapp
126 |
127 | ## 教程
128 |
129 | - [Electron开发实战之记账软件1——开篇](https://my.oschina.net/u/3667677/blog/3035515)
130 |
131 | - [Electron开发实战之记账软件2——使用electron-vue初始化项目并升级部分包](https://my.oschina.net/u/3667677/blog/3035513)
132 |
133 | - [Electron开发实战之记账软件3——使用UI框架vuetify实现页面](https://my.oschina.net/u/3667677/blog/3040223)
134 |
135 | - [Electron开发实战之记账软件4——无边框、窗口关闭、最大最小化、隐藏右键菜单](https://my.oschina.net/u/3667677/blog/3041467)
136 |
137 | - [Electron开发实战之记账软件5——制作ico格式的logo](https://my.oschina.net/u/3667677/blog/3041685)
138 |
139 | - [Electron开发实战之记账软件6——自定义系统托盘菜单、闪烁](https://my.oschina.net/u/3667677/blog/3041851)
140 |
141 | - [Electron开发实战之记账软件7——开机自动启动](https://my.oschina.net/u/3667677/blog/3042628)
142 |
143 | - [Electron开发实战之记账软件8——高级通知](https://my.oschina.net/u/3667677/blog/3043443)
144 |
145 | - [Electron开发实战之记账软件9——禁用一些事件](https://my.oschina.net/u/3667677/blog/3043516)
146 |
147 | - [Electron开发实战之记账软件10——保证只有一个实例](https://my.oschina.net/u/3667677/blog/3043529)
148 |
149 | - [Electron开发实战之记账软件11——打包及美化](https://my.oschina.net/u/3667677/blog/3043986)
150 |
151 | - [Electron开发实战之记账软件12——通过CI系统自动构建与发布](https://my.oschina.net/u/3667677/blog/3044025)
152 |
153 | - [Electron开发实战之记账软件13——添加Github徽章](https://my.oschina.net/u/3667677/blog/3044255)
154 |
155 | - [Electron开发实战之记账软件14——自动更新](https://my.oschina.net/u/3667677/blog/3046786)
156 |
157 | - [Electron开发实战之记账软件15——崩溃报告](https://my.oschina.net/u/3667677/blog/3047628)
158 |
159 | - [Electron开发实战之记账软件16——使用Docsify生成文档网站](https://my.oschina.net/u/3667677/blog/3048374)
160 |
161 | - [Electron开发实战之记账软件17——使用Lowdb存储数据](https://my.oschina.net/u/3667677/blog/3054535)
162 |
163 | - [Electron开发实战之记账软件18——备份、恢复、导入、导出](https://my.oschina.net/u/3667677/blog/3073660)
164 |
165 | - [Electron开发实战之记账软件19——通过协议唤起Electron应用](https://my.oschina.net/u/3667677/blog/3074681)
166 |
167 | Electron开发实战之记账软件1——迷你程序
168 |
169 | Electron开发实战之记账软件99——完结
170 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.0.{build}
2 |
3 | branches:
4 | only:
5 | - master
6 |
7 | image: Visual Studio 2019
8 | platform:
9 | - x64
10 |
11 | cache:
12 | - node_modules
13 | - '%APPDATA%\npm-cache'
14 | - '%USERPROFILE%\.electron'
15 | - '%USERPROFILE%\AppData\Local\Yarn\cache'
16 |
17 | init:
18 | - git config --global core.autocrlf input
19 |
20 | install:
21 | - ps: Install-Product node 12 x64
22 | - git reset --hard HEAD
23 | - yarn
24 | - node --version
25 |
26 | build_script:
27 | #- yarn build
28 | - npm run release
29 |
30 | test: off
31 |
--------------------------------------------------------------------------------
/assets/alipay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/assets/alipay.png
--------------------------------------------------------------------------------
/assets/commitMessageStandard.md:
--------------------------------------------------------------------------------
1 | #优雅的提交Commit信息
2 | 主要有以下组成
3 |
4 | - 标题行: 必填, 描述主要修改类型和内容
5 | - 主题内容: 描述为什么修改, 做了什么样的修改, 以及开发的思路等等
6 | - 页脚注释: 放 Breaking Changes 或 Closed Issues
7 |
8 | 常用的修改项
9 |
10 | - type: commit 的类型
11 | - feat: 新特性
12 | - fix: 修改问题
13 | - refactor: 代码重构
14 | - docs: 文档修改
15 | - style: 代码格式修改, 注意不是 css 修改
16 | - test: 测试用例修改
17 | - chore: 其他修改, 比如构建流程, 依赖管理.
18 | - scope: commit 影响的范围, 比如: route, component, utils, build...
19 | - subject: commit 的概述
20 | - body: commit 具体修改内容, 可以分为多行
21 | - footer: 一些备注, 通常是 BREAKING CHANGE 或修复的 bug 的链接.
22 |
23 | https://gitmoji.carloscuesta.me/
24 |
25 |
26 |
--------------------------------------------------------------------------------
/assets/home.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/assets/home.jpg
--------------------------------------------------------------------------------
/assets/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/assets/login.png
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/assets/logo.png
--------------------------------------------------------------------------------
/assets/tree.txt:
--------------------------------------------------------------------------------
1 | ├── .electron-vue // 配置及构建脚本
2 | │ ├── build.js // 生产环境构建脚本
3 | │ ├── dev-client.js // 开发服务器热重载脚本,主要用来实现开发阶段的页面自动刷新
4 | │ ├── dev-runner.js // 运行本地开发服务器
5 | │ ├── webpack.main.config.js // 主进程webpack配置文件
6 | │ ├── webpack.renderer.config.js // 渲染进程webpack配置文件
7 | │ └── webpack.web.config.js //
8 | ├── assets // 其他资源
9 | ├── build // 构建
10 | │ └── icons // 图标
11 | │ └── win-unpacked // 未打包资源
12 | ├── dist // 静态资源
13 | │ ├── electron
14 | │ │ └── main.js
15 | │ └── web
16 | ├── src // 源码目录
17 | │ ├── main // 主进程
18 | │ │ ├── index.dev.js
19 | │ │ └── index.js // 入口文件
20 | │ ├── renderer // 渲染进程
21 | │ │ ├── assets // 资源
22 | │ │ ├── components // 公共组件目录
23 | │ │ ├── router // 前端路由
24 | │ │ ├── store // 状态管理
25 | │ │ ├── views // 页面目录
26 | │ │ ├── App.vue // 根组件
27 | │ │ └── main.js // 入口文件
28 | │ └── index.ejs
29 | ├── static // 纯静态资源
30 | ├── .babelrc
31 | ├── .travis.yml // Windows和Linux持续集成配置文件
32 | ├── appveyor.yml // Mac持续集成配置文件
33 | ├── package-lock.json
34 | ├── package.json // 包配置文件
35 | └── README.md // 项目介绍
36 |
--------------------------------------------------------------------------------
/assets/treeNodeCli.md:
--------------------------------------------------------------------------------
1 | # 安装tree-node-cli
2 | `npm install -g tree-node-cli`
3 |
4 | # 忽略文件夹
5 | `tree -I "node_modules"`
6 |
7 | # 只看三级目录
8 | `tree -L 3`
9 |
10 | # 使用实例 Note: Use the command treee on Windows and Linux to avoid conflicts with built-in tree command.
11 | ```
12 | tree [options]
13 | treee -L 3 -I "node_modules|.idea|objects|.git" -a --dirs-first
14 | ```
15 |
16 | # 卸载
17 | `npm uninstall -g tree-node-cli`
--------------------------------------------------------------------------------
/assets/vueDevtools.md:
--------------------------------------------------------------------------------
1 | 举个例子:如果你用了ipc,在这里打断点平常是跑不到的,加上devtools才可以。
2 | ```
3 | ipcRenderer.on('getAutoStartValue', (event, result) => {
4 | this.autoStart = result
5 | })
6 | ```
7 |
8 | 0.局部或全局安装
9 | ```
10 | npm install -g @vue/devtools
11 | npm install --save-dev @vue/devtools
12 | ```
13 |
14 | 详细信息可查看
15 |
16 | https://github.com/vuejs/vue-devtools/blob/master/shells/electron/README.md
17 |
18 | 1.在vue前引入devtools
19 | ```
20 | import devtools from '@vue/devtools'
21 | import Vue from 'vue'
22 | ```
23 |
24 | 2.设置(暂时没用到)
25 | ```
26 | if (process.env.NODE_ENV === 'development') {
27 | devtools.connect('localhost', '9080')
28 | }
29 | ```
30 |
31 | 3.在index.ejs文件中添加
32 | ```
33 |
34 | ```
35 |
36 | 4.执行cmd文件(有两个文件vue-devtools,vue-devtools.cmd,因为我是windows,所以找到文件位置直接双击运行即可)
37 | ```
38 | ./node_modules/.bin/vue-devtools.cmd
39 | ```
40 |
--------------------------------------------------------------------------------
/assets/wechatpay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/assets/wechatpay.png
--------------------------------------------------------------------------------
/build/icons/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/build/icons/256x256.png
--------------------------------------------------------------------------------
/build/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/build/icons/icon.icns
--------------------------------------------------------------------------------
/build/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/build/icons/icon.ico
--------------------------------------------------------------------------------
/dist/electron/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/dist/electron/.gitkeep
--------------------------------------------------------------------------------
/dist/web/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/dist/web/.gitkeep
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # PocketBook
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ## 概述
16 |
17 | **使用Electron开发一套记账软件,几乎每一个实现都会有一片博文说明。**
18 |
19 |
20 | ## 下载安装
21 |
22 | macOS用户请下载最新版本的`dmg`文件,windows用户请下载最新版本的`exe`文件,linux用户请下载`AppImage`文件。
23 |
24 | 点击此处下载[应用](https://github.com/hilanmiao/LanMiaoDesktop/releases)。
25 |
26 | 安装完毕,点击此处[尝试打开](PocketBook:?a=1&b=2)。
27 |
28 | [PocketBook:?a=1&b=2](PocketBook:?a=1&b=2 "PocketBook:?a=1&b=2")
29 |
30 | ## 应用截图
31 |
32 | 
33 |
34 | 
35 |
36 | ## 开发说明
37 |
38 | > 目前仅针对Windows。Mac、Linux平台并未测试。
39 |
40 | ``` bash
41 | # install dependencies
42 | npm install
43 |
44 | # serve with hot reload at localhost:9080
45 | npm run dev
46 |
47 | # build electron application for production
48 | npm run build
49 |
50 | # lint all JS/Vue component files in `src/`
51 | npm run lint
52 | ```
53 |
54 |
55 | ## 其他相关
56 |
57 | - [NSIS-UI](https://github.com/hilanmiao/NSIS-UI):exe安装包美化程序。
58 |
59 |
60 | ## 赞助
61 |
62 | 如果你喜欢它,不妨给它点个star或者请我喝杯咖啡
63 |
64 | 支付宝:
65 |
66 |
67 |
68 | 微信:
69 |
70 |
71 |
72 |
73 | ## 开源协议
74 |
75 | 本项目基于 [MIT](http://opensource.org/licenses/MIT)协议,请自由地享受和参与开源。
76 |
77 |
78 | ## 贡献
79 |
80 | 如果你有好的意见或建议,欢迎给我们提 [issue] 或 [PR],为优化 [LanMiaoDesktop] 贡献力量
81 |
82 | [PR]: https://github.com/hilanmiao/LanMiaoDesktop/pulls
83 |
84 | [issue]: https://github.com/hilanmiao/LanMiaoDesktop/issues
85 |
86 | [LanMiaoDesktop]: https://github.com/youzan/vant-weapp
87 |
88 | ## 教程
89 |
90 | - [Electron开发实战之记账软件1——开篇](https://my.oschina.net/u/3667677/blog/3035515)
91 |
92 | - [Electron开发实战之记账软件2——使用electron-vue初始化项目并升级部分包](https://my.oschina.net/u/3667677/blog/3035513)
93 |
94 | - [Electron开发实战之记账软件3——使用UI框架vuetify实现页面](https://my.oschina.net/u/3667677/blog/3040223)
95 |
96 | - [Electron开发实战之记账软件4——无边框、窗口关闭、最大最小化、隐藏右键菜单](https://my.oschina.net/u/3667677/blog/3041467)
97 |
98 | - [Electron开发实战之记账软件5——制作ico格式的logo](https://my.oschina.net/u/3667677/blog/3041685)
99 |
100 | - [Electron开发实战之记账软件6——自定义系统托盘菜单、闪烁](https://my.oschina.net/u/3667677/blog/3041851)
101 |
102 | - [Electron开发实战之记账软件7——开机自动启动](https://my.oschina.net/u/3667677/blog/3042628)
103 |
104 | - [Electron开发实战之记账软件8——高级通知](https://my.oschina.net/u/3667677/blog/3043443)
105 |
106 | - [Electron开发实战之记账软件9——禁用一些事件](https://my.oschina.net/u/3667677/blog/3043516)
107 |
108 | - [Electron开发实战之记账软件10——保证只有一个实例](https://my.oschina.net/u/3667677/blog/3043529)
109 |
110 | - [Electron开发实战之记账软件11——打包及美化](https://my.oschina.net/u/3667677/blog/3043986)
111 |
112 | - [Electron开发实战之记账软件12——通过CI系统自动构建与发布](https://my.oschina.net/u/3667677/blog/3044025)
113 |
114 | - [Electron开发实战之记账软件13——添加Github徽章](https://my.oschina.net/u/3667677/blog/3044255)
115 |
116 | - [Electron开发实战之记账软件14——自动更新](https://my.oschina.net/u/3667677/blog/3046786)
117 |
118 | - [Electron开发实战之记账软件15——崩溃报告](https://my.oschina.net/u/3667677/blog/3047628)
119 |
120 | - [Electron开发实战之记账软件16——使用Docsify生成文档网站](https://my.oschina.net/u/3667677/blog/3048374)
121 |
122 | - [Electron开发实战之记账软件17——使用Lowdb存储数据](https://my.oschina.net/u/3667677/blog/3054535)
123 |
124 | - [Electron开发实战之记账软件18——备份、恢复、导入、导出](https://my.oschina.net/u/3667677/blog/3073660)
125 |
126 | - [Electron开发实战之记账软件19——通过协议唤起Electron应用](https://my.oschina.net/u/3667677/blog/3074681)
127 |
128 | Electron开发实战之记账软件1——迷你程序
129 |
130 | Electron开发实战之记账软件99——完结
131 |
--------------------------------------------------------------------------------
/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # PocketBook 0.0.1
4 |
5 | > A simple electron application
6 |
7 | [Star Me](https://github.com/hilanmiao/LanMiaoDesktop)
8 | [Get Started](#main)
9 |
--------------------------------------------------------------------------------
/docs/_navbar.md:
--------------------------------------------------------------------------------
1 | - [中文](/)
2 | - [English](/en/)
3 |
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | - [**介绍**]()
2 | - [**常见问题**](faq)
3 | - [**更新日志**](changelog)
--------------------------------------------------------------------------------
/docs/assets/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/docs/assets/icon.ico
--------------------------------------------------------------------------------
/docs/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/docs/assets/logo.png
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | ## 4.9.2(2019-04-21)
2 |
3 |
4 | ### Bug Fixes(Just an example)
5 |
6 | * re-render gitalk when router changed ([11ea1f8](https://github.com/docsifyjs/docsify/commit/11ea1f8))
7 |
8 |
9 | ### Features
10 |
11 | * allows relative path, fixed [#590](https://github.com/docsifyjs/docsify/issues/590) ([31654f1](https://github.com/docsifyjs/docsify/commit/31654f1))
12 |
--------------------------------------------------------------------------------
/docs/en/README.md:
--------------------------------------------------------------------------------
1 | # building
--------------------------------------------------------------------------------
/docs/en/_sidebar.md:
--------------------------------------------------------------------------------
1 | - [**Introduction**](en/)
2 | - [**FAQ**](en/faq)
3 | - [**Changelog**](en/changelog)
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | building
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PocketBook - An electron-vue project
6 |
7 |
8 |
10 |
11 |
12 |
26 |
27 |
28 | Loading...
29 |
30 |
31 |
32 |
73 |
74 |
75 |
76 |
77 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/docs/sw.js:
--------------------------------------------------------------------------------
1 | /* ===========================================================
2 | * docsify sw.js
3 | * ===========================================================
4 | * Copyright 2016 @huxpro
5 | * Licensed under Apache 2.0
6 | * Register service worker.
7 | * ========================================================== */
8 |
9 | const RUNTIME = 'docsify'
10 | const HOSTNAME_WHITELIST = [
11 | self.location.hostname,
12 | 'fonts.gstatic.com',
13 | 'fonts.googleapis.com',
14 | 'unpkg.com'
15 | ]
16 |
17 | // The Util Function to hack URLs of intercepted requests
18 | const getFixedUrl = (req) => {
19 | var now = Date.now()
20 | var url = new URL(req.url)
21 |
22 | // 1. fixed http URL
23 | // Just keep syncing with location.protocol
24 | // fetch(httpURL) belongs to active mixed content.
25 | // And fetch(httpRequest) is not supported yet.
26 | url.protocol = self.location.protocol
27 |
28 | // 2. add query for caching-busting.
29 | // Github Pages served with Cache-Control: max-age=600
30 | // max-age on mutable content is error-prone, with SW life of bugs can even extend.
31 | // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
32 | // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
33 | if (url.hostname === self.location.hostname) {
34 | url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
35 | }
36 | return url.href
37 | }
38 |
39 | /**
40 | * @Lifecycle Activate
41 | * New one activated when old isnt being used.
42 | *
43 | * waitUntil(): activating ====> activated
44 | */
45 | self.addEventListener('activate', event => {
46 | event.waitUntil(self.clients.claim())
47 | })
48 |
49 | /**
50 | * @Functional Fetch
51 | * All network requests are being intercepted here.
52 | *
53 | * void respondWith(Promise r)
54 | */
55 | self.addEventListener('fetch', event => {
56 | // Skip some of cross-origin requests, like those for Google Analytics.
57 | if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
58 | // Stale-while-revalidate
59 | // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
60 | // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
61 | const cached = caches.match(event.request)
62 | const fixedUrl = getFixedUrl(event.request)
63 | const fetched = fetch(fixedUrl, { cache: 'no-store' })
64 | const fetchedCopy = fetched.then(resp => resp.clone())
65 |
66 | // Call respondWith() with whatever we get first.
67 | // If the fetch fails (e.g disconnected), wait for the cache.
68 | // If there’s nothing in cache, wait for the fetch.
69 | // If neither yields a response, return offline pages.
70 | event.respondWith(
71 | Promise.race([fetched.catch(_ => cached), cached])
72 | .then(resp => resp || fetched)
73 | .catch(_ => { /* eat any errors */ })
74 | )
75 |
76 | // Update the cache with the version we fetched (only for ok status)
77 | event.waitUntil(
78 | Promise.all([fetchedCopy, caches.open(RUNTIME)])
79 | .then(([response, cache]) => response.ok && cache.put(event.request, response))
80 | .catch(_ => { /* eat any errors */ })
81 | )
82 | }
83 | })
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "PocketBook",
3 | "version": "1.0.0",
4 | "author": "hilanmiao ",
5 | "description": "An electron-vue project",
6 | "license": "MIT",
7 | "main": "./dist/electron/main.js",
8 | "scripts": {
9 | "build": "node .electron-vue/build.js && electron-builder",
10 | "build:dir": "node .electron-vue/build.js && electron-builder --dir",
11 | "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
12 | "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
13 | "dev": "node .electron-vue/dev-runner.js",
14 | "pack": "npm run pack:main && npm run pack:renderer",
15 | "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
16 | "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
17 | "postinstall": "electron-builder install-app-deps",
18 | "release": "node .electron-vue/build.js && electron-builder",
19 | "dist": "node .electron-vue/build.js && electron-builder"
20 | },
21 | "build": {
22 | "productName": "PocketBook",
23 | "appId": "com.electron.PocketBook",
24 | "copyright": "Copyright © 2019 lanmiao",
25 | "directories": {
26 | "output": "build"
27 | },
28 | "publish": {
29 | "provider": "github",
30 | "repo": "LanMiaoDesktop",
31 | "owner": "hilanmiao",
32 | "releaseType": "release",
33 | "private": false
34 | },
35 | "files": [
36 | "dist/electron/**/*"
37 | ],
38 | "dmg": {
39 | "contents": [
40 | {
41 | "x": 410,
42 | "y": 150,
43 | "type": "link",
44 | "path": "/Applications"
45 | },
46 | {
47 | "x": 130,
48 | "y": 150,
49 | "type": "file"
50 | }
51 | ]
52 | },
53 | "mac": {
54 | "icon": "build/icons/icon.icns"
55 | },
56 | "win": {
57 | "icon": "build/icons/icon.ico",
58 | "requestedExecutionLevel": "highestAvailable",
59 | "target": "nsis"
60 | },
61 | "nsis": {
62 | "oneClick": false,
63 | "allowToChangeInstallationDirectory": true
64 | },
65 | "linux": {
66 | "icon": "build/icons",
67 | "target": [
68 | "AppImage"
69 | ]
70 | }
71 | },
72 | "dependencies": {
73 | "@sentry/electron": "^0.17.1",
74 | "axios": "^0.21.1",
75 | "electron-updater": "^4.3.9",
76 | "element-ui": "^2.9.1",
77 | "exceljs": "^3.5.0",
78 | "fs-extra": "^8.0.1",
79 | "lodash": "^4.17.21",
80 | "lodash-id": "^0.14.0",
81 | "lowdb": "^1.0.0",
82 | "material-design-icons-iconfont": "^4.0.5",
83 | "moment": "^2.24.0",
84 | "vue": "^2.6.10",
85 | "vue-electron": "^1.0.6",
86 | "vue-router": "^3.0.3",
87 | "vuetify": "^1.5.11",
88 | "vuex": "^3.1.0",
89 | "vuex-electron": "^1.0.3",
90 | "winreg": "^1.2.4"
91 | },
92 | "devDependencies": {
93 | "@vue/devtools": "^5.0.9",
94 | "ajv": "^6.10.0",
95 | "babel-core": "^6.26.3",
96 | "babel-loader": "^7.1.4",
97 | "babel-minify-webpack-plugin": "^0.3.1",
98 | "babel-plugin-component": "^1.1.1",
99 | "babel-plugin-transform-runtime": "^6.23.0",
100 | "babel-preset-env": "^1.7.0",
101 | "babel-preset-stage-0": "^6.24.1",
102 | "babel-register": "^6.26.0",
103 | "cfonts": "^2.4.2",
104 | "chalk": "^2.4.2",
105 | "copy-webpack-plugin": "^5.0.2",
106 | "cross-env": "^5.2.0",
107 | "css-loader": "^2.1.1",
108 | "del": "^4.1.0",
109 | "devtron": "^1.4.0",
110 | "electron": "^9.4.0",
111 | "electron-builder": "^22.4.0",
112 | "electron-debug": "^2.2.0",
113 | "electron-devtools-installer": "^2.2.4",
114 | "file-loader": "^3.0.1",
115 | "html-webpack-plugin": "^3.2.0",
116 | "mini-css-extract-plugin": "^0.6.0",
117 | "multispinner": "^0.2.1",
118 | "node-loader": "^0.6.0",
119 | "sass": "^1.47.0",
120 | "sass-loader": "^7.1.0",
121 | "style-loader": "^0.23.1",
122 | "url-loader": "^1.1.2",
123 | "vue-html-loader": "^1.2.4",
124 | "vue-loader": "^15.7.0",
125 | "vue-style-loader": "^4.1.2",
126 | "vue-template-compiler": "^2.6.10",
127 | "webpack": "^4.29.6",
128 | "webpack-cli": "^3.3.0",
129 | "webpack-dev-server": "^3.3.1",
130 | "webpack-hot-middleware": "^2.24.3",
131 | "webpack-merge": "^4.2.1"
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/screenshot-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/screenshot-app.png
--------------------------------------------------------------------------------
/screenshot-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/screenshot-desktop.png
--------------------------------------------------------------------------------
/screenshot-frontend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/screenshot-frontend.png
--------------------------------------------------------------------------------
/screenshot-home-online.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/screenshot-home-online.png
--------------------------------------------------------------------------------
/screenshot-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/screenshot-web.png
--------------------------------------------------------------------------------
/sentry-symbols.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | let SentryCli;
4 | let download;
5 |
6 | try {
7 | SentryCli = require('@sentry/cli');
8 | download = require('electron-download');
9 | } catch (e) {
10 | console.error('ERROR: Missing required packages, please run:');
11 | console.error('npm install --save-dev @sentry/cli electron-download');
12 | process.exit(1);
13 | }
14 |
15 | const VERSION = /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?\b/i;
16 | const SYMBOL_CACHE_FOLDER = '.electron-symbols';
17 | const package = require('./package.json');
18 | const sentryCli = new SentryCli('./sentry.properties');
19 |
20 | async function main() {
21 | let version = getElectronVersion();
22 | if (!version) {
23 | console.error('Cannot detect electron version, check package.json');
24 | return;
25 | }
26 |
27 | console.log('We are starting to download all possible electron symbols');
28 | console.log('We need it in order to symbolicate native crashes');
29 | console.log(
30 | 'This step is only needed once whenever you update your electron version',
31 | );
32 | console.log('Just call this script again it should do everything for you.');
33 |
34 | let zipPath = await downloadSymbols({
35 | version,
36 | platform: 'darwin',
37 | arch: 'x64',
38 | dsym: true,
39 | });
40 | await sentryCli.execute(['upload-dif', '-t', 'dsym', zipPath], true);
41 |
42 | zipPath = await downloadSymbols({
43 | version,
44 | platform: 'win32',
45 | arch: 'ia32',
46 | symbols: true,
47 | });
48 | await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);
49 |
50 | zipPath = await downloadSymbols({
51 | version,
52 | platform: 'win32',
53 | arch: 'x64',
54 | symbols: true,
55 | });
56 | await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);
57 |
58 | zipPath = await downloadSymbols({
59 | version,
60 | platform: 'linux',
61 | arch: 'x64',
62 | symbols: true,
63 | });
64 | await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);
65 |
66 | console.log('Finished downloading and uploading to Sentry');
67 | console.log(`Feel free to delete the ${SYMBOL_CACHE_FOLDER}`);
68 | }
69 |
70 | function getElectronVersion() {
71 | if (!package) {
72 | return false;
73 | }
74 |
75 | let electronVersion =
76 | (package.dependencies && package.dependencies.electron) ||
77 | (package.devDependencies && package.devDependencies.electron);
78 |
79 | if (!electronVersion) {
80 | return false;
81 | }
82 |
83 | const matches = VERSION.exec(electronVersion);
84 | return matches ? matches[0] : false;
85 | }
86 |
87 | async function downloadSymbols(options) {
88 | return new Promise((resolve, reject) => {
89 | download(
90 | {
91 | ...options,
92 | cache: SYMBOL_CACHE_FOLDER,
93 | },
94 | (err, zipPath) => {
95 | if (err) {
96 | reject(err);
97 | } else {
98 | resolve(zipPath);
99 | }
100 | },
101 | );
102 | });
103 | }
104 |
105 | main().catch(e => console.error(e));
106 |
--------------------------------------------------------------------------------
/sentry.properties:
--------------------------------------------------------------------------------
1 | defaults.url=https://sentry.io/
2 | defaults.org=lanmiao
3 | defaults.project=lanmiaodesktop
4 | auth.token=d57bc5f368ae4c05bd6c59cac9631a6ed12b4811b2c44081a73a765b125ec9c2
5 | cli.executable=C:\\Users\\sheny\\AppData\\Roaming\\npm\\node_modules\\@sentry\\wizard\\node_modules\\@sentry\\cli\\bin\\sentry-cli
6 |
--------------------------------------------------------------------------------
/snap/snapcraft.yaml:
--------------------------------------------------------------------------------
1 |
2 | name: lanmiaodesktop
3 | version: '1.0.0' # just for humans, typically '1.2+git' or '1.3.2'
4 | summary: Desktop bookkeeping program(Electron demo) # 79 char long summary
5 | description: |
6 | this is an Electron demo.
7 | https://github.com/hilanmiao/LanMiaoDesktop
8 |
9 | #grade: devel # must be 'stable' to release into candidate/stable channels
10 | #confinement: devmode # use 'strict' once you have the right plugs and slots
11 | grade: stable # must be 'stable' to release into candidate/stable channels
12 | confinement: strict # use 'strict' once you have the right plugs and slots
13 |
14 | parts:
15 | my-part:
16 | # See 'snapcraft plugins'
17 | plugin: nil
18 |
19 | #
20 | #
21 | #sudo: false
22 | #
23 | #language: node_js
24 | #node_js: "9.11.1"
25 | #
26 | #before_install:
27 | # - cd app
28 | #
29 | #cache:
30 | # directories:
31 | # - node_modules
32 | # - app/node_modules
33 | # - $HOME/.cache/electron
34 | # - $HOME/.cache/electron-builder
35 | # - $HOME/.npm/_prebuilds
36 | #
37 | #env:
38 | # global:
39 | # - ELECTRON_CACHE=$HOME/.cache/electron
40 | # - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
41 | #
42 | #jobs:
43 | # include:
44 | # - stage: Test
45 | # script:
46 | # - npm test
47 | # - stage: Deploy Windows & Mac
48 | # if: tag IS present
49 | # os: osx
50 | # osx_image: xcode10.1
51 | # script:
52 | # - npm run release -- --mac --win
53 | # before_cache:
54 | # - rm -rf $HOME/.cache/electron-builder/wine
55 | # - stage: Deploy linux
56 | # if: tag IS present
57 | # os: linux
58 | # dist: trusty
59 | # script:
60 | # - npm run release
61 |
--------------------------------------------------------------------------------
/src/api/assets.js:
--------------------------------------------------------------------------------
1 | import db from '../datastore'
2 | import _ from 'lodash'
3 |
4 | const Table = 'assets'
5 |
6 | export function getModelById(id) {
7 | return new Promise((resolve, reject) => {
8 | try {
9 | const collection = db.get(Table)
10 | const model = collection.getById(id).value()
11 | resolve({
12 | code: 200,
13 | data: _.cloneDeep(model)
14 | })
15 | } catch (err) {
16 | return reject({
17 | code: 400,
18 | message: err.message
19 | })
20 | }
21 | })
22 | }
23 |
24 | export function getModelWhere(attrs) {
25 | return new Promise((resolve, reject) => {
26 | try {
27 | const collection = db.get(Table)
28 | const list = collection.filter(attrs).value()
29 | resolve({
30 | code: 200,
31 | data: _.cloneDeep(list)
32 | })
33 | } catch (err) {
34 | return reject({
35 | code: 400,
36 | message: err.message
37 | })
38 | }
39 | })
40 | }
41 |
42 | export function getModelAll() {
43 | return new Promise((resolve, reject) => {
44 | try {
45 | const collection = db.get(Table)
46 | const list = collection.value()
47 | resolve({
48 | code: 200,
49 | data: _.cloneDeep(list)
50 | })
51 | } catch (err) {
52 | return reject({
53 | code: 400,
54 | message: err.message
55 | })
56 | }
57 | })
58 | }
59 |
60 | export function getModelPagination(pagination, whereAttrs, filterFun) {
61 | return new Promise((resolve, reject) => {
62 | try {
63 | const collection = db.get(Table)
64 | const total = collection.size().value()
65 | const list = collection
66 | .filter(filterFun)
67 | .orderBy(pagination.sortBy, pagination.descending ? 'desc' : 'asc')
68 | .chunk(pagination.rowsPerPage === -1 ? total : pagination.rowsPerPage)
69 | .take(pagination.page)
70 | .last() // 因为上面用了chunk,是个二维数组,所以这里取最后一个
71 | .value()
72 | resolve({
73 | code: 200,
74 | data: _.cloneDeep({total: total, list: list})
75 | })
76 | } catch (err) {
77 | return reject({
78 | code: 400,
79 | message: err.message
80 | })
81 | }
82 | })
83 | }
84 |
85 | export function postModel(document) {
86 | return new Promise((resolve, reject) => {
87 | try {
88 | const collection = db.get(Table)
89 | const model = collection.insert(document).write()
90 | resolve({
91 | code: 200,
92 | data: _.cloneDeep(model)
93 | })
94 | } catch (err) {
95 | return reject({
96 | code: 400,
97 | message: err.message
98 | })
99 | }
100 | })
101 | }
102 |
103 | export function postOrPutModel(document) {
104 | return new Promise((resolve, reject) => {
105 | try {
106 | const collection = db.get(Table)
107 | const model = collection.upsert(document).write()
108 | resolve({
109 | code: 200,
110 | data: _.cloneDeep(model)
111 | })
112 | } catch (err) {
113 | return reject({
114 | code: 400,
115 | message: err.message
116 | })
117 | }
118 | })
119 | }
120 |
121 | export function putModelById(id, attrs) {
122 | return new Promise((resolve, reject) => {
123 | try {
124 | const collection = db.get(Table)
125 | const model = collection.updateById(id, attrs).write()
126 | resolve({
127 | code: 200,
128 | data: _.cloneDeep(model)
129 | })
130 | } catch (err) {
131 | return reject({
132 | code: 400,
133 | message: err.message
134 | })
135 | }
136 | })
137 | }
138 |
139 | export function putModelWhere(whereAttrs, attrs) {
140 | return new Promise((resolve, reject) => {
141 | try {
142 | const collection = db.get(Table)
143 | const model = collection.updateWhere(whereAttrs, attrs).write()
144 | resolve({
145 | code: 200,
146 | data: _.cloneDeep(model)
147 | })
148 | } catch (err) {
149 | return reject({
150 | code: 400,
151 | message: err.message
152 | })
153 | }
154 | })
155 | }
156 |
157 | export function replaceModelById(id, attrs) {
158 | return new Promise((resolve, reject) => {
159 | try {
160 | const collection = db.get(Table)
161 | const model = collection.replaceById(id, attrs).write()
162 | resolve({
163 | code: 200,
164 | data: _.cloneDeep(model)
165 | })
166 | } catch (err) {
167 | return reject({
168 | code: 400,
169 | message: err.message
170 | })
171 | }
172 | })
173 | }
174 |
175 | export function deleteModelById(id) {
176 | return new Promise((resolve, reject) => {
177 | try {
178 | const collection = db.get(Table)
179 | collection.removeById(id).write()
180 | resolve({
181 | code: 200
182 | })
183 | } catch (err) {
184 | return reject({
185 | code: 400,
186 | message: err.message
187 | })
188 | }
189 | })
190 | }
191 |
192 | export function deleteModelByIds(ids) {
193 | return new Promise((resolve, reject) => {
194 | try {
195 | const collection = db.get(Table)
196 | ids.forEach(id => {
197 | collection.removeById(id).write()
198 | })
199 | resolve({
200 | code: 200
201 | })
202 | } catch (err) {
203 | return reject({
204 | code: 400,
205 | message: err.message
206 | })
207 | }
208 | })
209 | }
210 |
211 | export function deleteModelWhere(whereAttrs) {
212 | return new Promise((resolve, reject) => {
213 | try {
214 | const collection = db.get(Table)
215 | const list = collection.removeWhere(whereAttrs).write()
216 | resolve({
217 | code: 200,
218 | data: _.cloneDeep(list)
219 | })
220 | } catch (err) {
221 | return reject({
222 | code: 400,
223 | message: err.message
224 | })
225 | }
226 | })
227 | }
228 |
--------------------------------------------------------------------------------
/src/api/category.js:
--------------------------------------------------------------------------------
1 | import db from '../datastore'
2 | import _ from 'lodash'
3 |
4 | const Table = 'category'
5 |
6 | export function getModelById(id) {
7 | return new Promise((resolve, reject) => {
8 | try {
9 | const collection = db.get(Table)
10 | const model = collection.getById(id).value()
11 | resolve({
12 | code: 200,
13 | data: _.cloneDeep(model)
14 | })
15 | } catch (err) {
16 | return reject({
17 | code: 400,
18 | message: err.message
19 | })
20 | }
21 | })
22 | }
23 |
24 | export function getModelWhere(attrs) {
25 | return new Promise((resolve, reject) => {
26 | try {
27 | const collection = db.get(Table)
28 | const list = collection.filter(attrs).value()
29 | resolve({
30 | code: 200,
31 | data: _.cloneDeep(list)
32 | })
33 | } catch (err) {
34 | return reject({
35 | code: 400,
36 | message: err.message
37 | })
38 | }
39 | })
40 | }
41 |
42 | export function getModelAll() {
43 | return new Promise((resolve, reject) => {
44 | try {
45 | const collection = db.get(Table)
46 | const list = collection.value()
47 | resolve({
48 | code: 200,
49 | data: _.cloneDeep(list)
50 | })
51 | } catch (err) {
52 | return reject({
53 | code: 400,
54 | message: err.message
55 | })
56 | }
57 | })
58 | }
59 |
60 | export function getModelPagination(pagination, whereAttrs, filterFun) {
61 | return new Promise((resolve, reject) => {
62 | try {
63 | const collection = db.get(Table)
64 | const total = collection.size().value()
65 | const list = collection
66 | .filter(filterFun)
67 | .orderBy(pagination.sortBy, pagination.descending ? 'desc' : 'asc')
68 | .chunk(pagination.rowsPerPage === -1 ? total : pagination.rowsPerPage)
69 | .take(pagination.page)
70 | .last() // 因为上面用了chunk,是个二维数组,所以这里取最后一个
71 | .value()
72 | resolve({
73 | code: 200,
74 | data: _.cloneDeep({total: total, list: list})
75 | })
76 | } catch (err) {
77 | return reject({
78 | code: 400,
79 | message: err.message
80 | })
81 | }
82 | })
83 | }
84 |
85 | export function postModel(document) {
86 | return new Promise((resolve, reject) => {
87 | try {
88 | const collection = db.get(Table)
89 | const model = collection.insert(document).write()
90 | resolve({
91 | code: 200,
92 | data: _.cloneDeep(model)
93 | })
94 | } catch (err) {
95 | return reject({
96 | code: 400,
97 | message: err.message
98 | })
99 | }
100 | })
101 | }
102 |
103 | export function postOrPutModel(document) {
104 | return new Promise((resolve, reject) => {
105 | try {
106 | const collection = db.get(Table)
107 | const model = collection.upsert(document).write()
108 | resolve({
109 | code: 200,
110 | data: _.cloneDeep(model)
111 | })
112 | } catch (err) {
113 | return reject({
114 | code: 400,
115 | message: err.message
116 | })
117 | }
118 | })
119 | }
120 |
121 | export function putModelById(id, attrs) {
122 | return new Promise((resolve, reject) => {
123 | try {
124 | const collection = db.get(Table)
125 | const model = collection.updateById(id, attrs).write()
126 | resolve({
127 | code: 200,
128 | data: _.cloneDeep(model)
129 | })
130 | } catch (err) {
131 | return reject({
132 | code: 400,
133 | message: err.message
134 | })
135 | }
136 | })
137 | }
138 |
139 | export function putModelWhere(whereAttrs, attrs) {
140 | return new Promise((resolve, reject) => {
141 | try {
142 | const collection = db.get(Table)
143 | const model = collection.updateWhere(whereAttrs, attrs).write()
144 | resolve({
145 | code: 200,
146 | data: _.cloneDeep(model)
147 | })
148 | } catch (err) {
149 | return reject({
150 | code: 400,
151 | message: err.message
152 | })
153 | }
154 | })
155 | }
156 |
157 | export function replaceModelById(id, attrs) {
158 | return new Promise((resolve, reject) => {
159 | try {
160 | const collection = db.get(Table)
161 | const model = collection.replaceById(id, attrs).write()
162 | resolve({
163 | code: 200,
164 | data: _.cloneDeep(model)
165 | })
166 | } catch (err) {
167 | return reject({
168 | code: 400,
169 | message: err.message
170 | })
171 | }
172 | })
173 | }
174 |
175 | export function deleteModelById(id) {
176 | return new Promise((resolve, reject) => {
177 | try {
178 | const collection = db.get(Table)
179 | collection.removeById(id).write()
180 | resolve({
181 | code: 200
182 | })
183 | } catch (err) {
184 | return reject({
185 | code: 400,
186 | message: err.message
187 | })
188 | }
189 | })
190 | }
191 |
192 | export function deleteModelByIds(ids) {
193 | return new Promise((resolve, reject) => {
194 | try {
195 | const collection = db.get(Table)
196 | ids.forEach(id => {
197 | collection.removeById(id).write()
198 | })
199 | resolve({
200 | code: 200
201 | })
202 | } catch (err) {
203 | return reject({
204 | code: 400,
205 | message: err.message
206 | })
207 | }
208 | })
209 | }
210 |
211 | export function deleteModelWhere(whereAttrs) {
212 | return new Promise((resolve, reject) => {
213 | try {
214 | const collection = db.get(Table)
215 | const list = collection.removeWhere(whereAttrs).write()
216 | resolve({
217 | code: 200,
218 | data: _.cloneDeep(list)
219 | })
220 | } catch (err) {
221 | return reject({
222 | code: 400,
223 | message: err.message
224 | })
225 | }
226 | })
227 | }
228 |
--------------------------------------------------------------------------------
/src/api/incomeAndExpenditure.js:
--------------------------------------------------------------------------------
1 | import db from '../datastore'
2 | import _ from 'lodash'
3 |
4 | const Table = 'incomeAndExpenditure'
5 | const TableAssets = 'assets'
6 |
7 | export function getModelById(id) {
8 | return new Promise((resolve, reject) => {
9 | try {
10 | const collection = db.get(Table)
11 | const model = collection.getById(id).value()
12 | resolve({
13 | code: 200,
14 | data: _.cloneDeep(model)
15 | })
16 | } catch (err) {
17 | return reject({
18 | code: 400,
19 | message: err.message
20 | })
21 | }
22 | })
23 | }
24 |
25 | export function getModelWhere(attrs) {
26 | return new Promise((resolve, reject) => {
27 | try {
28 | const collection = db.get(Table)
29 | const list = collection.find(attrs).value()
30 | resolve({
31 | code: 200,
32 | data: _.cloneDeep(list)
33 | })
34 | } catch (err) {
35 | return reject({
36 | code: 400,
37 | message: err.message
38 | })
39 | }
40 | })
41 | }
42 |
43 | export function getModelAll() {
44 | return new Promise((resolve, reject) => {
45 | try {
46 | const collection = db.get(Table)
47 | const list = collection.value()
48 | resolve({
49 | code: 200,
50 | data: _.cloneDeep(list)
51 | })
52 | } catch (err) {
53 | return reject({
54 | code: 400,
55 | message: err.message
56 | })
57 | }
58 | })
59 | }
60 |
61 | export function getModelExport(filterFun) {
62 | return new Promise((resolve, reject) => {
63 | try {
64 | const collection = db.get(Table)
65 | const list = collection.filter(filterFun).value()
66 | resolve({
67 | code: 200,
68 | data: _.cloneDeep(list)
69 | })
70 | } catch (err) {
71 | return reject({
72 | code: 400,
73 | message: err.message
74 | })
75 | }
76 | })
77 | }
78 |
79 | export function getModelPagination(pagination, whereAttrs, filterFun) {
80 | return new Promise((resolve, reject) => {
81 | try {
82 | const collection = db.get(Table)
83 | const total = collection.size().value()
84 | const list = collection
85 | .filter(filterFun)
86 | .orderBy(pagination.sortBy, pagination.descending ? 'desc' : 'asc')
87 | .chunk(pagination.rowsPerPage === -1 ? total : pagination.rowsPerPage)
88 | .take(pagination.page)
89 | .last() // 因为上面用了chunk,是个二维数组,所以这里取最后一个
90 | .value()
91 | resolve({
92 | code: 200,
93 | data: _.cloneDeep({total: total, list: list})
94 | })
95 | } catch (err) {
96 | return reject({
97 | code: 400,
98 | message: err.message
99 | })
100 | }
101 | })
102 | }
103 |
104 | export function postModel(document) {
105 | return new Promise((resolve, reject) => {
106 | try {
107 | const collection = db.get(Table)
108 | const model = collection.insert(document).write()
109 | const collectionAssets = db.get(TableAssets)
110 | let assetsAmountOfMoney = 0
111 | collection.filter({assetsId: model.assetsId}).value().forEach(item => {
112 | assetsAmountOfMoney += item.type ==='e' ? -item.amountOfMoney : item.amountOfMoney
113 | })
114 | // 更新资产表
115 | collectionAssets.updateById(model.assetsId, {assetsAmountOfMoney: assetsAmountOfMoney}).write()
116 | resolve({
117 | code: 200,
118 | data: _.cloneDeep(model)
119 | })
120 | } catch (err) {
121 | return reject({
122 | code: 400,
123 | message: err.message
124 | })
125 | }
126 | })
127 | }
128 |
129 | export function postOrPutModel(document) {
130 | return new Promise((resolve, reject) => {
131 | try {
132 | const collection = db.get(Table)
133 | const model = collection.upsert(document).write()
134 | resolve({
135 | code: 200,
136 | data: _.cloneDeep(model)
137 | })
138 | } catch (err) {
139 | return reject({
140 | code: 400,
141 | message: err.message
142 | })
143 | }
144 | })
145 | }
146 |
147 | export function putModelById(id, attrs) {
148 | return new Promise((resolve, reject) => {
149 | try {
150 | const collection = db.get(Table)
151 | const model = collection.updateById(id, attrs).write()
152 | const collectionAssets = db.get(TableAssets)
153 | let assetsAmountOfMoney = 0
154 | collection.filter({assetsId: model.assetsId}).value().forEach(item => {
155 | assetsAmountOfMoney += item.type ==='e' ? -item.amountOfMoney : item.amountOfMoney
156 | })
157 | // 更新资产表
158 | collectionAssets.updateById(model.assetsId, {assetsAmountOfMoney: assetsAmountOfMoney}).write()
159 | resolve({
160 | code: 200,
161 | data: _.cloneDeep(model)
162 | })
163 | } catch (err) {
164 | return reject({
165 | code: 400,
166 | message: err.message
167 | })
168 | }
169 | })
170 | }
171 |
172 | export function putModelWhere(whereAttrs, attrs) {
173 | return new Promise((resolve, reject) => {
174 | try {
175 | const collection = db.get(Table)
176 | const model = collection.updateWhere(whereAttrs, attrs).write()
177 | resolve({
178 | code: 200,
179 | data: _.cloneDeep(model)
180 | })
181 | } catch (err) {
182 | return reject({
183 | code: 400,
184 | message: err.message
185 | })
186 | }
187 | })
188 | }
189 |
190 | export function replaceModelById(id, attrs) {
191 | return new Promise((resolve, reject) => {
192 | try {
193 | const collection = db.get(Table)
194 | const model = collection.replaceById(id, attrs).write()
195 | resolve({
196 | code: 200,
197 | data: _.cloneDeep(model)
198 | })
199 | } catch (err) {
200 | return reject({
201 | code: 400,
202 | message: err.message
203 | })
204 | }
205 | })
206 | }
207 |
208 | export function deleteModelById(id) {
209 | return new Promise((resolve, reject) => {
210 | try {
211 | const collection = db.get(Table)
212 | collection.removeById(id).write()
213 | resolve({
214 | code: 200
215 | })
216 | } catch (err) {
217 | return reject({
218 | code: 400,
219 | message: err.message
220 | })
221 | }
222 | })
223 | }
224 |
225 | export function deleteModelByIds(ids) {
226 | return new Promise((resolve, reject) => {
227 | try {
228 | const collection = db.get(Table)
229 | ids.forEach(id => {
230 | collection.removeById(id).write()
231 | })
232 | resolve({
233 | code: 200
234 | })
235 | } catch (err) {
236 | return reject({
237 | code: 400,
238 | message: err.message
239 | })
240 | }
241 | })
242 | }
243 |
244 | export function deleteModelWhere(whereAttrs) {
245 | return new Promise((resolve, reject) => {
246 | try {
247 | const collection = db.get(Table)
248 | const list = collection.removeWhere(whereAttrs).write()
249 | resolve({
250 | code: 200,
251 | data: _.cloneDeep(list)
252 | })
253 | } catch (err) {
254 | return reject({
255 | code: 400,
256 | message: err.message
257 | })
258 | }
259 | })
260 | }
261 |
--------------------------------------------------------------------------------
/src/api/user.js:
--------------------------------------------------------------------------------
1 | import db from '../datastore'
2 | import _ from 'lodash'
3 |
4 | const Table = 'user'
5 |
6 | export function login(data) {
7 | return new Promise((resolve, reject) => {
8 | try {
9 | const user = db.read().get(Table).find({userId: data.name, password: data.password}).value()
10 | resolve(_.cloneDeep(user))
11 | } catch (err) {
12 | return reject(err)
13 | }
14 | })
15 | }
16 |
17 | export function getModelWhere(attrs) {
18 | return new Promise((resolve, reject) => {
19 | try {
20 | const collection = db.get(Table)
21 | const list = collection.filter(attrs).value()
22 | resolve({
23 | code: 200,
24 | data: _.cloneDeep(list)
25 | })
26 | } catch (err) {
27 | return reject({
28 | code: 400,
29 | message: err.message
30 | })
31 | }
32 | })
33 | }
34 |
35 | export function putModelById(id, attrs) {
36 | return new Promise((resolve, reject) => {
37 | try {
38 | const collection = db.get(Table)
39 | const model = collection.updateById(id, attrs).write()
40 | resolve({
41 | code: 200,
42 | data: _.cloneDeep(model)
43 | })
44 | } catch (err) {
45 | return reject({
46 | code: 400,
47 | message: err.message
48 | })
49 | }
50 | })
51 | }
52 |
--------------------------------------------------------------------------------
/src/datastore/README.md:
--------------------------------------------------------------------------------
1 | # 表设计
2 |
3 | ## User(用户表不是必须的,我只是用来模拟登录)
4 |
5 | | 字段名 | 数据类型 | 是否主键 | 描述 |
6 | |--------|--------|--------|--------|
7 | | id | string | Y | id |
8 | | userId | string | Y | 用户名 |
9 | | password | string | | 用户名 |
10 |
11 | ## IncomeAndExpenditure(收入/支出表)
12 |
13 | | 字段名 | 数据类型 | 是否主键 | 描述 |
14 | |--------|--------|--------|--------|
15 | | id | string | Y | id |
16 | | type | string | | 收入i\支出e |
17 | | amountOfMoney | int | | 金额 |
18 | | remark | string | | 备注 |
19 | | categoryId | string | | 类别id |
20 | | assetsId | string | | 资产id |
21 | | createdAt | string | | 创建时间 |
22 | | updatedAt | string | | 更新时间 |
23 |
24 | ## Assets(资产管理表)
25 |
26 | | 字段名 | 数据类型 | 是否主键 | 描述 |
27 | |--------|--------|--------|--------|
28 | | id | string | Y | id |
29 | | assetsName | string | Y | 资产名 |
30 | | assetsDetailed | string | | 资产详细 |
31 | | assetsAmountOfMoney | int | | 金额 |
32 |
33 | ## Category(收入支出类别表)
34 |
35 | | 字段名 | 数据类型 | 是否主键 | 描述 |
36 | |--------|--------|--------|--------|
37 | | id | string | Y | id |
38 | | category | string | Y | 类别名称 |
39 | | remark | string | | 备注 |
--------------------------------------------------------------------------------
/src/datastore/index.js:
--------------------------------------------------------------------------------
1 | import Datastore from 'lowdb'
2 | import LodashId from 'lodash-id'
3 | import FileSync from 'lowdb/adapters/FileSync'
4 | import path from 'path'
5 | import fs from 'fs-extra'
6 | // 引入remote模块
7 | import { app, remote } from 'electron'
8 | // package.json
9 | import pkg from '../../package.json'
10 |
11 | // 根据process.type来分辨在哪种模式使用哪种模块
12 | const APP = process.type === 'renderer' ? remote.app : app
13 |
14 | // 获取electron应用的用户目录
15 | const STORE_PATH = APP.getPath('userData')
16 | console.log(STORE_PATH)
17 | // dev C:\Users\JD\AppData\Roaming\Electron
18 | // prod C:\Users\JD\AppData\Roaming\PocketBook
19 |
20 | if (process.type !== 'renderer') {
21 | // 如果不存在路径
22 | if (!fs.pathExistsSync(STORE_PATH)) {
23 | // 就创建
24 | fs.mkdirpSync(STORE_PATH)
25 | }
26 | }
27 |
28 | // 以同步的方式初始化lowdb读写的json文件名以及存储路径
29 | const adapter = new FileSync(path.join(STORE_PATH, `/${pkg.name}_lowdb.json`))
30 |
31 | // lowdb接管该文件
32 | const db = Datastore(adapter)
33 | // 通过._mixin()引入lodash_id唯一id插件
34 | db._.mixin(LodashId)
35 |
36 | // 初始化数据
37 | if(!db.has('user').value()) {
38 | db.set('user',[]).write()
39 | db.get('user').insert({userId: 'admin', password: '123456'}).write()
40 | }
41 |
42 | if(!db.has('category').value()) {
43 | const tempData = [
44 | {
45 | "category": "breakfast",
46 | "remark": "daily breakfast expenses",
47 | },
48 | {
49 | "category": "dinner",
50 | "remark": "daily dinner expenses",
51 | },
52 | {
53 | "category": "lunch",
54 | "remark": "Daily lunch expenses",
55 | }
56 | ]
57 | db.set('category', []).write()
58 | tempData.forEach(item => {
59 | db.get('category').insert(item).write()
60 | })
61 | }
62 |
63 | if(!db.has('assets').value()) {
64 | const tempData = [
65 | {assetsName: 'me', assetsDetailed: 'My assets', assetsAmountOfMoney: 0},
66 | {assetsName: 'wife', assetsDetailed: 'My wife\'s assets', assetsAmountOfMoney: 0}
67 | ]
68 | db.set('assets', []).write()
69 | tempData.forEach(item => {
70 | db.get('assets').insert(item).write()
71 | })
72 | }
73 |
74 | if(!db.has('incomeAndExpenditure').value()) {
75 | db.set('incomeAndExpenditure', []).write()
76 | }
77 |
78 | export default db // 暴露出去
79 |
--------------------------------------------------------------------------------
/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PocketBook
6 |
7 |
8 | <% if (htmlWebpackPlugin.options.nodeModules) { %>
9 |
10 |
13 | <% } %>
14 |
15 |
16 |
17 |
18 | <% if (!process.browser) { %>
19 |
22 | <% } %>
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/main/index.dev.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is used specifically and only for development. It installs
3 | * `electron-debug` & `vue-devtools`. There shouldn't be any need to
4 | * modify this file, but it can be used to extend your development
5 | * environment.
6 | */
7 |
8 | /* eslint-disable */
9 |
10 | // Install `electron-debug` with `devtron`
11 | require('electron-debug')({ showDevTools: true })
12 |
13 | // Install `vue-devtools`
14 | require('electron').app.on('ready', () => {
15 | let installExtension = require('electron-devtools-installer')
16 | installExtension.default(installExtension.VUEJS_DEVTOOLS)
17 | .then(() => {})
18 | .catch(err => {
19 | console.log('Unable to install `vue-devtools`: \n', err)
20 | })
21 | })
22 |
23 | // Require `main` process to boot app
24 | require('./index')
--------------------------------------------------------------------------------
/src/main/index.js:
--------------------------------------------------------------------------------
1 | import {app, BrowserWindow, ipcMain, Menu, shell, Tray, Notification, dialog, crashReporter} from 'electron'
2 |
3 | // 自动更新相关
4 | import {autoUpdater} from 'electron-updater'
5 |
6 | // 引入自动启动模块
7 | const startOnBoot = require('./startOnBoot.js')
8 |
9 | // 崩溃报告
10 | import * as Sentry from '@sentry/electron'
11 |
12 | // package.json
13 | import pkg from '../../package.json'
14 |
15 | /**
16 | * Set `__static` path to static files in production
17 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
18 | */
19 | if (process.env.NODE_ENV !== 'development') {
20 | global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
21 | }
22 |
23 | let mainWindow, loginWindow
24 | const winURL = process.env.NODE_ENV === 'development'
25 | ? `http://localhost:9080`
26 | : `file://${__dirname}/index.html`
27 |
28 | const loginURL = process.env.NODE_ENV === 'development'
29 | ? `http://localhost:9080/#login`
30 | : `file://${__dirname}/index.html#login`
31 |
32 | const path = require('path')
33 | const ApplicationName = pkg.name
34 | // 托盘对象
35 | let appTray = null
36 | // 是否可以退出
37 | let trayClose = false
38 | // 系统托盘右键菜单
39 | let trayMenuTemplate
40 | // 系统托盘图标
41 | let iconPath
42 | // 图标的上上下文
43 | let contextMenu
44 | // 图标闪烁定时器
45 | let flashTrayTimer
46 | // 单一实例
47 | const gotTheLock = app.requestSingleInstanceLock()
48 |
49 | if (process.platform === 'win32') {
50 | app.setAppUserModelId(ApplicationName)
51 | }
52 |
53 | /**
54 | * 创建主窗口
55 | */
56 | function createLoginWindow() {
57 | if (loginWindow) {
58 | return
59 | }
60 |
61 | /**
62 | * Initial window options
63 | */
64 | loginWindow = new BrowserWindow({
65 | show: true,
66 | height: 360,
67 | width: 300,
68 | maxHeight: 360,
69 | maxWidth: 300,
70 | useContentSize: true,
71 | frame: false, // 无边框
72 | transparent: true, // 透明
73 | // fullscreen: true, // 全屏,
74 | resizable: false,
75 | maximizable: false,
76 | minimizable: false,
77 | webPreferences: {
78 | nodeIntegration: true
79 | },
80 | })
81 |
82 | loginWindow.loadURL(loginURL)
83 |
84 | // 为了防止闪烁,让画面准备好了再显示
85 | // 对于一个复杂的应用,ready-to-show 可能发出的太晚,会让应用感觉缓慢。 在这种情况下,建议立刻显示窗口,并使用接近应用程序背景的 backgroundColor
86 | // 请注意,即使是使用 ready-to-show 事件的应用程序,仍建议使用设置 backgroundColor 使应用程序感觉更原生。
87 | loginWindow.once('ready-to-show', () => {
88 | loginWindow.show()
89 | })
90 |
91 | loginWindow.on('close', (event) => {
92 |
93 | })
94 |
95 | loginWindow.on('closed', () => {
96 | loginWindow = null
97 | })
98 |
99 | ipcMain.on('openMainWindow', () => {
100 | if (!mainWindow) {
101 | createMainWindow()
102 | }
103 |
104 | // loginWindow.hide()
105 | loginWindow.destroy()
106 | mainWindow.show()
107 | mainWindow.focus()
108 | })
109 |
110 | ipcMain.on('openLoginWindow', () => {
111 | if (!loginWindow) {
112 | createLoginWindow()
113 | }
114 |
115 | // loginWindow.hide()
116 | mainWindow.destroy()
117 | loginWindow.show()
118 | loginWindow.focus()
119 | })
120 | }
121 |
122 | /**
123 | * 创建主窗口
124 | */
125 | function createMainWindow() {
126 | if (mainWindow) {
127 | return
128 | }
129 |
130 | /**
131 | * Initial window options
132 | */
133 | mainWindow = new BrowserWindow({
134 | show: false,
135 | height: 1000,
136 | width: 1600,
137 | minWidth: 900,
138 | minHeight: 600,
139 | useContentSize: true,
140 | frame: false, // 无边框
141 | transparent: true, // 透明
142 | // fullscreen: true, // 全屏
143 | webPreferences: {
144 | nodeIntegration: true
145 | },
146 | })
147 |
148 | mainWindow.loadURL(winURL)
149 |
150 | /**
151 | * 监听
152 | */
153 |
154 | mainWindow.on('close', (event) => {
155 | if(process.platform === 'win32') {
156 | if (!trayClose) {
157 | // 最小化
158 | mainWindow.hide()
159 | event.preventDefault()
160 | }
161 | }
162 | })
163 |
164 | mainWindow.on('closed', () => {
165 | mainWindow = null
166 | })
167 |
168 | mainWindow.on('maximize', () => {
169 |
170 | })
171 | }
172 |
173 | /**
174 | * 设置系统托盘
175 | */
176 | function createTray() {
177 | // 是否可以退出
178 | trayClose = false
179 |
180 | // 系统托盘图标
181 | iconPath = `${__static}/logoNotWin.png`
182 | let iconMessagePath = `${__static}/iconMessageNotWin.png`
183 | let iconTransparentPath = `${__static}/iconTransparentNotWin.png`
184 | // 通知图标
185 | const iconNoticePath = `${__static}/logo.png`
186 |
187 | if (process.platform === 'win32') {
188 | iconPath = `${__static}/logo.ico`
189 | iconMessagePath = `${__static}/iconMessage.ico`
190 | iconTransparentPath = `${__static}/iconTransparent.ico`
191 | }
192 |
193 |
194 | // 系统托盘右键菜单
195 | trayMenuTemplate = [
196 | {
197 | label: '崩溃报告测试 process.crash()',
198 | click: function () {
199 | console.log('process.crash()')
200 | process.crash()
201 | }
202 | },
203 | {
204 | label: '崩溃报告测试throw new Error',
205 | click: function () {
206 | console.log('Error test in main progress')
207 | throw new Error('Error test in main progress')
208 | }
209 | },
210 | {
211 | label: '托盘闪烁',
212 | click: function () {
213 | // 判断如果上一个定时器是否执行完
214 | if (flashTrayTimer) {
215 | return
216 | }
217 |
218 | // 任务栏闪烁
219 | // if (!mainWindow.isFocused()) {
220 | // mainWindow.showInactive();
221 | // mainWindow.flashFrame(true);
222 | // }
223 |
224 | //系统托盘图标闪烁
225 | appTray.setImage(iconMessagePath)
226 | let count = 0;
227 | flashTrayTimer = setInterval(function () {
228 | count++;
229 | if (count % 2 == 0) {
230 | appTray.setImage(iconTransparentPath)
231 | } else {
232 | appTray.setImage(iconMessagePath)
233 | }
234 | }, 600);
235 | }
236 | },
237 | {
238 | label: '弹出通知',
239 | click: function () {
240 | console.log(Notification.isSupported())
241 | let notification = new Notification({
242 | title: '通知的标题', // 通知的标题, 将在通知窗口的顶部显示
243 | body: '通知的正文文本', // 通知的正文文本, 将显示在标题或副标题下面
244 | icon: iconNoticePath, // 用于在该通知上显示的图标
245 | silent: true, // 在显示通知时是否发出系统提示音
246 | })
247 |
248 | notification.show()
249 | notification.on('click', () => {
250 | notification.close()
251 | console.log('click notification')
252 | })
253 | }
254 | },
255 | {
256 | label: '关于项目',
257 | click: function () {
258 | // 打开外部链接
259 | shell.openExternal('https://github.com/hilanmiao/LanMiaoDesktop')
260 | }
261 | },
262 | {
263 | label: '退出',
264 | click: function () {
265 | // 退出
266 | trayClose = true
267 | app.quit()
268 | }
269 | }
270 | ]
271 |
272 | appTray = new Tray(iconPath)
273 | // 图标的上上下文
274 | contextMenu = Menu.buildFromTemplate(trayMenuTemplate)
275 | // 设置此托盘图标的悬停提示内容
276 | appTray.setToolTip(ApplicationName)
277 | // 设置此图标的上下文菜单
278 | appTray.setContextMenu(contextMenu)
279 | // 主窗口显示隐藏切换
280 | appTray.on('click', () => {
281 | // 清楚图标闪烁定时器
282 | clearInterval(flashTrayTimer)
283 | flashTrayTimer = null
284 | // 还原图标
285 | appTray.setImage(iconPath)
286 | mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
287 | })
288 | }
289 |
290 | /**
291 | * 开机启动
292 | */
293 | function ipcStartOnBoot() {
294 | // 检查是否自动启动
295 | ipcMain.on('getAutoStartValue', () => {
296 | startOnBoot.getAutoStartValue(ApplicationName, (error, result) => {
297 | if (error) {
298 | mainWindow.webContents.send('getAutoStartValue', false)
299 | } else {
300 | mainWindow.webContents.send('getAutoStartValue', true)
301 | }
302 | })
303 | })
304 |
305 | // 设置开机自动启动
306 | ipcMain.on('enableAutoStart', () => {
307 | startOnBoot.enableAutoStart(ApplicationName, process.execPath)
308 | })
309 |
310 | // 取消开机自动启动
311 | ipcMain.on('disableAutoStart', () => {
312 | startOnBoot.disableAutoStart(ApplicationName)
313 | })
314 | }
315 |
316 | /**
317 | * 自动更新
318 | */
319 | function autoUpdate() {
320 | // 通过main进程发送事件给renderer进程,提示更新信息
321 | function sendUpdateMessage(obj) {
322 | mainWindow.webContents.send('updateMessage', obj)
323 | }
324 |
325 | // 监测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
326 | const message = {
327 | error: '检查更新出错',
328 | checking: '正在检查更新......',
329 | updateAva: '监测到新版本,正在下载......',
330 | updateNotAva: '现在使用的就是最新版本,不用下载'
331 | }
332 |
333 | // 当更新出现错误时触发
334 | autoUpdater.on('error', (err) => {
335 | // sendUpdateMessage('error')
336 | sendUpdateMessage({action: 'error', errorInfo: err})
337 | })
338 |
339 | // 当开始检查更新的时候触发
340 | autoUpdater.on('checking-for-update', () => {
341 | // sendUpdateMessage('checking')
342 | sendUpdateMessage({action: 'checking'})
343 | })
344 |
345 | // 当发现一个可用更新的时候触发,更新下载包会自动开始
346 | autoUpdater.autoDownload = false
347 | autoUpdater.on('update-available', (info) => {
348 | // sendUpdateMessage('updateAva')
349 | sendUpdateMessage({action: 'updateAva', updateInfo: info})
350 | })
351 |
352 | // 当没有可用更新的时候触发
353 | autoUpdater.on('update-not-available', (info) => {
354 | // sendUpdateMessage('updateNotAva')
355 | sendUpdateMessage({action: 'updateNotAva', updateInfo: info})
356 | })
357 |
358 | // 更新下载进度事件
359 | autoUpdater.on('download-progress', (progressObj) => {
360 | mainWindow.webContents.send('downloadProgress', progressObj)
361 | })
362 |
363 | /**
364 | * event Event
365 | * releaseNotes String - 新版本更新公告
366 | * releaseName String - 新的版本号
367 | * releaseDate Date - 新版本发布的日期
368 | * updateUrl String - 更新地址
369 | */
370 | autoUpdater.on('update-downloaded', (info) => {
371 | // 下载太快可能无法触发downloadProgress事件,所以手动通知一下
372 | mainWindow.webContents.send('downloadProgress', {percent: 100})
373 | // 可以手动选择是否立即退出并更新
374 | ipcMain.on('isUpdateNow', (e, arg) => {
375 | // some code here to handle event
376 | autoUpdater.quitAndInstall()
377 | })
378 | })
379 |
380 | ipcMain.on('checkForUpdate', () => {
381 | // 执行自动更新检查
382 | autoUpdater.checkForUpdates()
383 | })
384 |
385 | ipcMain.on('downloadUpdate', () => {
386 | // 下载
387 | autoUpdater.downloadUpdate()
388 | })
389 | }
390 |
391 |
392 | /**
393 | * 崩溃报告
394 | */
395 | function crashReport() {
396 | // 报告常规错误
397 | Sentry.init({
398 | dsn: 'https://8e0258fcf49d43d09d9fe7c6a0c8ea80@sentry.io/1455801',
399 | })
400 |
401 | // 报告系统错误
402 | crashReporter.start({
403 | companyName: 'lanmiao',
404 | productName: 'LanMiaoDesktop',
405 | ignoreSystemCrashHandler: true,
406 | submitURL: 'https://sentry.io/api/1455801/minidump/?sentry_key=8e0258fcf49d43d09d9fe7c6a0c8ea80'
407 | })
408 |
409 | // 渲染进程崩溃事件
410 | mainWindow.webContents.on('crashed', () => {
411 | const options = {
412 | type: 'error',
413 | title: '进程崩溃了',
414 | message: '这个进程已经崩溃.',
415 | buttons: ['重载', '退出'],
416 | };
417 | recordCrash().then(() => {
418 | dialog.showMessageBox(options, (index) => {
419 | if (index === 0) {
420 | reloadWindow(mainWindow)
421 | } else {
422 | app.quit()
423 | }
424 | });
425 | }).catch((e) => {
426 | console.log('err', e)
427 | });
428 | })
429 |
430 | function recordCrash() {
431 | return new Promise(resolve => {
432 | // 崩溃日志请求成功....
433 | resolve();
434 | })
435 | }
436 |
437 | function reloadWindow(mainWin) {
438 | if (mainWin.isDestroyed()) {
439 | app.relaunch();
440 | app.exit(0);
441 | } else {
442 | BrowserWindow.getAllWindows().forEach((w) => {
443 | if (w.id !== mainWin.id) {
444 | w.destroy()
445 | }
446 | });
447 | mainWin.reload();
448 | }
449 | }
450 | }
451 |
452 | /**
453 | * 协议处理
454 | */
455 | function protocalHandler() {
456 | const args = [];
457 | if (!app.isPackaged) {
458 | // 如果是开发阶段,需要把我们的脚本的绝对路径加入参数中
459 | args.push(path.resolve(process.argv[1]))
460 | }
461 | // 加一个 `--` 以确保后面的参数不被 Electron 处理
462 | args.push('--')
463 |
464 | // 注册协议
465 | const PROTOCOL = pkg.name
466 | app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, args)
467 |
468 | // 如果打开协议时,没有其他实例,则当前实例当做主实例,处理参数
469 | handleArgv(process.argv)
470 |
471 | // 其他实例启动时,主实例会通过 second-instance 事件接收其他实例的启动参数 `argv`
472 | app.on('second-instance', (event, argv) => {
473 | // Windows 下通过协议URL启动时,URL会作为参数,所以需要在这个事件里处理
474 | if (process.platform === 'win32') {
475 | handleArgv(argv)
476 | }
477 | })
478 |
479 | // macOS 下通过协议URL启动时,主实例会通过 open-url 事件接收这个 URL
480 | app.on('open-url', (event, urlStr) => {
481 | handleUrl(urlStr)
482 | })
483 |
484 | // 处理参数
485 | function handleArgv(argv) {
486 | const prefix = `${PROTOCOL}:`;
487 | // 开发阶段,跳过前两个参数(`electron.exe .`)
488 | // 打包后,跳过第一个参数(`myapp.exe`)
489 | const offset = app.isPackaged ? 1 : 2
490 | const url = argv.find((arg, i) => i >= offset && arg.startsWith(prefix))
491 | if (url) handleUrl(url)
492 | }
493 |
494 | // 解析Url
495 | function handleUrl(urlStr) {
496 | // myapp:?a=1&b=2
497 | const urlObj = new URL(urlStr);
498 | const {searchParams} = urlObj;
499 | console.log(urlObj.query); // -> a=1&b=2
500 | console.log(searchParams.get('a')); // -> 1
501 | console.log(searchParams.get('b')); // -> 2
502 | // 根据需要做其他事情
503 | }
504 |
505 | }
506 |
507 | /**
508 | * 单一实例
509 | */
510 | if (!gotTheLock) {
511 | app.quit()
512 | } else {
513 | app.on('second-instance', (event, commandLine, workingDirectory) => {
514 | // 当运行第二个实例时,将会聚焦到myWindow这个窗口
515 | // if (mainWindow) {
516 | // if (mainWindow.isMinimized()) mainWindow.restore()
517 | // mainWindow.focus()
518 | // }
519 | if (loginWindow) {
520 | loginWindow.focus()
521 | }
522 | })
523 |
524 | // 创建 mainWindow, 加载应用的其余部分, etc...
525 | app.on('ready', () => {
526 | createLoginWindow()
527 | createMainWindow()
528 | createTray()
529 | ipcStartOnBoot()
530 | autoUpdate()
531 | crashReport()
532 | protocalHandler()
533 | })
534 | }
535 |
536 | app.on('window-all-closed', () => {
537 | if (process.platform !== 'darwin') {
538 | app.quit()
539 | }
540 | })
541 |
542 | app.on('activate', () => {
543 | if (mainWindow === null) {
544 | createMainWindow()
545 | }
546 |
547 | if (loginWindow === null) {
548 | createLoginWindow()
549 | }
550 | })
551 |
--------------------------------------------------------------------------------
/src/main/startOnBoot.js:
--------------------------------------------------------------------------------
1 | // 引用winreg模块
2 | var WinReg = require('winreg')
3 | var startOnBoot = {
4 |
5 | // 设置自动启动
6 | enableAutoStart: function (name, file, callback) {
7 | var key = getKey()
8 | key.set(name, WinReg.REG_SZ, file, callback || noop)
9 | },
10 |
11 | // 取消自动启动
12 | disableAutoStart: function (name, callback) {
13 | var key = getKey()
14 | key.remove(name, callback || noop)
15 | },
16 |
17 | // 获取是否自动启动
18 | getAutoStartValue: function (name, callback) {
19 | var key = getKey()
20 | key.get(name, function (error, result) {
21 | if (result) {
22 | callback(null, result.value)
23 | } else {
24 | callback(error)
25 | }
26 | })
27 | }
28 | }
29 |
30 | var RUN_LOCATION = '\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
31 |
32 | // 获取注册表key
33 | function getKey() {
34 | return new WinReg({
35 | // hive: WinReg.HKCU, // CurrentUser,
36 | hive: WinReg.HKLM, // LocalMachine,
37 | key: RUN_LOCATION
38 | })
39 | }
40 |
41 | // callback自定义方法,你可以在这里写代码
42 | function noop() {
43 | }
44 |
45 | // 导出
46 | module.exports = startOnBoot
47 |
--------------------------------------------------------------------------------
/src/renderer/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
38 |
--------------------------------------------------------------------------------
/src/renderer/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/src/renderer/assets/.gitkeep
--------------------------------------------------------------------------------
/src/renderer/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/src/renderer/assets/logo.png
--------------------------------------------------------------------------------
/src/renderer/components/Layout/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
13 |
14 |
15 |
16 | {{title}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 |
30 |
31 | {{ item.heading }}
32 |
33 |
34 |
35 | Add
36 |
37 |
38 |
44 |
45 |
52 |
53 | {{ item.icon }}
54 |
55 |
56 |
57 | {{ item.text }}
58 |
59 |
60 |
61 |
67 |
68 |
69 | {{ item.text }}
70 |
71 |
72 |
77 |
78 | {{ subItem.text }}
79 |
80 |
81 |
82 | {{ subItem.icon }}
83 |
84 |
85 |
86 |
87 |
92 |
93 | exit_to_app
94 |
95 |
96 |
97 | Logout
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Balance: $10000.50
107 |
108 |
109 |
113 | remove
114 |
115 |
120 | filter_none
121 |
122 |
127 | crop_square
128 |
129 |
133 | close
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | © 2019
144 |
145 |
146 |
147 |
148 |
242 |
243 |
256 |
--------------------------------------------------------------------------------
/src/renderer/components/add/add.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 | add
12 |
13 |
14 |
15 |
16 | {{ formTitle }}
17 |
18 |
19 |
25 |
26 |
27 |
28 |
36 |
37 |
38 |
46 |
47 |
48 |
55 |
56 |
57 |
58 |
65 |
66 |
67 |
77 |
78 |
85 |
86 |
88 |
89 |
90 |
91 |
93 |
94 |
95 |
96 | *indicates required field
97 |
98 |
99 |
100 |
101 | Close
102 | Save
103 |
104 |
105 |
106 |
112 | {{ snackbarMsg}}
113 |
117 | Close
118 |
119 |
120 |
121 |
122 |
123 |
295 |
296 |
299 |
--------------------------------------------------------------------------------
/src/renderer/main.js:
--------------------------------------------------------------------------------
1 | // TODO:如果不用,记得注释掉,否则travis 和 appveyor 构建会失败
2 | // import devtools from '@vue/devtools'
3 | import Vue from 'vue'
4 | import axios from 'axios'
5 |
6 | import App from './App'
7 | import router from './router'
8 | import store from './store'
9 | import Vuetify from 'vuetify' // 引入Vuetify
10 | import 'vuetify/dist/vuetify.min.css' // 引入Vuetify的css文件
11 | import 'material-design-icons-iconfont/dist/material-design-icons.css' // 引入Material Desgin Icon的css文件
12 | import {Upload, Button} from 'element-ui'
13 |
14 | Vue.use(Vuetify)
15 | Vue.use(Button)
16 | Vue.use(Upload)
17 |
18 | if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
19 | Vue.http = Vue.prototype.$http = axios
20 | Vue.config.productionTip = false
21 |
22 | /* eslint-disable no-new */
23 | new Vue({
24 | components: { App },
25 | router,
26 | store,
27 | template: ''
28 | }).$mount('#app')
29 |
30 | // if (process.env.NODE_ENV === 'development') {
31 | // devtools.connect('localhost', '9080')
32 | // }
33 |
--------------------------------------------------------------------------------
/src/renderer/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 |
4 | Vue.use(Router)
5 |
6 | export default new Router({
7 | routes: [
8 | {
9 | path: '/login',
10 | name: 'login',
11 | component: require('@/views/Login/Login').default
12 | },
13 | {
14 | path: '/',
15 | name: 'home',
16 | redirect: '/home',
17 | component: require('@/components/Layout/Layout').default,
18 | children: [{
19 | path: 'home',
20 | component: require('@/views/Home/Home').default
21 | }]
22 | },
23 | {
24 | path: '/incomeAndExpenditure',
25 | name: 'incomeAndExpenditure',
26 | redirect: '/incomeAndExpenditure/incomeAndExpenditure',
27 | component: require('@/components/Layout/Layout').default,
28 | children: [{
29 | path: 'incomeAndExpenditure',
30 | component: require('@/views/IncomeAndExpenditure/IncomeAndExpenditure').default
31 | }]
32 | },
33 | {
34 | path: '/category',
35 | name: 'category',
36 | redirect: '/category/category',
37 | component: require('@/components/Layout/Layout').default,
38 | children: [{
39 | path: 'category',
40 | component: require('@/views/Category/Category').default
41 | }]
42 | },
43 | {
44 | path: '/assets',
45 | name: 'assets',
46 | redirect: '/assets/assets',
47 | component: require('@/components/Layout/Layout').default,
48 | children: [{
49 | path: 'assets',
50 | component: require('@/views/Assets/Assets').default
51 | }]
52 | },
53 | {
54 | path: '/settings',
55 | name: 'settings',
56 | redirect: '/settings/settings',
57 | component: require('@/components/Layout/Layout').default,
58 | children: [{
59 | path: 'settings',
60 | component: require('@/views/Settings/Settings').default
61 | }]
62 | },
63 | {
64 | path: '/notes',
65 | name: 'notes',
66 | redirect: '/notes/notes',
67 | component: require('@/components/Layout/Layout').default,
68 | children: [{
69 | path: 'notes',
70 | component: require('@/views/Notes/Notes').default
71 | }]
72 | },
73 | {
74 | path: '/help',
75 | name: 'help',
76 | redirect: '/help/help',
77 | component: require('@/components/Layout/Layout').default,
78 | children: [{
79 | path: 'help',
80 | component: require('@/views/Help/Help').default
81 | }]
82 | },
83 | {
84 | path: '/password',
85 | name: 'password',
86 | redirect: '/password/password',
87 | component: require('@/components/Layout/Layout').default,
88 | children: [{
89 | path: 'password',
90 | component: require('@/views/Password/Password').default
91 | }]
92 | },
93 | {
94 | path: '*',
95 | redirect: '/'
96 | }
97 | ]
98 | })
99 |
--------------------------------------------------------------------------------
/src/renderer/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | import {createPersistedState, createSharedMutations} from 'vuex-electron'
5 | import todoPlugin from './plugins/todo'
6 |
7 | import modules from './modules'
8 |
9 | Vue.use(Vuex)
10 |
11 | export default new Vuex.Store({
12 | modules,
13 | plugins: [
14 | todoPlugin,
15 | // createPersistedState(), // 需要统一,上面的todoPlugin没有括号,所以用变量
16 | createPersistedState,
17 | createSharedMutations
18 | ],
19 | strict: process.env.NODE_ENV !== 'production'
20 | })
21 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/Counter.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | main: 0
3 | }
4 |
5 | const mutations = {
6 | DECREMENT_MAIN_COUNTER (state) {
7 | state.main--
8 | },
9 | INCREMENT_MAIN_COUNTER (state) {
10 | state.main++
11 | }
12 | }
13 |
14 | const actions = {
15 | someAsyncTask ({ commit }) {
16 | // do something async
17 | commit('INCREMENT_MAIN_COUNTER')
18 | }
19 | }
20 |
21 | export default {
22 | state,
23 | mutations,
24 | actions
25 | }
26 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/dialogAdd.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | dialogAddShow: false
3 | }
4 |
5 | const mutations = {
6 | show(state, show) {
7 | state.dialogAddShow = show
8 | }
9 | }
10 |
11 | const actions = {
12 | updateDialog({commit}, show) {
13 | commit('show', show)
14 | }
15 | }
16 |
17 | export default {
18 | state,
19 | mutations,
20 | actions
21 | }
22 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The file enables `@/store/index.js` to import all vuex modules
3 | * in a one-shot manner. There should not be any reason to edit this file.
4 | */
5 |
6 | const files = require.context('.', false, /\.js$/)
7 | const modules = {}
8 |
9 | files.keys().forEach(key => {
10 | if (key === './index.js') return
11 | modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
12 | })
13 |
14 | export default modules
15 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/todo.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'vuetify-todos'
2 |
3 | const state = {
4 | todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]')
5 | }
6 |
7 | const mutations = {
8 | addTodo (state, todo) {
9 | state.todos.push(todo)
10 | },
11 | removeTodo (state, todo) {
12 | state.todos.splice(state.todos.indexOf(todo), 1)
13 | },
14 | editTodo (state, { todo, text = todo.text, done = todo.done }) {
15 | todo.text = text
16 | todo.done = done
17 | }
18 | }
19 |
20 | const actions = {
21 | addTodo ({ commit }, text) {
22 | commit('addTodo', {
23 | uid: Date.now(),
24 | text,
25 | done: false
26 | })
27 | },
28 | removeTodo ({ commit }, todo) {
29 | commit('removeTodo', todo)
30 | },
31 | toggleTodo ({ commit }, todo) {
32 | commit('editTodo', { todo, done: !todo.done })
33 | },
34 | editTodo ({ commit }, { todo, value }) {
35 | commit('editTodo', { todo, text: value })
36 | },
37 | toggleAll ({ state, commit }, done) {
38 | state.todos.forEach((todo) => {
39 | commit('editTodo', { todo, done })
40 | })
41 | },
42 | clearCompleted ({ state, commit }) {
43 | state.todos.filter(todo => todo.done)
44 | .forEach(todo => {
45 | commit('removeTodo', todo)
46 | })
47 | }
48 | }
49 |
50 | export default {
51 | state,
52 | mutations,
53 | actions
54 | }
55 |
--------------------------------------------------------------------------------
/src/renderer/store/plugins/todo.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'vuetify-todos'
2 |
3 | export default store => {
4 | store.subscribe((mutation, state) => {
5 | console.log(mutation, state)
6 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state.todo.todos))
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/src/renderer/views/Help/Help.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
https://github.com/hilanmiao/LanMiaoDesktop
6 |
7 |
8 |
9 |
10 |
73 |
74 |
99 |
--------------------------------------------------------------------------------
/src/renderer/views/Home/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
13 |
14 |
15 | Monthly statistics
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
26 | Reminder
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
68 |
69 |
72 |
--------------------------------------------------------------------------------
/src/renderer/views/Home/components/add/add.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | User Profile
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 | *indicates required field
29 |
30 |
31 |
32 | Close
33 | Save
34 |
35 |
36 |
37 |
38 |
39 |
54 |
55 |
58 |
--------------------------------------------------------------------------------
/src/renderer/views/Home/components/history/history.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
12 | History
13 | Here is every pay
14 |
15 |
16 |
21 |
22 | {{ props.item.createdAt }} |
23 | {{ props.item.assetsName }} |
24 | {{ props.item.categoryName }} |
25 | {{props.item.type === 'e'? '-':''}} {{ props.item.amountOfMoney }} |
26 |
27 |
28 |
36 | load more
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
151 |
152 |
158 |
--------------------------------------------------------------------------------
/src/renderer/views/Home/components/statsCurve/statsCurve.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
47 |
48 |
51 |
--------------------------------------------------------------------------------
/src/renderer/views/Home/components/statsIcon/statsIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
14 |
15 |
16 | {{item.icon}}
17 |
18 |
19 |
20 | ${{item.money}}
21 |
22 | {{item.text}}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
57 |
58 |
71 |
--------------------------------------------------------------------------------
/src/renderer/views/Home/components/todo/todo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 | check
17 |
18 |
19 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {{ remaining }} {{ remaining | pluralize('item') }} left
41 |
42 |
43 |
49 |
58 | {{ key | capitalize }}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
69 |
70 |
71 |
72 |
81 | Clear completed
82 |
83 |
84 |
85 |
86 |
87 |
145 |
146 |
149 |
--------------------------------------------------------------------------------
/src/renderer/views/Home/components/todo/todoItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 | edit
14 |
15 |
16 |
20 | {{ todo.text }}
21 |
22 |
23 |
29 | close
30 |
31 |
32 |
33 |
48 |
49 |
50 |
51 |
95 |
96 |
106 |
--------------------------------------------------------------------------------
/src/renderer/views/Login/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
18 | Log in
19 |
20 |
21 | filter_1
22 |
23 |
24 | filter_2
25 |
26 |
27 | filter_3
28 |
29 |
30 |
31 |
32 |
33 |
38 |
45 |
46 |
57 |
58 |
66 | lets go
67 |
68 |
69 |
70 |
71 |
72 |
76 | {{ loginErrMsg }}
77 |
78 |
79 |
80 |
81 |
82 |
83 |
131 |
132 |
150 |
--------------------------------------------------------------------------------
/src/renderer/views/Notes/Notes.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 | Check for updates
9 | new_releases
10 |
11 |
12 |
13 | thumb_up
14 | Already the latest version
15 |
16 |
17 |
21 | Download new version
22 | cloud_download
23 |
24 | Loading...
25 |
26 |
27 |
34 | {{ downloadPercent }}
35 |
36 |
41 |
45 |
46 | Please wait a moment
47 |
52 |
53 |
54 |
55 |
56 |
57 | update immediately?
58 | The installation package has been downloaded. Is it updated immediately?
59 |
60 |
61 |
62 | Disagree
63 | Agree
64 |
65 |
66 |
67 |
68 |
69 |
70 | error info {{errorInfo}}
71 |
72 |
73 |
74 |
75 |
76 | ReleaseNotes:
77 |
78 |
79 | v{{item.version}}({{item.releaseDate.substr(0,10)}})
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
174 |
175 |
178 |
--------------------------------------------------------------------------------
/src/renderer/views/Password/Password.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Change Password
7 |
8 |
9 |
15 |
16 |
17 |
18 |
24 |
25 |
26 |
37 |
38 |
39 |
50 |
51 |
52 |
53 | *indicates required field
54 |
55 |
56 |
57 |
58 | Save
59 |
60 |
61 |
62 |
63 |
69 | {{ snackbarMsg}}
70 |
74 | Close
75 |
76 |
77 |
78 |
79 |
80 |
176 |
177 |
191 |
--------------------------------------------------------------------------------
/src/renderer/views/Settings/Settings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | UserData
30 |
31 |
32 |
33 | Show User data path
34 |
35 | {{userDataPath}}
36 |
37 |
38 |
39 |
40 |
41 | Show data backup path
42 |
43 | {{backupPath}}
44 |
45 |
46 |
47 |
48 |
49 | Show export files path
50 |
51 | {{exportPath}}
52 |
53 |
54 |
55 |
56 |
57 |
58 | General
59 |
60 |
61 |
63 | cloud_download
64 |
65 |
66 |
67 | Backup data
68 | It is recommended to back up weekly
69 |
70 |
71 |
72 |
73 |
75 | history
76 |
77 |
78 |
79 | Recovery data
80 | It is recommended to back up data before recovery
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | Notifications
89 | Notify me about updates to apps or games that i downloaded
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Auto Start
99 | Boot automatically
100 |
101 |
102 |
103 |
104 | logout
105 |
106 |
107 |
108 |
109 |
110 |
116 | {{ snackbarMsg}}
117 |
121 | Close
122 |
123 |
124 |
125 |
126 |
127 |
128 |
293 |
294 |
297 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/.gitkeep
--------------------------------------------------------------------------------
/static/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/icon.ico
--------------------------------------------------------------------------------
/static/iconMessage.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconMessage.ico
--------------------------------------------------------------------------------
/static/iconMessage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconMessage.png
--------------------------------------------------------------------------------
/static/iconMessageNotWin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconMessageNotWin.png
--------------------------------------------------------------------------------
/static/iconMessageNotWin@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconMessageNotWin@2x.png
--------------------------------------------------------------------------------
/static/iconMessageNotWin@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconMessageNotWin@3x.png
--------------------------------------------------------------------------------
/static/iconTransparent.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconTransparent.ico
--------------------------------------------------------------------------------
/static/iconTransparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconTransparent.png
--------------------------------------------------------------------------------
/static/iconTransparentNotWin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconTransparentNotWin.png
--------------------------------------------------------------------------------
/static/iconTransparentNotWin@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconTransparentNotWin@2x.png
--------------------------------------------------------------------------------
/static/iconTransparentNotWin@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/iconTransparentNotWin@3x.png
--------------------------------------------------------------------------------
/static/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/logo.ico
--------------------------------------------------------------------------------
/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/logo.png
--------------------------------------------------------------------------------
/static/logoNotWin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/logoNotWin.png
--------------------------------------------------------------------------------
/static/logoNotWin@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/logoNotWin@2x.png
--------------------------------------------------------------------------------
/static/logoNotWin@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hilanmiao/LanMiaoDesktop/e7321d06a5b933b04004b2f9ef3814e76a4d9acd/static/logoNotWin@3x.png
--------------------------------------------------------------------------------