diff --git a/.firebase/hosting.cHVibGlj.cache b/.firebase/hosting.cHVibGlj.cache new file mode 100644 index 0000000..d0076ff --- /dev/null +++ b/.firebase/hosting.cHVibGlj.cache @@ -0,0 +1,2 @@ +index.html,1639197671433,cb28bcedfa5298c2ddee09c62c272657328775ae951e372209d166f505c798ad +favicon.ico,1639122716339,682d6968805db4fd6e11e95e9dc02ab78ae1a5e4cbb7d73cd9c2a192e839ceae diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..bdac102 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "jm-expense" + } +} diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..b9bd1f7 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh + +# abort on errors +set -e + +# build +npm run build + +# navigate into the build output directory +cd dist + +# if you are deploying to a custom domain +# echo 'www.example.com' > CNAME + +git init +git add -A +git commit -m 'deploy' + +# if you are deploying to https://.github.io +# git push -f https://github.com//.GitHub.io.git master + +# if you are deploying to https://.Github.io/ +# git push -f https://github.com//.git master:gh-pages +git push -f https://github.com/KarolChang/jm-expense-vue-ts.git master:gh-pages + +cd - \ No newline at end of file diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..6564edd --- /dev/null +++ b/firebase.json @@ -0,0 +1,12 @@ +{ + "hosting": { + "public": "dist", + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} diff --git a/package-lock.json b/package-lock.json index a135d84..5414574 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,12 @@ "name": "jm-expense-vue-ts", "version": "0.1.0", "dependencies": { + "axios": "^0.24.0", + "bootstrap": "^5.1.3", "core-js": "^3.6.5", + "dotenv": "^10.0.0", + "pinia": "^2.0.6", + "sweetalert2": "^11.3.0", "vue": "^3.0.0", "vue-class-component": "^8.0.0-0", "vue-router": "^4.0.0-0" @@ -1749,6 +1754,16 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz", + "integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@soda/friendly-errors-webpack-plugin": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", @@ -2970,6 +2985,15 @@ } } }, + "node_modules/@vue/cli-service/node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/@vue/cli-shared-utils": { "version": "4.5.15", "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.15.tgz", @@ -3840,6 +3864,14 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -4197,6 +4229,18 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "node_modules/bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "@popperjs/core": "^2.10.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6410,10 +6454,9 @@ } }, "node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "dev": true, + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", "engines": { "node": ">=10" } @@ -7639,7 +7682,6 @@ "version": "1.14.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", - "dev": true, "funding": [ { "type": "individual", @@ -11476,6 +11518,56 @@ "node": ">=6" } }, + "node_modules/pinia": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.6.tgz", + "integrity": "sha512-01mP4+KapIcTNSYLhQESy6GW0N8vY5wX3UqOwkC87e7DPjEusNJ8bENrKqdvZaRHbB2rDMOONeAbwMa3+n1/rw==", + "dependencies": { + "@vue/devtools-api": "^6.0.0-beta.20.1", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.1.tgz", + "integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", @@ -14184,6 +14276,14 @@ "boolbase": "~1.0.0" } }, + "node_modules/sweetalert2": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.3.0.tgz", + "integrity": "sha512-C0TFp0VLxgx+PmhJ0mL8qzx+iYjnCLdDbvQHKY6KAGI+xwawMvLkStPgw2LmJl6itaDhR/qLQStPFIbr1VK9Ow==", + "funding": { + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -14855,7 +14955,7 @@ "version": "4.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.6.tgz", "integrity": "sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17840,6 +17940,12 @@ "fastq": "^1.6.0" } }, + "@popperjs/core": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz", + "integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==", + "peer": true + }, "@soda/friendly-errors-webpack-plugin": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", @@ -18699,7 +18805,8 @@ "version": "4.5.15", "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.15.tgz", "integrity": "sha512-fqap+4HN+w+InDxlA3hZTOGE0tzBTgXhKLoDydhywqgmhQ1D9JA6Feh94ze6tG8DsWX58/ujYUqA8jAz17FJtg==", - "dev": true + "dev": true, + "requires": {} }, "@vue/cli-service": { "version": "4.5.15", @@ -18763,6 +18870,14 @@ "webpack-chain": "^6.4.0", "webpack-dev-server": "^3.11.0", "webpack-merge": "^4.2.2" + }, + "dependencies": { + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true + } } }, "@vue/cli-shared-utils": { @@ -18925,7 +19040,8 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz", "integrity": "sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==", - "dev": true + "dev": true, + "requires": {} }, "@vue/reactivity": { "version": "3.2.24", @@ -19193,7 +19309,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "7.2.0", @@ -19223,13 +19340,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true + "dev": true, + "requires": {} }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "alphanum-sort": { "version": "1.0.2", @@ -19496,6 +19615,14 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "requires": { + "follow-redirects": "^1.14.4" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -19788,6 +19915,12 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "requires": {} + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -21573,10 +21706,9 @@ } }, "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "dev": true + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" }, "dotenv-expand": { "version": "5.1.0", @@ -22567,8 +22699,7 @@ "follow-redirects": { "version": "1.14.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", - "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", - "dev": true + "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==" }, "for-in": { "version": "1.0.2", @@ -25566,6 +25697,23 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, + "pinia": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.6.tgz", + "integrity": "sha512-01mP4+KapIcTNSYLhQESy6GW0N8vY5wX3UqOwkC87e7DPjEusNJ8bENrKqdvZaRHbB2rDMOONeAbwMa3+n1/rw==", + "requires": { + "@vue/devtools-api": "^6.0.0-beta.20.1", + "vue-demi": "*" + }, + "dependencies": { + "vue-demi": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.1.tgz", + "integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==", + "requires": {} + } + } + }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", @@ -27847,6 +27995,11 @@ } } }, + "sweetalert2": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.3.0.tgz", + "integrity": "sha512-C0TFp0VLxgx+PmhJ0mL8qzx+iYjnCLdDbvQHKY6KAGI+xwawMvLkStPgw2LmJl6itaDhR/qLQStPFIbr1VK9Ow==" + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -28369,7 +28522,7 @@ "version": "4.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.6.tgz", "integrity": "sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==", - "dev": true + "devOptional": true }, "uglify-js": { "version": "3.4.10", @@ -28734,7 +28887,8 @@ "vue-class-component": { "version": "8.0.0-rc.1", "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-8.0.0-rc.1.tgz", - "integrity": "sha512-w1nMzsT/UdbDAXKqhwTmSoyuJzUXKrxLE77PCFVuC6syr8acdFDAq116xgvZh9UCuV0h+rlCtxXolr3Hi3HyPQ==" + "integrity": "sha512-w1nMzsT/UdbDAXKqhwTmSoyuJzUXKrxLE77PCFVuC6syr8acdFDAq116xgvZh9UCuV0h+rlCtxXolr3Hi3HyPQ==", + "requires": {} }, "vue-eslint-parser": { "version": "7.11.0", diff --git a/package.json b/package.json index ca70bd4..c8d3dfd 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,12 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "axios": "^0.24.0", + "bootstrap": "^5.1.3", "core-js": "^3.6.5", + "dotenv": "^10.0.0", + "pinia": "^2.0.6", + "sweetalert2": "^11.3.0", "vue": "^3.0.0", "vue-class-component": "^8.0.0-0", "vue-router": "^4.0.0-0" @@ -41,7 +46,13 @@ "parserOptions": { "parser": "@typescript-eslint/parser" }, - "rules": {} + "rules": {}, + "globals": { + "defineProps": "readonly", + "defineEmits": "readonly", + "defineExpose": "readonly", + "withDefaults": "readonly" + } }, "browserslist": [ "> 1%", diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..a58cfec --- /dev/null +++ b/public/404.html @@ -0,0 +1,26 @@ + + + + + + + + + <%= htmlWebpackPlugin.options.title %> + + + + + + +
+ + + diff --git a/public/favicon.ico b/public/favicon.ico index df36fcf..5aa2159 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html index 3e5a139..8bebdba 100644 --- a/public/index.html +++ b/public/index.html @@ -1,15 +1,29 @@ - - - - + + + + + <%= htmlWebpackPlugin.options.title %> + +
diff --git a/src/App.vue b/src/App.vue index b964355..46398b1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,30 +1,82 @@ + + diff --git a/src/apis/expense.ts b/src/apis/expense.ts new file mode 100644 index 0000000..3abc647 --- /dev/null +++ b/src/apis/expense.ts @@ -0,0 +1,27 @@ +import { apiHelper } from './helper' +import { RecordInput } from '../models/Record' +import { MessageInput } from '../models/LineBot' + +export default { + getAll() { + return apiHelper.get('/record/all') + }, + getOne(id: number) { + return apiHelper.get(`/record/${id}`) + }, + create(data: RecordInput) { + return apiHelper.post('/record/create', data) + }, + edit(id: number, data: RecordInput) { + return apiHelper.put(`/record/edit/${id}`, data) + }, + close(data: { records: string; totalAmount: number; recorder: string }) { + return apiHelper.put('/close', data) + }, + getLogs() { + return apiHelper.get('/log/all') + }, + pushLineMsg(data: { to: string[]; messages: MessageInput[] }) { + return apiHelper.post('/lineBot/push', data) + } +} diff --git a/src/apis/helper.ts b/src/apis/helper.ts new file mode 100644 index 0000000..a8b6ee5 --- /dev/null +++ b/src/apis/helper.ts @@ -0,0 +1,6 @@ +import axios from 'axios' + +const baseURL = 'http://jm-expense-mysql.herokuapp.com' +export const apiHelper = axios.create({ + baseURL +}) diff --git a/src/assets/capoo.gif b/src/assets/capoo.gif new file mode 100644 index 0000000..85bbd31 Binary files /dev/null and b/src/assets/capoo.gif differ diff --git a/src/assets/jianmiau-login.png b/src/assets/jianmiau-login.png new file mode 100644 index 0000000..6eaf3d7 Binary files /dev/null and b/src/assets/jianmiau-login.png differ diff --git a/src/assets/jianmiau.jpeg b/src/assets/jianmiau.jpeg new file mode 100644 index 0000000..5aa2159 Binary files /dev/null and b/src/assets/jianmiau.jpeg differ diff --git a/src/assets/karol-login.png b/src/assets/karol-login.png new file mode 100644 index 0000000..976f68d Binary files /dev/null and b/src/assets/karol-login.png differ diff --git a/src/assets/karol.png b/src/assets/karol.png new file mode 100644 index 0000000..1ca82d6 Binary files /dev/null and b/src/assets/karol.png differ diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue deleted file mode 100644 index c7d2103..0000000 --- a/src/components/HelloWorld.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue new file mode 100644 index 0000000..92ffe1e --- /dev/null +++ b/src/components/Navbar.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue new file mode 100644 index 0000000..6f7cf5f --- /dev/null +++ b/src/components/Sidebar.vue @@ -0,0 +1,94 @@ + + + diff --git a/src/components/Spinner.vue b/src/components/Spinner.vue new file mode 100644 index 0000000..7b2a3bf --- /dev/null +++ b/src/components/Spinner.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/main.ts b/src/main.ts index 3e79677..b06afe1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,9 @@ import { createApp } from 'vue' import App from './App.vue' import router from './router' +import { createPinia } from 'pinia' -createApp(App).use(router).mount('#app') +import 'bootstrap' +import 'bootstrap/dist/css/bootstrap.min.css' + +createApp(App).use(router).use(createPinia()).mount('#app') diff --git a/src/models/LineBot.ts b/src/models/LineBot.ts new file mode 100644 index 0000000..6e59303 --- /dev/null +++ b/src/models/LineBot.ts @@ -0,0 +1,11 @@ +export interface MessageInput { + type: string + // text + text?: string + // sticker + packageId?: string + stickerId?: string + // image or video + originalContentUrl?: string + previewImageUrl?: string +} diff --git a/src/models/Log.ts b/src/models/Log.ts new file mode 100644 index 0000000..0d17f1b --- /dev/null +++ b/src/models/Log.ts @@ -0,0 +1,21 @@ +export type RecorderType = '建喵' | '豬涵' + +export type ActionType = '新增' | '編輯' | '結算' + +export class Log { + recorder!: RecorderType + action!: ActionType + item?: string + merchant?: string + amount?: number + date?: Date + itemBefore?: string + merchantBefore?: string + amountBefore?: number + dateBefore?: Date + closeAmount?: number + RecordId?: number + RecordIds?: string + createdAt!: Date + updatedAt!: Date +} diff --git a/src/models/Record.ts b/src/models/Record.ts new file mode 100644 index 0000000..f1659c3 --- /dev/null +++ b/src/models/Record.ts @@ -0,0 +1,23 @@ +export type RecorderType = '建喵' | '豬涵' + +export class Record { + id!: number + date!: Date + item?: string + merchant?: string + amount!: number + recorder?: RecorderType + isClosed?: boolean + deletedAt?: Date | null + createdAt!: Date + updatedAt!: Date +} + +export class RecordInput { + date!: Date + item!: string + merchant!: string + amount!: string + recorder?: RecorderType + editor?: RecorderType +} diff --git a/src/router/index.ts b/src/router/index.ts index b6ccb1b..f9f7053 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,16 +1,51 @@ import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' -import Home from '../views/Home.vue' +const root = '/jm-expense-vue-ts' -const routes: RouteRecordRaw[] = [ +export const routes: RouteRecordRaw[] = [ { path: '/', - name: 'Home', - component: Home + redirect: `${root}/` }, { - path: '/about', - name: 'About', - component: () => import('../views/About.vue') + path: `${root}/`, + name: 'Home', + component: () => import('../views/Home.vue'), + meta: { + pageTitle: '首頁', + show: true + } + }, + { + path: `${root}/record`, + name: 'Record', + component: () => import('../views/Record.vue'), + meta: { + pageTitle: '未結算紀錄', + show: true + } + }, + { + path: `${root}/closedRecord`, + name: 'ClosedRecord', + component: () => import('../views/ClosedRecord.vue'), + meta: { + pageTitle: '已結算紀錄', + show: true + } + }, + { + path: `${root}/logs`, + name: 'Logs', + component: () => import('../views/Logs.vue'), + meta: { + pageTitle: '更動紀錄', + show: true + } + }, + { + path: '/:pathMatch(.*)*', + name: 'NotFound', + component: () => import('../views/NotFound.vue') } ] diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..29f80b6 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,14 @@ +import { defineStore } from 'pinia' + +export const useStore = defineStore('index', { + state: () => ({ + currentUser: localStorage.getItem('jm-user') + }), + // getter: {} + actions: { + switchUser(user: string) { + localStorage.setItem('jm-user', user) + this.currentUser = user + } + } +}) diff --git a/src/utils/helper.ts b/src/utils/helper.ts new file mode 100644 index 0000000..31e5772 --- /dev/null +++ b/src/utils/helper.ts @@ -0,0 +1,18 @@ +export const showWeekDay = (date: Date) => { + switch (new Date(date).getDay()) { + case 1: + return '一' + case 2: + return '二' + case 3: + return '三' + case 4: + return '四' + case 5: + return '五' + case 6: + return '六' + case 0: + return '日' + } +} diff --git a/src/utils/swal.ts b/src/utils/swal.ts new file mode 100644 index 0000000..baf1d07 --- /dev/null +++ b/src/utils/swal.ts @@ -0,0 +1,13 @@ +import Swal from 'sweetalert2' + +export const Toast = Swal.mixin({ + toast: true, + position: 'bottom-end', + showConfirmButton: false, + timer: 3000 // toast 停留多久 +}) + +export const ConfirmBox = Swal.mixin({ + showConfirmButton: true, + showCancelButton: true +}) diff --git a/src/views/About.vue b/src/views/About.vue deleted file mode 100644 index 3fa2807..0000000 --- a/src/views/About.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/src/views/ClosedRecord.vue b/src/views/ClosedRecord.vue new file mode 100644 index 0000000..e69d6de --- /dev/null +++ b/src/views/ClosedRecord.vue @@ -0,0 +1,181 @@ + + + + diff --git a/src/views/Home.vue b/src/views/Home.vue index 8bd6c57..e95802d 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -1,18 +1,137 @@ - + + + diff --git a/src/views/Logs.vue b/src/views/Logs.vue new file mode 100644 index 0000000..c831c88 --- /dev/null +++ b/src/views/Logs.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/src/views/NotFound.vue b/src/views/NotFound.vue new file mode 100644 index 0000000..f0122d9 --- /dev/null +++ b/src/views/NotFound.vue @@ -0,0 +1,9 @@ + diff --git a/src/views/Record.vue b/src/views/Record.vue new file mode 100644 index 0000000..8a0dba5 --- /dev/null +++ b/src/views/Record.vue @@ -0,0 +1,399 @@ + + + + diff --git a/vue.config.js b/vue.config.js new file mode 100644 index 0000000..d70e4ba --- /dev/null +++ b/vue.config.js @@ -0,0 +1,3 @@ +module.exports = { + publicPath: process.env.NODE_ENV === 'production' ? '/jm-expense-vue-ts/' : '/' +}