feat: change theme and index style

This commit is contained in:
廖庆斯
2024-11-20 00:09:25 +08:00
parent 6aaf607ed7
commit 9a97a1ee72
19 changed files with 5190 additions and 14633 deletions

18923
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@openai/realtime-api-beta": "github:openai/openai-realtime-api-beta",
"animate.css": "^4.1.1",
"axios": "^0.27.2",
"clipboard": "^2.0.11",
"compressorjs": "^1.2.1",

View File

@@ -1,18 +1,19 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
<html lang="zh-cn" data-theme="light">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,user-scalable=no"
/>
<title>Geek-AI 创作助手</title>
</head>
<body>
<noscript>
<strong>请开启JavaScript支持</strong>
</noscript>
<div id="app"></div>
</body>
</head>
<body>
<noscript>
<strong>请开启JavaScript支持</strong>
</noscript>
<div id="app"></div>
</body>
</html>

View File

@@ -0,0 +1,9 @@
.btn-go{
background: var(--btnColor);
color: #fff;
border-radius: 5px;
padding: 5px 10px;
&:hover{
color: #fff;
}
}

View File

@@ -0,0 +1,24 @@
@font-face {
font-family: "OPlusSans3-Regular";
src: url("../fonts/OPlusSans3-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "OPlusSans3-Medium";
src: url("../fonts/OPlusSans3-Medium.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
$font-regular = "OPlusSans3-Regular", "PingFangSC-Regular", "Roboto", "sans-serif";
$font-medium = "OPlusSans3-Medium", "PingFangSC-Medium", "Roboto", "sans-serif";
.font-regular {
font-family: $font-regular;
font-weight: normal;
}
.font-medium {
font-family: $font-medium;
font-weight: 500;
}

View File

@@ -1,7 +1,8 @@
.index-page {
margin: 0
overflow hidden
color #ffffff
color var(--text-color)
display flex
justify-content center
align-items baseline
@@ -47,11 +48,20 @@
display flex
padding 20px 0
color #ffffff
color var(--text-color);
.iconfont{
color var(--text-color);
font-size: 28px;
}
.icon-book{
margin-right: 6px;
}
.title {
font-size 24px
color var(--text-color);
font-size: 24px;
padding 10px 10px 0 10px
font-weight: 700;
}
.logo {
@@ -112,6 +122,34 @@
}
}
}
.nav-item-box{
width: 100%;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.645,0.045,0.355,1);
aspect-ratio: 1.1028 / 1;
background: var( --card-bg)
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 160px
// min-height: 190px;
i{
display: inline-block
min-width: 48px;
width: 48px;
height: 48px;
font-size: 38px
border-radius: 24px;
color: var(--normal-color)
}
&:hover{
box-shadow: 0 4px 14px 0 rgba(17, 13, 83, .18);
transform: translateY(-8px);}
}
}
}
@@ -122,3 +160,37 @@
}
}
.cursor-ani {
position: relative;
}
.cursor-ani::after {
content: '';
position: absolute;
width: 1px;
height: 28px;
background: #333;
transform: translateX(3px) translateY(3px);
animation: cursor-blinks 0.8s infinite forwards;
}
@keyframes cursor-blinks {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.cursor-ani {
display: inline-block;
min-height: 34px;
font-size: 1.5em;
}
.msg-text span {
transition: color 0.3s ease; /* */
font-weight: bold;
}

View File

@@ -0,0 +1,21 @@
@import 'font.styl'
:root[data-theme="dark"]{
--text-color: rgba(255, 255, 255, 1) !important; //
--normal-color: rgba(163, 174, 208, 1); //
p, h1, h2, h3, h4, h5, h6, article {
color: var(--text-color) !important;
font-family: $font-regular;
}
html,
body,
#app,
.wrapper {
background-color: rgb(13, 20, 53) !important;
font-family: $font-regular;
}
--btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce);
--card-bg: rgba(17, 28, 68, 1);
}

View File

@@ -0,0 +1,26 @@
@import 'font.styl'
:root[data-theme="light"] {
// rgba(43, 54, 116, 1)
--text-color: #5b62ce; //
--normal-color: rgba(43, 54, 116, 1); //
p, h1, h2, h3, h4, h5, h6, article {
color: var(--text-color) !important;
font-family: $font-regular;
}
html,
body,
#app,
.wrapper {
background: linear-gradient(88deg, #fff3f3 1.44%, #e7e8ff);
// background: linear-gradient(180deg, #fff, #fff4fa 40%, #fbd9fd 55%, #e2d5ff 70%);
font-family: $font-regular;
}
--btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce);
--card-bg:#fff;
}

View File

Binary file not shown.

Binary file not shown.

View File

@@ -1,8 +1,9 @@
@font-face {
font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1731289567907') format('woff2'),
url('iconfont.woff?t=1731289567907') format('woff'),
url('iconfont.ttf?t=1731289567907') format('truetype');
font-family: 'iconfont'; /* Project id 4125778 */
src: url('//at.alicdn.com/t/c/font_4125778_gs96jfl3hlc.woff2?t=1732009095144') format('woff2'),
url('//at.alicdn.com/t/c/font_4125778_gs96jfl3hlc.woff?t=1732009095144') format('woff'),
url('//at.alicdn.com/t/c/font_4125778_gs96jfl3hlc.ttf?t=1732009095144') format('truetype');
}
.iconfont {

View File

@@ -1,9 +1,11 @@
<template>
<div class="foot-container">
<div class="footer">
<div><span :style="{color:textColor}">{{copyRight}}</span></div>
<div>
<span>{{ copyRight }}</span>
</div>
<div v-if="!license.de_copy">
<a :href="gitURL" target="_blank" :style="{color:textColor}">
<a :href="gitURL" target="_blank">
{{ title }} -
{{ version }}
</a>
@@ -12,37 +14,45 @@
</div>
</template>
<script setup>
import { ref } from "vue";
import { httpGet } from "@/utils/http";
import { showMessageError } from "@/utils/dialog";
import { getLicenseInfo, getSystemInfo } from "@/store/cache";
import {ref} from "vue";
import {httpGet} from "@/utils/http";
import {showMessageError} from "@/utils/dialog";
import {getLicenseInfo, getSystemInfo} from "@/store/cache";
const title = ref("")
const version = ref(process.env.VUE_APP_VERSION)
const gitURL = ref(process.env.VUE_APP_GIT_URL)
const copyRight = ref('')
const license = ref({})
const title = ref("");
const version = ref(process.env.VUE_APP_VERSION);
const gitURL = ref(process.env.VUE_APP_GIT_URL);
const copyRight = ref("");
const license = ref({});
const props = defineProps({
textColor: {
type: String,
default: '#ffffff'
},
default: "#ffffff"
}
});
// 获取系统配置
getSystemInfo().then(res => {
title.value = res.data.title??process.env.VUE_APP_TITLE
copyRight.value = res.data.copyright.length>1?res.data.copyright:'极客学长 © 2023 - '+new Date().getFullYear()+' All rights reserved.'
}).catch(e => {
showMessageError("获取系统配置失败:" + e.message)
})
getSystemInfo()
.then((res) => {
title.value = res.data.title ?? process.env.VUE_APP_TITLE;
copyRight.value =
res.data.copyright.length > 1
? res.data.copyright
: "极客学长 © 2023 - " +
new Date().getFullYear() +
" All rights reserved.";
})
.catch((e) => {
showMessageError("获取系统配置失败:" + e.message);
});
getLicenseInfo().then(res => {
license.value = res.data
}).catch(e => {
showMessageError("获取 License 失败:" + e.message)
})
getLicenseInfo()
.then((res) => {
license.value = res.data;
})
.catch((e) => {
showMessageError("获取 License 失败:" + e.message);
});
</script>
<style scoped lang="stylus">
@@ -62,11 +72,15 @@ getLicenseInfo().then(res => {
width 100%
a {
color:var(--text-color)
&:hover {
text-decoration underline
}
}
span{
color:var(--text-color)
}
}
}
</style>

View File

