This commit is contained in:
jxxghp
2023-06-24 20:25:03 +08:00
parent d637d6cbc6
commit 5e3fd33599
11 changed files with 208 additions and 126 deletions
+4 -4
View File
@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ThemeSwitcherTheme } from '@layouts/types'; import { ref, watch } from 'vue'
import { ref, watch } from 'vue'; import { useTheme } from 'vuetify'
import { useTheme } from 'vuetify'; import type { ThemeSwitcherTheme } from '@layouts/types'
const props = defineProps<{ const props = defineProps<{
themes: ThemeSwitcherTheme[] themes: ThemeSwitcherTheme[]
@@ -15,6 +15,7 @@ const { state: currentThemeName, next: getNextThemeName, index: currentThemeInde
const changeTheme = () => { const changeTheme = () => {
const nextTheme = getNextThemeName() const nextTheme = getNextThemeName()
globalTheme.name.value = nextTheme globalTheme.name.value = nextTheme
savedTheme.value = nextTheme savedTheme.value = nextTheme
localStorage.setItem('theme', nextTheme) localStorage.setItem('theme', nextTheme)
@@ -29,7 +30,6 @@ watch(() => globalTheme.name.value, val => {
onMounted(() => { onMounted(() => {
globalTheme.name.value = savedTheme.value globalTheme.name.value = savedTheme.value
}) })
</script> </script>
<template> <template>
+1 -1
View File
@@ -1,8 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import logo from '@images/logo.svg?raw'
import type { Component } from 'vue' import type { Component } from 'vue'
import { PerfectScrollbar } from 'vue3-perfect-scrollbar' import { PerfectScrollbar } from 'vue3-perfect-scrollbar'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import logo from '@images/logo.svg?raw'
interface Props { interface Props {
tag?: string | Component tag?: string | Component
+6 -7
View File
@@ -1,6 +1,5 @@
import router from '@/router'
import axios from 'axios' import axios from 'axios'
import router from '@/router'
// 创建axios实例 // 创建axios实例
const api = axios.create({ const api = axios.create({
@@ -12,9 +11,8 @@ api.interceptors.request.use(config => {
const token = localStorage.getItem('token') const token = localStorage.getItem('token')
// 在请求头中添加token // 在请求头中添加token
if (token) { if (token)
config.headers['Authorization'] = `Bearer ${token}` config.headers.Authorization = `Bearer ${token}`
}
return config return config
}) })
@@ -23,10 +21,11 @@ api.interceptors.request.use(config => {
api.interceptors.response.use(response => { api.interceptors.response.use(response => {
return response.data return response.data
}, error => { }, error => {
if (! error.response) { if (!error.response) {
// 请求超时 // 请求超时
return Promise.reject(error) return Promise.reject(error)
} else if (error.response.status === 403) { }
else if (error.response.status === 403) {
// token验证失败,跳转到登录页面 // token验证失败,跳转到登录页面
router.push('/login') router.push('/login')
} }
@@ -1,9 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useTheme } from 'vuetify'
import VerticalNavSectionTitle from '@/@layouts/components/VerticalNavSectionTitle.vue' import VerticalNavSectionTitle from '@/@layouts/components/VerticalNavSectionTitle.vue'
import upgradeBannerDark from '@images/pro/upgrade-banner-dark.png'
import upgradeBannerLight from '@images/pro/upgrade-banner-light.png'
import VerticalNavLayout from '@layouts/components/VerticalNavLayout.vue' import VerticalNavLayout from '@layouts/components/VerticalNavLayout.vue'
import VerticalNavLink from '@layouts/components/VerticalNavLink.vue' import VerticalNavLink from '@layouts/components/VerticalNavLink.vue'
@@ -13,12 +9,6 @@ import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
import UserProfile from '@/layouts/components/UserProfile.vue' import UserProfile from '@/layouts/components/UserProfile.vue'
// Banner // Banner
const vuetifyTheme = useTheme()
const upgradeBanner = computed(() => {
return vuetifyTheme.global.name.value === 'light' ? upgradeBannerLight : upgradeBannerDark
})
</script> </script>
<template> <template>
+50 -14
View File
@@ -1,24 +1,40 @@
<script setup lang="ts"> <script setup lang="ts">
import router from "@/router"; import router from '@/router'
import avatar1 from "@images/avatars/avatar-1.png"; import avatar1 from '@images/avatars/avatar-1.png'
// 执行注销操作 // 执行注销操作
const logout = () => { const logout = () => {
// 清除登录状态信息,例如删除令牌 // 清除登录状态信息,例如删除令牌
localStorage.removeItem("token"); localStorage.removeItem('token')
// 重定向到登录页面或其他适当的页面 // 重定向到登录页面或其他适当的页面
router.push("/login"); router.push('/login')
}; }
</script> </script>
<template> <template>
<VBadge dot location="bottom right" offset-x="3" offset-y="3" color="success" bordered> <VBadge
<VAvatar class="cursor-pointer" color="primary" variant="tonal"> dot
location="bottom right"
offset-x="3"
offset-y="3"
color="success"
bordered
>
<VAvatar
class="cursor-pointer"
color="primary"
variant="tonal"
>
<VImg :src="avatar1" /> <VImg :src="avatar1" />
<!-- SECTION Menu --> <!-- SECTION Menu -->
<VMenu activator="parent" width="230" location="bottom end" offset="14px"> <VMenu
activator="parent"
width="230"
location="bottom end"
offset="14px"
>
<VList> <VList>
<!-- 👉 User Avatar & Name --> <!-- 👉 User Avatar & Name -->
<VListItem> <VListItem>
@@ -31,22 +47,34 @@ const logout = () => {
offset-y="3" offset-y="3"
color="success" color="success"
> >
<VAvatar color="primary" variant="tonal"> <VAvatar
color="primary"
variant="tonal"
>
<VImg :src="avatar1" /> <VImg :src="avatar1" />
</VAvatar> </VAvatar>
</VBadge> </VBadge>
</VListItemAction> </VListItemAction>
</template> </template>
<VListItemTitle class="font-weight-semibold"> 管理员 </VListItemTitle> <VListItemTitle class="font-weight-semibold">
管理员
</VListItemTitle>
<VListItemSubtitle>Admin</VListItemSubtitle> <VListItemSubtitle>Admin</VListItemSubtitle>
</VListItem> </VListItem>
<VDivider class="my-2" /> <VDivider class="my-2" />
<!-- 👉 Profile --> <!-- 👉 Profile -->
<VListItem link to="account-settings"> <VListItem
link
to="account-settings"
>
<template #prepend> <template #prepend>
<VIcon class="me-2" icon="mdi-account-outline" size="22" /> <VIcon
class="me-2"
icon="mdi-account-outline"
size="22"
/>
</template> </template>
<VListItemTitle>个人中心</VListItemTitle> <VListItemTitle>个人中心</VListItemTitle>
@@ -55,7 +83,11 @@ const logout = () => {
<!-- 👉 FAQ --> <!-- 👉 FAQ -->
<VListItem link> <VListItem link>
<template #prepend> <template #prepend>
<VIcon class="me-2" icon="mdi-help-circle-outline" size="22" /> <VIcon
class="me-2"
icon="mdi-help-circle-outline"
size="22"
/>
</template> </template>
<VListItemTitle>帮助</VListItemTitle> <VListItemTitle>帮助</VListItemTitle>
@@ -67,7 +99,11 @@ const logout = () => {
<!-- 👉 Logout --> <!-- 👉 Logout -->
<VListItem @click="logout"> <VListItem @click="logout">
<template #prepend> <template #prepend>
<VIcon class="me-2" icon="mdi-logout" size="22" /> <VIcon
class="me-2"
icon="mdi-logout"
size="22"
/>
</template> </template>
<VListItemTitle>注销</VListItemTitle> <VListItemTitle>注销</VListItemTitle>
+29 -12
View File
@@ -1,14 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import misc404 from "@images/pages/404.png"; import { useTheme } from 'vuetify'
import miscMaskDark from "@images/pages/misc-mask-dark.png"; import misc404 from '@images/pages/404.png'
import miscMaskLight from "@images/pages/misc-mask-light.png"; import miscMaskDark from '@images/pages/misc-mask-dark.png'
import tree from "@images/pages/tree.png"; import miscMaskLight from '@images/pages/misc-mask-light.png'
import { useTheme } from "vuetify"; import tree from '@images/pages/tree.png'
const vuetifyTheme = useTheme()
const vuetifyTheme = useTheme();
const authThemeMask = computed(() => { const authThemeMask = computed(() => {
return vuetifyTheme.global.name.value === "light" ? miscMaskLight : miscMaskDark; return vuetifyTheme.global.name.value === 'light' ? miscMaskLight : miscMaskDark
}); })
</script> </script>
<template> <template>
@@ -21,14 +22,30 @@ const authThemeMask = computed(() => {
<!-- 👉 Image --> <!-- 👉 Image -->
<div class="misc-avatar w-100 text-center"> <div class="misc-avatar w-100 text-center">
<VImg :src="misc404" alt="Coming Soon" :max-width="800" class="mx-auto" /> <VImg
<VBtn to="/" class="mt-10"> 返回 </VBtn> :src="misc404"
alt="Coming Soon"
:max-width="800"
class="mx-auto"
/>
<VBtn
to="/"
class="mt-10"
>
返回
</VBtn>
</div> </div>
<!-- 👉 Footer --> <!-- 👉 Footer -->
<VImg :src="tree" class="misc-footer-tree d-none d-md-block" /> <VImg
:src="tree"
class="misc-footer-tree d-none d-md-block"
/>
<VImg :src="authThemeMask" class="misc-footer-img d-none d-md-block" /> <VImg
:src="authThemeMask"
class="misc-footer-img d-none d-md-block"
/>
</div> </div>
</template> </template>
+10 -10
View File
@@ -1,14 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import AnalyticsAward from '@/views/dashboard/AnalyticsAward.vue'; import AnalyticsAward from '@/views/dashboard/AnalyticsAward.vue'
import AnalyticsBarCharts from '@/views/dashboard/AnalyticsBarCharts.vue'; import AnalyticsBarCharts from '@/views/dashboard/AnalyticsBarCharts.vue'
import AnalyticsDepositWithdraw from '@/views/dashboard/AnalyticsDepositWithdraw.vue'; import AnalyticsDepositWithdraw from '@/views/dashboard/AnalyticsDepositWithdraw.vue'
import AnalyticsSalesByCountries from '@/views/dashboard/AnalyticsSalesByCountries.vue'; import AnalyticsSalesByCountries from '@/views/dashboard/AnalyticsSalesByCountries.vue'
import AnalyticsTotalEarning from '@/views/dashboard/AnalyticsTotalEarning.vue'; import AnalyticsTotalEarning from '@/views/dashboard/AnalyticsTotalEarning.vue'
import AnalyticsTotalProfitLineCharts from '@/views/dashboard/AnalyticsTotalProfitLineCharts.vue'; import AnalyticsTotalProfitLineCharts from '@/views/dashboard/AnalyticsTotalProfitLineCharts.vue'
import AnalyticsTransactions from '@/views/dashboard/AnalyticsTransactions.vue'; import AnalyticsTransactions from '@/views/dashboard/AnalyticsTransactions.vue'
import AnalyticsUserTable from '@/views/dashboard/AnalyticsUserTable.vue'; import AnalyticsUserTable from '@/views/dashboard/AnalyticsUserTable.vue'
import AnalyticsWeeklyOverview from '@/views/dashboard/AnalyticsWeeklyOverview.vue'; import AnalyticsWeeklyOverview from '@/views/dashboard/AnalyticsWeeklyOverview.vue'
import CardStatisticsVertical from '@core/components/cards/CardStatisticsVertical.vue'; import CardStatisticsVertical from '@core/components/cards/CardStatisticsVertical.vue'
const totalProfit = { const totalProfit = {
title: 'Total Profit', title: 'Total Profit',
+99 -59
View File
@@ -1,90 +1,104 @@
<script setup lang="ts"> <script setup lang="ts">
import { useTheme } from "vuetify"; import { useTheme } from 'vuetify'
import api from "@/api"; import api from '@/api'
import router from "@/router"; import router from '@/router'
import logo from "@images/logo.svg?raw"; import logo from '@images/logo.svg?raw'
import authV1MaskDark from "@images/pages/auth-v1-mask-dark.png"; import authV1MaskDark from '@images/pages/auth-v1-mask-dark.png'
import authV1MaskLight from "@images/pages/auth-v1-mask-light.png"; import authV1MaskLight from '@images/pages/auth-v1-mask-light.png'
import authV1Tree2 from "@images/pages/auth-v1-tree-2.png"; import authV1Tree2 from '@images/pages/auth-v1-tree-2.png'
import authV1Tree from "@images/pages/auth-v1-tree.png"; import authV1Tree from '@images/pages/auth-v1-tree.png'
const form = ref({ const form = ref({
username: "", username: '',
password: "", password: '',
remember: true, remember: true,
}); })
const vuetifyTheme = useTheme(); const vuetifyTheme = useTheme()
const authThemeMask = computed(() => { const authThemeMask = computed(() => {
return vuetifyTheme.global.name.value === "light" ? authV1MaskLight : authV1MaskDark; return vuetifyTheme.global.name.value === 'light' ? authV1MaskLight : authV1MaskDark
}); })
const isPasswordVisible = ref(false); const isPasswordVisible = ref(false)
const errorMessage = ref(""); const errorMessage = ref('')
// 登录获取token事件 // 登录获取token事件
const login = () => { const login = () => {
errorMessage.value = ""; errorMessage.value = ''
if (!form.value.username || !form.value.password) { if (!form.value.username || !form.value.password) {
errorMessage.value = "请输入用户名和密码"; errorMessage.value = '请输入用户名和密码'
return;
return
} }
// 用户名密码 // 用户名密码
const formData = new FormData(); const formData = new FormData()
formData.append("username", form.value.username);
formData.append("password", form.value.password); formData.append('username', form.value.username)
formData.append('password', form.value.password)
// 请求token // 请求token
api api
.post("/login/access-token", formData, { .post('/login/access-token', formData, {
headers: { headers: {
Accept: "application/json", // 设置 Accept 类型 Accept: 'application/json', // 设置 Accept 类型
}, },
}) })
.then((response: any) => { .then((response: any) => {
// 获取token // 获取token
const token = response.access_token; const token = response.access_token
// 将token保存在本地存储中,用于后续请求 // 将token保存在本地存储中,用于后续请求
localStorage.setItem("token", token); localStorage.setItem('token', token)
//保存保持登录状态
localStorage.setItem("remember", form.value.remember.toString()); // 保存保持登录状态
localStorage.setItem('remember', form.value.remember.toString())
// 跳转到首页 // 跳转到首页
router.push("/"); router.push('/')
}) })
.catch((error: any) => { .catch((error: any) => {
// 登录失败,显示错误提示 // 登录失败,显示错误提示
if (!error.response) { if (!error.response)
errorMessage.value = "登录失败,请检查网络连接"; errorMessage.value = '登录失败,请检查网络连接'
} else if (error.response.status === 401) {
errorMessage.value = "登录失败,请检查用户名和密码是否正确"; else if (error.response.status === 401)
} else if (error.response.status === 403) { errorMessage.value = '登录失败,请检查用户名和密码是否正确'
errorMessage.value = "登录失败,您没有权限访问";
} else if (error.response.status === 500) { else if (error.response.status === 403)
errorMessage.value = "登录失败,服务器错误"; errorMessage.value = '登录失败,您没有权限访问'
} else {
errorMessage.value = `登录失败 ${error.response.status},请检查用户名和密码是否正确`; else if (error.response.status === 500)
} errorMessage.value = '登录失败,服务器错误'
});
}; else
errorMessage.value = `登录失败 ${error.response.status},请检查用户名和密码是否正确`
})
}
// 自动登录 // 自动登录
onMounted(() => { onMounted(() => {
// 获取token // 获取token
const token = localStorage.getItem("token"); const token = localStorage.getItem('token')
// 获取保持登录状态
const remember = localStorage.getItem("remember");
// 如果token存在,且保持登录状态为true,则跳转到首页
if (token && remember === "true") {
router.push("/");
}
});
// 获取保持登录状态
const remember = localStorage.getItem('remember')
// 如果token存在,且保持登录状态为true,则跳转到首页
if (token && remember === 'true')
router.push('/')
})
</script> </script>
<template> <template>
<div class="auth-wrapper d-flex align-center justify-center pa-4"> <div class="auth-wrapper d-flex align-center justify-center pa-4">
<VCard class="auth-card pa-4 pt-7" max-width="448" min-width="448"> <VCard
class="auth-card pa-4 pt-7"
max-width="448"
min-width="448"
>
<VCardItem class="justify-center"> <VCardItem class="justify-center">
<template #prepend> <template #prepend>
<div class="d-flex"> <div class="d-flex">
@@ -98,8 +112,12 @@ onMounted(() => {
</VCardItem> </VCardItem>
<VCardText class="pt-2"> <VCardText class="pt-2">
<h5 class="text-h5 font-weight-semibold mb-1">欢迎使用 MoviePilot! 👋🏻</h5> <h5 class="text-h5 font-weight-semibold mb-1">
<p class="mb-0">请输入用户名密码登录</p> 欢迎使用 MoviePilot! 👋🏻
</h5>
<p class="mb-0">
请输入用户名密码登录
</p>
</VCardText> </VCardText>
<VCardText> <VCardText>
@@ -107,7 +125,12 @@ onMounted(() => {
<VRow> <VRow>
<!-- username --> <!-- username -->
<VCol cols="12"> <VCol cols="12">
<VTextField v-model="form.username" label="用户名" type="text" required /> <VTextField
v-model="form.username"
label="用户名"
type="text"
required
/>
</VCol> </VCol>
<!-- password --> <!-- password -->
@@ -119,19 +142,33 @@ onMounted(() => {
:append-inner-icon=" :append-inner-icon="
isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline' isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
" "
@click:append-inner="isPasswordVisible = !isPasswordVisible"
required required
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/> />
<div v-if="errorMessage" class="text-error mt-1">{{ errorMessage }}</div> <div
v-if="errorMessage"
class="text-error mt-1"
>
{{ errorMessage }}
</div>
<!-- remember me checkbox --> <!-- remember me checkbox -->
<div class="d-flex align-center justify-space-between flex-wrap mt-1 mb-4"> <div class="d-flex align-center justify-space-between flex-wrap mt-1 mb-4">
<VCheckbox v-model="form.remember" label="保持登录" required /> <VCheckbox
v-model="form.remember"
label="保持登录"
required
/>
</div> </div>
<!-- login button --> <!-- login button -->
<VBtn block type="submit"> 登录 </VBtn> <VBtn
block
type="submit"
>
登录
</VBtn>
</VCol> </VCol>
</VRow> </VRow>
</VForm> </VForm>
@@ -151,7 +188,10 @@ onMounted(() => {
/> />
<!-- bg img --> <!-- bg img -->
<VImg class="auth-footer-mask d-none d-md-block" :src="authThemeMask" /> <VImg
class="auth-footer-mask d-none d-md-block"
:src="authThemeMask"
/>
</div> </div>
</template> </template>
+3 -3
View File
@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import CardBasic from '@/views/pages/cards/card-basic/CardBasic.vue'; import CardBasic from '@/views/pages/cards/card-basic/CardBasic.vue'
import CardNavigation from '@/views/pages/cards/card-basic/CardNavigation.vue'; import CardNavigation from '@/views/pages/cards/card-basic/CardNavigation.vue'
import CardSolid from '@/views/pages/cards/card-basic/CardSolid.vue'; import CardSolid from '@/views/pages/cards/card-basic/CardSolid.vue'
</script> </script>
<template> <template>
+1 -1
View File
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import CardSolid from '@/views/pages/cards/card-basic/CardSolid.vue'; import CardSolid from '@/views/pages/cards/card-basic/CardSolid.vue'
</script> </script>
<template> <template>
+1 -1
View File
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import CardSolid from '@/views/pages/cards/card-basic/CardSolid.vue'; import CardSolid from '@/views/pages/cards/card-basic/CardSolid.vue'
</script> </script>
<template> <template>