Quellcode durchsuchen

feat(useTitle): Add useTitle

feat(useNProgress): Add useNProgress
kailong321200875 vor 3 Jahren
Ursprung
Commit
c5ab3599c8

+ 1 - 2
.vscode/settings.json

@@ -14,6 +14,5 @@
   "i18n-ally.enabledParsers": ["ts"],
   "i18n-ally.sourceLanguage": "en",
   "i18n-ally.displayLanguage": "zh-CN",
-  "i18n-ally.enabledFrameworks": ["vue", "react"],
-  "god.tsconfig": "./tsconfig.json"
+  "i18n-ally.enabledFrameworks": ["vue", "react"]
 }

+ 4 - 1
mock/user/index.ts

@@ -1,8 +1,10 @@
-import { config } from '@/config/axios'
+import { config } from '@/config/axios/config'
 import { MockMethod } from 'vite-plugin-mock'
 
 const { result_code } = config
 
+const timeout = 2000
+
 const List: {
   username: string
   password: string
@@ -28,6 +30,7 @@ export default [
   {
     url: '/user/login',
     method: 'post',
+    timeout,
     response: ({ body }) => {
       const data = body
       let hasUser = false

+ 14 - 11
package.json

@@ -26,13 +26,14 @@
   },
   "dependencies": {
     "@iconify/iconify": "^2.1.0",
-    "@vueuse/core": "^7.5.1",
+    "@vueuse/core": "^7.5.3",
     "@zxcvbn-ts/core": "^1.2.0",
     "animate.css": "^4.1.1",
     "axios": "^0.24.0",
-    "element-plus": "1.3.0-beta.1",
+    "element-plus": "1.3.0-beta.2",
     "lodash-es": "^4.17.21",
     "mockjs": "^1.1.0",
+    "nprogress": "^0.2.0",
     "pinia": "^2.0.9",
     "qs": "^6.10.2",
     "vue": "3.2.26",
@@ -44,28 +45,30 @@
   "devDependencies": {
     "@commitlint/cli": "^16.0.1",
     "@commitlint/config-conventional": "^16.0.0",
-    "@iconify/json": "^1.1.450",
+    "@iconify/json": "^1.1.453",
     "@intlify/vite-plugin-vue-i18n": "^3.2.1",
     "@purge-icons/generated": "^0.7.0",
     "@types/lodash-es": "^4.17.5",
-    "@types/node": "^17.0.5",
-    "@typescript-eslint/eslint-plugin": "^5.8.1",
-    "@typescript-eslint/parser": "^5.8.1",
+    "@types/node": "^17.0.8",
+    "@types/nprogress": "^0.2.0",
+    "@types/qs": "^6.9.7",
+    "@typescript-eslint/eslint-plugin": "^5.9.0",
+    "@typescript-eslint/parser": "^5.9.0",
     "@vitejs/plugin-vue": "^2.0.1",
     "@vitejs/plugin-vue-jsx": "^1.3.3",
-    "autoprefixer": "^10.4.1",
+    "autoprefixer": "^10.4.2",
     "commitizen": "^4.2.4",
     "eslint": "^8.6.0",
     "eslint-config-prettier": "^8.3.0",
-    "eslint-define-config": "^1.2.1",
+    "eslint-define-config": "^1.2.2",
     "eslint-plugin-prettier": "^4.0.0",
     "eslint-plugin-vue": "^8.2.0",
     "husky": "^7.0.4",
     "less": "^4.1.2",
-    "lint-staged": "^12.1.4",
+    "lint-staged": "^12.1.7",
     "postcss": "^8.4.5",
     "postcss-html": "^1.3.0",
-    "postcss-less": "^5.0.0",
+    "postcss-less": "^6.0.0",
     "prettier": "^2.5.1",
     "pretty-quick": "^3.1.3",
     "rimraf": "^3.0.2",
@@ -82,7 +85,7 @@
     "vite-plugin-style-import": "^1.4.1",
     "vite-plugin-svg-icons": "^1.1.0",
     "vite-plugin-windicss": "^1.6.1",
-    "vue-tsc": "^0.30.1",
+    "vue-tsc": "^0.30.2",
     "windicss": "^3.4.2",
     "windicss-analysis": "^0.3.5"
   },

Datei-Diff unterdrückt, da er zu groß ist
+ 76 - 498
pnpm-lock.yaml


+ 2 - 1
src/views/Login/api.ts → src/api/login/index.ts

@@ -1,7 +1,8 @@
 import { useAxios } from '@/hooks/web/useAxios'
+import type { UserLoginType } from './types'
 
 const { request } = useAxios()
 
-export const loginApi = ({ data }: AxiosConfig) => {
+export const loginApi = (data: UserLoginType) => {
   return request({ url: '/user/login', method: 'post', data })
 }

+ 4 - 0
src/api/login/types.ts

@@ -0,0 +1,4 @@
+export type UserLoginType = {
+  username: string
+  password: string
+}

+ 1 - 1
src/components/ConfigGlobal/src/ConfigGlobal.vue

@@ -16,7 +16,7 @@ provide('configGlobal', props)
 </script>
 
 <template>
-  <ElConfigProvider :locale="locale.elLocale" :size="size">
+  <ElConfigProvider :locale="locale.elLocale" :message="{ max: 1 }" :size="size">
     <slot></slot>
   </ElConfigProvider>
 </template>

+ 0 - 2
src/config/app.ts

@@ -21,7 +21,6 @@ export interface AppState {
   greyMode: boolean
   showBackTop: boolean
   showMenuTab: boolean
-  requestTime: boolean
   isDark: boolean
   size: ElememtPlusSzie
   sizeMap: ElememtPlusSzie[]
@@ -44,7 +43,6 @@ export const appModules: AppState = {
   greyMode: false, // 是否开始灰色模式,用于特殊悼念日
   showBackTop: true, // 是否显示回到顶部
   showMenuTab: false, // 是否固定一级菜单
-  requestTime: false, // 是否在接口调用时添加时间戳,避免IE缓存
   isDark: wsCache.get('isDark') || false, // 是否是暗黑模式
   size: wsCache.get('default') || 'default', // 组件尺寸
   sizeMap: ['default', 'large', 'small']

+ 1 - 1
src/config/axios.ts → src/config/axios/config.ts

@@ -6,7 +6,7 @@ const config: {
     test: string
   }
   result_code: number | string
-  default_headers: AxiosHeadersType
+  default_headers: AxiosHeaders
   request_timeout: number
 } = {
   /**

+ 1 - 4
src/plugins/axios/index.ts → src/config/axios/index.ts

@@ -10,7 +10,7 @@ import { ElMessage } from 'element-plus'
 
 import qs from 'qs'
 
-import { config } from '@/config/axios'
+import { config } from '@/config/axios/config'
 
 const { result_code, base_url } = config
 
@@ -25,7 +25,6 @@ const service: AxiosInstance = axios.create({
 // request拦截器
 service.interceptors.request.use(
   (config: AxiosRequestConfig) => {
-    console.log('我进来了吗')
     if (
       config.method === 'post' &&
       (config.headers as AxiosRequestHeaders)['Content-Type'] ===
@@ -59,7 +58,6 @@ service.interceptors.request.use(
 // response 拦截器
 service.interceptors.response.use(
   (response: AxiosResponse<Recordable>) => {
-    console.log(response)
     if (response.data.code === result_code) {
       return response.data
     } else {
@@ -67,7 +65,6 @@ service.interceptors.response.use(
     }
   },
   (error: AxiosError) => {
-    console.log(error)
     console.log('err' + error) // for debug
     ElMessage.error(error.message)
     return Promise.reject(error)

+ 3 - 7
src/hooks/web/useAxios.ts

@@ -1,12 +1,8 @@
-import { service } from '@/plugins/axios'
+import { service } from '@/config/axios'
 
 import { AxiosPromise } from 'axios'
 
-import { useAppStoreWithOut } from '@/store/modules/app'
-
-import { config } from '@/config/axios'
-
-const appStore = useAppStoreWithOut()
+import { config } from '@/config/axios/config'
 
 const { default_headers } = config
 
@@ -22,7 +18,7 @@ export function useAxios() {
     return service({
       url: url,
       method,
-      params: appStore.getRequestTime ? { time: new Date().getTime(), ...(params || {}) } : params,
+      params,
       data,
       responseType: responseType,
       headers: {

+ 32 - 0
src/hooks/web/useNProgress.ts

@@ -0,0 +1,32 @@
+import { watch, ref, nextTick, unref } from 'vue'
+import type { NProgressOptions } from 'nprogress'
+import NProgress from 'nprogress'
+import 'nprogress/nprogress.css'
+import { useCssVar } from '@vueuse/core'
+
+const primaryColor = useCssVar('--el-color-primary', document.documentElement)
+
+export function useNProgress() {
+  const isLoading = ref(false)
+  NProgress.configure({ showSpinner: false } as NProgressOptions)
+
+  watch(
+    () => isLoading.value,
+    async (loading: boolean) => {
+      loading ? NProgress.start() : NProgress.done()
+      await nextTick()
+      const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef
+      if (bar) {
+        bar.style.background = unref(primaryColor.value)
+      }
+    }
+  )
+
+  function toggle() {
+    isLoading.value = !isLoading.value
+  }
+
+  return {
+    toggle
+  }
+}

+ 25 - 0
src/hooks/web/useTitle.ts

@@ -0,0 +1,25 @@
+import { watch, ref } from 'vue'
+import { isString } from '@/utils/is'
+import { useAppStoreWithOut } from '@/store/modules/app'
+import { useI18n } from '@/hooks/web/useI18n'
+
+const appStore = useAppStoreWithOut()
+
+export function useTitle(newTitle?: string) {
+  const { t } = useI18n()
+  const title = ref(
+    newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle
+  )
+
+  watch(
+    title,
+    (n, o) => {
+      if (isString(n) && n !== o && document) {
+        document.title = n
+      }
+    },
+    { immediate: true }
+  )
+
+  return title
+}

+ 9 - 0
src/layout/Layout.vue

@@ -0,0 +1,9 @@
+<script setup lang="ts"></script>
+
+<template>
+  <section>
+    <router-view v-slot="{ Component, route }">
+      <component :is="Component" :key="route.fullPath" />
+    </router-view>
+  </section>
+</template>

+ 6 - 0
src/locales/en.ts

@@ -22,6 +22,12 @@ export default {
     remember: 'Remember me',
     forgetPassword: 'Forget password'
   },
+  router: {
+    login: 'Login'
+  },
+  mock: {
+    loginErr: 'Wrong account or password'
+  },
   formDemo: {
     input: 'Input',
     inputNumber: 'InputNumber',

+ 6 - 0
src/locales/zh-CN.ts

@@ -22,6 +22,12 @@ export default {
     remember: '记住我',
     forgetPassword: '忘记密码'
   },
+  router: {
+    login: '登录'
+  },
+  mock: {
+    loginErr: '账号或密码错误'
+  },
   formDemo: {
     input: '输入框',
     inputNumber: '数字输入框',

+ 2 - 0
src/main.ts

@@ -29,6 +29,8 @@ import { createApp } from 'vue'
 
 import App from './App.vue'
 
+import './permission'
+
 async function setupAll() {
   const app = createApp(App)
 

+ 51 - 0
src/permission.ts

@@ -0,0 +1,51 @@
+import router from './router'
+import { useAppStoreWithOut } from '@/store/modules/app'
+import { useCache } from '@/hooks/web/useCache'
+// import type { RouteRecordRaw } from 'vue-router'
+import { useTitle } from '@/hooks/web/useTitle'
+import { useNProgress } from '@/hooks/web/useNProgress'
+
+const appStore = useAppStoreWithOut()
+
+const { wsCache } = useCache()
+
+const { toggle } = useNProgress()
+
+const whiteList = ['/login'] // 不重定向白名单
+
+router.beforeEach((to, from, next) => {
+  console.log(from)
+  toggle()
+  if (wsCache.get(appStore.getUserInfo)) {
+    if (to.path === '/login') {
+      next({ path: '/' })
+    } else {
+      // if (permissionStore.getIsAddRouters) {
+      //   next()
+      //   return
+      // }
+      // permissionStore.generateRoutes().then(() => {
+      //   permissionStore.addRouters.forEach(async (route) => {
+      //     await router.addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
+      //   })
+      //   const redirectPath = from.query.redirect || to.path
+      //   const redirect = decodeURIComponent(redirectPath as string)
+      //   const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }
+      //   permissionStore.setIsAddRouters(true)
+      //   next(nextData)
+      // })
+      next()
+    }
+  } else {
+    if (whiteList.indexOf(to.path) !== -1) {
+      next()
+    } else {
+      next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
+    }
+  }
+})
+
+router.afterEach(async (to) => {
+  useTitle(to?.meta?.title as string)
+  toggle() // 结束Progress
+})

+ 22 - 2
src/router/index.ts

@@ -2,16 +2,36 @@ import { createRouter, createWebHashHistory } from 'vue-router'
 import type { RouteRecordRaw } from 'vue-router'
 import type { App } from 'vue'
 // import { getParentLayout } from './helper'
-import { t } from '@/hooks/web/useI18n'
+import { useI18n } from '@/hooks/web/useI18n'
+const { t } = useI18n()
+
+/* Layout */
+const Layout = () => import('@/layout/Layout.vue')
 
 export const constantRouterMap: AppRouteRecordRaw[] = [
+  {
+    path: '/redirect',
+    component: Layout,
+    name: 'Redirect',
+    children: [
+      {
+        path: '/redirect/:path*',
+        name: 'Redirect',
+        component: () => import('@/views/Redirect/Redirect.vue'),
+        meta: {}
+      }
+    ],
+    meta: {
+      hidden: true
+    }
+  },
   {
     path: '/login',
     component: () => import('@/views/Login/Login.vue'),
     name: 'Login',
     meta: {
       hidden: true,
-      title: t('common.login'),
+      title: t('router.login'),
       noTagsView: true
     }
   }

+ 0 - 6
src/store/modules/app.ts

@@ -58,9 +58,6 @@ export const useAppStore = defineStore({
     getShowMenuTab(): boolean {
       return this.showMenuTab
     },
-    getRequestTime(): boolean {
-      return this.requestTime
-    },
     getIsDark(): boolean {
       return this.isDark
     },
@@ -117,9 +114,6 @@ export const useAppStore = defineStore({
     setShowMenuTab(showMenuTab: boolean) {
       this.showMenuTab = showMenuTab
     },
-    setRequestTime(requestTime: boolean) {
-      this.requestTime = requestTime
-    },
     setIsDark(isDark: boolean) {
       this.isDark = isDark
       if (this.isDark) {

+ 9 - 7
src/views/Login/components/LoginForm.vue

@@ -5,7 +5,8 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { ElButton, ElCheckbox, ElLink } from 'element-plus'
 import { required } from '@/utils/formRules'
 import { useForm } from '@/hooks/web/useForm'
-import { loginApi } from '../api'
+import { loginApi } from '@/api/login'
+import type { UserLoginType } from '@/api/login/types'
 
 const { t } = useI18n()
 
@@ -87,12 +88,11 @@ async function signIn() {
   if (validate) {
     loading.value = true
     const { getFormData } = methods
-    const formData = await getFormData()
-    const res = await loginApi({
-      data: formData
-    })
+    const formData = (await getFormData()) as UserLoginType
+    const res = await loginApi(formData)
+      .catch(() => {})
+      .finally(() => (loading.value = false))
     console.log(res)
-    loading.value = false
   }
 }
 </script>
@@ -118,7 +118,9 @@ async function signIn() {
     </template>
 
     <template #login>
-      <ElButton type="primary" class="w-[100%]" @click="signIn">{{ t('login.login') }}</ElButton>
+      <ElButton :loading="loading" type="primary" class="w-[100%]" @click="signIn">
+        {{ t('login.login') }}
+      </ElButton>
     </template>
 
     <template #otherIcon>

+ 30 - 0
src/views/Redirect/Redirect.vue

@@ -0,0 +1,30 @@
+<template>
+  <div></div>
+</template>
+<script setup lang="ts">
+import { unref } from 'vue'
+import { useRouter } from 'vue-router'
+
+const { currentRoute, replace } = useRouter()
+
+const { params, query } = unref(currentRoute)
+const { path, _redirect_type = 'path' } = params
+
+Reflect.deleteProperty(params, '_redirect_type')
+Reflect.deleteProperty(params, 'path')
+
+const _path = Array.isArray(path) ? path.join('/') : path
+
+if (_redirect_type === 'name') {
+  replace({
+    name: _path,
+    query,
+    params
+  })
+} else {
+  replace({
+    path: _path.startsWith('/') ? _path : '/' + _path,
+    query
+  })
+}
+</script>

+ 11 - 7
types/global.d.ts

@@ -16,16 +16,20 @@ declare type ComponentRef<T> = InstanceType<T>
 
 declare type LocaleType = 'zh-CN' | 'en'
 
+declare type AxiosHeaders =
+  | 'application/json'
+  | 'application/x-www-form-urlencoded'
+  | 'multipart/form-data'
+
+declare type AxiosMethod = 'get' | 'post' | 'delete' | 'put'
+
+declare type AxiosResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
+
 declare type AxiosConfig = {
   params?: Recordable
   data?: Recordable
   url?: string
-  method?: 'get' | 'post' | 'delete' | 'put'
+  method?: AxiosMethod
   headersType?: string
-  responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
+  responseType?: AxiosResponseType
 }
-
-declare type AxiosHeadersType =
-  | 'application/json'
-  | 'application/x-www-form-urlencoded'
-  | 'multipart/form-data'

+ 0 - 1
vite.config.ts

@@ -22,7 +22,6 @@ function pathResolve(dir: string) {
 export default ({ command, mode }: ConfigEnv): UserConfig => {
   let env = null
   const isBuild = command === 'build'
-  console.log(isBuild)
   if (!isBuild) {
     env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root)
   } else {

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.