24 changed files with 319 additions and 4141 deletions
-
67.eslintrc
-
BIN.husky/commit-msg
-
1.husky/pre-commit
-
23README.md
-
BINcommitlint.config.js
-
39package.json
-
3768pnpm-lock.yaml
-
BINpublic/favicon.ico
-
5src/apis/system.ts
-
10src/components/common/FTTable/expand.ts
-
197src/components/common/FTTable/index.vue
-
8src/env.d.ts
-
4src/libs/constant.ts
-
154src/libs/http.ts
-
20src/libs/token.ts
-
28src/main.ts
-
37src/router/index.ts
-
30src/router/routes.ts
-
10src/stores/useMainStore.ts
-
15src/views/login/index.vue
-
2src/views/main/index.vue
-
2src/views/page/index.vue
-
30tsconfig.json
-
2vite.config.ts
@ -1,67 +0,0 @@ |
|||||
{ |
|
||||
"root": true, |
|
||||
"env": { |
|
||||
"browser": true, |
|
||||
"node": true, |
|
||||
"vue/setup-compiler-macros": true, |
|
||||
"es2021": true |
|
||||
}, |
|
||||
"globals": { |
|
||||
"defineEmits": "readonly", |
|
||||
"defineProps": "readonly", |
|
||||
"defineExpose": "readonly" |
|
||||
}, |
|
||||
"parser": "vue-eslint-parser", |
|
||||
"extends": [ |
|
||||
"eslint:recommended", |
|
||||
"plugin:vue/vue3-recommended", |
|
||||
"plugin:@typescript-eslint/recommended", |
|
||||
"plugin:prettier/recommended", |
|
||||
"prettier", |
|
||||
"./.eslintrc-auto-import.json" |
|
||||
], |
|
||||
"parserOptions": { |
|
||||
"ecmaVersion": 12, |
|
||||
"parser": "@typescript-eslint/parser", |
|
||||
"sourceType": "module", |
|
||||
"ecmaFeatures": { |
|
||||
"jsx": true |
|
||||
} |
|
||||
}, |
|
||||
"plugins": ["vue", "@typescript-eslint", "prettier"], |
|
||||
"rules": { |
|
||||
"no-explicit-any": "off", |
|
||||
"@typescript-eslint/ban-ts-ignore": "off", |
|
||||
"@typescript-eslint/no-unused-vars": "off", |
|
||||
"@typescript-eslint/explicit-function-return-type": "off", |
|
||||
"@typescript-eslint/no-explicit-any": "off", |
|
||||
"@typescript-eslint/no-var-requires": "off", |
|
||||
"@typescript-eslint/no-empty-function": "off", |
|
||||
"@typescript-eslint/no-use-before-define": "off", |
|
||||
"@typescript-eslint/ban-ts-comment": "off", |
|
||||
"@typescript-eslint/ban-types": "off", |
|
||||
"@typescript-eslint/no-non-null-assertion": "off", |
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off", |
|
||||
"no-var": "error", |
|
||||
"prettier/prettier": "error", |
|
||||
"vue/multi-word-component-names": 0, |
|
||||
"camelcase": 2, |
|
||||
"consistent-this": [2, "that"], |
|
||||
"eqeqeq": 2, |
|
||||
"no-await-in-loop": 0, |
|
||||
"consistent-return": 0, |
|
||||
"no-plusplus": 0, |
|
||||
"no-unused-expressions": 0, |
|
||||
"operator-linebreak": 0, |
|
||||
"comma-dangle": [ |
|
||||
2, |
|
||||
{ |
|
||||
"arrays": "ignore", |
|
||||
"objects": "ignore", |
|
||||
"imports": "never", |
|
||||
"exports": "never", |
|
||||
"functions": "ignore" |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
npm run lint:lint-staged |
|
@ -0,0 +1,23 @@ |
|||||
|
### 前端项目模板 |
||||
|
##### 文件目录 |
||||
|
- src |
||||
|
- components 组件目录 |
||||
|
- apis 接口文件目录 |
||||
|
- assets 静态资源目录 |
||||
|
- hooks hooks目录 |
||||
|
- libs 公共工具类 |
||||
|
- router 页面路由 |
||||
|
- stores 项目公共状态管理 |
||||
|
- types 类型申明 |
||||
|
- views 页面目录 |
||||
|
##### 项目运行 |
||||
|
项目采用pnpm 进行依赖构建,请先安装pnpm |
||||
|
[pnpm - 速度快、节省磁盘空间的软件包管理器](https://pnpm.io/) |
||||
|
```bash |
||||
|
npm install pnpm -g |
||||
|
``` |
||||
|
|
||||
|
```bash |
||||
|
pnpm install // 安装依赖 |
||||
|
pnpm dev // 运行项目 |
||||
|
``` |
3768
pnpm-lock.yaml
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,9 +1,8 @@ |
|||||
import http from 'libs/http'; |
|
||||
import {AxiosResponse} from "axios"; |
|
||||
|
import http from 'libs/http' |
||||
|
|
||||
/** |
/** |
||||
* 登录接口 |
* 登录接口 |
||||
* @param params |
* @param params |
||||
*/ |
*/ |
||||
|
|
||||
export const login = (params : {username:string; password:string}) => http.post('/auth/login', params); |
|
||||
|
export const login = (params: { username: string, password: string }) => http.post('/auth/login', params) |
@ -1,7 +1,7 @@ |
|||||
export default { |
export default { |
||||
props: ['row', 'render', 'index', 'column'], |
|
||||
inheritAttrs: false, |
|
||||
setup(props: any) { |
|
||||
return () => props.render(props.row); |
|
||||
}, |
|
||||
|
props: ['row', 'render', 'index', 'column'], |
||||
|
inheritAttrs: false, |
||||
|
setup(props: any) { |
||||
|
return () => props.render(props.row) |
||||
|
}, |
||||
} |
} |
@ -1,6 +1,6 @@ |
|||||
declare module '*.vue' { |
declare module '*.vue' { |
||||
import type { DefineComponent } from 'vue'; |
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
|
||||
const component: DefineComponent<{}, {}, any>; |
|
||||
export default component; |
|
||||
|
import type { DefineComponent } from 'vue' |
||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
|
const component: DefineComponent<{}, {}, any> |
||||
|
export default component |
||||
} |
} |
@ -1,5 +1,5 @@ |
|||||
// 请求头里token的名称
|
// 请求头里token的名称
|
||||
export const HEADER_TOKEN_KEY = 'Authorization'; |
|
||||
|
export const HEADER_TOKEN_KEY = 'Authorization' |
||||
|
|
||||
// sessionStorage里token的名称
|
// sessionStorage里token的名称
|
||||
export const SESSIONSTORAGE_TOKEN_KEY = 'web_token'; |
|
||||
|
export const SESSIONSTORAGE_TOKEN_KEY = 'web_token' |
@ -1,93 +1,99 @@ |
|||||
import axios from 'axios'; |
|
||||
import { ElMessage } from 'element-plus'; |
|
||||
import { getToken } from 'libs/token'; |
|
||||
import { HEADER_TOKEN_KEY } from '@/libs/constant'; |
|
||||
|
import { HEADER_TOKEN_KEY } from '@/libs/constant' |
||||
|
import axios from 'axios' |
||||
|
import { ElMessage } from 'element-plus' |
||||
|
import { getToken } from 'libs/token' |
||||
|
|
||||
const http = axios.create({ |
const http = axios.create({ |
||||
baseURL: `/api`, |
|
||||
timeout: 1000 * 60 |
|
||||
}); |
|
||||
|
baseURL: `/api`, |
||||
|
timeout: 1000 * 60, |
||||
|
}) |
||||
|
|
||||
// 请求拦截器
|
// 请求拦截器
|
||||
http.interceptors.request.use( |
http.interceptors.request.use( |
||||
(config) => { |
|
||||
if (getToken()) { |
|
||||
config.headers![HEADER_TOKEN_KEY] = getToken(); |
|
||||
} |
|
||||
return config; |
|
||||
}, |
|
||||
(error: any) => { |
|
||||
return Promise.reject(error); |
|
||||
|
(config) => { |
||||
|
if (getToken()) { |
||||
|
config.headers![HEADER_TOKEN_KEY] = getToken() |
||||
} |
} |
||||
); |
|
||||
|
return config |
||||
|
}, |
||||
|
(error: any) => { |
||||
|
return Promise.reject(error) |
||||
|
}, |
||||
|
) |
||||
|
|
||||
// 响应拦截器
|
// 响应拦截器
|
||||
http.interceptors.response.use( |
http.interceptors.response.use( |
||||
(response) => { |
|
||||
if ( |
|
||||
response.status === 200 && |
|
||||
response.data.data !== undefined && |
|
||||
!response.data.data |
|
||||
) { |
|
||||
// 返回错误拦截
|
|
||||
ElMessage.error(response.data.msg); |
|
||||
return Promise.reject(response); |
|
||||
} else if ( |
|
||||
response.config.url?.includes('/files/download') || |
|
||||
response.config.url?.includes('downloadStream') |
|
||||
) { |
|
||||
return response.data; |
|
||||
} else if (response.data instanceof Blob) { |
|
||||
return response.data; |
|
||||
} else if ( |
|
||||
response.config.url?.includes('/espAnalysis/generateReport') || |
|
||||
response.config.url?.includes('/dcDataQuery/downLoadData') |
|
||||
) { |
|
||||
return response; |
|
||||
} |
|
||||
return response.data.data; // 返回数据体
|
|
||||
}, |
|
||||
(error: any) => { |
|
||||
console.log(error); |
|
||||
if (error.response && error.response.status === 401) { |
|
||||
ElMessage.error('账号权限过期'); |
|
||||
// TODO 登出
|
|
||||
} else { |
|
||||
if (error.message.indexOf('timeout') > -1) { |
|
||||
ElMessage.error('请求超时'); |
|
||||
} else if (error.message.indexOf('Network') > -1) { |
|
||||
ElMessage.error('网络连接错误'); |
|
||||
} else { |
|
||||
ElMessage.error(error.message); |
|
||||
} |
|
||||
error.response = { |
|
||||
data: { |
|
||||
res: false |
|
||||
} |
|
||||
}; |
|
||||
return Promise.reject(error.response); |
|
||||
} |
|
||||
|
(response) => { |
||||
|
if ( |
||||
|
response.status === 200 |
||||
|
&& response.data.data !== undefined |
||||
|
&& !response.data.data |
||||
|
) { |
||||
|
// 返回错误拦截
|
||||
|
ElMessage.error(response.data.msg) |
||||
|
return Promise.reject(response) |
||||
} |
} |
||||
); |
|
||||
|
else if ( |
||||
|
response.config.url?.includes('/files/download') |
||||
|
|| response.config.url?.includes('downloadStream') |
||||
|
) { |
||||
|
return response.data |
||||
|
} |
||||
|
else if (response.data instanceof Blob) { |
||||
|
return response.data |
||||
|
} |
||||
|
else if ( |
||||
|
response.config.url?.includes('/espAnalysis/generateReport') |
||||
|
|| response.config.url?.includes('/dcDataQuery/downLoadData') |
||||
|
) { |
||||
|
return response |
||||
|
} |
||||
|
return response.data.data // 返回数据体
|
||||
|
}, |
||||
|
(error: any) => { |
||||
|
console.log(error) |
||||
|
if (error.response && error.response.status === 401) { |
||||
|
ElMessage.error('账号权限过期') |
||||
|
// TODO 登出
|
||||
|
} |
||||
|
else { |
||||
|
if (error.message.includes('timeout')) { |
||||
|
ElMessage.error('请求超时') |
||||
|
} |
||||
|
else if (error.message.includes('Network')) { |
||||
|
ElMessage.error('网络连接错误') |
||||
|
} |
||||
|
else { |
||||
|
ElMessage.error(error.message) |
||||
|
} |
||||
|
error.response = { |
||||
|
data: { |
||||
|
res: false, |
||||
|
}, |
||||
|
} |
||||
|
return Promise.reject(error.response) |
||||
|
} |
||||
|
}, |
||||
|
) |
||||
|
|
||||
// 封装 GET 请求
|
// 封装 GET 请求
|
||||
export const get = <T>(url: string, params?: any): Promise<T> => { |
|
||||
return http.get(url, { params }); |
|
||||
}; |
|
||||
|
export function get<T>(url: string, params?: any): Promise<T> { |
||||
|
return http.get(url, { params }) |
||||
|
} |
||||
|
|
||||
// 封装 POST 请求
|
// 封装 POST 请求
|
||||
export const post = <T>(url: string, data?: any): Promise<T> => { |
|
||||
return http.post(url, data); |
|
||||
}; |
|
||||
|
export function post<T>(url: string, data?: any): Promise<T> { |
||||
|
return http.post(url, data) |
||||
|
} |
||||
|
|
||||
// 封装 PUT 请求
|
// 封装 PUT 请求
|
||||
export const put = <T>(url: string, data?: any): Promise<T> => { |
|
||||
return http.put(url, data); |
|
||||
}; |
|
||||
|
export function put<T>(url: string, data?: any): Promise<T> { |
||||
|
return http.put(url, data) |
||||
|
} |
||||
|
|
||||
// 封装 DELETE 请求
|
// 封装 DELETE 请求
|
||||
export const del = <T>(url: string, params?: any): Promise<T> => { |
|
||||
return http.delete(url, { params }); |
|
||||
}; |
|
||||
|
export function del<T>(url: string, params?: any): Promise<T> { |
||||
|
return http.delete(url, { params }) |
||||
|
} |
||||
|
|
||||
export default http; |
|
||||
|
export default http |
@ -1,13 +1,13 @@ |
|||||
import { SESSIONSTORAGE_TOKEN_KEY } from './constant'; |
|
||||
|
import { SESSIONSTORAGE_TOKEN_KEY } from './constant' |
||||
|
|
||||
export const getToken = () => { |
|
||||
return sessionStorage[SESSIONSTORAGE_TOKEN_KEY]; |
|
||||
}; |
|
||||
|
export function getToken() { |
||||
|
return sessionStorage[SESSIONSTORAGE_TOKEN_KEY] |
||||
|
} |
||||
|
|
||||
export const setToken = (token: string) => { |
|
||||
sessionStorage[SESSIONSTORAGE_TOKEN_KEY] = token; |
|
||||
}; |
|
||||
|
export function setToken(token: string) { |
||||
|
sessionStorage[SESSIONSTORAGE_TOKEN_KEY] = token |
||||
|
} |
||||
|
|
||||
export const delToken = () => { |
|
||||
sessionStorage.removeItem(SESSIONSTORAGE_TOKEN_KEY); |
|
||||
}; |
|
||||
|
export function delToken() { |
||||
|
sessionStorage.removeItem(SESSIONSTORAGE_TOKEN_KEY) |
||||
|
} |
@ -1,17 +1,17 @@ |
|||||
import { createApp } from 'vue'; |
|
||||
import { createPinia } from 'pinia'; |
|
||||
import App from './app.vue'; |
|
||||
import router from './router'; |
|
||||
import ElementPlus from 'element-plus'; |
|
||||
import 'element-plus/dist/index.css'; |
|
||||
import locale from 'element-plus/es/locale/lang/zh-cn' |
|
||||
import FtTable from 'components/common/FTTable/index.vue' |
import FtTable from 'components/common/FTTable/index.vue' |
||||
|
import ElementPlus from 'element-plus' |
||||
|
import locale from 'element-plus/es/locale/lang/zh-cn' |
||||
|
import { createPinia } from 'pinia' |
||||
|
import { createApp } from 'vue' |
||||
|
import App from './app.vue' |
||||
|
import router from './router' |
||||
|
import 'element-plus/dist/index.css' |
||||
|
|
||||
const app = createApp(App); |
|
||||
const pinia = createPinia(); |
|
||||
app.component('FtTable', FtTable); |
|
||||
|
const app = createApp(App) |
||||
|
const pinia = createPinia() |
||||
|
app.component('FtTable', FtTable) |
||||
app |
app |
||||
.use(router) |
|
||||
.use(pinia) |
|
||||
.use(ElementPlus, { locale: locale, zIndex: 3000 }) |
|
||||
.mount('#app') |
|
||||
|
.use(router) |
||||
|
.use(pinia) |
||||
|
.use(ElementPlus, { locale, zIndex: 3000 }) |
||||
|
.mount('#app') |
@ -1,23 +1,26 @@ |
|||||
import { createRouter, createWebHashHistory, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; |
|
||||
import { getToken } from '@/libs/token'; |
|
||||
import routes from './routes'; |
|
||||
|
import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router' |
||||
|
import { getToken } from '@/libs/token' |
||||
|
import { createRouter, createWebHashHistory } from 'vue-router' |
||||
|
import routes from './routes' |
||||
|
|
||||
const router = createRouter({ |
const router = createRouter({ |
||||
history: createWebHashHistory(), |
|
||||
routes |
|
||||
}); |
|
||||
|
history: createWebHashHistory(), |
||||
|
routes, |
||||
|
}) |
||||
|
|
||||
router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => { |
router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => { |
||||
if (getToken()) { |
|
||||
next(); |
|
||||
} else { |
|
||||
// 未登录
|
|
||||
if (to.name === 'login') { |
|
||||
next(); |
|
||||
} else { |
|
||||
next({ name: 'login' }); |
|
||||
} |
|
||||
|
if (getToken()) { |
||||
|
next() |
||||
|
} |
||||
|
else { |
||||
|
// 未登录
|
||||
|
if (to.name === 'login') { |
||||
|
next() |
||||
} |
} |
||||
}); |
|
||||
|
else { |
||||
|
next({ name: 'login' }) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
export default router; |
|
||||
|
export default router |
@ -1,28 +1,28 @@ |
|||||
import { RouteRecordRaw } from 'vue-router'; |
|
||||
|
import type { RouteRecordRaw } from 'vue-router' |
||||
|
|
||||
const authRoutes: RouteRecordRaw[] = [ |
const authRoutes: RouteRecordRaw[] = [ |
||||
{ |
{ |
||||
path: '', |
|
||||
name: 'home', |
|
||||
component: () => import('../views/page/index.vue'), |
|
||||
meta: { |
|
||||
isDefault: true, |
|
||||
tagName: '首页' |
|
||||
} |
|
||||
|
path: '', |
||||
|
name: 'home', |
||||
|
component: () => import('../views/page/index.vue'), |
||||
|
meta: { |
||||
|
isDefault: true, |
||||
|
tagName: '首页', |
||||
|
}, |
||||
}, |
}, |
||||
]; |
|
||||
|
] |
||||
const routes: RouteRecordRaw[] = [ |
const routes: RouteRecordRaw[] = [ |
||||
{ |
{ |
||||
path: '/login', |
path: '/login', |
||||
name: 'login', |
name: 'login', |
||||
component: () => import('../views/login/index.vue') |
|
||||
|
component: () => import('../views/login/index.vue'), |
||||
}, |
}, |
||||
{ |
{ |
||||
path: '/', |
path: '/', |
||||
component: () => import('../views/main/index.vue'), |
component: () => import('../views/main/index.vue'), |
||||
children: authRoutes |
|
||||
} |
|
||||
]; |
|
||||
|
children: authRoutes, |
||||
|
}, |
||||
|
] |
||||
|
|
||||
export { authRoutes }; |
|
||||
export default routes; |
|
||||
|
export { authRoutes } |
||||
|
export default routes |
@ -1,13 +1,13 @@ |
|||||
import { defineStore } from 'pinia' |
import { defineStore } from 'pinia' |
||||
|
|
||||
export const useMainStore = defineStore('main',{ |
|
||||
state:()=>{ |
|
||||
|
export const useMainStore = defineStore('main', { |
||||
|
state: () => { |
||||
return { |
return { |
||||
userInfo: { |
userInfo: { |
||||
userName: '', |
userName: '', |
||||
} |
|
||||
|
}, |
||||
} |
} |
||||
}, |
}, |
||||
getters:{}, |
|
||||
actions:{} |
|
||||
|
getters: {}, |
||||
|
actions: {}, |
||||
}) |
}) |
@ -1,17 +1,16 @@ |
|||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
import {setToken} from "libs/token"; |
|
||||
import router from "@/router"; |
|
||||
import {login} from "apis/system"; |
|
||||
|
import router from '@/router' |
||||
|
import { login } from 'apis/system' |
||||
|
import { setToken } from 'libs/token' |
||||
|
|
||||
login({username: 'admin', password: '12345'}).then((res) => { |
|
||||
console.log(res) |
|
||||
// setToken(res.data) |
|
||||
|
login({ username: 'admin', password: '12345' }).then((res) => { |
||||
|
setToken(res.data) |
||||
|
router.push('/') |
||||
}) |
}) |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
|
|
||||
|
<div>登录</div> |
||||
</template> |
</template> |
||||
|
|
||||
<style scoped></style> |
<style scoped></style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue