diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..7d2af0e --- /dev/null +++ b/.env.development @@ -0,0 +1,4 @@ +# 开发环境配置 +VITE_ENV=development +VITE_API_URL=/api +VITE_APP_TITLE=Template Admin (Dev) diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..8238f7d --- /dev/null +++ b/.env.production @@ -0,0 +1,4 @@ +# 生产环境配置 +VITE_ENV=production +VITE_API_URL=/prod-api +VITE_APP_TITLE=Template Admin diff --git a/src/api/auth/index.ts b/src/api/auth/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/index.ts b/src/api/index.ts deleted file mode 100644 index 09c7dc9..0000000 --- a/src/api/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// API 请求封装 -export const fetchData = async (url: string) => { - const response = await fetch(url); - return response.json(); -}; - diff --git a/src/components.d.ts b/src/components.d.ts deleted file mode 100644 index faa543c..0000000 --- a/src/components.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint-disable */ -// @ts-nocheck -// Generated by unplugin-vue-components -// Read more: https://github.com/vuejs/core/pull/3399 -// biome-ignore lint: disable -export {} - -/* prettier-ignore */ -declare module 'vue' { - export interface GlobalComponents { - AdminLayout: typeof import('./components/layout/AdminLayout.vue')['default'] - AdvancedForm: typeof import('./components/form/AdvancedForm.vue')['default'] - AdvancedTable: typeof import('./components/table/AdvancedTable.vue')['default'] - Breadcrumb: typeof import('./components/common/Breadcrumb.vue')['default'] - FooterBar: typeof import('./components/layout/FooterBar.vue')['default'] - HeaderNav: typeof import('./components/layout/HeaderNav.vue')['default'] - MainContent: typeof import('./components/layout/MainContent.vue')['default'] - RouterLink: typeof import('vue-router')['RouterLink'] - RouterView: typeof import('vue-router')['RouterView'] - SideMenu: typeof import('./components/layout/SideMenu.vue')['default'] - TabsBar: typeof import('./components/layout/TabsBar.vue')['default'] - YourComponent: typeof import('./components/YourComponent.vue')['default'] - } -} diff --git a/src/components/fallingTable/components/searchBox.vue b/src/components/fallingTable/components/searchBox.vue new file mode 100644 index 0000000..b03e25a --- /dev/null +++ b/src/components/fallingTable/components/searchBox.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/src/components/fallingTable/index.vue b/src/components/fallingTable/index.vue new file mode 100644 index 0000000..b900d3a --- /dev/null +++ b/src/components/fallingTable/index.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/src/enum/componentsEnum.ts b/src/enum/componentsEnum.ts new file mode 100644 index 0000000..9fc74b0 --- /dev/null +++ b/src/enum/componentsEnum.ts @@ -0,0 +1,4 @@ +export enum ComponentsEnum { + Input = 'Input', + Select = 'Select' +} diff --git a/src/request/index.ts b/src/request/index.ts new file mode 100644 index 0000000..5a2751b --- /dev/null +++ b/src/request/index.ts @@ -0,0 +1,130 @@ +import type { AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios' +import axios from 'axios' + +// 对应后端的Result结构 +export interface Result { + code: number + msg: string + data: T + isSuccess?: boolean +} + +// 对应后端的ResultCode枚举 +export enum ResultCode { + SUCCESS = 200, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + FORBIDDEN = 403, + NOT_FOUND = 404, + INTERNAL_SERVER_ERROR = 500, + SERVICE_UNAVAILABLE = 503, +} + +// 创建axios实例 +const service: AxiosInstance = axios.create({ + baseURL: import.meta.env.VITE_API_URL, // 从环境变量获取API基础URL + timeout: 10000, // 请求超时时间 +}) + +// 请求拦截器 +service.interceptors.request.use( + (config) => { + // 在发送请求之前做些什么,例如添加token + const token = localStorage.getItem('token') + if (token) { + config.headers = config.headers || {} + config.headers['Authorization'] = token + } + return config + }, + (error: AxiosError) => { + // 对请求错误做些什么 + return Promise.reject(error) + }, +) + +// 响应拦截器 +service.interceptors.response.use( + (response) => { + const res = response.data + + // 如果自定义code不是200,则判断为错误 + if (res.code !== ResultCode.SUCCESS) { + // 处理特定的错误码 + switch (res.code) { + case ResultCode.UNAUTHORIZED: + // 未授权,跳转到登录页 + window.location.href = '/login' + break + case ResultCode.FORBIDDEN: + // 无权限,显示提示 + console.error('无权限访问') + break + default: + console.error(res.msg || '请求失败') + } + return Promise.reject(new Error(res.msg || 'Error')) + } else { + // 成功请求,标记isSuccess为true + res.isSuccess = true + return res + } + }, + (error: AxiosError) => { + // 对响应错误做点什么 + let message = error.message + if (error.response) { + // 处理HTTP状态码 + switch (error.response.status) { + case 400: + message = '请求参数错误' + break + case 401: + message = '未授权,请登录' + window.location.href = '/login' + break + case 403: + message = '拒绝访问' + break + case 404: + message = '请求资源不存在' + break + case 500: + message = '服务器内部错误' + break + case 503: + message = '服务不可用' + break + } + } + console.error(message) + return Promise.reject(error) + }, +) + +// 封装通用请求方法 +export function request(config: AxiosRequestConfig): Promise> { + return service(config) +} + +// 封装GET请求 +export function get(url: string, params?: any): Promise> { + return request({ method: 'GET', url, params }) +} + +// 封装POST请求 +export function post(url: string, data?: any): Promise> { + return request({ method: 'POST', url, data }) +} + +// 封装PUT请求 +export function put(url: string, data?: any): Promise> { + return request({ method: 'PUT', url, data }) +} + +// 封装DELETE请求 +export function del(url: string, params?: any): Promise> { + return request({ method: 'DELETE', url, params }) +} + +export default service diff --git a/src/auto-imports.d.ts b/src/types/auto-imports.d.ts similarity index 98% rename from src/auto-imports.d.ts rename to src/types/auto-imports.d.ts index 0088a0a..bd5b8f3 100644 --- a/src/auto-imports.d.ts +++ b/src/types/auto-imports.d.ts @@ -66,7 +66,7 @@ declare global { const triggerRef: typeof import('vue')['triggerRef'] const unref: typeof import('vue')['unref'] const useAttrs: typeof import('vue')['useAttrs'] - const useCounterStore: typeof import('./stores/counter')['useCounterStore'] + const useCounterStore: typeof import('../stores/counter')['useCounterStore'] const useCssModule: typeof import('vue')['useCssModule'] const useCssVars: typeof import('vue')['useCssVars'] const useId: typeof import('vue')['useId'] @@ -153,7 +153,7 @@ declare module 'vue' { readonly triggerRef: UnwrapRef readonly unref: UnwrapRef readonly useAttrs: UnwrapRef - readonly useCounterStore: UnwrapRef + readonly useCounterStore: UnwrapRef readonly useCssModule: UnwrapRef readonly useCssVars: UnwrapRef readonly useId: UnwrapRef diff --git a/src/types/components.d.ts b/src/types/components.d.ts new file mode 100644 index 0000000..c2fbbf5 --- /dev/null +++ b/src/types/components.d.ts @@ -0,0 +1,47 @@ +/* eslint-disable */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +// biome-ignore lint: disable +export {} + +/* prettier-ignore */ +declare module 'vue' { + export interface GlobalComponents { + ABadge: typeof import('ant-design-vue/es')['Badge'] + ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] + ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] + AButton: typeof import('ant-design-vue/es')['Button'] + ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] + ACol: typeof import('ant-design-vue/es')['Col'] + AdminLayout: typeof import('./../components/layout/AdminLayout.vue')['default'] + ADropdown: typeof import('ant-design-vue/es')['Dropdown'] + AForm: typeof import('ant-design-vue/es')['Form'] + AFormItem: typeof import('ant-design-vue/es')['FormItem'] + AInput: typeof import('ant-design-vue/es')['Input'] + AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] + ALayout: typeof import('ant-design-vue/es')['Layout'] + ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent'] + ALayoutFooter: typeof import('ant-design-vue/es')['LayoutFooter'] + ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader'] + ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider'] + AMenu: typeof import('ant-design-vue/es')['Menu'] + AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider'] + AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] + ARow: typeof import('ant-design-vue/es')['Row'] + ASpace: typeof import('ant-design-vue/es')['Space'] + ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] + ATabPane: typeof import('ant-design-vue/es')['TabPane'] + ATabs: typeof import('ant-design-vue/es')['Tabs'] + Breadcrumb: typeof import('./../components/common/Breadcrumb.vue')['default'] + FallingTable: typeof import('./../components/fallingTable/index.vue')['default'] + FooterBar: typeof import('./../components/layout/FooterBar.vue')['default'] + HeaderNav: typeof import('./../components/layout/HeaderNav.vue')['default'] + MainContent: typeof import('./../components/layout/MainContent.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + SearchBox: typeof import('./../components/fallingTable/components/searchBox.vue')['default'] + SideMenu: typeof import('./../components/layout/SideMenu.vue')['default'] + TabsBar: typeof import('./../components/layout/TabsBar.vue')['default'] + } +} diff --git a/tsconfig.app.json b/tsconfig.app.json index 913b8f2..a445c1d 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -4,7 +4,7 @@ "exclude": ["src/**/__tests__/*"], "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - + "module": "es2020", "paths": { "@/*": ["./src/*"] } diff --git a/vite.config.ts b/vite.config.ts index f4412f6..d398728 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,6 +6,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx' import vueDevTools from 'vite-plugin-vue-devtools' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' +import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers' // https://vite.dev/config/ export default defineConfig({ @@ -14,34 +15,34 @@ export default defineConfig({ vueJsx(), vueDevTools(), AutoImport({ - // 自动导入Vue相关API imports: [ 'vue', 'vue-router', 'pinia', - // 可以添加更多需要自动导入的库 ], - // 生成自动导入的TS声明文件 - dts: 'src/auto-imports.d.ts', - // 自动导入目录下的模块 + resolvers: [ + AntDesignVueResolver({ + importStyle: 'less', // 可选: 'css' | 'less',根据你是否启用了 less 主题定制 + }), + ], + dts: 'src/types/auto-imports.d.ts', dirs: [ - 'src/composables', + 'src/components', 'src/stores', ], - // 自动导入的API前缀 vueTemplate: true, }), Components({ - // 指定组件所在目录,默认为 src/components dirs: ['src/components'], - // 组件的有效文件扩展名 extensions: ['vue', 'tsx'], - // 配置文件生成位置 - dts: 'src/components.d.ts', - // 搜索子目录 + dts: 'src/types/components.d.ts', deep: true, - // 允许子目录作为组件的命名空间前缀 directoryAsNamespace: false, + resolvers: [ + AntDesignVueResolver({ + importStyle: 'less', // 保持与 AutoImport 中一致 + }), + ], }), ], resolve: {