teddyhuang 5 роки тому
джерело
коміт
c3b3aa366a
26 змінених файлів з 1046 додано та 119 видалено
  1. +2
    -0
      .gitignore
  2. +2
    -0
      app/.idea/.gitignore
  3. +81
    -79
      app/package-lock.json
  4. +1
    -1
      app/package.json
  5. +2
    -0
      app/public/index.html
  6. +40
    -6
      app/src/App.vue
  7. +201
    -0
      app/src/components/AssetGroup.vue
  8. +8
    -0
      app/src/components/FileAndData.vue
  9. +153
    -0
      app/src/components/Hardware.vue
  10. +154
    -0
      app/src/components/Home.vue
  11. +122
    -4
      app/src/components/Login.vue
  12. +8
    -0
      app/src/components/Person.vue
  13. +8
    -0
      app/src/components/Software.vue
  14. +3
    -3
      app/src/main.js
  15. +2
    -2
      app/src/plugins/axios.js
  16. +41
    -17
      app/src/router/index.js
  17. +15
    -0
      pom.xml
  18. +2
    -1
      src/main/java/com/moze/rms/RmsApplication.java
  19. +55
    -0
      src/main/java/com/moze/rms/config/JdbiFactory.java
  20. +23
    -0
      src/main/java/com/moze/rms/controller/HardwareController.java
  21. +19
    -0
      src/main/java/com/moze/rms/controller/JsonResult.java
  22. +55
    -0
      src/main/java/com/moze/rms/controller/StatusCode.java
  23. +18
    -0
      src/main/java/com/moze/rms/dao/HardwareDAO.java
  24. +23
    -0
      src/main/java/com/moze/rms/entity/model/Hardware.java
  25. +5
    -3
      src/main/resources/application-dev.properties
  26. +3
    -3
      src/main/resources/application-pro.properties

+ 2
- 0
.gitignore Переглянути файл

@@ -1,3 +1,5 @@
/.idea/
/src/main/resources/public/
/target/
/rms.iml
/app/node_modules/

+ 2
- 0
app/.idea/.gitignore Переглянути файл

@@ -0,0 +1,2 @@
# Default ignored files
/workspace.xml

+ 81
- 79
app/package-lock.json Переглянути файл

@@ -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",


+ 1
- 1
app/package.json Переглянути файл

@@ -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"
},


+ 2
- 0
app/public/index.html Переглянути файл

@@ -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>


+ 40
- 6
app/src/App.vue Переглянути файл

@@ -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>

+ 201
- 0
app/src/components/AssetGroup.vue Переглянути файл

@@ -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>

+ 8
- 0
app/src/components/FileAndData.vue Переглянути файл

@@ -0,0 +1,8 @@
<template>
<div></div>
</template>
<script>
export default {
name: ''
}
</script>

+ 153
- 0
app/src/components/Hardware.vue Переглянути файл

@@ -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>

+ 154
- 0
app/src/components/Home.vue Переглянути файл

@@ -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>

+ 122
- 4
app/src/components/Login.vue Переглянути файл

@@ -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>

+ 8
- 0
app/src/components/Person.vue Переглянути файл

@@ -0,0 +1,8 @@
<template>
<div></div>
</template>
<script>
export default {
name: ''
}
</script>

+ 8
- 0
app/src/components/Software.vue Переглянути файл

@@ -0,0 +1,8 @@
<template>
<div></div>
</template>
<script>
export default {
name: ''
}
</script>

+ 3
- 3
app/src/main.js Переглянути файл

@@ -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
- 2
app/src/plugins/axios.js Переглянути файл

@@ -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';


+ 41
- 17
app/src/router/index.js Переглянути файл

@@ -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({


+ 15
- 0
pom.xml Переглянути файл

@@ -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>


+ 2
- 1
src/main/java/com/moze/rms/RmsApplication.java Переглянути файл

@@ -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);


+ 55
- 0
src/main/java/com/moze/rms/config/JdbiFactory.java Переглянути файл

@@ -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);
}
}

+ 23
- 0
src/main/java/com/moze/rms/controller/HardwareController.java Переглянути файл

@@ -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());
}


}

+ 19
- 0
src/main/java/com/moze/rms/controller/JsonResult.java Переглянути файл

@@ -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;
}
}

+ 55
- 0
src/main/java/com/moze/rms/controller/StatusCode.java Переглянути файл

@@ -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;
}
}

+ 18
- 0
src/main/java/com/moze/rms/dao/HardwareDAO.java Переглянути файл

@@ -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();


}

+ 23
- 0
src/main/java/com/moze/rms/entity/model/Hardware.java Переглянути файл

@@ -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;
}

+ 5
- 3
src/main/resources/application-dev.properties Переглянути файл

@@ -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,!




+ 3
- 3
src/main/resources/application-pro.properties Переглянути файл

@@ -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,!

Завантаження…
Відмінити
Зберегти