feat(useTitle): Add useTitle

feat(useNProgress): Add useNProgress
This commit is contained in:
kailong321200875 2022-01-09 10:57:50 +08:00
parent 3fc7d4d39a
commit c5ab3599c8
24 changed files with 1225 additions and 844 deletions

View File

@ -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"]
}

View File

@ -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

View File

@ -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"
},

File diff suppressed because it is too large Load Diff

View File

@ -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
src/api/login/types.ts Normal file
View File

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

View File

@ -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>

View File

@ -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']

View File

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

View File

@ -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)

View File

@ -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: {

View File

@ -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
src/hooks/web/useTitle.ts Normal file
View File

@ -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
src/layout/Layout.vue Normal file
View File

@ -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>

View File

@ -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',

View File

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

View File

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

51
src/permission.ts Normal file
View File

@ -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
})

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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>

View File

@ -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>

18
types/global.d.ts vendored
View File

@ -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'

View File

@ -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 {