封装message,统一处理http响应
This commit is contained in:
parent
7bf0b5fb7f
commit
4f70b83f54
@ -5,7 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run dev",
|
"start": "npm run dev",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc && vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
@ -17,6 +17,7 @@
|
|||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.11.10",
|
||||||
"@vitejs/plugin-vue": "^3.2.0",
|
"@vitejs/plugin-vue": "^3.2.0",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3",
|
||||||
"vite": "^3.2.4",
|
"vite": "^3.2.4",
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<button @click="showMessage">显示消息</button>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {ref} from "vue";
|
|
||||||
|
import message from "./components/message";
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
setup() {
|
setup() {
|
||||||
return {}
|
return {
|
||||||
|
showMessage() {
|
||||||
|
message.info('test display')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -13,7 +13,39 @@ body {
|
|||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 'Source Han Sans SC', 'Microsoft YaHei', 'Microsoft YaHei UI', "Helvetica Neue", sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 'Source Han Sans SC', 'Microsoft YaHei', 'Microsoft YaHei UI', "Helvetica Neue", sans-serif;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
.container{
|
|
||||||
|
.container {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-wrapper {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: #000000d9;
|
||||||
|
font-size: 14px;
|
||||||
|
font-variant: tabular-nums;
|
||||||
|
line-height: 1.5715;
|
||||||
|
list-style: none;
|
||||||
|
font-feature-settings: "tnum";
|
||||||
|
position: fixed;
|
||||||
|
top: 8px;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1010;
|
||||||
|
width: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.message-notice {
|
||||||
|
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
.message-notice-content {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
|
||||||
|
pointer-events: all
|
||||||
|
}
|
17
admin-fe/src/components/message/Toast.vue
Normal file
17
admin-fe/src/components/message/Toast.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div class="message-notice">
|
||||||
|
<div class="message-notice-content">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
message: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
23
admin-fe/src/components/message/index.ts
Normal file
23
admin-fe/src/components/message/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import {createVNode, render} from 'vue'
|
||||||
|
import Toast from './Toast.vue'
|
||||||
|
|
||||||
|
export const toast = (message: string) => {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
let div = document.querySelector('.message-wrapper');
|
||||||
|
if (!div) {
|
||||||
|
div = document.createElement('div');
|
||||||
|
div.classList.add("message-wrapper");
|
||||||
|
document.body.append(div);
|
||||||
|
}
|
||||||
|
// TODO 完善多消息同时共存 - 主要时添加新的容器
|
||||||
|
const messageNode = createVNode(Toast, {message})
|
||||||
|
render(messageNode, div)
|
||||||
|
// @ts-ignore
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
div.removeChild(messageNode.el as Node)
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
let timer = 0;
|
||||||
|
export default {
|
||||||
|
toast,
|
||||||
|
}
|
@ -2,7 +2,7 @@ import {createApp} from 'vue';
|
|||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import {httpConfig} from "./util/http";
|
import {httpConfig} from "./util/http";
|
||||||
|
import './assets/app.css'
|
||||||
|
|
||||||
httpConfig.baseURL = "http://localhost:8080"
|
httpConfig.baseURL = "http://localhost:8080"
|
||||||
|
|
||||||
|
4
admin-fe/src/service/types.d.ts
vendored
Normal file
4
admin-fe/src/service/types.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
type AdminLoginModel = {
|
||||||
|
account: string
|
||||||
|
token: string
|
||||||
|
}
|
@ -1,7 +1,18 @@
|
|||||||
|
import {toast} from "../components/message";
|
||||||
|
|
||||||
export type HttpMethod = 'get' | 'post' | 'delete' | 'put'
|
export type HttpMethod = 'get' | 'post' | 'delete' | 'put'
|
||||||
|
|
||||||
|
export enum RequestDataContentType {
|
||||||
|
JSON = 'application/json;charset=UTF-8',
|
||||||
|
FORM = 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
|
FORM_DATA = 'multipart/form-data;charset=UTF-8',
|
||||||
|
}
|
||||||
|
|
||||||
export const httpConfig = {
|
export const httpConfig = {
|
||||||
baseURL: ''
|
baseURL: '',
|
||||||
|
globalErrorHandler: {
|
||||||
|
1201: '请求参数不合法'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export type ResponseModel<T> = {
|
export type ResponseModel<T> = {
|
||||||
code: number
|
code: number
|
||||||
@ -12,27 +23,80 @@ export type ResponseModel<T> = {
|
|||||||
|
|
||||||
class Http {
|
class Http {
|
||||||
post<T>(url, data) {
|
post<T>(url, data) {
|
||||||
return this.request<T>(url, 'post', data)
|
return this.request<T>(url, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
request<T>(url: string, method: HttpMethod, data: any = null) {
|
get<T>(url, data = null) {
|
||||||
return new Promise<ResponseModel<T>>((resolve, reject) => {
|
return this.request<T>(url, data, 'get')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /xxxx?a=1&b=2
|
||||||
|
* NAME: value
|
||||||
|
* NAME2: value
|
||||||
|
* ....
|
||||||
|
*
|
||||||
|
* POSTDATA
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param method
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
request<T>(url: string, data: any = null, method: HttpMethod = 'post', type: 'normal' | 'form' = 'normal') {
|
||||||
|
return new Promise<T>((resolve, reject) => {
|
||||||
|
let contentType = RequestDataContentType.FORM;
|
||||||
|
if (data) {
|
||||||
|
if (type === 'form') {
|
||||||
|
const form = new FormData();
|
||||||
|
for (const key in data) {
|
||||||
|
form.append(key, data[key]);
|
||||||
|
}
|
||||||
|
data = form; // 将data有{} => formData
|
||||||
|
contentType = RequestDataContentType.FORM_DATA; // 由于使用formData
|
||||||
|
method = 'post';
|
||||||
|
} else if (method == 'post') {
|
||||||
|
// 将数据对象 转成json
|
||||||
|
data = JSON.stringify(data);
|
||||||
|
contentType = RequestDataContentType.JSON;
|
||||||
|
} else {
|
||||||
|
// 装对象转成 key=value&key=value
|
||||||
|
const params = [];
|
||||||
|
for (const key in data) {
|
||||||
|
params.push(`${key}=${data[key]}`);
|
||||||
|
}
|
||||||
|
data = params.join('&')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fetch(httpConfig.baseURL + url, {
|
fetch(httpConfig.baseURL + url, {
|
||||||
method,
|
method,
|
||||||
body: JSON.stringify(data),
|
body: data,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=utf-8'
|
'Content-Type': contentType
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(res => res.json()) // 只要json的响应数据
|
.then(res => res.json()) // 只要json的响应数据
|
||||||
.then((res: ResponseModel<T>) => {
|
.then((res: ResponseModel<T>) => {
|
||||||
if (res.code !== 0) {
|
const {code, data, message} = res;
|
||||||
|
if (code !== 0) {
|
||||||
|
const err = httpConfig.globalErrorHandler[code]
|
||||||
|
// 需要统一处理数据
|
||||||
|
if (code === 403) {
|
||||||
|
toast("登录凭证无效或者已过期")
|
||||||
|
return;
|
||||||
|
} else if (err) {
|
||||||
|
toast(httpConfig.globalErrorHandler[code])
|
||||||
|
return;
|
||||||
|
}
|
||||||
reject(Error(res.message))
|
reject(Error(res.message))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(res.data)
|
resolve(res.data)
|
||||||
}).catch(reject)
|
})
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,14 @@
|
|||||||
<input placeholder="请输入密码" type="text" v-model="data.password">
|
<input placeholder="请输入密码" type="text" v-model="data.password">
|
||||||
{{ data.password }}
|
{{ data.password }}
|
||||||
</p>
|
</p>
|
||||||
<button type="submit">登录</button>
|
<div class="error">{{ err }}</div>
|
||||||
|
<button :disabled="loading" :type="loading?'button':'submit'">{{loading?'正在登录':'登录'}}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {reactive} from "vue";
|
import {reactive, ref} from "vue";
|
||||||
import http from "../util/http";
|
import http from "../util/http";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -27,25 +28,36 @@ export default {
|
|||||||
account: '',
|
account: '',
|
||||||
password: ''
|
password: ''
|
||||||
})
|
})
|
||||||
|
const loading = ref(false)
|
||||||
|
const err = ref()
|
||||||
|
|
||||||
function onLogin(e: Event) {
|
function onLogin(e: Event) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
http.post('/admin/user/login', data).then(result => {
|
loading.value = true
|
||||||
|
http.post<AdminLoginModel>('/admin/user/login', data).then(result => {
|
||||||
console.log(result)
|
console.log(result)
|
||||||
}).catch(err => {
|
}).catch(e => {
|
||||||
console.log(err)
|
err.value = e.message
|
||||||
|
console.log(e)
|
||||||
|
}).finally(()=>{
|
||||||
|
loading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data, onLogin
|
data, onLogin, err,loading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.login-wrapper {
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-wrapper {
|
||||||
|
width: 500px;
|
||||||
|
margin: 50px auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
13
admin-fe/tsconfig.json
Normal file
13
admin-fe/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue",
|
||||||
|
"vite.config.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist",
|
||||||
|
"**/*.js"
|
||||||
|
]
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
server: {
|
||||||
|
port: 10086
|
||||||
|
},
|
||||||
// 必须配置vue插件
|
// 必须配置vue插件
|
||||||
plugins: [vue()]
|
plugins: [vue()]
|
||||||
}
|
}
|
@ -17,6 +17,11 @@
|
|||||||
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.15.17.tgz#870daa61c257dfa0ee4f0ed71704d55e9af8e20c"
|
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.15.17.tgz#870daa61c257dfa0ee4f0ed71704d55e9af8e20c"
|
||||||
integrity sha512-IA1O7f7qxw2DX8oqTpugHElr926phs7Rq8ULXleBMk4go5K05BU0mI8BfCkWcYAvcmVaMc13bv5W3LIUlU6Y9w==
|
integrity sha512-IA1O7f7qxw2DX8oqTpugHElr926phs7Rq8ULXleBMk4go5K05BU0mI8BfCkWcYAvcmVaMc13bv5W3LIUlU6Y9w==
|
||||||
|
|
||||||
|
"@types/node@^18.11.10":
|
||||||
|
version "18.11.10"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz#4c64759f3c2343b7e6c4b9caf761c7a3a05cee34"
|
||||||
|
integrity sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==
|
||||||
|
|
||||||
"@vitejs/plugin-vue@^3.2.0":
|
"@vitejs/plugin-vue@^3.2.0":
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz#a1484089dd85d6528f435743f84cdd0d215bbb54"
|
resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz#a1484089dd85d6528f435743f84cdd0d215bbb54"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package me.xiaoyan.point.api.controller.admin;
|
package me.xiaoyan.point.api.controller.admin;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.xiaoyan.point.api.error.BizException;
|
import me.xiaoyan.point.api.error.BizException;
|
||||||
import me.xiaoyan.point.api.pojo.UserInfo;
|
import me.xiaoyan.point.api.pojo.UserInfo;
|
||||||
@ -33,8 +34,10 @@ public class UserAdminController {
|
|||||||
userStoreMap.put("test", UserAdminInfo.create(2, "test", "123123"));
|
userStoreMap.put("test", UserAdminInfo.create(2, "test", "123123"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
@PostMapping("login")
|
@PostMapping("login")
|
||||||
public UserAdminInfo login(@Validated @RequestBody UserAdminInfo user) {
|
public UserAdminInfo login(@Validated @RequestBody UserAdminInfo user) {
|
||||||
|
Thread.sleep(1);
|
||||||
// 判断是否存在账号
|
// 判断是否存在账号
|
||||||
if (!userStoreMap.containsKey(user.getAccount())) {
|
if (!userStoreMap.containsKey(user.getAccount())) {
|
||||||
throw BizException.create("账号不存在");
|
throw BizException.create("账号不存在");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user