diff --git a/.env.development b/.env.development index 3a3adad..5d2653c 100644 --- a/.env.development +++ b/.env.development @@ -1,7 +1,10 @@ # 开发环境 VITE_USER_NODE_ENV = development -# 开发环境地址前缀 (一般 '/' 或 './' 都可以,如果开发环境 '/' 打包预览白屏,请使用 './') +# 打包路径 开发环境地址前缀 (一般 '/' 或 './' 都可以,如果开发环境 '/' 打包预览白屏,请使用 './') VITE_PUBLIC_PATH = './' +# 请求路径 管理系统/开发环境 +VITE_APP_BASE_URL = '/your-request-path' + VITE_GLOB_APP_TITLE = SnowAdmin \ No newline at end of file diff --git a/.env.production b/.env.production index bbfa6b5..899679f 100644 --- a/.env.production +++ b/.env.production @@ -4,4 +4,7 @@ VITE_USER_NODE_ENV = production # 打包路径 (就是网站前缀, 例如部署到 http://dcodes.gitee.io/SnowAdmin/ 域名下, 就需要填写 /SnowAdmin/), 一般填一个斜杠 / VITE_PUBLIC_PATH = '/' +# 请求路径 管理系统/开发环境 +VITE_APP_BASE_URL = '/' + VITE_GLOB_APP_TITLE = SnowAdmin diff --git a/.env.test b/.env.test index 2075598..3a8c45b 100644 --- a/.env.test +++ b/.env.test @@ -1,7 +1,10 @@ # 开发环境 -VITE_USER_NODE_ENV = development +VITE_USER_NODE_ENV = test # 开发环境地址前缀 (一般 '/' 或 './' 都可以) VITE_PUBLIC_PATH = '/test' +# 请求路径 管理系统/开发环境 +VITE_APP_BASE_URL = '/' + VITE_GLOB_APP_TITLE = SnowAdmin \ No newline at end of file diff --git a/index.html b/index.html index 3b536a6..9c43e06 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + <%= title %> diff --git a/src/api/index.ts b/src/api/index.ts index ba40802..7c0b478 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,17 +1,15 @@ import axios from "axios"; -import { useRouter } from "vue-router"; -const router = useRouter(); +import router from "@/router"; +import { Message } from "@arco-design/web-vue"; // 创建axios实例 const service = axios.create({ - baseURL: "https://mock.apifox.com/m1/4457302-4103374-default", - timeout: 5000 + baseURL: "/api" }); // 请求拦截器 service.interceptors.request.use( function (config: any) { // 发送请求之前做什么 // 获取token鉴权 - console.log("token?", localStorage.getItem("user-info")); let userInfo: any = {}; if (localStorage.getItem("user-info")) { userInfo = JSON.parse(localStorage.getItem("user-info") as string); @@ -19,12 +17,6 @@ service.interceptors.request.use( if (userInfo.token) { // 有token,在请求头中携带token config.headers.Authorization = userInfo.token; - } else { - // 如果没有token,则不允许发送请求,并且跳转至登录页要求登录,获取对应的token - if (config.url !== "/login") { - router.push("/login"); - Promise.reject("未登录"); - } } return config; }, @@ -37,11 +29,27 @@ service.interceptors.request.use( // 响应拦截器 service.interceptors.response.use( function (response: any) { - return response.data; + if (response.status != 200) { + Message.error("服务器异常,请联系管理员"); + return Promise.reject(response.data); + } + let res = response.data; + if (res.code == 401) { + Message.error("登录状态已过期"); + router.push("/login"); + return Promise.reject(res); + } else if (res.code == 404) { + Message.error("请求连接超时"); + return Promise.reject(res); + } else if (res.code != 200) { + Message.error(res.msg); + return Promise.reject(res); + } else { + // 返回数据 + return Promise.resolve(res); + } }, function (error: any) { - console.log("响应拦截", error, router); - console.log("身份信息过期"); localStorage.removeItem("user-info"); router.push("/login"); return Promise.reject(error); diff --git a/src/api/modules/home.ts b/src/api/modules/home.ts deleted file mode 100644 index 86e8813..0000000 --- a/src/api/modules/home.ts +++ /dev/null @@ -1,4 +0,0 @@ -import axios from "@/api"; - -// 第三板指标 -export const getTargetAPI = () => axios.get("/notice-list"); diff --git a/src/api/modules/login.ts b/src/api/modules/login.ts new file mode 100644 index 0000000..5e17545 --- /dev/null +++ b/src/api/modules/login.ts @@ -0,0 +1,10 @@ +import axios from "@/api"; + +// 登录 +export const LoginAPI = (data: any) => { + return axios({ + url: "store/store.php?action=Login", + method: "post", + data + }); +}; diff --git a/src/assets/logo/snow.svg b/src/assets/logo/snow.svg new file mode 100644 index 0000000..3390a7d --- /dev/null +++ b/src/assets/logo/snow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/hooks/useMneuMethod.ts b/src/hooks/useMenuMethod.ts similarity index 92% rename from src/hooks/useMneuMethod.ts rename to src/hooks/useMenuMethod.ts index 69f5861..de85910 100644 --- a/src/hooks/useMneuMethod.ts +++ b/src/hooks/useMenuMethod.ts @@ -2,7 +2,7 @@ * 菜单处理hooks,内置多种菜单处理场景 * @returns 菜单处理方法 */ -export const useMneuMethod = () => { +export const useMenuMethod = () => { /** * 多级菜单的显示隐藏 * @param {Menu.MenuOptions} item 菜单的item diff --git a/src/layout/components/Menu/menu-item.vue b/src/layout/components/Menu/menu-item.vue index 3d042c6..0a7f351 100644 --- a/src/layout/components/Menu/menu-item.vue +++ b/src/layout/components/Menu/menu-item.vue @@ -19,7 +19,7 @@ diff --git a/src/layout/layout-head/index.vue b/src/layout/layout-head/index.vue index 93e466f..28530ac 100644 --- a/src/layout/layout-head/index.vue +++ b/src/layout/layout-head/index.vue @@ -50,7 +50,7 @@ import { storeToRefs } from "pinia"; import { useRoutesListStore } from "@/store/modules/route-list"; import { useRoutingMethod } from "@/hooks/useRoutingMethod"; import { useThemeConfig } from "@/store/modules/theme-config"; -import { useMneuMethod } from "@/hooks/useMneuMethod"; +import { useMenuMethod } from "@/hooks/useMenuMethod"; defineOptions({ name: "LayoutHead" }); const router = useRouter(); const routerStore = useRoutesListStore(); @@ -58,7 +58,7 @@ const themeStore = useThemeConfig(); const { routeTree, currentRoute } = storeToRefs(routerStore); const { isFooter, language } = storeToRefs(themeStore); -const { menuShow, aMenuShow } = useMneuMethod(); +const { menuShow, aMenuShow } = useMenuMethod(); const drawing = ref(true); watch(language, () => { diff --git a/src/layout/layout-mixing/index.vue b/src/layout/layout-mixing/index.vue index b8c79aa..e30e5fc 100644 --- a/src/layout/layout-mixing/index.vue +++ b/src/layout/layout-mixing/index.vue @@ -53,7 +53,7 @@ import { useRoutesListStore } from "@/store/modules/route-list"; import { useRoutingMethod } from "@/hooks/useRoutingMethod"; import { storeToRefs } from "pinia"; import { useThemeConfig } from "@/store/modules/theme-config"; -import { useMneuMethod } from "@/hooks/useMneuMethod"; +import { useMenuMethod } from "@/hooks/useMenuMethod"; defineOptions({ name: "LayoutMixing" }); const route = useRoute(); const router = useRouter(); @@ -61,7 +61,7 @@ const routerStore = useRoutesListStore(); const themeStore = useThemeConfig(); const { isFooter, collapsed, asideDark, language } = storeToRefs(themeStore); const { routeTree } = storeToRefs(routerStore); -const { aMenuShow } = useMneuMethod(); +const { aMenuShow } = useMenuMethod(); const drawing = ref(true); watch(language, () => { diff --git a/src/utils/index.ts b/src/utils/index.ts index d6c7fa6..4a99f5a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -103,3 +103,61 @@ export function webDefaultLanguage() { return "enUS"; } } + +/** + * 时间戳转 年月日时分秒 + * 若timestamp不传则取当前时间 + * 若type不传则取 年月日时分秒 + * @param { number } timestamp 时间戳 + * @return 返回年月日时分秒字符串 + */ +export const getTimestamp = (timestamp: string | number | null, type: string) => { + let date = null; + if (timestamp) { + date = new Date(timestamp); + } else { + date = new Date(); + } + let Year = String(date.getFullYear()); + let Moth = String(date.getMonth() + 1).padStart(2, "0"); + let Day = String(date.getDate()).padStart(2, "0"); + let Hour = String(date.getHours()).padStart(2, "0"); + let Minute = String(date.getMinutes()).padStart(2, "0"); + let Seconds = String(date.getSeconds()).padStart(2, "0"); + if (type === "yyyy") { + return `${Year}`; + } + if (type === "yyyy-MM") { + return `${Year}-${Moth}`; + } + if (type === "yyyy-MM-dd") { + return `${Year}-${Moth}-${Day}`; + } + return `${Year}-${Moth}-${Day} ${Hour}:${Minute}:${Seconds}`; +}; + +/** + * 给formData循环添加参数,过滤null、undefined、空字符串、NaN + * 示例:let data = appendFormData(your-object); + * @param { object } obj 参数对象 + * @return 返回formData对象 + */ +export const appendFormData = (obj: any) => { + let formData = new FormData(); + function deepAppendFormData(formData: any, data: any, parentKey = "") { + if (Array.isArray(data) || (typeof data === "object" && data !== null)) { + // 如果数据是数组或对象,序列化为 JSON 字符串 + formData.append(parentKey, JSON.stringify(data)); + } else if (data !== null && data !== undefined && !Number.isNaN(data) && data !== "") { + // 如果数据是基本类型,直接添加 + formData.append(parentKey, data); + } + } + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + deepAppendFormData(formData, obj[key], key); + } + } + deepAppendFormData(formData, obj); + return formData; +}; diff --git a/src/utils/px2px.ts b/src/utils/px2px.ts new file mode 100644 index 0000000..bb71b17 --- /dev/null +++ b/src/utils/px2px.ts @@ -0,0 +1,15 @@ +/** + * px转响应式px + * 示例:$px2px(20, 1920),在1920px的设计稿上元素大小为20px,若在其它窗口大小下,会根据窗口大小自动缩放 + * @param {number} px 设计稿元素大小 + * @param {number} viewport 设计稿窗口大小 + * @returns {number} 响应式px + */ +export const px2px = (px: number, viewport: number): number => { + const viewportWidth: number = (window.innerWidth || document.documentElement.clientWidth) as number; + let originalWidth = px; + let originalViewport = viewport; + // 计算新视口下的宽度 + let newWidth = (originalWidth / originalViewport) * viewportWidth; + return parseInt(String(newWidth)); +}; diff --git a/src/views/login/components/login-form.vue b/src/views/login/components/login-form.vue index 8cb8a66..dab96eb 100644 --- a/src/views/login/components/login-form.vue +++ b/src/views/login/components/login-form.vue @@ -41,6 +41,7 @@ import { Message } from "@arco-design/web-vue"; import { useRouter } from "vue-router"; import { useUserInfoStore } from "@/store/modules/user-info"; + const router = useRouter(); const form = ref({ username: "admin", @@ -102,16 +103,19 @@ const verify = ref({ }); const verifyCodeChange = (code: string) => (verify.value.verifyCode = code); -const onSubmit = ({ errors }: any) => { +const onSubmit = async ({ errors }: any) => { if (errors) return; - // 存储用户信息 + // 你的登录请求 + // ...... + + // 登录成功-存储用户信息 let stores = useUserInfoStore(); let account = { - username: form.value.username, - roles: ["admin"] + username: form.value.username, // 用户名 + roles: ["admin"] // 角色权限 }; - stores.setAccount(account); - stores.setToken("DC-Admin"); + stores.setAccount(account); // 存储用户信息 + stores.setToken("Your-Token"); Message.success("登录成功"); router.replace("/home"); }; diff --git a/vite.config.ts b/vite.config.ts index 2deb6d1..2343f07 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -18,12 +18,19 @@ export default defineConfig(({ mode }) => { // 获取跟路径对应的文件 const env = loadEnv(mode, root); return { - // 开发或生产环境服务的公共基础路径 + // 生产环境服务的公共基础路径-用于生出环境的代理的路径 base: env.VITE_PUBLIC_PATH, server: { - // host: "0.0.0.0", - // open: true, - proxy: {} + host: "0.0.0.0", + open: true, + // 为开发服务器配置自定义代理规则-用于开发时的代理 + proxy: { + "/api": { + target: env.VITE_APP_BASE_URL, + changeOrigin: true, + rewrite: path => path.replace(/^\/api/, "") + } + } }, plugins: [ vue(),