feat: build framework

This commit is contained in:
2024-03-19 22:51:10 +08:00
parent fbe51e7668
commit afb892c5e9
19 changed files with 2784 additions and 15 deletions

16
.editorconfig Normal file
View File

@@ -0,0 +1,16 @@
# Editor configuration, see http://editorconfig.org
# 表示是最顶层的 EditorConfig 配置文件
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格tab | space
indent_size = 2 # 缩进大小
end_of_line = crlf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false

4
.env Normal file
View File

@@ -0,0 +1,4 @@
# 生产环境配置
NODE_ENV= 'production'
# 生产环境
VUE_APP_BASE_API = '/api'

8
.env.development Normal file
View File

@@ -0,0 +1,8 @@
# 开发环境配置
NODE_ENV= 'development'
# 开发环境
VUE_APP_BASE_API = '/dev-api'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true

4
.env.production Normal file
View File

@@ -0,0 +1,4 @@
# 生产环境配置
NODE_ENV= 'production'
# 生产环境
VUE_APP_BASE_API = '/api'

71
auto-import.d.ts vendored Normal file
View File

@@ -0,0 +1,71 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useLink: typeof import('vue-router')['useLink']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
}

14
components.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
export interface GlobalComponents {
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}

View File

@@ -4,16 +4,27 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --host --mode development",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.21"
"@ant-design/icons-vue": "^7.0.1",
"ant-design-vue": "4.1.2",
"axios": "^1.6.8",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.21",
"vue-router": "4"
},
"devDependencies": {
"@types/node": "^20.11.29",
"@vitejs/plugin-legacy": "^5.3.2",
"@vitejs/plugin-vue": "^5.0.4",
"less": "^4.2.0",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.1.6",
"vue-tsc": "^1.8.27"
}

2354
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ import HelloWorld from './components/HelloWorld.vue'
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
<img src="assets/svg/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />

6
src/api/login/login.ts Normal file
View File

@@ -0,0 +1,6 @@
import request from '@/utils/axios/request'
// 演示代码
export const postLogin = (params: any) => {
return request.post('/user/login', params);
}

View File

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -1,5 +1,17 @@
import { createApp } from 'vue'
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import router from '@/router/index'
import {createPinia} from 'pinia'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
createApp(App).mount('#app')
const app = createApp(App)
const store = createPinia()
store.use(piniaPluginPersistedstate);
// 挂载store
app.use(store)
app.use(Antd);
app.use(router)
app.mount('#app')

12
src/router/index.ts Normal file
View File

