1
0
mirror of https://github.com/zclzone/vue-naive-admin.git synced 2025-12-28 12:10:20 +08:00

9 Commits

Author SHA1 Message Date
张传龙
100b91a118 style: reset.css 2022-09-25 17:14:01 +08:00
张传龙
26b71f0ec6 perf: unocss demo 2022-09-25 17:13:35 +08:00
张传龙
92e7ada37b fix: toLogin 2022-09-24 15:40:34 +08:00
张传龙
2d879d0592 refactor: http interceptors 2022-09-24 15:27:26 +08:00
张传龙
8806a6cb43 fix: keep-alive key 2022-09-24 14:44:59 +08:00
张传龙
85a04fd06d fix: logout 2022-09-21 17:14:41 +08:00
张传龙
4a5b8dd005 fix: fix 2022-09-21 17:13:39 +08:00
张传龙
2f7da255e5 style: naive theme 2022-09-18 20:18:55 +08:00
张传龙
6664ae8f7b refactor: folders 2022-09-18 20:05:40 +08:00
49 changed files with 284 additions and 225 deletions

View File

@@ -11,7 +11,27 @@
"primaryColor": "#316C72FF",
"primaryColorHover": "#316C72E3",
"primaryColorPressed": "#2B4C59FF",
"primaryColorSuppl": "#316C7263"
"primaryColorSuppl": "#316C72E3",
"infoColor": "#2080F0FF",
"infoColorHover": "#4098FCFF",
"infoColorPressed": "#1060C9FF",
"infoColorSuppl": "#4098FCFF",
"successColor": "#18A058FF",
"successColorHover": "#36AD6AFF",
"successColorPressed": "#0C7A43FF",
"successColorSuppl": "#36AD6AFF",
"warningColor": "#F0A020FF",
"warningColorHover": "#FCB040FF",
"warningColorPressed": "#C97C10FF",
"warningColorSuppl": "#FCB040FF",
"errorColor": "#D03050FF",
"errorColorHover": "#DE576DFF",
"errorColorPressed": "#AB1F3FFF",
"errorColorSuppl": "#DE576DFF"
}
}
}

View File