@@ -0,0 +1,56 @@
<template>
<div class="theme-box" @click="toggleTheme">
<span class="iconfont icon-yueliang">{{
themePage === "light" ? "&#xe679;" : "&#xe60b;"
}}</span>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
// 定义主题状态,初始值从 localStorage 获取
const themePage = ref(localStorage.getItem("theme") || "light");
// 切换主题函数
const toggleTheme = () => {
themePage.value = themePage.value === "light" ? "dark" : "light";
document.documentElement.setAttribute("data-theme", themePage.value); // 设置 HTML 的 data-theme 属性
localStorage.setItem("theme", themePage.value); // 保存主题到 localStorage
};
onMounted(() => {
document.documentElement.setAttribute("data-theme", themePage.value);
});
</script>
<style lang="stylus" scoped>
@import '@/assets/iconfont/iconfont.css'
.theme-box{
position: fixed;
right: 40px;
bottom: 262px;
cursor: pointer;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 50%;
width 35px;
height: 35px;
line-height: 35px;
text-align: center;
background-color: rgb(146, 147, 148);
transition: all 0.3s ease;
&:hover{
transform: scale(1.1);
}
&:active{
transform: scale(0.9);
}
.iconfont{
font-size: 20px;
color: yellow;
transition: transform 0.3s ease;
}
}
</style>

View File

@@ -5,63 +5,74 @@
// * @Author yangjian102621@163.com
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import {createApp} from 'vue'
import ElementPlus from "element-plus"
import "element-plus/dist/index.css"
import '@/assets/iconfont/iconfont.css';
import 'vant/lib/index.css';
import App from './App.vue'
import {createPinia} from "pinia";
import { createApp } from "vue";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import "@/assets/iconfont/iconfont.css";
import "vant/lib/index.css";
import App from "./App.vue";
import { useThemeStore } from "@/store/theme";
import { createPinia } from "pinia";
import "animate.css/animate.min.css";
import {
ActionSheet,
Badge,
Button,
Cell,
CellGroup,
Circle,
Col,
Collapse,
CollapseItem,
ConfigProvider,
Dialog,
Divider,
DropdownItem,
DropdownMenu,
Empty,
Field,
Form,
Grid,
GridItem,
Icon,
Image,
ImagePreview,
Lazyload,
List,
Loading,
NavBar,
NoticeBar,
Notify,
Overlay,
Picker,
Popup,
Row,
Search,
ShareSheet,
Slider,
Sticky,
SwipeCell,
Switch,
Tab,
Tabbar,
TabbarItem,
Tabs,
Tag,
TextEllipsis,
Uploader
ActionSheet,
Badge,
Button,
Cell,
CellGroup,
Circle,
Col,
Collapse,
CollapseItem,
ConfigProvider,
Dialog,
Divider,
DropdownItem,
DropdownMenu,
Empty,
Field,
Form,
Grid,
GridItem,
Icon,
Image,
ImagePreview,
Lazyload,
List,
Loading,
NavBar,
NoticeBar,
Notify,
Overlay,
Picker,
Popup,
Row,
Search,
ShareSheet,
Slider,
Sticky,
SwipeCell,
Switch,
Tab,
Tabbar,
TabbarItem,
Tabs,
Tag,
TextEllipsis,
Uploader
} from "vant";
import {router} from "@/router";
import 'v3-waterfall/dist/style.css'
import { router } from "@/router";
import "v3-waterfall/dist/style.css";
import V3waterfall from "v3-waterfall";
import "@/assets/css/theme-dark.styl";
import "@/assets/css/theme-light.styl";
import "@/assets/css/common.styl";
const pinia = createPinia();
const themeStore = useThemeStore(pinia); // 使用 theme store
// 设置初始主题
document.documentElement.setAttribute("data-theme", themeStore.theme);
const app = createApp(App);
app.use(createPinia());
@@ -70,12 +81,12 @@ app.use(Tabbar);
app.use(TabbarItem);
app.use(NavBar);
app.use(Search);
app.use(Cell)
app.use(Image)
app.use(TextEllipsis)
app.use(Notify)
app.use(Picker)
app.use(Popup)
app.use(Cell);
app.use(Image);
app.use(TextEllipsis);
app.use(Notify);
app.use(Picker);
app.use(Popup);
app.use(List);
app.use(Form);
app.use(Field);
@@ -91,12 +102,12 @@ app.use(ShareSheet);
app.use(Switch);
app.use(Uploader);
app.use(Tag);
app.use(V3waterfall)
app.use(Overlay)
app.use(Col)
app.use(Row)
app.use(Slider)
app.use(Badge)
app.use(V3waterfall);
app.use(Overlay);
app.use(Col);
app.use(Row);
app.use(Slider);
app.use(Badge);
app.use(Collapse);
app.use(CollapseItem);
app.use(Grid);
@@ -111,6 +122,4 @@ app.use(Tabs);
app.use(Divider);
app.use(NoticeBar);
app.use(ActionSheet);
app.use(router).use(ElementPlus).mount('#app')
app.use(router).use(ElementPlus).mount("#app");

