enable use random pure color background for index page

This commit is contained in:
RockYang 2024-07-19 18:43:01 +08:00
parent 7463cfc66c
commit cb0dacd5e0
14 changed files with 290 additions and 151 deletions

View File

@ -6,6 +6,7 @@
* 功能优化:增加 session 和系统配置缓存,确保每个页面只进行一次 session 和 get system config 请求 * 功能优化:增加 session 和系统配置缓存,确保每个页面只进行一次 session 和 get system config 请求
* 功能优化:在应用列表页面,无需先添加模型到用户工作区,可以直接使用 * 功能优化:在应用列表页面,无需先添加模型到用户工作区,可以直接使用
* 功能新增MJ 绘图失败的任务不会自动删除,而是会在列表页显示失败详细错误信息 * 功能新增MJ 绘图失败的任务不会自动删除,而是会在列表页显示失败详细错误信息
* 功能新增:允许在管理后台设置首页显示的导航菜单
* 功能新增:增加 Suno 文生音乐页面功能 * 功能新增:增加 Suno 文生音乐页面功能
## v4.1.0 ## v4.1.0

View File

@ -228,5 +228,6 @@ type SystemConfig struct {
SdNegPrompt string `json:"sd_neg_prompt"` // SD 默认反向提示词 SdNegPrompt string `json:"sd_neg_prompt"` // SD 默认反向提示词
IndexBgURL string `json:"index_bg_url"` // 前端首页背景图片 IndexBgURL string `json:"index_bg_url"` // 前端首页背景图片
IndexNavs []int `json:"index_navs"` // 首页显示的导航菜单
Copyright string `json:"copyright"` // 版权信息 Copyright string `json:"copyright"` // 版权信息
} }

View File