@@ -1,4 +1,4 @@
import request from '@/utils/http'
import { request } from '@/utils'
export default {
getUser: () => request.get('/user'),

View File

@@ -18,7 +18,7 @@ import { defineComponent, h } from 'vue'
import { useLoadingBar, useDialog, useMessage, useNotification } from 'naive-ui'
import { useCssVar } from '@vueuse/core'
import { kebabCase } from 'lodash-es'
import { setupMessage, setupDialog } from '@/utils/common/naiveTools'
import { setupMessage, setupDialog } from '@/utils'
import { naiveThemeOverrides } from '~/settings'
function setupCssVar() {

View File

@@ -38,16 +38,16 @@ const wrapper = ref(null)
const isOverflow = ref(false)
const refreshIsOverflow = debounce(() => {
const wrapperWidth = wrapper.value.offsetWidth
const contentWidth = content.value.offsetWidth
const wrapperWidth = wrapper.value?.offsetWidth
const contentWidth = content.value?.offsetWidth
isOverflow.value = contentWidth > wrapperWidth
resetTranslateX(wrapperWidth, contentWidth)
}, 200)
function handleMouseWheel(e) {
const { wheelDelta } = e
const wrapperWidth = wrapper.value.offsetWidth
const contentWidth = content.value.offsetWidth
const wrapperWidth = wrapper.value?.offsetWidth
const contentWidth = content.value?.offsetWidth
/**
* @wheelDelta 平行滚动的值 >0 右移 <0: 左移
* @translateX 内容translateX的值

View File

@@ -1,5 +1,5 @@
<script setup>
import { renderIcon, renderCustomIcon } from '@/utils/icon'
import { renderIcon, renderCustomIcon } from '@/utils'
const props = defineProps({
icon: {

View File

@@ -10,7 +10,7 @@
</template>
<script setup>
import { isNullOrWhitespace } from '@/utils/is'
import { isNullOrWhitespace } from '@/utils'
defineProps({
label: {

View File

@@ -1,4 +1,4 @@
import { isNullOrWhitespace } from '@/utils/is'
import { isNullOrWhitespace } from '@/utils'
const ACTIONS = {
view: '查看',

View File

@@ -1,13 +1,13 @@
<template>
<router-view v-slot="{ Component, route }">
<KeepAlive :include="keepAliveRouteNames">
<component :is="Component" v-if="appStore.reloadFlag" :key="route.meta?.key || route.fullPath" />
<component :is="Component" v-if="appStore.reloadFlag" :key="appStore.aliveKeys[route.name] || route.fullPath" />
</KeepAlive>
</router-view>
</template>
<script setup>
import { useAppStore } from '@/store/modules/app'
import { useAppStore } from '@/store'
import { useRouter } from 'vue-router'
const appStore = useAppStore()
const router = useRouter()

View File

@@ -12,7 +12,7 @@
</template>
<script setup>
import { renderCustomIcon, renderIcon } from '@/utils/icon'
import { renderCustomIcon, renderIcon } from '@/utils'
const router = useRouter()
const route = useRoute()

View File

@@ -6,7 +6,7 @@
</template>
<script setup>
import { useAppStore } from '@/store/modules/app'
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>

View File

@@ -8,8 +8,8 @@
</template>
<script setup>
import { useUserStore } from '@/store/modules/user'
import { renderIcon } from '@/utils/icon'
import { useUserStore } from '@/store'
import { renderIcon } from '@/utils'
const userStore = useUserStore()

View File

@@ -8,7 +8,7 @@
</template>
<script setup>
import { useAppStore } from '@/store/modules/app'
import { useAppStore } from '@/store'
const title = import.meta.env.VITE_TITLE
const appStore = useAppStore()

View File

@@ -12,11 +12,8 @@
</template>
<script setup>
import { usePermissionStore } from '@/store/modules/permission'
import { isExternal } from '@/utils/is'
import { useAppStore } from '@/store/modules/app'
import { renderCustomIcon, renderIcon } from '@/utils/icon'
import { usePermissionStore, useAppStore } from '@/store'
import { renderCustomIcon, renderIcon, isExternal } from '@/utils'
const router = useRouter()
const curRoute = useRoute()

View File

@@ -11,9 +11,9 @@
</template>
<script setup>
import { useTagsStore } from '@/store/modules/tags'
import { renderIcon } from '@/utils/icon'
import { useAppStore } from '@/store/modules/app'
import { useTagsStore, useAppStore } from '@/store'
import { renderIcon } from '@/utils'
import { useLocalStorage } from '@vueuse/core'
const props = defineProps({
show: {
@@ -79,7 +79,7 @@ const actionMap = new Map([
() => {
if (route.meta?.keepAlive) {
// 重置keepAlive
route.meta.key = +new Date()
appStore.setAliveKeys(route.name, +new Date())
}
appStore.reloadPage()
},

View File

@@ -24,7 +24,7 @@
<script setup>
import ContextMenu from './ContextMenu.vue'
import { useTagsStore } from '@/store/modules/tags'
import { useTagsStore } from '@/store'
import ScrollX from '@/components/common/ScrollX.vue'
const route = useRoute()

View File

@@ -30,7 +30,7 @@ import AppHeader from './components/header/index.vue'
import SideBar from './components/sidebar/index.vue'
import AppMain from './components/AppMain.vue'
import AppTags from './components/tags/index.vue'
import { useAppStore } from '@/store/modules/app'
import { useAppStore } from '@/store'
import { header, tags } from '~/settings'
const appStore = useAppStore()

View File

@@ -1,6 +1,7 @@
/** 重置样式 */
import '@/styles/reset.css'
import '@/styles/index.scss'
import 'uno.css'
import '@/styles/global.scss'
import 'virtual:svg-icons-register'
import { createApp } from 'vue'

View File

@@ -1,5 +1,4 @@
import { getToken, refreshAccessToken } from '@/utils/token'
import { isNullOrWhitespace } from '@/utils/is'
import { getToken, refreshAccessToken, isNullOrWhitespace } from '@/utils'
const WHITE_LIST = ['/login', '/404']
export function createPermissionGuard(router) {

View File

@@ -1,24 +1,30 @@
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
import { setupRouterGuard } from './guard'
import { basicRoutes as routes, EMPTY_ROUTE, NOT_FOUND_ROUTE } from './routes'
import { getToken } from '@/utils/token'
import { isNullOrWhitespace } from '@/utils/is'
import { useUserStore } from '@/store/modules/user'
import { usePermissionStore } from '@/store/modules/permission'
import { basicRoutes, EMPTY_ROUTE, NOT_FOUND_ROUTE } from './routes'
import { getToken, isNullOrWhitespace } from '@/utils'
import { useUserStore, usePermissionStore } from '@/store'
const isHash = import.meta.env.VITE_USE_HASH === 'true'
export const router = createRouter({
history: isHash ? createWebHashHistory('/') : createWebHistory('/'),
routes,
routes: basicRoutes,
scrollBehavior: () => ({ left: 0, top: 0 }),
})
export async function resetRouter() {
router.getRoutes().forEach((route) => {
const { name } = route
router.hasRoute(name) && router.removeRoute(name)
})
export async function setupRouter(app) {
await addDynamicRoutes()
setupRouterGuard(router)
app.use(router)
}
export async function resetRouter() {
const basicRouteNames = getRouteNames(basicRoutes)
router.getRoutes().forEach((route) => {
const name = route.name
if (!basicRouteNames.includes(name)) {
router.removeRoute(name)
}
})
}
export async function addDynamicRoutes() {
@@ -46,8 +52,14 @@ export async function addDynamicRoutes() {
}
}
export async function setupRouter(app) {
await addDynamicRoutes()
setupRouterGuard(router)
app.use(router)
export function getRouteNames(routes) {
return routes.map((route) => getRouteName(route)).flat(1)
}
function getRouteName(route) {
const names = [route.name]
if (route.children && route.children.length) {
names.push(...route.children.map((item) => getRouteName(item)).flat(1))
}
return names
}

View File

@@ -3,3 +3,5 @@ import { createPinia } from 'pinia'
export function setupStore(app) {
app.use(createPinia())
}
export * from './modules'

View File

@@ -5,6 +5,8 @@ export const useAppStore = defineStore('app', {
return {
reloadFlag: true,
collapsed: false,
/** keepAlive路由的key重新赋值可重置keepAlive */
aliveKeys: {},
}
},
actions: {
@@ -25,5 +27,8 @@ export const useAppStore = defineStore('app', {
setCollapsed(collapsed) {
this.collapsed = collapsed
},
setAliveKeys(key, val) {
this.aliveKeys[key] = val
},
},
})

View File

@@ -0,0 +1,4 @@
export * from './app'
export * from './permission'
export * from './tags'
export * from './user'

View File

@@ -53,5 +53,8 @@ export const usePermissionStore = defineStore('permission', {
this.accessRoutes = accessRoutes
return accessRoutes
},
resetPermission() {
this.$reset()
},
},
})

View File

@@ -1,4 +1,4 @@
import { sStorage } from '@/utils/cache'
import { sStorage } from '@/utils'
export const activeTag = sStorage.get('activeTag')
export const tags = sStorage.get('tags')

View File

@@ -1,7 +1,7 @@
import { defineStore } from 'pinia'
import { activeTag, tags, WITHOUT_TAG_PATHS } from './helpers'
import { router } from '@/router'
import { sStorage } from '@/utils/cache'
import { sStorage } from '@/utils'
export const useTagsStore = defineStore('tag', {
state() {
@@ -57,5 +57,9 @@ export const useTagsStore = defineStore('tag', {
router.push(filterTags[filterTags.length - 1].path)
}
},
resetTags() {
this.setTags([])
this.setActiveTag('')
},
},
})

View File

@@ -1,6 +1,7 @@
import { defineStore } from 'pinia'
import { removeToken } from '@/utils/token'
import { toLogin } from '@/utils/auth'
import { resetRouter } from '@/router'
import { useTagsStore, usePermissionStore } from '@/store'
import { removeToken, toLogin } from '@/utils'
import api from '@/api'
export const useUserStore = defineStore('user', {
@@ -35,8 +36,13 @@ export const useUserStore = defineStore('user', {
}
},
async logout() {
const { resetTags } = useTagsStore()
const { resetPermission } = usePermissionStore()
removeToken()
this.userInfo = {}
resetTags()
resetPermission()
resetRouter()
this.$reset()
toLogin()
},
setUserInfo(userInfo = {}) {

View File

@@ -1,14 +1,16 @@
html {
font-size: 4px; // * 1rem = 4px 方便unocss计算在unocss中 1字体单位 = 0.25rem相当于 1等份 = 1px
}
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
background-color: #f2f2f2;
font-family: 'Encode Sans Condensed', sans-serif;
}
html {
font-size: 4px; // * 1rem = 4px 方便unocss计算在unocss中 1字体单位 = 0.25rem相当于 1等份 = 1px
}
body {
font-size: 16px;
}
#app {
@@ -16,7 +18,7 @@ body {
height: 100%;
}
/* router view transition fade-slide */
/* transition fade-slide */
.fade-slide-leave-active,
.fade-slide-enter-active {
transition: all 0.3s;
@@ -32,7 +34,6 @@ body {
transform: translateX(30px);
}
/* 自定义滚动条样式 */
/* 自定义滚动条样式 */
.cus-scroll {
overflow: auto;

View File

@@ -12,7 +12,7 @@ html {
a {
text-decoration: none;
color: #333;
color: inherit;
}
a:hover,
@@ -33,8 +33,3 @@ textarea {
border: none;
resize: none;
}
body {
font-size: 14px;
font-weight: 400;
}

View File

@@ -1,14 +0,0 @@
import { router } from '@/router'
export function toLogin() {
router.replace({
path: '/login',
query: { ...router.currentRoute.value.query, redirect: router.currentRoute.value.path },
})
}
export function toFourZeroFour() {
router.replace({
path: '/404',
})
}

16
src/utils/auth/auth.js Normal file
View File

@@ -0,0 +1,16 @@
import { router } from '@/router'
export function toLogin() {
const currentRoute = unref(router.currentRoute)
const needRedirect = !currentRoute.meta.requireAuth && !['/404', '/login'].includes(router.currentRoute.value.path)
router.replace({
path: '/login',
query: needRedirect ? { ...currentRoute.query, redirect: currentRoute.path } : {},
})
}
export function toFourZeroFour() {
router.replace({
path: '/404',
})
}

2
src/utils/auth/index.js Normal file
View File

@@ -0,0 +1,2 @@
export * from './auth'
export * from './token'

View File

@@ -1,4 +1,4 @@
import { lStorage } from './cache'
import { lStorage } from '@/utils'
import api from '@/api'
const TOKEN_CODE = 'access_token'

View File

@@ -0,0 +1,76 @@
import dayjs from 'dayjs'
/**
* @desc 格式化时间
* @param {(Object|string|number)} time
* @param {string} format
* @returns {string | null}
*/
export function formatDateTime(time = undefined, format = 'YYYY-MM-DD HH:mm:ss') {
return dayjs(time).format(format)
}
export function formatDate(date = undefined, format = 'YYYY-MM-DD') {
return formatDateTime(date, format)
}
/**
* @desc 函数节流
* @param {Function} fn
* @param {Number} wait
* @returns {Function}
*/
export function throttle(fn, wait) {
var context, args
var previous = 0
return function () {
var now = +new Date()
context = this
args = arguments
if (now - previous > wait) {
fn.apply(context, args)
previous = now
}
}
}
/**
* @desc 函数防抖
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(method, wait, immediate) {
let timeout
return function (...args) {
let context = this
if (timeout) {
clearTimeout(timeout)
}
// 立即执行需要两个条件一是immediate为true二是timeout未被赋值或被置为null
if (immediate) {
/**
* 如果定时器不存在则立即执行并设置一个定时器wait毫秒后将定时器置为null
* 这样确保立即执行后wait毫秒内不会被再次触发
*/
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) {
method.apply(context, args)
}
} else {
// 如果immediate为false则函数wait毫秒后执行
timeout = setTimeout(() => {
/**
* args是一个类数组对象所以使用fn.apply
* 也可写作method.call(context, ...args)
*/
method.apply(context, args)
}, wait)
}
}
}

View File

@@ -0,0 +1,4 @@
export * from './common'
export * from './is'
export * from './icon'
export * from './naiveTools'

View File

@@ -1,4 +1,4 @@
import { isNullOrUndef } from '@/utils/is'
import { isNullOrUndef } from '@/utils'
export function setupMessage(NMessage) {
let loadingMessage = null

View File

@@ -1,7 +1,4 @@
import { useUserStore } from '@/store/modules/user'
import { isNullOrUndef } from '@/utils/is'
import { removeToken } from '@/utils/token'
import { toLogin } from '@/utils/auth'
import { useUserStore } from '@/store'
export function addBaseParams(params) {
if (!params.userId) {
@@ -9,21 +6,14 @@ export function addBaseParams(params) {
}
}
export function resolveResError(error = {}) {
let { code, message } = error
if (isNullOrUndef(code)) {
// 未知错误
code = -1
message = message ?? '接口未知异常!'
} else {
export function resolveResError(code, message) {
switch (code) {
case 400:
message = message ?? '请求参数错误'
break
case 401:
message = message ?? '登录已过期'
removeToken()
toLogin()
useUserStore().logout()
break
case 403:
message = message ?? '没有权限'
@@ -35,9 +25,8 @@ export function resolveResError(error = {}) {
message = message ?? '服务器异常'
break
default:
message = message ?? '操作异常!'
message = message ?? `${code}】: 未知异常!`
break
}
}
return { code, message }
return message
}

View File

@@ -1,5 +1,5 @@
import axios from 'axios'
import { repReject, repResolve, reqReject, reqResolve } from './interceptors'
import { resReject, resResolve, reqReject, reqResolve } from './interceptors'
export function createAxios(options = {}) {
const defaultOptions = {
@@ -10,10 +10,10 @@ export function createAxios(options = {}) {
...options,
})
service.interceptors.request.use(reqResolve, reqReject)
service.interceptors.response.use(repResolve, repReject)
service.interceptors.response.use(resResolve, resReject)
return service
}
export default createAxios({
export const request = createAxios({
baseURL: import.meta.env.VITE_BASE_API,
})

View File

@@ -1,5 +1,4 @@
import { getToken } from '@/utils/token'
import { toLogin } from '@/utils/auth'
import { getToken } from '@/utils'
import { resolveResError } from './helpers'
export function reqResolve(config) {
@@ -10,9 +9,7 @@ export function reqResolve(config) {
const token = getToken()
if (!token) {
// * 未登录或者token过期的情况下,跳转登录页重新登录
toLogin()
return Promise.reject({ code: '-1', message: '未登录' })
return Promise.reject({ code: 401, message: '登录已过期,请重新登录!' })
}
/**
@@ -28,19 +25,34 @@ export function reqReject(error) {
return Promise.reject(error)
}
export function repResolve(response) {
const { noNeedTip } = response.config
if (response.data?.code !== 0) {
const { code, message } = resolveResError(response?.data)
!noNeedTip && $message.error(message)
return Promise.reject({ code, message, error: response?.data })
export function resResolve(response) {
// TODO: 处理不同的 response.headers
const { data, status, config, statusText } = response
if (data?.code !== 0) {
const code = data?.code ?? status
/** 根据code处理对应的操作并返回处理后的message */
const message = resolveResError(code, data?.message ?? statusText)
/** 需要错误提醒 */
!config.noNeedTip && $message.error(message)
return Promise.reject({ code, message, error: data || response })
}
return Promise.resolve(response?.data)
return Promise.resolve(data)
}
export function repReject(error) {
const { noNeedTip } = error.response?.config || error.config
const { code, message } = resolveResError(error.response?.data)
!noNeedTip && $message.error(message)
export function resReject(error) {
if (!error || !error.response) {
const code = error?.code
/** 根据code处理对应的操作并返回处理后的message */
const message = resolveResError(code, error.message)
$message?.error(message)
return Promise.reject({ code, message, error })
}
const { data, status, config } = error.response
const code = data?.code ?? status
const message = resolveResError(code, data?.message ?? error.message)
/** 需要错误提醒 */
!config?.noNeedTip && $message.error(message)
return Promise.reject({ code, message, error: error.response?.data || error.response })
}

View File

@@ -1,76 +1,4 @@
import dayjs from 'dayjs'
/**
* @desc 格式化时间
* @param {(Object|string|number)} time
* @param {string} format
* @returns {string | null}
*/
export function formatDateTime(time = undefined, format = 'YYYY-MM-DD HH:mm:ss') {
return dayjs(time).format(format)
}
export function formatDate(date = undefined, format = 'YYYY-MM-DD') {
return formatDateTime(date, format)
}
/**
* @desc 函数节流
* @param {Function} fn
* @param {Number} wait
* @returns {Function}
*/
export function throttle(fn, wait) {
var context, args
var previous = 0
return function () {
var now = +new Date()
context = this
args = arguments
if (now - previous > wait) {
fn.apply(context, args)
previous = now
}
}
}
/**
* @desc 函数防抖
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(method, wait, immediate) {
let timeout
return function (...args) {
let context = this
if (timeout) {
clearTimeout(timeout)
}
// 立即执行需要两个条件一是immediate为true二是timeout未被赋值或被置为null
if (immediate) {
/**
* 如果定时器不存在则立即执行并设置一个定时器wait毫秒后将定时器置为null
* 这样确保立即执行后wait毫秒内不会被再次触发
*/
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) {
method.apply(context, args)
}
} else {
// 如果immediate为false则函数wait毫秒后执行
timeout = setTimeout(() => {
/**
* args是一个类数组对象所以使用fn.apply
* 也可写作method.call(context, ...args)
*/
method.apply(context, args)
}, wait)
}
}
}
export * from './common'
export * from './storage'
export * from './http'
export * from './auth'

View File

@@ -1,4 +1,4 @@
import { isNullOrUndef } from '@/utils/is'
import { isNullOrUndef } from '@/utils'
class Storage {
constructor(option) {

View File

@@ -10,8 +10,8 @@
</a>
</p>
<div f-c-c mt-20 w-350 rounded-10 b-1 bc-ccc>
<div flex w-360 flex-wrap justify-around p-10>
<div f-c-c flex-col mt-20 w-350>
<div flex flex-wrap justify-around p-10 rounded-10 b-1 bc-ccc>
<div w-50 h-50 b-1 rounded-5 f-c-c p-10 m-20>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
@@ -34,12 +34,12 @@
<span w-6 h-6 rounded-3 bg-black></span>
</div>
</div>
<div w-50 h-50 b-1 rounded-5 f-c-c flex-col p-10 m-20>
<div w-50 h-50 b-1 rounded-5 flex-col justify-between items-center p-10 m-20>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<span w-6 h-6 rounded-3 bg-black></span>
<div w-6 h-6 rounded-3 bg-black></div>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
@@ -60,7 +60,7 @@
</div>
</div>
</div>
<h2 font-normal text-14 mt-10 color-gray>Flex 骰子</h2>
</div>
<h2 font-normal text-14 text-center w-350 mt-10 color-gray>Flex 骰子</h2>
</CommonPage>
</template>

View File

@@ -1,4 +1,4 @@
import request from '@/utils/http'
import { request } from '@/utils'
export default {
getPosts: (params = {}) => request.get('posts', { params }),

View File

@@ -82,11 +82,9 @@
<script setup>
import { NButton, NSwitch } from 'naive-ui'
import { formatDateTime } from '@/utils'
import { renderIcon } from '@/utils/icon'
import { formatDateTime, renderIcon, isNullOrUndef } from '@/utils'
import { useCRUD } from '@/composables'
import api from './api'
import { isNullOrUndef } from '@/utils/is'
defineOptions({ name: 'CrudTable' })

View File

@@ -1,4 +1,4 @@
import request from '@/utils/http'
import { request } from '@/utils'
export default {
login: (data) => request.post('/auth/login', data, { noNeedToken: true }),

View File

@@ -46,8 +46,7 @@
</template>
<script setup>
import { lStorage } from '@/utils/cache'
import { setToken } from '@/utils/token'
import { lStorage, setToken } from '@/utils'
import { useStorage } from '@vueuse/core'
import bgImg from '@/assets/images/login_bg.webp'
import api from './api'

View File

@@ -52,7 +52,7 @@
</template>
<script setup>
import { useUserStore } from '@/store/modules/user'
import { useUserStore } from '@/store'
const userStore = useUserStore()
</script>