15
web/src/store/theme.js Normal file
View File

@@ -0,0 +1,15 @@
// src/store/index.js
import { defineStore } from "pinia";
export const useThemeStore = defineStore("theme", {
state: () => ({
theme: localStorage.getItem("theme") || "light" // 默认从 localStorage 获取主题
}),
actions: {
setTheme(theme) {
this.theme = theme;
document.documentElement.setAttribute("data-theme", theme);
localStorage.setItem("theme", theme); // 保存到 localStorage
}
}
});

View File

@@ -1,178 +1,194 @@
<template>
<div class="index-page" :style="{height: winHeight+'px'}">
<div :class="theme.imageBg?'color-bg image-bg':'color-bg'" :style="{backgroundImage:'url('+bgStyle.backgroundImage+')', backgroundColor:bgStyle.backgroundColor}"></div>
<div class="index-page">
<ThemeChange />
<div class="menu-box">
<el-menu
mode="horizontal"
:ellipsis="false"
>
<el-menu mode="horizontal" :ellipsis="false">
<div class="menu-item">
<el-image :src="logo" class="logo" alt="Geek-AI"/>
<div class="title" :style="{color:theme.textColor}">{{ title }}</div>
<el-image :src="logo" class="logo" alt="Geek-AI" />
</div>
<div class="menu-item">
<span v-if="!license.de_copy">
<a :href="docsURL" target="_blank">
<el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" class="shadow" round>
<i class="iconfont icon-book"></i>
<span>文档</span>
</el-button>
</a>
<a :href="gitURL" target="_blank">
<el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" class="shadow" round>
<i class="iconfont icon-github"></i>
<span>源码</span>
</el-button>
</a>
<el-tooltip
v-if="!license.de_copy"
class="box-item"
effect="light"
content="部署文档"
placement="bottom"
>
<a :href="docsURL" class="link-button" target="_blank">
<i class="iconfont icon-book"></i>
</a>
</el-tooltip>
<el-tooltip
v-if="!license.de_copy"
class="box-item"
effect="light"
content="项目源码"
placement="bottom"
>
<a :href="gitURL" class="link-button" target="_blank">
<i class="iconfont icon-github"></i>
</a>
</el-tooltip>
</span>
<span v-if="!isLogin">
<el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" @click="router.push('/login')" class="shadow" round>登录</el-button>
<el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" @click="router.push('/register')" class="shadow" round>注册</el-button>
<!-- <el-button @click="router.push('/login')" class="shadow" round
>登录</el-button
>
<el-button @click="router.push('/register')" class="shadow" round
>注册</el-button
> -->
<el-button
@click="router.push('/login')"
class="btn-go animate__animated animate__pulse animate__infinite"
round
>登录/注册</el-button
>
</span>
</div>
</el-menu>
</div>
<div class="content">
<h1 :style="{color:theme.textColor}">欢迎使用 {{ title }}</h1>
<p :style="{color:theme.textColor}">{{ slogan }}</p>
<h1 class="animate__animated animate__backInDown">
{{ title }}
</h1>
<div class="msg-text cursor-ani">
<span
v-for="(char, index) in displayedChars"
:key="index"
:style="{ color: rainbowColor(index) }"
>
{{ char }}
</span>
</div>
<div class="navs">
<el-space wrap>
<div v-for="item in navs" :key="item.url" class="nav-item">
<el-button @click="router.push(item.url)" :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" class="shadow" :dark="false">
<i :class="'iconfont '+iconMap[item.url]"></i>
<span>{{item.name}}</span>
</el-button>
<div class="navs animate__animated animate__backInDown">
<el-space wrap :size="14">
<div
v-for="item in navs"
:key="item.url"
class="nav-item-box"
@click="router.push(item.url)"
>
<i :class="'iconfont ' + iconMap[item.url]"></i>
<div>{{ item.name }}</div>
</div>
</el-space>
</div>
</div>
<footer-bar :text-color="theme.textColor" />
<footer-bar />
</div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import {useRouter} from "vue-router";
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import FooterBar from "@/components/FooterBar.vue";
import {httpGet} from "@/utils/http";
import {ElMessage} from "element-plus";
import {checkSession, getLicenseInfo, getSystemInfo} from "@/store/cache";
import {isMobile} from "@/utils/libs";
import ThemeChange from "@/components/ThemeChange.vue";
import { httpGet } from "@/utils/http";
import { ElMessage } from "element-plus";
import { checkSession, getLicenseInfo, getSystemInfo } from "@/store/cache";
import { isMobile } from "@/utils/libs";
const router = useRouter()
const router = useRouter();
if (isMobile()) {
router.push("/mobile/index")
router.push("/mobile/index");
}
const title = ref("")
const logo = ref("")
const slogan = ref("")
const license = ref({de_copy: true})
const winHeight = window.innerHeight - 150
const isLogin = ref(false)
const docsURL = ref(process.env.VUE_APP_DOCS_URL)
const gitURL = ref(process.env.VUE_APP_GIT_URL)
const navs = ref([])
const btnColors = ref([
{bgColor: "#fff143", textColor: "#50616D"},
{bgColor: "#eaff56", textColor: "#50616D"},
{bgColor: "#bddd22", textColor: "#50616D"},
{bgColor: "#1bd1a5", textColor: "#50616D"},
{bgColor: "#e0eee8", textColor: "#50616D"},
{bgColor: "#7bcfa6", textColor: "#50616D"},
{bgColor: "#bce672", textColor: "#50616D"},
{bgColor: "#44cef6", textColor: "#ffffff"},
{bgColor: "#70f3ff", textColor: "#50616D"},
{bgColor: "#fffbf0", textColor: "#50616D"},
{bgColor: "#d6ecf0", textColor: "#50616D"},
{bgColor: "#88ada6", textColor: "#50616D"},
{bgColor: "#30dff3", textColor: "#50616D"},
{bgColor: "#d3e0f3", textColor: "#50616D"},
{bgColor: "#e9e7ef", textColor: "#50616D"},
{bgColor: "#eacd76", textColor: "#50616D"},
{bgColor: "#f2be45", textColor: "#50616D"},
{bgColor: "#549688", textColor: "#ffffff"},
{bgColor: "#758a99", textColor: "#ffffff"},
{bgColor: "#41555d", textColor: "#ffffff"},
{bgColor: "#21aa93", textColor: "#ffffff"},
{bgColor: "#0aa344", textColor: "#ffffff"},
{bgColor: "#f05654", textColor: "#ffffff"},
{bgColor: "#db5a6b", textColor: "#ffffff"},
{bgColor: "#db5a6b", textColor: "#ffffff"},
{bgColor: "#8d4bbb", textColor: "#ffffff"},
{bgColor: "#426666", textColor: "#ffffff"},
{bgColor: "#177cb0", textColor: "#ffffff"},
{bgColor: "#395260", textColor: "#ffffff"},
{bgColor: "#519a73", textColor: "#ffffff"},
{bgColor: "#75878a", textColor: "#ffffff"},
])
const iconMap =ref(
{
"/chat": "icon-chat",
"/mj": "icon-mj",
"/sd": "icon-sd",
"/dalle": "icon-dalle",
"/images-wall": "icon-image",
"/suno": "icon-suno",
"/xmind": "icon-xmind",
"/apps": "icon-app",
"/member": "icon-vip-user",
"/invite": "icon-share",
"/luma": "icon-luma",
}
)
const bgStyle = {}
const color = btnColors.value[Math.floor(Math.random() * btnColors.value.length)]
const theme = ref({bgColor: "#ffffff", btnBgColor: color.bgColor, btnTextColor: color.textColor, textColor: "#ffffff", imageBg:true})
const title = ref("");
const logo = ref("");
const slogan = ref("");
const license = ref({ de_copy: true });
const isLogin = ref(false);
const docsURL = ref(process.env.VUE_APP_DOCS_URL);
const gitURL = ref(process.env.VUE_APP_GIT_URL);
const navs = ref([]);
const iconMap = ref({
"/chat": "icon-chat",
"/mj": "icon-mj",
"/sd": "icon-sd",
"/dalle": "icon-dalle",
"/images-wall": "icon-image",
"/suno": "icon-suno",
"/xmind": "icon-xmind",
"/apps": "icon-app",
"/member": "icon-vip-user",
"/invite": "icon-share",
"/luma": "icon-luma"
});
const displayedChars = ref([]);
const initAnimation = ref("");
let timer = null; // 定时器句柄
// 初始化间隔时间和随机时间数组
const interTime = ref(50);
const interArr = [90, 100, 70, 88, 80, 110, 85, 400, 90, 99];
onMounted(() => {
getSystemInfo().then(res => {
title.value = res.data.title
logo.value = res.data.logo
if (res.data.index_bg_url === 'color') {
// 随机选取一种颜色
theme.value.bgColor = color.bgColor
theme.value.btnBgColor = color.bgColor
theme.value.textColor = color.textColor
theme.value.btnTextColor = color.textColor
// 设置背景颜色
bgStyle.backgroundColor = theme.value.bgColor
bgStyle.backgroundImage = "/images/transparent-bg.png"
theme.value.imageBg = false
} else if (res.data.index_bg_url) {
bgStyle.backgroundImage = res.data.index_bg_url
} else {
bgStyle.backgroundImage = "/images/index-bg.jpg"
}
getSystemInfo()
.then((res) => {
title.value = res.data.title;
logo.value = res.data.logo;
slogan.value = res.data.slogan;
if (timer) clearInterval(timer); // 清除定时器
timer = setInterval(setContent, interTime.value);
})
.catch((e) => {
ElMessage.error("获取系统配置失败:" + e.message);
});
slogan.value = res.data.slogan
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
getLicenseInfo()
.then((res) => {
license.value = res.data;
})
.catch((e) => {
license.value = { de_copy: false };
ElMessage.error("获取 License 配置失败:" + e.message);
});
getLicenseInfo().then(res => {
license.value = res.data
}).catch(e => {
license.value = {de_copy: false}
ElMessage.error("获取 License 配置失败:" + e.message)
})
httpGet("/api/menu/list?index=1")
.then((res) => {
navs.value = res.data;
})
.catch((e) => {
ElMessage.error("获取导航菜单失败:" + e.message);
});
httpGet("/api/menu/list?index=1").then(res => {
navs.value = res.data
}).catch(e => {
ElMessage.error("获取导航菜单失败:" + e.message)
})
checkSession().then(() => {
isLogin.value = true
}).catch(()=>{})
})
checkSession()
.then(() => {
isLogin.value = true;
})
.catch(() => {});
});
// 打字机内容逐字符显示
const setContent = () => {
if (initAnimation.value.length >= slogan.value.length) {
// 文本已全部输出
initAnimation.value = "";
displayedChars.value = [];
if (timer) clearInterval(timer);
timer = setInterval(setContent, interTime.value);
return;
} else {
const nextChar = slogan.value.charAt(initAnimation.value.length);
initAnimation.value += slogan.value.charAt(initAnimation.value.length); // 逐字符追加
displayedChars.value.push(nextChar);
interTime.value = interArr[Math.floor(Math.random() * interArr.length)]; // 设置随机间隔
if (timer) clearInterval(timer);
timer = setInterval(setContent, interTime.value);
}
};
// 计算彩虹色
const rainbowColor = (index) => {
const hue = (index * 40) % 360; // 每个字符间隔40度形成彩虹色
return `hsl(${hue}, 90%, 50%)`; // 色调(hue),饱和度(70%),亮度(50%)
};
</script>
<style lang="stylus" scoped>

