基础框架搭建

This commit is contained in:
walker 2023-06-02 18:03:39 +08:00
parent 0fe6b2c393
commit dd1137ba60
7 changed files with 290 additions and 3 deletions

42
src/api/ApiCommon.ts Normal file
View File

@ -0,0 +1,42 @@
export class PageQueryDto {
current: number;
size: number;
orders?: OrderItemDto[];
constructor(current: number, size: number, orders?: OrderItemDto[]) {
this.current = current;
this.size = size;
this.orders = orders;
}
}
export class OrderItemDto {
column: string;
asc: boolean;
constructor(column: string, asc: boolean) {
this.column = column;
this.asc = asc;
}
static asc(column: string): OrderItemDto {
return new OrderItemDto(column, true);
}
static desc(column: string): OrderItemDto {
return new OrderItemDto(column, false);
}
}
export interface PageDto<T = unknown> {
records: T[];
/**
*
*/
total: number;
/**
*
*/
current: number;
/**
*
*/
size: number;
}

63
src/api/UserApi.ts Normal file
View File

@ -0,0 +1,63 @@
import { api } from 'src/boot/axios';
import { PageDto, PageQueryDto } from './ApiCommon';
const UserUriBase = '/api/user';
interface RegisterInfo {
name: string;
mobile: string;
password: string;
}
interface User {
id: string;
name: string;
mobile: string;
password: string;
registerTime: string;
}
/**
*
* @param info
* @returns
*/
export async function register(info: RegisterInfo): Promise<User> {
const response = await api.post(`${UserUriBase}/register`, info);
return response.data as User;
}
interface LoginInfo {
account: string;
password: string;
}
const PasswordSult = '';
/**
*
* @param loginInfo
* @returns
*/
export async function login(loginInfo: LoginInfo): Promise<string> {
const response = await api.post(`${UserUriBase}/login`, loginInfo);
return response.data;
}
export class PagingQueryParams extends PageQueryDto {
name?: string;
}
/**
*
* @param params
* @returns
*/
export async function pageQuery(
params: PagingQueryParams
): Promise<PageDto<User>> {
const response = await api.get(`${UserUriBase}/paging`, {
params: params,
});
return response.data;
}

View File

@ -1,5 +1,9 @@
import { boot } from 'quasar/wrappers';
import axios, { AxiosInstance } from 'axios';
import { AxiosError } from 'axios';
import { Dialog } from 'quasar';
import { boot } from 'quasar/wrappers';
import { getJwtToken } from 'src/configs/TokenManage';
import { getHttpBase } from 'src/configs/UrlManage';
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
@ -7,17 +11,94 @@ declare module '@vue/runtime-core' {
}
}
interface ErrorData {
status: number;
title: string;
detail: string;
properties: { [key: string]: unknown };
}
export class ApiError {
origin: AxiosError;
/**
*
*/
code: number;
/**
*
*/
title: string;
/**
*
*/
detail?: string;
constructor(origin: AxiosError<unknown, unknown>) {
this.origin = origin;
const response = origin.response;
if (response) {
const err = response.data as ErrorData;
this.code = err.properties.code as number;
this.title = err.title;
this.detail = err.detail;
} else {
this.code = origin.status || -1;
this.title = origin.message;
}
}
static from(err: AxiosError<unknown, unknown>): ApiError {
return new ApiError(err);
}
/**
*
* @returns
*/
isAuthError(): boolean {
return this.origin.response?.status === 401;
}
}
// Be careful when using SSR for cross-request state pollution
// due to creating a Singleton instance here;
// If any client changes this (global) instance, it might be a
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
const api = axios.create({ baseURL: 'https://api.example.com' });
const api = axios.create({ baseURL: getHttpBase() });
export default boot(({ app }) => {
export default boot(({ app, router }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api
// 拦截请求,添加
api.interceptors.request.use(
(config) => {
config.headers.Authorization = getJwtToken();
return config;
},
(err: AxiosError) => {
return Promise.reject(ApiError.from(err));
}
);
api.interceptors.response.use(
(response) => {
return response;
},
(err) => {
if (err.response && err.response.status === 401) {
Dialog.create({
title: '认证失败',
message: '认证失败或登录超时,请重新登录',
persistent: true,
}).onOk(() => {
router.push({ name: 'login' });
});
}
return Promise.reject(ApiError.from(err));
}
);
app.config.globalProperties.$axios = axios;
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
// so you won't necessarily have to import axios in each vue file

View File

@ -0,0 +1,9 @@
const JwtTokenKey = 'jwttoken';
export function saveJwtToken(token: string) {
sessionStorage.setItem(JwtTokenKey, `Bearer ${token}`);
}
export function getJwtToken(): string | null {
return sessionStorage.getItem(JwtTokenKey);
}

11
src/configs/UrlManage.ts Normal file
View File

@ -0,0 +1,11 @@
function getHost(): string {
return '192.168.3.212:9081';
}
export function getHttpBase() {
return `http://${getHost()}`;
}
export function getWebsocketUrl() {
return `ws://${getHost()}/ws-xiannccda`;
}

68
src/pages/UserLogin.vue Normal file
View File

@ -0,0 +1,68 @@
<template>
<q-layout>
<q-page-container>
<q-page class="flex bg-image flex-center">
<q-card
v-bind:style="$q.screen.lt.sm ? { width: '80%' } : { width: '30%' }"
style="min-width: 350px"
>
<q-card-section>
<q-avatar size="100px" class="absolute-center shadow-10">
<img src="icons/favicon-96x96.png" />
</q-avatar>
</q-card-section>
<q-card-section>
<div class="text-center q-pt-lg">
<div class="col text-h6 ellipsis">登录</div>
</div>
</q-card-section>
<q-card-section>
<q-form @submit="doLogin" class="q-gutter-md">
<q-input
filled
v-model="loginInfo.account"
label="Username"
lazy-rules
/>
<q-input
type="password"
filled
v-model="loginInfo.password"
label="Password"
lazy-rules
/>
<div class="text-right">
<q-btn flat label="注册" to="/register" color="primary" />
</div>
<div class="text-center">
<q-btn label="Login" type="submit" color="primary" />
</div>
</q-form>
</q-card-section>
</q-card>
</q-page>
</q-page-container>
</q-layout>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
const loginInfo = reactive({
account: '',
password: '',
});
function doLogin() {
console.log(loginInfo);
}
</script>
<style>
.bg-image {
background-image: linear-gradient(135deg, #5481fd 0%, #0e02b1 80%);
}
</style>

View File

@ -3,10 +3,23 @@ import { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
component: () => import('layouts/MainLayout.vue'),
children: [{ path: '', component: () => import('pages/IndexPage.vue') }],
},
{
path: '/login',
name: 'login',
component: () => import('pages/UserLogin.vue'),
},
{
path: '/register',
name: 'register',
component: () => import('pages/UserRegister.vue'),
},
// Always leave this as last one,
// but you can also remove it
{