@ -27,9 +27,15 @@ func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler {
// List 数据列表 // List 数据列表
func (h *MenuHandler) List(c *gin.Context) { func (h *MenuHandler) List(c *gin.Context) {
index := h.GetBool(c, "index")
var items []model.Menu var items []model.Menu
var list = make([]vo.Menu, 0) var list = make([]vo.Menu, 0)
res := h.DB.Where("enabled", true).Order("sort_num ASC").Find(&items) session := h.DB.Session(&gorm.Session{})
session = session.Where("enabled", true)
if index {
session = session.Where("id IN ?", h.App.SysConfig.IndexNavs)
}
res := session.Order("sort_num ASC").Find(&items)
if res.Error == nil { if res.Error == nil {
for _, item := range items { for _, item := range items {
var product vo.Menu var product vo.Menu

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,116 @@
.index-page {
margin: 0
overflow hidden
color #ffffff
display flex
justify-content center
align-items baseline
padding-top 150px
.color-bg {
position absolute
top 0
left 0
width 100vw
height 100vh
}
.image-bg {
filter: blur(8px);
background-size: cover;
background-position: center;
}
.shadow {
box-shadow rgba(0, 0, 0, 0.3) 0px 0px 3px
&:hover {
box-shadow rgba(0, 0, 0, 0.3) 0px 0px 8px
}
}
.menu-box {
position absolute
top 0
width 100%
display flex
.el-menu {
padding 0 30px
width 100%
display flex
justify-content space-between
background none
border none
.menu-item {
display flex
padding 20px 0
color #ffffff
.title {
font-size 24px
padding 10px 10px 0 10px
}
.el-image {
height 50px
}
.el-button {
margin-left 10px
span {
margin-left 5px
}
}
}
}
}
.content {
text-align: center;
position relative
h1 {
font-size: 5rem;
margin-bottom: 1rem;
}
p {
font-size: 1.5rem;
margin-bottom: 2rem;
}
.navs {
display flex
max-width 900px
padding 20px
.nav-item {
width 200px
.el-button {
width 100%
padding: 25px 20px;
font-size: 1.3rem;
transition: all 0.3s ease;
.iconfont {
font-size 24px
margin-right 10px
position relative
top -2px
}
}
}
}
}
.footer {
.el-link__inner {
color #ffffff
}
}
}

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4125778 */ font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1721292490257') format('woff2'), src: url('iconfont.woff2?t=1721356513025') format('woff2'),
url('iconfont.woff?t=1721292490257') format('woff'), url('iconfont.woff?t=1721356513025') format('woff'),
url('iconfont.ttf?t=1721292490257') format('truetype'); url('iconfont.ttf?t=1721356513025') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-app:before {
content: "\e64f";
}
.icon-pause:before { .icon-pause:before {
content: "\e693"; content: "\e693";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,13 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "1503777",
"name": "应用",
"font_class": "app",
"unicode": "e64f",
"unicode_decimal": 58959
},
{ {
"icon_id": "7156146", "icon_id": "7156146",
"name": "暂停", "name": "暂停",

Binary file not shown.

View File

@ -1,13 +1,15 @@
<template> <template>
<div class="foot-container"> <div class="foot-container">
<div class="footer"> <div class="footer">
<div v-if="license.de_copy">{{copyRight}}</div> <div v-if="license.de_copy" :style="{color:textColor}">{{copyRight}}</div>
<div v-else> <div v-else>
<span>{{copyRight}}</span> <span :style="{color:textColor}">{{copyRight}}</span>
<el-link type="primary" :href="gitURL" target="_blank" style="--el-link-text-color:#ffffff"> <div>
<a :href="gitURL" target="_blank" :style="{color:textColor}">
{{ title }} - {{ title }} -
{{ version }} {{ version }}
</el-link> </a>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -23,7 +25,12 @@ const version = ref(process.env.VUE_APP_VERSION)
const gitURL = ref(process.env.VUE_APP_GIT_URL) const gitURL = ref(process.env.VUE_APP_GIT_URL)
const copyRight = ref('') const copyRight = ref('')
const license = ref({}) const license = ref({})
const props = defineProps({
textColor: {
type: String,
default: '#ffffff'
},
});
// //
httpGet("/api/config/get?key=system").then(res => { httpGet("/api/config/get?key=system").then(res => {
@ -56,8 +63,10 @@ httpGet("/api/config/license").then(res => {
padding 20px; padding 20px;
width 100% width 100%
.el-link { a {
color #409eff &:hover {
text-decoration underline
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="index-page" :style="{height: winHeight+'px'}"> <div class="index-page" :style="{height: winHeight+'px'}">
<div class="index-bg" :style="{backgroundImage: 'url('+bgImgUrl+')'}"></div> <div :class="theme.imageBg?'color-bg image-bg':'color-bg'" :style="{backgroundImage:'url('+bgStyle.backgroundImage+')', backgroundColor:bgStyle.backgroundColor}"></div>
<div class="menu-box"> <div class="menu-box">
<el-menu <el-menu
mode="horizontal" mode="horizontal"
@ -8,19 +8,19 @@
> >
<div class="menu-item"> <div class="menu-item">
<el-image :src="logo" alt="Geek-AI"/> <el-image :src="logo" alt="Geek-AI"/>
<div class="title">{{ title }}</div> <div class="title" :style="{color:theme.textColor}">{{ title }}</div>
</div> </div>
<div class="menu-item"> <div class="menu-item">
<span v-if="!license.de_copy"> <span v-if="!license.de_copy">
<a :href="docsURL" target="_blank"> <a :href="docsURL" target="_blank">
<el-button type="primary" round> <el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" class="shadow" round>
<i class="iconfont icon-book"></i> <i class="iconfont icon-book"></i>
<span>文档</span> <span>文档</span>
</el-button> </el-button>
</a> </a>
<a :href="gitURL" target="_blank"> <a :href="gitURL" target="_blank">
<el-button type="success" round> <el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" class="shadow" round>
<i class="iconfont icon-github"></i> <i class="iconfont icon-github"></i>
<span>源码</span> <span>源码</span>
</el-button> </el-button>
@ -28,36 +28,29 @@
</span> </span>
<span v-if="!isLogin"> <span v-if="!isLogin">
<el-button @click="router.push('/login')" round>登录</el-button> <el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" @click="router.push('/login')" class="shadow" round>登录</el-button>
<el-button @click="router.push('/register')" round>注册</el-button> <el-button :color="theme.btnBgColor" :style="{color: theme.btnTextColor}" @click="router.push('/register')" class="shadow" round>注册</el-button>
</span> </span>
</div> </div>
</el-menu> </el-menu>
</div> </div>
<div class="content"> <div class="content">
<h1>欢迎使用 {{ title }}</h1> <h1 :style="{color:theme.textColor}">欢迎使用 {{ title }}</h1>
<p>{{ slogan }}</p> <p :style="{color:theme.textColor}">{{ slogan }}</p>
<el-button @click="router.push('/chat')" color="#ffffff" style="color:#007bff" :dark="false">
<i class="iconfont icon-chat"></i>
<span>AI 对话</span>
</el-button>
<el-button @click="router.push('/mj')" color="#C4CCFD" style="color:#424282" :dark="false">
<i class="iconfont icon-mj"></i>
<span>MJ 绘画</span>
</el-button>
<el-button @click="router.push('/sd')" color="#4AE6DF" style="color:#424282" :dark="false"> <div class="navs">
<i class="iconfont icon-sd"></i> <el-space wrap>
<span>SD 绘画</span> <div v-for="item in navs" 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> </el-button>
<el-button @click="router.push('/xmind')" color="#FFFD55" style="color:#424282" :dark="false"> </div>
<i class="iconfont icon-xmind"></i> </el-space>
<span>思维导图</span> </div>
</el-button>
<!-- <div id="animation-container"></div>-->
</div> </div>
<footer-bar /> <footer-bar :text-color="theme.textColor" />
</div> </div>
</template> </template>
@ -82,23 +75,82 @@ const logo = ref("")
const slogan = ref("") const slogan = ref("")
const license = ref({}) const license = ref({})
const winHeight = window.innerHeight - 150 const winHeight = window.innerHeight - 150
const bgImgUrl = ref('')
const isLogin = ref(false) const isLogin = ref(false)
const docsURL = ref(process.env.VUE_APP_DOCS_URL) const docsURL = ref(process.env.VUE_APP_DOCS_URL)
const gitURL = ref(process.env.VUE_APP_GIT_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",
}
)
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})
onMounted(() => { onMounted(() => {
httpGet("/api/config/get?key=system").then(res => { httpGet("/api/config/get?key=system").then(res => {
title.value = res.data.title title.value = res.data.title
logo.value = res.data.logo logo.value = res.data.logo
if (res.data.index_bg_url) { if (res.data.index_bg_url === 'color') {
bgImgUrl.value = res.data.index_bg_url //
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 { } else {
bgImgUrl.value = "/images/index-bg.jpg" bgStyle.backgroundImage = "/images/index-bg.jpg"
} }
if (res.data.slogan) {
slogan.value = res.data.slogan slogan.value = res.data.slogan
}
}).catch(e => { }).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message) ElMessage.error("获取系统配置失败:" + e.message)
}) })
@ -106,7 +158,13 @@ onMounted(() => {
httpGet("/api/config/license").then(res => { httpGet("/api/config/license").then(res => {
license.value = res.data license.value = res.data
}).catch(e => { }).catch(e => {
ElMessage.error("获取 License 配置:" + e.message) ElMessage.error("获取 License 配置失败:" + e.message)
})
httpGet("/api/menu/list?index=1").then(res => {
navs.value = res.data
}).catch(e => {
ElMessage.error("获取导航菜单失败:" + e.message)
}) })
checkSession().then(() => { checkSession().then(() => {
@ -117,107 +175,5 @@ onMounted(() => {
<style lang="stylus" scoped> <style lang="stylus" scoped>
@import '@/assets/iconfont/iconfont.css' @import '@/assets/iconfont/iconfont.css'
.index-page { @import "@/assets/css/index.styl"
margin: 0
overflow hidden
color #ffffff
display flex
justify-content center
align-items baseline
padding-top 150px
.index-bg {
position absolute
top 0
left 0
width 100vw
height 100vh
filter: blur(8px);
background-size: cover;
background-position: center;
}
.menu-box {
position absolute
top 0
width 100%
display flex
.el-menu {
padding 0 30px
width 100%
display flex
justify-content space-between
background none
border none
.menu-item {
display flex
padding 20px 0
color #ffffff
.title {
font-size 24px
padding 10px 10px 0 10px
}
.el-image {
height 50px
}
.el-button {
margin-left 10px
span {
margin-left 5px
}
}
}
}
}
.content {
text-align: center;
position relative
h1 {
font-size: 5rem;
margin-bottom: 1rem;
}
p {
font-size: 1.5rem;
margin-bottom: 2rem;
}
.el-button {
padding: 25px 20px;
font-size: 1.3rem;
transition: all 0.3s ease;
.iconfont {
font-size 1.6rem
margin-right 10px
}
}
#animation-container {
display flex
justify-content center
width 100%
height: 300px;
position: absolute;
top: 350px
}
}
.footer {
.el-link__inner {
color #ffffff
}
}
}
</style> </style>

View File

@ -50,6 +50,38 @@
</template> </template>
</el-input> </el-input>
<el-button type="primary" @click="system.index_bg_url = 'https://api.dujin.org/bing/1920.php'">使用动态背景</el-button> <el-button type="primary" @click="system.index_bg_url = 'https://api.dujin.org/bing/1920.php'">使用动态背景</el-button>
<el-button @click="system.index_bg_url = 'color'">使用纯色背景</el-button>
</div>
</el-form-item>
<el-form-item label="首页导航菜单" prop="index_navs">
<div class="tip-input">
<el-select
v-model="system['index_navs']"
multiple
:filterable="true"
placeholder="请选择菜单,多选"
style="width: 100%"
>
<el-option
v-for="item in menus"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<div class="info">
<el-tooltip
class="box-item"
effect="dark"
content="被选中的菜单将会在首页导航栏显示"
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</div> </div>
</el-form-item> </el-form-item>
@ -407,6 +439,7 @@ const models = ref([])
const openAIModels = ref([]) const openAIModels = ref([])
const notice = ref("") const notice = ref("")
const license = ref({is_active: false}) const license = ref({is_active: false})
const menus = ref([])
onMounted(() => { onMounted(() => {
// //
@ -431,6 +464,12 @@ onMounted(() => {
ElMessage.error("获取模型失败:" + e.message) ElMessage.error("获取模型失败:" + e.message)
}) })
httpGet('/api/admin/menu/list').then(res => {
menus.value = res.data
}).catch(e => {
ElMessage.error("获取模型失败:" + e.message)
})
fetchLicense() fetchLicense()
}) })