@@ -0,0 +1,12 @@
import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router'
const routes: Array<RouteRecordRaw> = [
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router

8
src/store/index.ts Normal file
View File

@@ -0,0 +1,8 @@
import {useAuthStore} from "@/store/modules/user.ts";
export default function useStore() {
return {
user: useAuthStore(),
}
}

53
src/store/modules/user.ts Normal file
View File

@@ -0,0 +1,53 @@
import {defineStore} from 'pinia'
// 加载storage模块获取token存储token
import {getItem, setItem} from "@/utils/storage/storage";
const TOKEN_KEY: String = "X-Token";
export const useAuthStore = defineStore('user', () => {
const user = ref<any>({
token: getItem(TOKEN_KEY) ? getItem(TOKEN_KEY) : null,
userId: Number,
LOADING: false,
userInfo: Object
})
function setUser(data: any) {
user.value = data
setItem(TOKEN_KEY, user.value.token);
}
function getUser() {
return user.value
}
function clearUser() {
user.value = {
token: null,
userId: null,
isLogin: false,
userInfo: {}
}
}
function showLoading() {
user.value.LOADING = true;
}
function hideLoading() {
user.value.LOADING = false;
}
return {
user,
setUser,
getUser,
clearUser,
showLoading,
hideLoading
}
}, {
// 开启数据持久化
persist: true
}
)

136
src/utils/axios/request.ts Normal file
View File

@@ -0,0 +1,136 @@
import axios from 'axios'
import useStore from "@/store";
import * as process from "process";
// import { BASE_URL } from "@/config";
// console.log(BASE_URL)
const BaseURL:any = process.env.VUE_APP_BASE_API
// 数据返回的接口
// 定义请求响应参数不含data
interface Result {
code: number;
msg: string,
total?: number
}
// 请求响应参数包含data
interface ResultData<T = any> extends Result {
data?: T;
}
const URL: string = BaseURL
enum RequestEnums {
TIMEOUT = 20000,
OVERDUE = 600, // 登录失效
FAIL = 999, // 请求失败
SUCCESS = 200, // 请求成功
}
const config = {
// 默认地址
baseURL: URL as string,
// 设置超时时间
timeout: RequestEnums.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true
}
class RequestHttp {
// 定义成员变量并指定类型
service: any;
public constructor(config: any) {
// 实例化axios
this.service = axios.create(config);
/**
* 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: any) => {
const token = useStore().user.getUser().token;
if (token) {
return {
...config,
headers: {
'Authorization': token, // 请求头中携带token信息
}
}
}
},
(error: any) => {
// 请求报错
Promise.reject(error)
}
)
/**
* 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: any) => {
const {data} = response; // 解构
if (data.code === RequestEnums.OVERDUE) {
// 登录信息失效应跳转到登录页面并清空本地的token
// router.replace({
// path: '/login'
// })
return Promise.reject(data);
}
// 全局错误信息拦截防止下载文件得时候返回数据流没有code直接报错
if (data.code && data.code !== RequestEnums.SUCCESS) {
return Promise.reject(data)
}
return data;
},
(error: any) => {
const {response} = error;
if (response) {
this.handleCode(response.status)
}
if (!window.navigator.onLine) {
// 可以跳转到错误页面,也可以不做操作
// return router.replace({
// path: '/404'
// });
}
}
)
}
handleCode(code: number): void {
switch (code) {
case 401:
break;
default:
break;
}
}
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, {params});
}
post<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params);
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params);
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, {params});
}
}
// 导出一个实例对象
export default new RequestHttp(config);

View File

@@ -0,0 +1,23 @@
/* 存储数据 */
export const setItem = (key: any, value: any) => {
// 将数组、对象类型的数据转换为 JSON 格式字符串进行存储
if (typeof value === "object") {
value = JSON.stringify(value);
}
window.localStorage.setItem(key, value);
};
/* 获取数据 */
export const getItem = (key: any) => {
const data:any = window.localStorage.getItem(key);
try {
return JSON.parse(data);
} catch (err) {
return data;
}
};
/* 删除数据 */
export const removeItem = (key: any) => {
window.localStorage.removeItem(key);
};

View File

@@ -1,5 +1,9 @@
{
"compilerOptions": {
"typeRoots": [
"node_modules/@types",
"src/types"
],
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
@@ -13,6 +17,11 @@
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"baseUrl": "./",
"paths": {
"@": ["src"],
"@/*": ["src/*"]
},
/* Linting */
"strict": true,

View File

@@ -1,7 +1,49 @@
import { defineConfig } from 'vite'
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from 'path'
import legacyPlugin from '@vitejs/plugin-legacy'
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite"
//自动导入ui-组件 比如说ant-design-vue element-plus等
import Components from 'unplugin-vue-components/vite';
//ant-design-vue
import {AntDesignVueResolver} from "unplugin-vue-components/resolvers"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
//设置别名
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [
vue(),
legacyPlugin({
targets: ['chrome 52'], // 需要兼容的目标列表
additionalLegacyPolyfills: ['regenerator-runtime/runtime'], // 面向IE11时需要此插件
}),
AutoImport({
//安装两行后你会发现在组件中不用再导入refreactive等
imports: ['vue', 'vue-router'],
dts: "auto-import.d.ts",
//ant-design-vue
resolvers: [AntDesignVueResolver()]
}),
Components({
//ant-design-vue importStyle = false 样式就没了
resolvers: [AntDesignVueResolver({importStyle: true, resolveIcons: true})],
}),
],
server: {
proxy: {
"/dev-api": {
//target是代理的目标路径
target: "",
changeOrigin: true, //必须要开启跨域
rewrite: (path) => path.replace(/\/dev-api/, ""), // 路径重写
},
},
}
})