diff --git a/src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecord.java b/src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecord.java index c6b4aa0..4d13142 100644 --- a/src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecord.java +++ b/src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecord.java @@ -14,6 +14,11 @@ public class DiActiveRecord { @JsonIgnore public Boolean isNewRecord = true; + // to map + public Map toMap() { + return DiActiveRecord.exportModelData(this); + } + // save model public void save() { var context = DiApplicationContextProvider.getContext(); diff --git a/src/src/main/java/com/my/graphiteDigesterBg/diframe/api/DiApiUser.java b/src/src/main/java/com/my/graphiteDigesterBg/diframe/api/DiApiUser.java index 35ad279..fb55091 100644 --- a/src/src/main/java/com/my/graphiteDigesterBg/diframe/api/DiApiUser.java +++ b/src/src/main/java/com/my/graphiteDigesterBg/diframe/api/DiApiUser.java @@ -17,10 +17,10 @@ public class DiApiUser extends DiApiControllerBase { @ResponseBody @RequestMapping("/api/user/login") public DiApiResponse login(@RequestBody Map params) { - Integer id = (Integer)params.get("id"); + String account = (String)params.get("account"); String password = (String)params.get("password"); - var user = DiActiveRecord.findOne(DiMdbUser.class, id); + var user = DiActiveRecord.findOne(DiMdbUser.class, Map.of("account", account)); if ( null == user || !user.matchPassword(password) ) { return this.error("无效的账号或密码"); } diff --git a/src/src/main/java/com/my/graphiteDigesterBg/diframe/configuration/WebConfig.java b/src/src/main/java/com/my/graphiteDigesterBg/diframe/configuration/WebConfig.java index 1e43434..c7c4eaf 100644 --- a/src/src/main/java/com/my/graphiteDigesterBg/diframe/configuration/WebConfig.java +++ b/src/src/main/java/com/my/graphiteDigesterBg/diframe/configuration/WebConfig.java @@ -1,2 +1,16 @@ -package com.my.graphiteDigesterBg.diframe.configuration;public class WebConfig { +package com.my.graphiteDigesterBg.diframe.configuration; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowCredentials(true) + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE") + .allowedHeaders("*") + .exposedHeaders("*"); + } } diff --git a/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbRole.java b/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbRole.java index 757df0e..4680c52 100644 --- a/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbRole.java +++ b/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbRole.java @@ -1,2 +1,15 @@ -package com.my.graphiteDigesterBg.diframe.model;public class DiMdbRole { +package com.my.graphiteDigesterBg.diframe.model; +import com.my.graphiteDigesterBg.diframe.DiActiveRecord; +public class DiMdbRole extends DiActiveRecord { + // id + public Integer id; + // name + public String name; + // permissions + public String permissions; + + // get table name + public static String getTableName() { + return "app_roles"; + } } diff --git a/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbUser.java b/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbUser.java index 6a70d2e..fd0dcb0 100644 --- a/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbUser.java +++ b/src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbUser.java @@ -51,4 +51,9 @@ public class DiMdbUser extends DiActiveRecord { String salt = this.salt; return DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()); } + + // get role + public DiMdbRole getRole() { + return DiActiveRecord.findOne(DiMdbRole.class, this.roleId); + } } diff --git a/src/web/package-lock.json b/src/web/package-lock.json index ee2aad6..cc9228e 100644 --- a/src/web/package-lock.json +++ b/src/web/package-lock.json @@ -8,9 +8,11 @@ "name": "vue3-demo", "version": "0.1.0", "dependencies": { + "@ant-design/icons-vue": "^7.0.1", "ant-design-vue": "^4.1.1", "axios": "^1.6.5", "core-js": "^3.8.3", + "pinia": "^2.1.7", "vue": "^3.2.13" }, "devDependencies": { @@ -3179,6 +3181,11 @@ "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true }, + "node_modules/@vue/devtools-api": { + "version": "6.5.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz", + "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==" + }, "node_modules/@vue/reactivity": { "version": "3.4.15", "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.15.tgz", @@ -8668,6 +8675,50 @@ "node": ">=0.10.0" } }, + "node_modules/pinia": { + "version": "2.1.7", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", + "dependencies": { + "@vue/devtools-api": "^6.5.0", + "vue-demi": ">=0.14.5" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.3.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "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/pirates": { "version": "4.0.6", "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz", @@ -14335,6 +14386,11 @@ } } }, + "@vue/devtools-api": { + "version": "6.5.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz", + "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==" + }, "@vue/reactivity": { "version": "3.4.15", "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.15.tgz", @@ -18743,6 +18799,23 @@ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, + "pinia": { + "version": "2.1.7", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", + "requires": { + "@vue/devtools-api": "^6.5.0", + "vue-demi": ">=0.14.5" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "requires": {} + } + } + }, "pirates": { "version": "4.0.6", "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz", diff --git a/src/web/package.json b/src/web/package.json index bb8aa3e..ecc43bd 100644 --- a/src/web/package.json +++ b/src/web/package.json @@ -8,9 +8,11 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "@ant-design/icons-vue": "^7.0.1", "ant-design-vue": "^4.1.1", "axios": "^1.6.5", "core-js": "^3.8.3", + "pinia": "^2.1.7", "vue": "^3.2.13" }, "devDependencies": { @@ -38,7 +40,8 @@ "parser": "@babel/eslint-parser" }, "rules": { - "vue/multi-word-component-names": "off" + "vue/multi-word-component-names": "off", + "no-debugger": "off" } }, "browserslist": [ diff --git a/src/web/src/App.vue b/src/web/src/App.vue index 259a687..0b8979f 100644 --- a/src/web/src/App.vue +++ b/src/web/src/App.vue @@ -1,6 +1,12 @@ diff --git a/src/web/src/assets/icon/logo.svg b/src/web/src/assets/icon/logo.svg new file mode 100644 index 0000000..747c9d1 --- /dev/null +++ b/src/web/src/assets/icon/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/web/src/assets/icon/logout.svg b/src/web/src/assets/icon/logout.svg new file mode 100644 index 0000000..949d308 --- /dev/null +++ b/src/web/src/assets/icon/logout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/web/src/assets/img/login-pic.png b/src/web/src/assets/img/login-pic.png new file mode 100644 index 0000000..3f28bfd Binary files /dev/null and b/src/web/src/assets/img/login-pic.png differ diff --git a/src/web/src/main.js b/src/web/src/main.js index 7bb0f1b..1fcde5b 100644 --- a/src/web/src/main.js +++ b/src/web/src/main.js @@ -1,9 +1,19 @@ import { createApp } from 'vue' +import { createPinia } from 'pinia' import App from './App.vue' import Antd from 'ant-design-vue'; +import * as AntdIcons from '@ant-design/icons-vue' import "./diframe/style/tailwind.css" import "./style/app.css" import 'ant-design-vue/dist/reset.css'; + const app = createApp(App); +app.use(createPinia()); app.use(Antd); +(() => { + let icons = AntdIcons; + for ( let icon in icons ) { + app.component(icon, icons[icon]); + } +})(); app.mount('#app'); diff --git a/src/web/src/pages/login/Page.vue b/src/web/src/pages/login/Page.vue new file mode 100644 index 0000000..6d679d5 --- /dev/null +++ b/src/web/src/pages/login/Page.vue @@ -0,0 +1,64 @@ + + \ No newline at end of file diff --git a/src/web/src/pages/main/Page.vue b/src/web/src/pages/main/Page.vue index cd9ce6d..eae90a5 100644 --- a/src/web/src/pages/main/Page.vue +++ b/src/web/src/pages/main/Page.vue @@ -26,20 +26,32 @@ - + + + + + + - +
-
-
实验操作
-
操作记录
-
预设管理
-
酸液管理
-
用户管理
-
功能测试
+ +
-
退出登录
+
+ + 退出登录 +
@@ -47,5 +59,42 @@ \ No newline at end of file +import ContentUserManagement from './contents/UserManagement.vue' +import ContentHistory from './contents/History.vue' +import ContentTaskStepManagement from './contents/TaskStepManagement.vue' +import ContentAcidManagement from './contents/AcidManagement.vue' +import ContentTest from './contents/Test.vue' +import ApiClient from '@/utils/ApiClient'; +/** @var {AppStore} */ +const appStore = useAppStore(); +/** @var {Ref} */ +const activeContent = ref('operation'); +/** @var {Array<{key:string,title:string}>} */ +const contents = [ + {key:'operation',title:'实验操作',icon:'dashboard'}, + {key:'hisotry',title: '操作记录',icon:'file-text'}, + {key:'preset', title: '预设管理',icon:'partition'}, + {key:'acid', title: '酸液管理',icon:'experiment'}, + {key:'user', title: '用户管理',icon:'user'}, + {key:'test', title: '功能测试',icon:'issues-close'}, +]; + +// action logout +async function actionUserLogout() { + let client = ApiClient.getClient(); + await client.userLogout(); + appStore.setAccessToken(null); +} + +// action switch content +function actionSwitchContent(content) { + activeContent.value = content; +} + + \ No newline at end of file diff --git a/src/web/src/pages/main/contents/AcidManagement.vue b/src/web/src/pages/main/contents/AcidManagement.vue new file mode 100644 index 0000000..8e51812 --- /dev/null +++ b/src/web/src/pages/main/contents/AcidManagement.vue @@ -0,0 +1,43 @@ + \ No newline at end of file diff --git a/src/web/src/pages/main/contents/History.vue b/src/web/src/pages/main/contents/History.vue new file mode 100644 index 0000000..07c2294 --- /dev/null +++ b/src/web/src/pages/main/contents/History.vue @@ -0,0 +1,29 @@ + + \ No newline at end of file diff --git a/src/web/src/pages/main/contents/Operation.vue b/src/web/src/pages/main/contents/Operation.vue index 7fedc43..afea4ca 100644 --- a/src/web/src/pages/main/contents/Operation.vue +++ b/src/web/src/pages/main/contents/Operation.vue @@ -86,7 +86,7 @@
-
+
diff --git a/src/web/src/pages/main/contents/TaskStepManagement.vue b/src/web/src/pages/main/contents/TaskStepManagement.vue new file mode 100644 index 0000000..584d108 --- /dev/null +++ b/src/web/src/pages/main/contents/TaskStepManagement.vue @@ -0,0 +1,21 @@ + + \ No newline at end of file diff --git a/src/web/src/pages/main/contents/Test.vue b/src/web/src/pages/main/contents/Test.vue new file mode 100644 index 0000000..56612c3 --- /dev/null +++ b/src/web/src/pages/main/contents/Test.vue @@ -0,0 +1,33 @@ + + \ No newline at end of file diff --git a/src/web/src/pages/main/contents/UserManagement.vue b/src/web/src/pages/main/contents/UserManagement.vue new file mode 100644 index 0000000..e999fe9 --- /dev/null +++ b/src/web/src/pages/main/contents/UserManagement.vue @@ -0,0 +1,74 @@ + + \ No newline at end of file diff --git a/src/web/src/stores/AppStore.js b/src/web/src/stores/AppStore.js new file mode 100644 index 0000000..63f5b5b --- /dev/null +++ b/src/web/src/stores/AppStore.js @@ -0,0 +1,16 @@ +import { defineStore } from 'pinia' +export const useAppStore = defineStore('AppStore', { + state: () => { + return { + accessToken : null, + }; + }, + + // set access token + actions: { + // set access token + setAccessToken( token ) { + this.accessToken = token; + }, + }, +}) \ No newline at end of file diff --git a/src/web/src/utils/ApiClient.js b/src/web/src/utils/ApiClient.js index 0ff797f..e025ff1 100644 --- a/src/web/src/utils/ApiClient.js +++ b/src/web/src/utils/ApiClient.js @@ -1,4 +1,5 @@ import axios from 'axios'; +import { useAppStore } from '@/stores/AppStore'; export default class ApiClient { // client instance static client = null; @@ -16,13 +17,45 @@ export default class ApiClient { // constructor constructor() { - + } // call api async call( name, params ) { - const response = await axios.post(`/api/${name}`, params); - return response.data; + const appStore = useAppStore(); + + let headers = {}; + headers['App-Access-Token'] = appStore.accessToken; + + const response = await axios.post(`http://localhost:8080/api/${name}`, params, { + headers : headers, + }); + + if ( !response.data.success ) { + throw new Error(response.data.message); + } + + return response.data.data; + } + + // user login + async userLogin( params ) { + return await this.call('user/login', params); + } + + // user logout + async userLogout() { + return await this.call('user/logout'); + } + + // user list + async userList() { + return await this.call('user/list'); + } + + // user delete + async userDelete( id ) { + return await this.call('user/delete', {id}); } // append task @@ -33,6 +66,7 @@ export default class ApiClient { }); } + // task action execute async taskActionExecute() { return await this.call('task/task-action-execute'); } diff --git a/src/web/vue.config.js b/src/web/vue.config.js index 910e297..1678aec 100644 --- a/src/web/vue.config.js +++ b/src/web/vue.config.js @@ -1,4 +1,9 @@ const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ - transpileDependencies: true + transpileDependencies: true, + + // @link https://cli.vuejs.org/config/#devserver + devServer : { + port : 9001, + }, })