View File

@@ -1,10 +1,13 @@
<template>
<div class="admin-home" v-if="isLogin">
<admin-sidebar/>
<admin-sidebar />
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
<admin-header/>
<admin-tags/>
<div :class="'content '+theme" :style="{height:contentHeight+'px'}">
<admin-header />
<admin-tags />
<div
:class="'content ' + theme"
:style="{ height: contentHeight + 'px' }"
>
<router-view v-slot="{ Component }">
<transition name="move" mode="out-in">
<keep-alive :include="tags.nameList">
@@ -17,40 +20,43 @@
</div>
</template>
<script setup>
import {useSidebarStore} from '@/store/sidebar';
import {useTagsStore} from '@/store/tags';
import { useSidebarStore } from "@/store/sidebar";
import { useTagsStore } from "@/store/tags";
import AdminHeader from "@/components/admin/AdminHeader.vue";
import AdminSidebar from "@/components/admin/AdminSidebar.vue";
import AdminTags from "@/components/admin/AdminTags.vue";
import {useRouter} from "vue-router";
import {checkAdminSession} from "@/store/cache";
import {ref, watch} from "vue";
import {useSharedStore} from "@/store/sharedata";
import { useRouter } from "vue-router";
import { checkAdminSession } from "@/store/cache";
import { ref, watch } from "vue";
import { useSharedStore } from "@/store/sharedata";
const sidebar = useSidebarStore();
const tags = useTagsStore();
const isLogin = ref(false)
const contentHeight = window.innerHeight - 80
const store = useSharedStore()
const theme = ref(store.adminTheme)
const isLogin = ref(false);
const contentHeight = window.innerHeight - 80;
const store = useSharedStore();
const theme = ref(store.adminTheme);
// 获取会话信息
const router = useRouter();
checkAdminSession().then(() => {
isLogin.value = true
}).catch(() => {
router.replace('/admin/login')
})
watch(() => store.adminTheme, (val) => {
theme.value = val
})
checkAdminSession()
.then(() => {
isLogin.value = true;
})
.catch(() => {
router.replace("/admin/login");
});
watch(
() => store.adminTheme,
(val) => {
theme.value = val;
}
);
</script>
<style scoped lang="stylus">
@import '@/assets/css/color-dark.styl';
// @import '@/assets/css/color-dark.styl';
@import '@/assets/css/main.styl';
@import '@/assets/iconfont/iconfont.css';
</style>

