| @@ -1,3 +1,5 @@ | |||
| /.idea/ | |||
| /src/main/resources/public/ | |||
| /target/ | |||
| /rms.iml | |||
| /app/node_modules/ | |||
| @@ -0,0 +1,2 @@ | |||
| # Default ignored files | |||
| /workspace.xml | |||
| @@ -1710,16 +1710,6 @@ | |||
| "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", | |||
| "dev": true | |||
| }, | |||
| "ansi-styles": { | |||
| "version": "4.3.0", | |||
| "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | |||
| "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "color-convert": "^2.0.1" | |||
| } | |||
| }, | |||
| "cacache": { | |||
| "version": "13.0.1", | |||
| "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", | |||
| @@ -1752,34 +1742,6 @@ | |||
| "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", | |||
| "dev": true | |||
| }, | |||
| "chalk": { | |||
| "version": "4.1.0", | |||
| "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", | |||
| "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "ansi-styles": "^4.1.0", | |||
| "supports-color": "^7.1.0" | |||
| } | |||
| }, | |||
| "color-convert": { | |||
| "version": "2.0.1", | |||
| "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", | |||
| "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "color-name": "~1.1.4" | |||
| } | |||
| }, | |||
| "color-name": { | |||
| "version": "1.1.4", | |||
| "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", | |||
| "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", | |||
| "dev": true, | |||
| "optional": true | |||
| }, | |||
| "css-loader": { | |||
| "version": "3.6.0", | |||
| "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", | |||
| @@ -1823,13 +1785,6 @@ | |||
| } | |||
| } | |||
| }, | |||
| "has-flag": { | |||
| "version": "4.0.0", | |||
| "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", | |||
| "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", | |||
| "dev": true, | |||
| "optional": true | |||
| }, | |||
| "icss-utils": { | |||
| "version": "4.1.1", | |||
| "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", | |||
| @@ -1839,18 +1794,6 @@ | |||
| "postcss": "^7.0.14" | |||
| } | |||
| }, | |||
| "loader-utils": { | |||
| "version": "2.0.0", | |||
| "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", | |||
| "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "big.js": "^5.2.2", | |||
| "emojis-list": "^3.0.0", | |||
| "json5": "^2.1.2" | |||
| } | |||
| }, | |||
| "postcss-modules-extract-imports": { | |||
| "version": "2.0.0", | |||
| "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", | |||
| @@ -1914,16 +1857,6 @@ | |||
| "minipass": "^3.1.1" | |||
| } | |||
| }, | |||
| "supports-color": { | |||
| "version": "7.2.0", | |||
| "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | |||
| "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "has-flag": "^4.0.0" | |||
| } | |||
| }, | |||
| "terser-webpack-plugin": { | |||
| "version": "2.3.8", | |||
| "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", | |||
| @@ -1940,18 +1873,6 @@ | |||
| "terser": "^4.6.12", | |||
| "webpack-sources": "^1.4.3" | |||
| } | |||
| }, | |||
| "vue-loader-v16": { | |||
| "version": "npm:vue-loader@16.1.2", | |||
| "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz", | |||
| "integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "chalk": "^4.1.0", | |||
| "hash-sum": "^2.0.0", | |||
| "loader-utils": "^2.0.0" | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| @@ -11180,6 +11101,87 @@ | |||
| } | |||
| } | |||
| }, | |||
| "vue-loader-v16": { | |||
| "version": "npm:vue-loader@16.1.2", | |||
| "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz", | |||
| "integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "chalk": "^4.1.0", | |||
| "hash-sum": "^2.0.0", | |||
| "loader-utils": "^2.0.0" | |||
| }, | |||
| "dependencies": { | |||
| "ansi-styles": { | |||
| "version": "4.3.0", | |||
| "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | |||
| "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "color-convert": "^2.0.1" | |||
| } | |||
| }, | |||
| "chalk": { | |||
| "version": "4.1.0", | |||
| "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", | |||
| "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "ansi-styles": "^4.1.0", | |||
| "supports-color": "^7.1.0" | |||
| } | |||
| }, | |||
| "color-convert": { | |||
| "version": "2.0.1", | |||
| "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", | |||
| "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "color-name": "~1.1.4" | |||
| } | |||
| }, | |||
| "color-name": { | |||
| "version": "1.1.4", | |||
| "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", | |||
| "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", | |||
| "dev": true, | |||
| "optional": true | |||
| }, | |||
| "has-flag": { | |||
| "version": "4.0.0", | |||
| "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", | |||
| "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", | |||
| "dev": true, | |||
| "optional": true | |||
| }, | |||
| "loader-utils": { | |||
| "version": "2.0.0", | |||
| "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", | |||
| "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "big.js": "^5.2.2", | |||
| "emojis-list": "^3.0.0", | |||
| "json5": "^2.1.2" | |||
| } | |||
| }, | |||
| "supports-color": { | |||
| "version": "7.2.0", | |||
| "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | |||
| "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", | |||
| "dev": true, | |||
| "optional": true, | |||
| "requires": { | |||
| "has-flag": "^4.0.0" | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| "vue-router": { | |||
| "version": "3.5.1", | |||
| "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.1.tgz", | |||
| @@ -3,7 +3,7 @@ | |||
| "version": "0.1.0", | |||
| "private": true, | |||
| "scripts": { | |||
| "serve": "vue-cli-service serve", | |||
| "serve": "vue-cli-service serve --port 3000", | |||
| "build": "vue-cli-service build", | |||
| "lint": "vue-cli-service lint" | |||
| }, | |||
| @@ -5,6 +5,8 @@ | |||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
| <meta name="viewport" content="width=device-width,initial-scale=1.0"> | |||
| <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | |||
| <link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet"> | |||
| <title><%= htmlWebpackPlugin.options.title %></title> | |||
| </head> | |||
| <body> | |||
| @@ -1,10 +1,44 @@ | |||
| <template> | |||
| <v-app> | |||
| <router-view /> | |||
| </v-app> | |||
| <v-app> | |||
| <router-view @authenticated="setAuthenticated"/> | |||
| </v-app> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'App', | |||
| } | |||
| export default { | |||
| name: 'App', | |||
| computed: { | |||
| authenticated() { | |||
| return localStorage.getItem('username') != null; | |||
| } | |||
| }, | |||
| mounted() { | |||
| if (!this.authenticated) { | |||
| this.$router.replace({name: "login"}); | |||
| } else { | |||
| this.$router.replace({name: "asset_group"}); | |||
| } | |||
| }, | |||
| methods: { | |||
| setAuthenticated(user) { | |||
| if (user) { | |||
| localStorage.setItem('username', user.username); | |||
| localStorage.setItem('roles', user.roles); | |||
| } else { | |||
| this.logout(); | |||
| } | |||
| this.$router.replace({ name: 'asset_group' }); | |||
| }, | |||
| logout() { | |||
| this.$axios.get('/logout').then((resp) => { | |||
| console.log(resp); | |||
| }) | |||
| .catch(function (resp) { | |||
| console.log(resp); | |||
| }); | |||
| localStorage.removeItem('username'); | |||
| localStorage.removeItem('roles'); | |||
| this.$router.replace({name: "login"}); | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| @@ -0,0 +1,201 @@ | |||
| <template> | |||
| <div class="dashboard"> | |||
| <h1>歷史報表檢視</h1> | |||
| <v-card> | |||
| <v-card-title> | |||
| 歷史報表 -- 共 {{reports.length}} 筆 | |||
| <v-spacer></v-spacer> | |||
| <v-select | |||
| v-model="select" | |||
| :items="reportconfs" | |||
| :rules="[v => !!v || '必須要選擇一個報表']" | |||
| label="選擇報表" | |||
| required | |||
| item-text="nameId" | |||
| item-value="id" | |||
| @change="loadReport()" | |||
| ></v-select> | |||
| <div class="px-1"></div> | |||
| <v-menu | |||
| v-model="menu2" | |||
| :close-on-content-click="false" | |||
| :nudge-right="40" | |||
| transition="scale-transition" | |||
| offset-y | |||
| min-width="100px" | |||
| class="mr-3" | |||
| > | |||
| <template v-slot:activator="{ on }"> | |||
| <v-text-field | |||
| v-model="date" | |||
| label="選擇報表日期" | |||
| prepend-icon="mdi-calendar" | |||
| readonly | |||
| v-on="on" | |||
| @change="loadReport()" | |||
| ></v-text-field> | |||
| </template> | |||
| <v-date-picker v-model="date" @input="menu2 = false;loadReport()"></v-date-picker> | |||
| </v-menu> | |||
| <div v-if="isAdmin" class="pl-4 mb-3"> | |||
| <v-tooltip bottom> | |||
| <template v-slot:activator="{ on, attrs }"> | |||
| <v-btn | |||
| color="primary" | |||
| dark | |||
| v-bind="attrs" | |||
| v-on="on" | |||
| @click="generateReport()" | |||
| > | |||
| 重新產生報表 | |||
| </v-btn> | |||
| </template> | |||
| <span>以選定日期重新產生報表, 不會發送 Email 及 FTP</span> | |||
| </v-tooltip> | |||
| </div> | |||
| </v-card-title> | |||
| <v-data-table | |||
| :headers="headers" | |||
| :items="reports" | |||
| :search="search" | |||
| > | |||
| <template v-slot:item.fileName="{ item }"> | |||
| <a href="javascript:void(0)" @click="download(item.filePath)">{{item.fileName}}</a> | |||
| </template> | |||
| </v-data-table> | |||
| </v-card> | |||
| <v-snackbar | |||
| v-model="snackbar" | |||
| :color="snackbarColor" | |||
| :timeout="snackbarTimeout" | |||
| absolute | |||
| right | |||
| rounded="pill" | |||
| top | |||
| > | |||
| {{snackbarMessage}}. | |||
| </v-snackbar> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import axios from 'axios'; | |||
| export default { | |||
| name: 'AssetGroup', | |||
| data () { | |||
| return { | |||
| search: '', | |||
| select: null, | |||
| reportconfs: [], | |||
| date: new Date().toISOString().substr(0, 10), | |||
| menu2: false, | |||
| headers: [ | |||
| { text: '檔案名稱', value: 'fileName',}, | |||
| { text: '產生時間', value: 'fileDate' }, | |||
| // { text: '產生狀態', value: 'genstate' }, | |||
| // { text: 'FTP 狀態', value: 'ftpstate' }, | |||
| // { text: 'Email 狀態', value: 'emailstate' }, | |||
| ], | |||
| reports: [], | |||
| snackbar: false, | |||
| snackbarMessage: "", | |||
| snackbarColor: "blue-grey", | |||
| snackbarTimeout: 1000, | |||
| } | |||
| }, | |||
| created() { | |||
| this.initialize(); | |||
| }, | |||
| computed: { | |||
| isAdmin() { | |||
| let roles = localStorage.getItem( 'roles'); | |||
| let admin = (roles != null && roles.indexOf('ADMIN') >= 0 ); | |||
| console.log("isAdmin", admin); | |||
| return admin; | |||
| } | |||
| }, | |||
| methods: { | |||
| initialize() { | |||
| // axios.get("/api/access/reportview/listconf", this.editedItem) | |||
| // .then((result) => { | |||
| // let json = result.data; | |||
| // this.reportconfs = json.data | |||
| // if (this.reportconfs && this.reportconfs.length > 0) { | |||
| // this.select = this.reportconfs[0].id; | |||
| // this.loadReport(); | |||
| // } | |||
| // }); | |||
| }, | |||
| loadReport() { | |||
| axios.get("/api/access/reportview/listreport/"+ this.select + "/" + this.date) | |||
| .then((result) => { | |||
| let json = result.data; | |||
| if (json.status === "OK") | |||
| this.reports = json.data; | |||
| else { | |||
| this.reports = []; | |||
| this.$root.$emit('showError', json.message); | |||
| } | |||
| }); | |||
| }, | |||
| download(file) { | |||
| axios({ | |||
| url: '/api/access/reportview/download?file=' + file, //your url | |||
| method: 'GET', | |||
| responseType: 'blob', // important | |||
| }).then((response) => { | |||
| const url = window.URL.createObjectURL(new Blob([response.data])); | |||
| const link = document.createElement('a'); | |||
| console.log(response); | |||
| console.log(file); | |||
| file = file.replace("\\", "/"); | |||
| const fileName = file.substr(file.lastIndexOf("/")+1); | |||
| link.href = url; | |||
| link.setAttribute('download', fileName); //or any other extension | |||
| document.body.appendChild(link); | |||
| link.click(); | |||
| }); | |||
| }, | |||
| generateReport() { | |||
| let reportId = this.select; | |||
| let date = this.date; | |||
| axios({ | |||
| url: '/api/access/reportview/genreport/' + reportId + "/" + date, //your url | |||
| method: 'GET', | |||
| }).then((response) => { | |||
| let json = response.data; | |||
| if (json.status === "OK") { | |||
| this.snackbarMessage = "報表在背景執行,請稍候…"; | |||
| this.snackbarColor = "blue-grey"; | |||
| this.snackbarTimeout = 1000, | |||
| this.snackbar = true; | |||
| setTimeout(this.checkReportStatus, 1000); | |||
| } | |||
| else { | |||
| this.snackbarMessage = "報表無法執行:" + json.message; | |||
| this.snackbarColor = "deep-orange"; | |||
| this.snackbar = true; | |||
| } | |||
| }); | |||
| }, | |||
| checkReportStatus() { | |||
| axios({ | |||
| url: '/api/access/reportview/genreport/status', //your url | |||
| method: 'GET', | |||
| }).then((response) => { | |||
| let json = response.data; | |||
| if (json.status === "OK" && json.data === true) { | |||
| this.snackbarMessage = "報表仍在背景執行,請稍候…"; | |||
| this.snackbar = true; | |||
| setTimeout(this.checkReportStatus, 1000); | |||
| } | |||
| else{ | |||
| this.snackbarMessage = "報表執行完畢!"; | |||
| this.snackbarTimeout = 3000, | |||
| this.snackbar = true; | |||
| this.loadReport(); | |||
| } | |||
| }); | |||
| }, | |||
| }, | |||
| } | |||
| </script> | |||
| @@ -0,0 +1,8 @@ | |||
| <template> | |||
| <div></div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: '' | |||
| } | |||
| </script> | |||
| @@ -0,0 +1,153 @@ | |||
| <template> | |||
| <div> | |||
| <v-data-table class="pa-3 pt-5" :headers="headers"> | |||
| <template v-slot:top> | |||
| <v-toolbar flat> | |||
| <v-dialog | |||
| v-model="dialog" | |||
| max-width="500px" | |||
| > | |||
| <template v-slot:activator="{ on, attrs }"> | |||
| <v-spacer></v-spacer> | |||
| <v-btn | |||
| color="primary" | |||
| v-bind="attrs" | |||
| v-on="on" | |||
| > | |||
| <v-icon large>mdi-plus-circle</v-icon> | |||
| 新增 | |||
| </v-btn> | |||
| </template> | |||
| <v-card> | |||
| <v-card-title> | |||
| <!-- <span class="headline">{{ formTitle }}</span>--> | |||
| </v-card-title> | |||
| <!-- <v-card-text>--> | |||
| <!-- <v-container>--> | |||
| <!-- <v-row>--> | |||
| <!-- <v-col--> | |||
| <!-- cols="12"--> | |||
| <!-- sm="6"--> | |||
| <!-- md="4"--> | |||
| <!-- >--> | |||
| <!-- <v-text-field--> | |||
| <!-- v-model="editedItem.name"--> | |||
| <!-- label="Dessert name"--> | |||
| <!-- ></v-text-field>--> | |||
| <!-- </v-col>--> | |||
| <!-- <v-col--> | |||
| <!-- cols="12"--> | |||
| <!-- sm="6"--> | |||
| <!-- md="4"--> | |||
| <!-- >--> | |||
| <!-- <v-text-field--> | |||
| <!-- v-model="editedItem.calories"--> | |||
| <!-- label="Calories"--> | |||
| <!-- ></v-text-field>--> | |||
| <!-- </v-col>--> | |||
| <!-- <v-col--> | |||
| <!-- cols="12"--> | |||
| <!-- sm="6"--> | |||
| <!-- md="4"--> | |||
| <!-- >--> | |||
| <!-- <v-text-field--> | |||
| <!-- v-model="editedItem.fat"--> | |||
| <!-- label="Fat (g)"--> | |||
| <!-- ></v-text-field>--> | |||
| <!-- </v-col>--> | |||
| <!-- <v-col--> | |||
| <!-- cols="12"--> | |||
| <!-- sm="6"--> | |||
| <!-- md="4"--> | |||
| <!-- >--> | |||
| <!-- <v-text-field--> | |||
| <!-- v-model="editedItem.carbs"--> | |||
| <!-- label="Carbs (g)"--> | |||
| <!-- ></v-text-field>--> | |||
| <!-- </v-col>--> | |||
| <!-- <v-col--> | |||
| <!-- cols="12"--> | |||
| <!-- sm="6"--> | |||
| <!-- md="4"--> | |||
| <!-- >--> | |||
| <!-- <v-text-field--> | |||
| <!-- v-model="editedItem.protein"--> | |||
| <!-- label="Protein (g)"--> | |||
| <!-- ></v-text-field>--> | |||
| <!-- </v-col>--> | |||
| <!-- </v-row>--> | |||
| <!-- </v-container>--> | |||
| <!-- </v-card-text>--> | |||
| <v-card-actions> | |||
| <v-spacer></v-spacer> | |||
| <!-- <v-btn--> | |||
| <!-- color="blue darken-1"--> | |||
| <!-- text--> | |||
| <!-- @click="close"--> | |||
| <!-- >--> | |||
| <!-- Cancel--> | |||
| <!-- </v-btn>--> | |||
| <!-- <v-btn--> | |||
| <!-- color="blue darken-1"--> | |||
| <!-- text--> | |||
| <!-- @click="save"--> | |||
| <!-- >--> | |||
| <!-- Save--> | |||
| <!-- </v-btn>--> | |||
| </v-card-actions> | |||
| </v-card> | |||
| </v-dialog> | |||
| <!-- <v-dialog v-model="dialogDelete" max-width="500px">--> | |||
| <!-- <v-card>--> | |||
| <!-- <v-card-title class="headline">Are you sure you want to delete this item?</v-card-title>--> | |||
| <!-- <v-card-actions>--> | |||
| <!-- <v-spacer></v-spacer>--> | |||
| <!-- <v-btn color="blue darken-1" text @click="closeDelete">Cancel</v-btn>--> | |||
| <!-- <v-btn color="blue darken-1" text @click="deleteItemConfirm">OK</v-btn>--> | |||
| <!-- <v-spacer></v-spacer>--> | |||
| <!-- </v-card-actions>--> | |||
| <!-- </v-card>--> | |||
| <!-- </v-dialog>--> | |||
| </v-toolbar> | |||
| </template> | |||
| </v-data-table> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: '', | |||
| data() { | |||
| return { | |||
| dialog: false, | |||
| headers: [ | |||
| {text: '主機名稱', value: 'hostname'}, | |||
| {text: '資產群組', value: 'type'}, | |||
| {text: '對應應用系統', value: 'descript'}, | |||
| {text: 'IP1', value: 'ip1'}, | |||
| {text: 'IP2', value: 'ip2'}, | |||
| {text: '機型(型號)', value: 'model'}, | |||
| {text: '環境別', value: 'environment'}, | |||
| {text: '放置地點', value: 'position'}, | |||
| {text: '機櫃編號', value: 'position_number'}, | |||
| {text: '數量', value: 'amount'}, | |||
| {text: '擁有者', value: 'owner_department'}, | |||
| {text: '保管單位', value: 'keep_department'}, | |||
| {text: '保管者', value: 'keeper'}, | |||
| {text: '備註', value: 'remark'}, | |||
| ], | |||
| items: [], | |||
| } | |||
| }, | |||
| mounted() { | |||
| this.getHardwares(); | |||
| }, | |||
| methods: { | |||
| async getHardwares() { | |||
| this.items = await this.$axios.get('/hardware/hardwares'); | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| @@ -0,0 +1,154 @@ | |||
| <template> | |||
| <v-app id="inspire"> | |||
| <v-navigation-drawer | |||
| v-model="drawer" | |||
| :clipped="$vuetify.breakpoint.lgAndUp" | |||
| app | |||
| > | |||
| <v-list dense> | |||
| <template v-for="item in items"> | |||
| <v-list-group | |||
| v-if="item.children" | |||
| :key="item.text" | |||
| v-model="item.model" | |||
| :prepend-icon="item.model ? item.icon : item['icon-alt']" | |||
| append-icon="" | |||
| > | |||
| <template v-slot:activator> | |||
| <v-list-item-content> | |||
| <v-list-item-title> | |||
| {{ item.text }} | |||
| </v-list-item-title> | |||
| </v-list-item-content> | |||
| </template> | |||
| <v-list-item | |||
| v-for="(child, i) in item.children" | |||
| :key="i" | |||
| link | |||
| :to="child.route" | |||
| > | |||
| <v-list-item-action v-if="child.icon"> | |||
| <v-icon>{{ child.icon }}</v-icon> | |||
| </v-list-item-action> | |||
| <v-list-item-content> | |||
| <v-list-item-title> | |||
| {{ child.text }} | |||
| </v-list-item-title> | |||
| </v-list-item-content> | |||
| </v-list-item> | |||
| </v-list-group> | |||
| <v-list-item | |||
| v-else | |||
| :key="item.text" | |||
| link | |||
| :to="item.route" | |||
| > | |||
| <v-list-item-action> | |||
| <v-icon>{{ item.icon }}</v-icon> | |||
| </v-list-item-action> | |||
| <v-list-item-content> | |||
| <v-list-item-title> | |||
| {{ item.text }} | |||
| </v-list-item-title> | |||
| </v-list-item-content> | |||
| </v-list-item> | |||
| </template> | |||
| </v-list> | |||
| </v-navigation-drawer> | |||
| <v-app-bar | |||
| :clipped-left="$vuetify.breakpoint.lgAndUp" | |||
| app | |||
| color="blue darken-3" | |||
| dark | |||
| > | |||
| <v-app-bar-nav-icon @click.stop="drawer = !drawer" /> | |||
| <v-toolbar-title | |||
| style="width: 400px" | |||
| class="ml-0 pl-4" | |||
| > | |||
| <span class="hidden-sm-and-down">資訊資產管理系統</span> | |||
| </v-toolbar-title> | |||
| <v-spacer /> | |||
| <v-btn | |||
| icon | |||
| large | |||
| @click="logout" | |||
| > | |||
| <v-avatar | |||
| size="32px" | |||
| item | |||
| > | |||
| <v-img | |||
| src="logo.svg" | |||
| alt="Vuetify" | |||
| /></v-avatar> | |||
| </v-btn> | |||
| </v-app-bar> | |||
| <v-content> | |||
| <v-container class="grey lighten-5"> | |||
| <keep-alive> | |||
| <router-view></router-view> | |||
| </keep-alive> | |||
| </v-container> | |||
| </v-content> | |||
| <v-snackbar v-model="error" top :timeout="2500"> | |||
| {{ message }} | |||
| <v-btn | |||
| color="pink" | |||
| text | |||
| @click="error = false" | |||
| > | |||
| Close | |||
| </v-btn> | |||
| </v-snackbar> | |||
| </v-app> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| components: { | |||
| }, | |||
| props: { | |||
| source: String, | |||
| }, | |||
| data: () => ({ | |||
| dialog: false, | |||
| drawer: null, | |||
| error: false, | |||
| message: "", | |||
| }), | |||
| created(){ | |||
| this.$root.$on('showError', (message) => { | |||
| this.error = true; | |||
| this.message = message; | |||
| }); | |||
| }, | |||
| computed: { | |||
| isAdmin() { | |||
| let roles = localStorage.getItem('roles'); | |||
| return (roles != null && roles.indexOf('ADMIN') >= 0 ); | |||
| }, | |||
| items() { | |||
| let ret = [ | |||
| { icon: 'mdi-history', text: '資訊資產群組', route: '/asset_group' }, | |||
| { icon: 'mdi-history', text: '硬體', route: '/hardware' }, | |||
| { icon: 'mdi-history', text: '軟體', route: '/software' }, | |||
| { icon: 'mdi-history', text: '資料與文件', route: '/fileAndData' }, | |||
| { icon: 'mdi-history', text: '人員', route: '/person' }, | |||
| ]; | |||
| // if (this.isAdmin) { | |||
| // ret.push({ icon: 'mdi-account', text: '帳號權限管理', route: '/account' }); | |||
| // ret.push({ icon: 'mdi-poll-box', text: '報表組態管理', route: '/report_conf' }); | |||
| // ret.push({ icon: 'mdi-message', text: '通知信管理', route: '/notice' }); | |||
| // ret.push({ icon: 'mdi-account-details', text: '收信人管理', route: '/receiver' }); | |||
| // } | |||
| return ret; | |||
| } | |||
| }, | |||
| methods: { | |||
| logout(){ | |||
| this.$emit("authenticated", null); | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| @@ -1,10 +1,128 @@ | |||
| <template> | |||
| <v-container> | |||
| <div class="d-flex justify-center font-weight-bold title">Login</div> | |||
| </v-container> | |||
| <v-main> | |||
| <v-container | |||
| class='fill-height' | |||
| fluid | |||
| > | |||
| <v-row | |||
| align='center' | |||
| justify='center' | |||
| > | |||
| <v-col | |||
| cols='12' | |||
| sm='8' | |||
| md='4' | |||
| > | |||
| <v-card class='elevation-12'> | |||
| <v-toolbar | |||
| color='primary' | |||
| dark | |||
| flat | |||
| > | |||
| <v-toolbar-title>資訊資產管理系統</v-toolbar-title> | |||
| <v-spacer /> | |||
| </v-toolbar> | |||
| <v-card-text> | |||
| <v-form> | |||
| <v-text-field | |||
| v-model='input.username' | |||
| label='Login' | |||
| name='login' | |||
| prepend-icon='mdi-account' | |||
| type='text' | |||
| /> | |||
| <v-text-field | |||
| v-model='input.password' | |||
| id='password' | |||
| label='Password' | |||
| name='password' | |||
| prepend-icon='mdi-lock' | |||
| type='password' | |||
| /> | |||
| </v-form> | |||
| </v-card-text> | |||
| <v-card-actions> | |||
| <v-spacer /> | |||
| <v-btn color='primary' v-on:click='logintest()'>Login</v-btn> | |||
| </v-card-actions> | |||
| </v-card> | |||
| </v-col> | |||
| </v-row> | |||
| </v-container> | |||
| <v-snackbar v-model='loginError' top :timeout='2500'> | |||
| {{ message }} | |||
| <v-btn | |||
| color='pink' | |||
| text | |||
| @click='loginError = false' | |||
| > | |||
| Close | |||
| </v-btn> | |||
| </v-snackbar> | |||
| </v-main> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'Login' | |||
| name: 'Login', | |||
| data() { | |||
| return { | |||
| input: { | |||
| username: '', | |||
| password: '', | |||
| }, | |||
| loginError : false, | |||
| message : '', | |||
| } | |||
| }, | |||
| methods: { | |||
| logintest() { | |||
| console.log('asd'); | |||
| this.$emit('authenticated', | |||
| { | |||
| 'username':'aaa', | |||
| 'roles': 'aaa', | |||
| }); | |||
| }, | |||
| login() { | |||
| this.loginError = false; | |||
| const _this = this; | |||
| if(this.input.username !== '' && this.input.password !== '') { | |||
| const bodyFormData = new FormData(); | |||
| bodyFormData.set('username', this.input.username); | |||
| bodyFormData.set('password', this.input.password); | |||
| this.$axios.post('/login', this.input).then((resp) => { | |||
| if (resp.status === 200) { | |||
| //成功 | |||
| const json = resp.data; | |||
| if (json.status === 'OK') { | |||
| _this.$emit('authenticated', | |||
| { | |||
| 'username': _this.input.username, | |||
| 'roles': json.data | |||
| }); | |||
| _this.$router.replace({ name: 'report_view' }); | |||
| } else { | |||
| _this.message = json.message; | |||
| _this.loginError = true; | |||
| } | |||
| } else { | |||
| _this.message = 'The username and / or password is incorrect'; | |||
| _this.loginError = true; | |||
| } | |||
| console.log(resp); | |||
| }) | |||
| .catch(function (response) { | |||
| //handle error | |||
| console.log(response); | |||
| }); | |||
| } else { | |||
| this.message = '請輸入帳號及密碼'; | |||
| this.loginError = true; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| @@ -0,0 +1,8 @@ | |||
| <template> | |||
| <div></div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: '' | |||
| } | |||
| </script> | |||
| @@ -0,0 +1,8 @@ | |||
| <template> | |||
| <div></div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: '' | |||
| } | |||
| </script> | |||
| @@ -6,12 +6,12 @@ import router from './router/index' | |||
| import axios from './plugins/axios' | |||
| import vuetify from '@/plugins/vuetify' | |||
| Vue.config.productionTip = false | |||
| Vue.config.productionTip = false; | |||
| Vue.prototype.$axios = axios; | |||
| new Vue({ | |||
| router, | |||
| axios, | |||
| vuetify, | |||
| render: h => h(App), | |||
| }).$mount('#app') | |||
| }).$mount('#app'); | |||
| @@ -2,11 +2,11 @@ import axios from 'axios'; | |||
| //在開發環境中的測試 development | |||
| if(process.env.NODE_ENV === 'development') { | |||
| axios.defaults.baseURL = '/' | |||
| axios.defaults.baseURL = '/api' | |||
| } | |||
| //在生產環境中的測試 production | |||
| if(process.env.NODE_ENV === 'production') { | |||
| axios.defaults.baseURL = '/' | |||
| axios.defaults.baseURL = '/api' | |||
| } | |||
| axios.defaults.headers.post['Content-Type'] = 'charset=utf-8'; | |||
| axios.defaults.headers.common.Pragma = 'no-cache'; | |||
| @@ -1,29 +1,53 @@ | |||
| import Vue from 'vue' | |||
| import Router from 'vue-router' | |||
| import Login from '@/components/Login' | |||
| import Home from "../components/Home"; | |||
| import AssetGroup from "../components/AssetGroup"; | |||
| import Hardware from "../components/Hardware"; | |||
| import Software from "../components/Software"; | |||
| import FileAndData from "../components/FileAndData"; | |||
| import Person from "../components/Person"; | |||
| Vue.use(Router) | |||
| Vue.use(Router); | |||
| export const constantRoutes = [ | |||
| { | |||
| path: '/', | |||
| alias: '/login', | |||
| path: '/login', | |||
| name: 'login', | |||
| component: Login, | |||
| }, | |||
| // { | |||
| // path: '/', | |||
| // component: , | |||
| // name: '', | |||
| // children: [ | |||
| // { | |||
| // path: '', | |||
| // component: () => import(''), | |||
| // name: '', | |||
| // meta: { | |||
| // }, | |||
| // }, | |||
| // ], | |||
| // }, | |||
| { | |||
| path: '/home', | |||
| component: Home, | |||
| name: 'home', | |||
| children: [ | |||
| { | |||
| path: '/asset_group', | |||
| name: 'asset_group', | |||
| component: AssetGroup | |||
| }, | |||
| { | |||
| path: '/hardware', | |||
| name: 'hardware', | |||
| component: Hardware | |||
| }, | |||
| { | |||
| path: '/software', | |||
| name: 'software', | |||
| component: Software | |||
| }, | |||
| { | |||
| path: '/fileAndData', | |||
| name: 'fileAndData', | |||
| component: FileAndData | |||
| }, | |||
| { | |||
| path: '/person', | |||
| name: 'person', | |||
| component: Person | |||
| }, | |||
| ] | |||
| } | |||
| ]; | |||
| export default new Router({ | |||
| @@ -57,6 +57,10 @@ | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-starter-jdbc</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.jdbi</groupId> | |||
| @@ -92,6 +96,17 @@ | |||
| <version>1.18.16</version> | |||
| <optional>true</optional> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.slf4j</groupId> | |||
| <artifactId>slf4j-api</artifactId> | |||
| <version>1.7.30</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.slf4j</groupId> | |||
| <artifactId>slf4j-simple</artifactId> | |||
| <version>1.7.30</version> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -3,9 +3,10 @@ package com.moze.rms; | |||
| import org.springframework.boot.SpringApplication; | |||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
| import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; | |||
| @SpringBootApplication | |||
| public class RmsApplication { | |||
| public class RmsApplication extends SpringBootServletInitializer { | |||
| public static void main(String[] args) { | |||
| SpringApplication.run(RmsApplication.class, args); | |||
| @@ -0,0 +1,55 @@ | |||
| package com.moze.rms.config; | |||
| import com.moze.rms.dao.HardwareDAO; | |||
| import org.jdbi.v3.core.Jdbi; | |||
| import org.jdbi.v3.core.statement.SqlLogger; | |||
| import org.jdbi.v3.core.statement.StatementContext; | |||
| import org.jdbi.v3.sqlobject.SqlObjectPlugin; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.context.annotation.Bean; | |||
| import org.springframework.context.annotation.Configuration; | |||
| import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; | |||
| import javax.sql.DataSource; | |||
| @Configuration | |||
| public class JdbiFactory { | |||
| @Autowired | |||
| DataSource dataSource; | |||
| @Bean | |||
| public Jdbi getInstance() { | |||
| TransactionAwareDataSourceProxy proxy = new TransactionAwareDataSourceProxy(dataSource); | |||
| Jdbi jdbi = Jdbi.create(proxy); | |||
| jdbi.setSqlLogger(new SqlLogger() { | |||
| @Override | |||
| public void logBeforeExecution(StatementContext ctx) { | |||
| if (ctx.getStatement() != null) | |||
| System.out.println(ctx.getStatement().toString()); | |||
| } | |||
| // @Override | |||
| // public void logAfterExecution(StatementContext context) { | |||
| //// jdbi.setSqlLogger(SqlLogger.NOP_SQL_LOGGER); | |||
| // } | |||
| // @Override | |||
| // public void logException(StatementContext context, SQLException ex) { | |||
| //// jdbi.setSqlLogger(SqlLogger.NOP_SQL_LOGGER); | |||
| // } | |||
| }); | |||
| return jdbi.installPlugin(new SqlObjectPlugin()); | |||
| } | |||
| // @Bean | |||
| // public Jdbi jdbi(DataSource ds, List<JdbiPlugin> jdbiPlugins, List<RowMapper<?>> rowMappers) { | |||
| // TransactionAwareDataSourceProxy proxy = new TransactionAwareDataSourceProxy(ds); | |||
| // Jdbi jdbi = Jdbi.create(proxy); | |||
| // jdbiPlugins.forEach(plugin -> jdbi.installPlugin(plugin)); | |||
| // rowMappers.forEach(mapper -> jdbi.registerRowMapper(mapper)); | |||
| // return jdbi; | |||
| // } | |||
| @Bean | |||
| public HardwareDAO hardwareDAO(Jdbi jdbi) { | |||
| return jdbi.onDemand(HardwareDAO.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.moze.rms.controller; | |||
| import com.moze.rms.dao.HardwareDAO; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| @RestController | |||
| @RequestMapping("/api/hardware") | |||
| public class HardwareController { | |||
| @Autowired | |||
| HardwareDAO hardwareDAO; | |||
| @GetMapping("/hardwares") | |||
| public JsonResult getHardwares() { | |||
| return new JsonResult(StatusCode.SUCCESS, hardwareDAO.findAll()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| package com.moze.rms.controller; | |||
| import lombok.Data; | |||
| import lombok.NoArgsConstructor; | |||
| @Data | |||
| @NoArgsConstructor | |||
| public class JsonResult { | |||
| private int code; | |||
| private String message; | |||
| private Object data; | |||
| public JsonResult(StatusCode statusCode, Object data) { | |||
| this.code = statusCode.getCode(); | |||
| this.message = statusCode.getMessage(); | |||
| this.data = data; | |||
| } | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| package com.moze.rms.controller; | |||
| public enum StatusCode { | |||
| /** 成功 */ | |||
| SUCCESS(200, "成功"), | |||
| /** 沒有登入 */ | |||
| NOT_LOGIN(400, "帳號或密碼錯誤"), | |||
| /** 發生異常 */ | |||
| EXCEPTION(401, "發生異常"), | |||
| /** 系統錯誤 */ | |||
| SYS_ERROR(402, "系統錯誤"), | |||
| /** 引數錯誤 */ | |||
| PARAMS_ERROR(403, "引數錯誤 "), | |||
| /** 不支援或已經廢棄 */ | |||
| NOT_SUPPORTED(410, "不支援或已經廢棄"), | |||
| /** AuthCode錯誤 */ | |||
| INVALID_AUTHCODE(444, "無效的AuthCode"), | |||
| /** 太頻繁的呼叫 */ | |||
| TOO_FREQUENT(445, "太頻繁的呼叫"), | |||
| /** 未知的錯誤 */ | |||
| UNKNOWN_ERROR(499, "未知錯誤"); | |||
| private int code; | |||
| private String message; | |||
| StatusCode(int code, String message) { | |||
| this.code = code; | |||
| this.message = message; | |||
| } | |||
| public int getCode() { | |||
| return code; | |||
| } | |||
| public void setCode(int code) { | |||
| this.code = code; | |||
| } | |||
| public String getMessage() { | |||
| return message; | |||
| } | |||
| public void setMessage(String message) { | |||
| this.message = message; | |||
| } | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| package com.moze.rms.dao; | |||
| import com.moze.rms.entity.model.Hardware; | |||
| import org.jdbi.v3.sqlobject.config.RegisterBeanMapper; | |||
| import org.jdbi.v3.sqlobject.statement.SqlQuery; | |||
| import org.springframework.stereotype.Repository; | |||
| import java.util.List; | |||
| @RegisterBeanMapper(Hardware.class) | |||
| public interface HardwareDAO { | |||
| @SqlQuery("select * from hardware;") | |||
| List<Hardware> findAll(); | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.moze.rms.entity.model; | |||
| import lombok.Data; | |||
| import lombok.NoArgsConstructor; | |||
| @Data | |||
| @NoArgsConstructor | |||
| public class Hardware { | |||
| private String hostname; | |||
| private String type; | |||
| private String descript; | |||
| private String ip1; | |||
| private String ip2; | |||
| private String model; | |||
| private String environment; | |||
| private String position; | |||
| private String position_number; | |||
| private Integer amount; | |||
| private String owner_department; | |||
| private String keep_department; | |||
| private String keeper; | |||
| private String remark; | |||
| } | |||
| @@ -1,6 +1,8 @@ | |||
| server.servlet.context-path=/ | |||
| spring.datasource.url=jdbc:postgresql:// | |||
| spring.datasource.username= | |||
| spring.datasource.password= | |||
| spring.datasource.url=jdbc:postgresql://172.105.222.191:54132/rms | |||
| spring.datasource.username=moze | |||
| spring.datasource.password=moze794064,! | |||
| @@ -1,3 +1,3 @@ | |||
| spring.datasource.url=jdbc:postgresql:// | |||
| spring.datasource.username= | |||
| spring.datasource.password= | |||
| spring.datasource.url=jdbc:postgresql://172.105.222.191:54132/rms | |||
| spring.datasource.username=moze | |||
| spring.datasource.password=moze794064,! | |||