diff --git a/frontend/.env.development b/frontend/.env.development index 072e0f8..5d89297 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,2 +1,6 @@ NODE_ENV='development' + VITE_TITLE="" +# 路由模式 +# Optional: hash | history +VITE_ROUTER_MODE = hash \ No newline at end of file diff --git a/frontend/.env.production b/frontend/.env.production index 444ec20..a5993a4 100644 --- a/frontend/.env.production +++ b/frontend/.env.production @@ -1,2 +1,6 @@ NODE_ENV='production' + VITE_TITLE="" +# 路由模式 +# Optional: hash | history +VITE_ROUTER_MODE = hash \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 809d047..dfe4c6c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,6 +37,8 @@ "sass": "^1.77.8", "tailwindcss": "^3.4.7", "typescript": "~5.4.0", + "unplugin-auto-import": "^0.18.2", + "unplugin-vue-components": "^0.27.4", "vite": "^5.3.1", "vite-plugin-node-polyfills": "^0.22.0", "vite-plugin-svg-icons": "^2.0.1", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 6f68e3f..59fa68e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,13 +1,13 @@ - - + diff --git a/frontend/src/api/user/index.ts b/frontend/src/api/user/index.ts index 15531ec..44fabc7 100644 --- a/frontend/src/api/user/index.ts +++ b/frontend/src/api/user/index.ts @@ -1,5 +1,5 @@ import createAxios from '@/utils/http' -import { useUserInfoStore } from '@/stores/user' +import { useUserInfoStore } from '@/stores/modules/user' import { sm3Digest } from '@/assets/commjs/sm3.js' import { sm2, encrypt } from '@/assets/commjs/sm2.js' diff --git a/frontend/src/assets/images/logo.png b/frontend/src/assets/images/logo.png new file mode 100644 index 0000000..1889798 Binary files /dev/null and b/frontend/src/assets/images/logo.png differ diff --git a/frontend/src/assets/styles/tailMain.css b/frontend/src/assets/styles/tailMain.css index 855175a..56c5d07 100644 --- a/frontend/src/assets/styles/tailMain.css +++ b/frontend/src/assets/styles/tailMain.css @@ -3,6 +3,7 @@ @tailwind utilities; + /* 申明字体为东方大楷 */ @font-face { font-family: 'DongFangDaKai'; @@ -16,4 +17,3 @@ font-family: "MFBanHei"; /* Project id 1513211 */ src: url('@/assets/font/MFBanHei.ttf?t=1643094287456') format('truetype'); } - diff --git a/frontend/src/constants/storeKey.ts b/frontend/src/constants/storeKey.ts index a477600..412b47f 100644 --- a/frontend/src/constants/storeKey.ts +++ b/frontend/src/constants/storeKey.ts @@ -5,6 +5,9 @@ // 用户信息 export const USER_INFO = 'userInfo' +// 用户信息 +export const TABS_INFO = 'tabsInfo' + // WEB端布局配置 export const STORE_CONFIG = 'storeConfig' diff --git a/frontend/src/layouts/CnHeader/index.vue b/frontend/src/layouts/CnHeader/index.vue new file mode 100644 index 0000000..21a79c8 --- /dev/null +++ b/frontend/src/layouts/CnHeader/index.vue @@ -0,0 +1,92 @@ + + + + diff --git a/frontend/src/layouts/index.vue b/frontend/src/layouts/index.vue new file mode 100644 index 0000000..bff62b2 --- /dev/null +++ b/frontend/src/layouts/index.vue @@ -0,0 +1,34 @@ + + + diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 9562623..4faec90 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,9 +1,9 @@ import { createApp } from 'vue' import App from './App.vue' -// element-plus -import ElementPlus from 'element-plus' -import 'element-plus/dist/index.css' +// element-plus 全局引入图标 +import * as ElementPlusIconsVue from '@element-plus/icons-vue' + // 使用pinia import pinia from '@/stores' // 导入路由 @@ -18,11 +18,16 @@ import registerGlobComp from '@/components' //创建实例 const app = createApp(App) const setupAll = async () => { + //全局注册图标 + for(const [key, component] of Object.entries(ElementPlusIconsVue)){ + app.component(key, component) + } + app .use(Router) // 使用路由 - .use(ElementPlus) // 使用ele-plus组件 .use(pinia) // 使用pinia .use(registerGlobComp) // 使用全局自定义组件 + .use(ElementPlusIconsVue) // 使用element-plus图标 //待路由初始化完毕后,挂载app diff --git a/frontend/src/router/routerMap.ts b/frontend/src/router/routerMap.ts index 558b8fd..9a6786e 100644 --- a/frontend/src/router/routerMap.ts +++ b/frontend/src/router/routerMap.ts @@ -10,6 +10,18 @@ const constantRouterMap = [ name: 'login', component: Login, }, + { + path: '/home', + name: 'home', + component: import("@/layouts/index.vue"), + children: [ + { + path: '/home', + name: 'home', + component: () => import("@/views/Home.vue"), + }, + ], + }, ] export default constantRouterMap diff --git a/frontend/src/stores/interface/index.ts b/frontend/src/stores/interface/index.ts new file mode 100644 index 0000000..d6b8ef9 --- /dev/null +++ b/frontend/src/stores/interface/index.ts @@ -0,0 +1,62 @@ +export type LayoutType = "vertical" | "classic" | "transverse" | "columns"; + +export type AssemblySizeType = "large" | "default" | "small"; + +export type LanguageType = "zh" | "en" | null; + +/* GlobalState */ +export interface GlobalState { + layout: LayoutType; + assemblySize: AssemblySizeType; + language: LanguageType; + maximize: boolean; + primary: string; + isDark: boolean; + isGrey: boolean; + isWeak: boolean; + asideInverted: boolean; + headerInverted: boolean; + isCollapse: boolean; + accordion: boolean; + watermark: boolean; + breadcrumb: boolean; + breadcrumbIcon: boolean; + tabs: boolean; + tabsIcon: boolean; + footer: boolean; +} + +/* UserState */ +export interface UserState { + token: string; + userInfo: { name: string }; +} + +/* tabsMenuProps */ +export interface TabsMenuProps { + icon: string; + title: string; + path: string; + name: string; + close: boolean; + isKeepAlive: boolean; +} + +/* TabsState */ +export interface TabsState { + tabsMenuList: TabsMenuProps[]; +} + +/* AuthState */ +export interface AuthState { + routeName: string; + authButtonList: { + [key: string]: string[]; + }; + authMenuList: Menu.MenuOptions[]; +} + +/* KeepAliveState */ +export interface KeepAliveState { + keepAliveName: string[]; +} diff --git a/frontend/src/stores/user/index.ts b/frontend/src/stores/modules/user/index.ts similarity index 100% rename from frontend/src/stores/user/index.ts rename to frontend/src/stores/modules/user/index.ts diff --git a/frontend/src/theme/index.scss b/frontend/src/theme/index.scss new file mode 100644 index 0000000..22a98cf --- /dev/null +++ b/frontend/src/theme/index.scss @@ -0,0 +1,22 @@ +@forward "element-plus/theme-chalk/src/common/var.scss" with ( + $colors: ( + "primary": (//主题色 + "base": #003078, + ), + "success": (//成功色 + "base": #67C23A, + ), + "warning": (//警告色 + "base": #E6A23C, + ), + "danger": (//危险色 + "base": #F56C6C, + ), + "error": (//错误色 + "base": #FF4949, + ), + "info": (//信息色 + "base": #409EFF, + ), + ), +); diff --git a/frontend/src/types/global.d.ts b/frontend/src/types/global.d.ts index 7bd63d6..f7b0abe 100644 --- a/frontend/src/types/global.d.ts +++ b/frontend/src/types/global.d.ts @@ -31,3 +31,78 @@ interface ApiResponse { } type ApiPromise = Promise> + +/* Menu */ +declare namespace Menu { + interface MenuOptions { + path: string; + name: string; + component?: string | (() => Promise); + redirect?: string; + meta: MetaProps; + children?: MenuOptions[]; + } + interface MetaProps { + icon: string; + title: string; + activeMenu?: string; + isLink?: string; + isHide: boolean; + isFull: boolean; + isAffix: boolean; + isKeepAlive: boolean; + } +} + +/* FileType */ +declare namespace File { + type ImageMimeType = + | "image/apng" + | "image/bmp" + | "image/gif" + | "image/jpeg" + | "image/pjpeg" + | "image/png" + | "image/svg+xml" + | "image/tiff" + | "image/webp" + | "image/x-icon"; + + type ExcelMimeType = "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; +} + +/* Vite */ +declare type Recordable = Record; + +declare interface ViteEnv { + VITE_USER_NODE_ENV: "development" | "production" | "test"; + VITE_GLOB_APP_TITLE: string; + VITE_PORT: number; + VITE_OPEN: boolean; + VITE_REPORT: boolean; + VITE_ROUTER_MODE: "hash" | "history"; + VITE_BUILD_COMPRESS: "gzip" | "brotli" | "gzip,brotli" | "none"; + VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean; + VITE_DROP_CONSOLE: boolean; + VITE_PWA: boolean; + VITE_DEVTOOLS: boolean; + VITE_PUBLIC_PATH: string; + VITE_API_URL: string; + VITE_PROXY: [string, string][]; +} + +interface ImportMetaEnv extends ViteEnv { + __: unknown; +} + +/* __APP_INFO__ */ +declare const __APP_INFO__: { + pkg: { + name: string; + version: string; + dependencies: Recordable; + devDependencies: Recordable; + }; + lastBuildTime: string; +}; + diff --git a/frontend/src/types/utils.d.ts b/frontend/src/types/utils.d.ts new file mode 100644 index 0000000..8476216 --- /dev/null +++ b/frontend/src/types/utils.d.ts @@ -0,0 +1,17 @@ +type ObjToKeyValUnion = { + [K in keyof T]: { key: K; value: T[K] }; +}[keyof T]; + +type ObjToKeyValArray = { + [K in keyof T]: [K, T[K]]; +}[keyof T]; + +type ObjToSelectedValueUnion = { + [K in keyof T]: T[K]; +}[keyof T]; + +type Optional = Omit & Partial>; + +type GetOptional = { + [P in keyof T as T[P] extends Required[P] ? never : P]: T[P]; +}; diff --git a/frontend/src/types/window.d.ts b/frontend/src/types/window.d.ts new file mode 100644 index 0000000..1582726 --- /dev/null +++ b/frontend/src/types/window.d.ts @@ -0,0 +1,8 @@ +declare global { + interface Navigator { + msSaveOrOpenBlob: (blob: Blob, fileName: string) => void; + browserLanguage: string; + } +} + +export {}; diff --git a/frontend/src/utils/http/index.ts b/frontend/src/utils/http/index.ts index c8bcf59..2e969c9 100644 --- a/frontend/src/utils/http/index.ts +++ b/frontend/src/utils/http/index.ts @@ -3,7 +3,7 @@ import axios from 'axios' import { ElLoading, ElNotification, type LoadingOptions } from 'element-plus' import { refreshToken } from '@/api/user' import router from '@/router/index' -import { useUserInfoStore } from '@/stores/user' +import { useUserInfoStore } from '@/stores/modules/user' window.requests = [] window.tokenRefreshing = false diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts new file mode 100644 index 0000000..e1eb997 --- /dev/null +++ b/frontend/src/utils/index.ts @@ -0,0 +1,288 @@ +import { isArray } from "@/utils/is"; + +const mode = import.meta.env.VITE_ROUTER_MODE; + +/** + * @description 获取localStorage + * @param {String} key Storage名称 + * @returns {String} + */ +export function localGet(key: string) { + const value = window.localStorage.getItem(key); + try { + return JSON.parse(window.localStorage.getItem(key) as string); + } catch (error) { + return value; + } +} + +/** + * @description 存储localStorage + * @param {String} key Storage名称 + * @param {*} value Storage值 + * @returns {void} + */ +export function localSet(key: string, value: any) { + window.localStorage.setItem(key, JSON.stringify(value)); +} + +/** + * @description 清除localStorage + * @param {String} key Storage名称 + * @returns {void} + */ +export function localRemove(key: string) { + window.localStorage.removeItem(key); +} + +/** + * @description 清除所有localStorage + * @returns {void} + */ +export function localClear() { + window.localStorage.clear(); +} + +/** + * @description 判断数据类型 + * @param {*} val 需要判断类型的数据 + * @returns {String} + */ +export function isType(val: any) { + if (val === null) return "null"; + if (typeof val !== "object") return typeof val; + else return Object.prototype.toString.call(val).slice(8, -1).toLocaleLowerCase(); +} + +/** + * @description 生成唯一 uuid + * @returns {String} + */ +export function generateUUID() { + let uuid = ""; + for (let i = 0; i < 32; i++) { + let random = (Math.random() * 16) | 0; + if (i === 8 || i === 12 || i === 16 || i === 20) uuid += "-"; + uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16); + } + return uuid; +} + +/** + * 判断两个对象是否相同 + * @param {Object} a 要比较的对象一 + * @param {Object} b 要比较的对象二 + * @returns {Boolean} 相同返回 true,反之 false + */ +export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) { + if (!a || !b) return false; + let aProps = Object.getOwnPropertyNames(a); + let bProps = Object.getOwnPropertyNames(b); + if (aProps.length != bProps.length) return false; + for (let i = 0; i < aProps.length; i++) { + let propName = aProps[i]; + let propA = a[propName]; + let propB = b[propName]; + if (!b.hasOwnProperty(propName)) return false; + if (propA instanceof Object) { + if (!isObjectValueEqual(propA, propB)) return false; + } else if (propA !== propB) { + return false; + } + } + return true; +} + +/** + * @description 生成随机数 + * @param {Number} min 最小值 + * @param {Number} max 最大值 + * @returns {Number} + */ +export function randomNum(min: number, max: number): number { + let num = Math.floor(Math.random() * (min - max) + max); + return num; +} + +/** + * @description 获取当前时间对应的提示语 + * @returns {String} + */ +export function getTimeState() { + let timeNow = new Date(); + let hours = timeNow.getHours(); + if (hours >= 6 && hours <= 10) return `早上好 ⛅`; + if (hours >= 10 && hours <= 14) return `中午好 🌞`; + if (hours >= 14 && hours <= 18) return `下午好 🌞`; + if (hours >= 18 && hours <= 24) return `晚上好 🌛`; + if (hours >= 0 && hours <= 6) return `凌晨好 🌛`; +} + +/** + * @description 获取浏览器默认语言 + * @returns {String} + */ +export function getBrowserLang() { + let browserLang = navigator.language ? navigator.language : navigator.browserLanguage; + let defaultBrowserLang = ""; + if (["cn", "zh", "zh-cn"].includes(browserLang.toLowerCase())) { + defaultBrowserLang = "zh"; + } else { + defaultBrowserLang = "en"; + } + return defaultBrowserLang; +} + +/** + * @description 获取不同路由模式所对应的 url + params + * @returns {String} + */ +export function getUrlWithParams() { + const url = { + hash: location.hash.substring(1), + history: location.pathname + location.search + }; + return url[mode]; +} + +/** + * @description 使用递归扁平化菜单,方便添加动态路由 + * @param {Array} menuList 菜单列表 + * @returns {Array} + */ +export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[] { + let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList)); + return newMenuList.flatMap(item => [item, ...(item.children ? getFlatMenuList(item.children) : [])]); +} + +/** + * @description 使用递归过滤出需要渲染在左侧菜单的列表 (需剔除 isHide == true 的菜单) + * @param {Array} menuList 菜单列表 + * @returns {Array} + * */ +export function getShowMenuList(menuList: Menu.MenuOptions[]) { + let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList)); + return newMenuList.filter(item => { + item.children?.length && (item.children = getShowMenuList(item.children)); + return !item.meta?.isHide; + }); +} + +/** + * @description 使用递归找出所有面包屑存储到 pinia/vuex 中 + * @param {Array} menuList 菜单列表 + * @param {Array} parent 父级菜单 + * @param {Object} result 处理后的结果 + * @returns {Object} + */ +export const getAllBreadcrumbList = (menuList: Menu.MenuOptions[], parent = [], result: { [key: string]: any } = {}) => { + for (const item of menuList) { + result[item.path] = [...parent, item]; + if (item.children) getAllBreadcrumbList(item.children, result[item.path], result); + } + return result; +}; + +/** + * @description 使用递归处理路由菜单 path,生成一维数组 (第一版本地路由鉴权会用到,该函数暂未使用) + * @param {Array} menuList 所有菜单列表 + * @param {Array} menuPathArr 菜单地址的一维数组 ['**','**'] + * @returns {Array} + */ +export function getMenuListPath(menuList: Menu.MenuOptions[], menuPathArr: string[] = []): string[] { + for (const item of menuList) { + if (typeof item === "object" && item.path) menuPathArr.push(item.path); + if (item.children?.length) getMenuListPath(item.children, menuPathArr); + } + return menuPathArr; +} + +/** + * @description 递归查询当前 path 所对应的菜单对象 (该函数暂未使用) + * @param {Array} menuList 菜单列表 + * @param {String} path 当前访问地址 + * @returns {Object | null} + */ +export function findMenuByPath(menuList: Menu.MenuOptions[], path: string): Menu.MenuOptions | null { + for (const item of menuList) { + if (item.path === path) return item; + if (item.children) { + const res = findMenuByPath(item.children, path); + if (res) return res; + } + } + return null; +} + +/** + * @description 使用递归过滤需要缓存的菜单 name (该函数暂未使用) + * @param {Array} menuList 所有菜单列表 + * @param {Array} keepAliveNameArr 缓存的菜单 name ['**','**'] + * @returns {Array} + * */ +export function getKeepAliveRouterName(menuList: Menu.MenuOptions[], keepAliveNameArr: string[] = []) { + menuList.forEach(item => { + item.meta.isKeepAlive && item.name && keepAliveNameArr.push(item.name); + item.children?.length && getKeepAliveRouterName(item.children, keepAliveNameArr); + }); + return keepAliveNameArr; +} + +/** + * @description 格式化表格单元格默认值 (el-table-column) + * @param {Number} row 行 + * @param {Number} col 列 + * @param {*} callValue 当前单元格值 + * @returns {String} + * */ +export function formatTableColumn(row: number, col: number, callValue: any) { + // 如果当前值为数组,使用 / 拼接(根据需求自定义) + if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "--"; + return callValue ?? "--"; +} + +/** + * @description 处理 ProTable 值为数组 || 无数据 + * @param {*} callValue 需要处理的值 + * @returns {String} + * */ +export function formatValue(callValue: any) { + // 如果当前值为数组,使用 / 拼接(根据需求自定义) + if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "--"; + return callValue ?? "--"; +} + +/** + * @description 处理 prop 为多级嵌套的情况,返回的数据 (列如: prop: user.name) + * @param {Object} row 当前行数据 + * @param {String} prop 当前 prop + * @returns {*} + * */ +export function handleRowAccordingToProp(row: { [key: string]: any }, prop: string) { + if (!prop.includes(".")) return row[prop] ?? "--"; + prop.split(".").forEach(item => (row = row[item] ?? "--")); + return row; +} + +/** + * @description 处理 prop,当 prop 为多级嵌套时 ==> 返回最后一级 prop + * @param {String} prop 当前 prop + * @returns {String} + * */ +export function handleProp(prop: string) { + const propArr = prop.split("."); + if (propArr.length == 1) return prop; + return propArr[propArr.length - 1]; +} + + +/** + * @description 递归查找 callValue 对应的 enum 值 + * */ +export function findItemNested(enumData: any, callValue: any, value: string, children: string) { + return enumData.reduce((accumulator: any, current: any) => { + if (accumulator) return accumulator; + if (current[value] === callValue) return current; + if (current[children]) return findItemNested(current[children], callValue, value, children); + }, null); +} diff --git a/frontend/src/utils/is/index.ts b/frontend/src/utils/is/index.ts new file mode 100644 index 0000000..5678491 --- /dev/null +++ b/frontend/src/utils/is/index.ts @@ -0,0 +1,125 @@ +/** + * @description: 判断值是否未某个类型 + */ +export function is(val: unknown, type: string) { + return Object.prototype.toString.call(val) === `[object ${type}]`; +} + +/** + * @description: 是否为函数 + */ +export function isFunction(val: unknown): val is T { + return is(val, "Function"); +} + +/** + * @description: 是否已定义 + */ +export const isDef = (val?: T): val is T => { + return typeof val !== "undefined"; +}; + +/** + * @description: 是否未定义 + */ +export const isUnDef = (val?: T): val is T => { + return !isDef(val); +}; + +/** + * @description: 是否为对象 + */ +export const isObject = (val: any): val is Record => { + return val !== null && is(val, "Object"); +}; + +/** + * @description: 是否为时间 + */ +export function isDate(val: unknown): val is Date { + return is(val, "Date"); +} + +/** + * @description: 是否为数值 + */ +export function isNumber(val: unknown): val is number { + return is(val, "Number"); +} + +/** + * @description: 是否为AsyncFunction + */ +export function isAsyncFunction(val: unknown): val is Promise { + return is(val, "AsyncFunction"); +} + +/** + * @description: 是否为promise + */ +export function isPromise(val: unknown): val is Promise { + return is(val, "Promise") && isObject(val) && isFunction(val.then) && isFunction(val.catch); +} + +/** + * @description: 是否为字符串 + */ +export function isString(val: unknown): val is string { + return is(val, "String"); +} + +/** + * @description: 是否为boolean类型 + */ +export function isBoolean(val: unknown): val is boolean { + return is(val, "Boolean"); +} + +/** + * @description: 是否为数组 + */ +export function isArray(val: any): val is Array { + return val && Array.isArray(val); +} + +/** + * @description: 是否客户端 + */ +export const isClient = () => { + return typeof window !== "undefined"; +}; + +/** + * @description: 是否为浏览器 + */ +export const isWindow = (val: any): val is Window => { + return typeof window !== "undefined" && is(val, "Window"); +}; + +/** + * @description: 是否为 element 元素 + */ +export const isElement = (val: unknown): val is Element => { + return isObject(val) && !!val.tagName; +}; + +/** + * @description: 是否为 null + */ +export function isNull(val: unknown): val is null { + return val === null; +} + +/** + * @description: 是否为 null || undefined + */ +export function isNullOrUnDef(val: unknown): val is null | undefined { + return isUnDef(val) || isNull(val); +} + +/** + * @description: 是否为 16 进制颜色 + */ +export const isHexColor = (str: string) => { + return /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(str); +}; diff --git a/frontend/src/utils/logout/index.ts b/frontend/src/utils/logout/index.ts index ddd2c64..256342a 100644 --- a/frontend/src/utils/logout/index.ts +++ b/frontend/src/utils/logout/index.ts @@ -1,4 +1,4 @@ -import { useUserInfoStore } from '@/stores/user' +import { useUserInfoStore } from '@/stores/modules/user' import { USER_INFO } from '@/constants/storeKey' export function clearUserInfo() { diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index b3fe48b..f42c6c4 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -38,7 +38,7 @@ import { ref, onMounted } from "vue"; const router = useRouter(); import { useRouter } from "vue-router"; import { Avatar, Lock } from "@element-plus/icons-vue"; -import { useUserInfoStore } from "@/stores/user"; +import { useUserInfoStore } from "@/stores/modules/user"; const userInfoStore = useUserInfoStore(); @@ -61,12 +61,12 @@ const handleLogin = () => { loginLoading.value = true; if (LoginForm.value.loginUserName && LoginForm.value.loginUserPassword) { setTimeout(() => { - // router.push({ - // path: "/admin", - // query: { - // name: LoginForm.value.loginUserName, - // }, - // }); + router.push({ + path: "/home", + query: { + name: LoginForm.value.loginUserName, + }, + }); userInfoStore.dataFill({ loginName: LoginForm.value.loginUserName }); console.log(userInfoStore.loginName) }, 1500); diff --git a/frontend/src/views/dashboard/index.vue b/frontend/src/views/dashboard/index.vue new file mode 100644 index 0000000..9928094 --- /dev/null +++ b/frontend/src/views/dashboard/index.vue @@ -0,0 +1,231 @@ + + + diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index fd178d6..200c659 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -3,6 +3,9 @@ import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' import vue from '@vitejs/plugin-vue' import path from 'path' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' export default defineConfig((config) => { return { @@ -13,6 +16,16 @@ export default defineConfig((config) => { iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], symbolId: 'icon-[dir]-[name]', }), + AutoImport({ + resolvers: [ElementPlusResolver({ + importStyle: "sass", + })] + }), + Components({ + resolvers: [ElementPlusResolver({ + importStyle: "sass", + })] + }), ], // 基础配置 base: './', @@ -30,6 +43,10 @@ export default defineConfig((config) => { }, javascriptEnabled: true, }, + scss: { + // 引入index.scss覆盖文件 + additionalData: `@use "@/theme/index.scss" as *;`, + } }, }, build: {