View File

@@ -1,31 +1,36 @@
const {defineConfig} = require('@vue/cli-service')
let webpack = require('webpack')
const { defineConfig } = require("@vue/cli-service");
const path = require("path");
let webpack = require("webpack");
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false, //关闭eslint校验
productionSourceMap: false, //在生产模式中禁用 Source Map既可以减少包大小也可以加密源码
configureWebpack: {
// disable performance hints
performance: {
hints: false
},
plugins: [
new webpack.optimize.MinChunkSizePlugin({minChunkSize: 10000})
]
transpileDependencies: true,
lintOnSave: false, //关闭eslint校验
productionSourceMap: false, //在生产模式中禁用 Source Map既可以减少包大小也可以加密源码
configureWebpack: {
// disable performance hints
performance: {
hints: false
},
publicPath: '/',
outputDir: 'dist',
crossorigin: "anonymous",
devServer: {
allowedHosts: "all",
port: 8888,
proxy: {
'/static/upload/': {
target: process.env.VUE_APP_API_HOST,
changeOrigin: true,
}
}
plugins: [new webpack.optimize.MinChunkSizePlugin({ minChunkSize: 10000 })],
resolve: {
alias: {
"@": path.resolve(__dirname, "src")
}
}
})
},
publicPath: "/",
outputDir: "dist",
crossorigin: "anonymous",
devServer: {
allowedHosts: "all",
port: 8888,
proxy: {
"/static/upload/": {
target: process.env.VUE_APP_API_HOST,
changeOrigin: true
}
}
}
});