mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-11-09 18:53:43 +08:00
支持在 Chat 页面显示,隐藏对话列表
This commit is contained in:
@@ -131,7 +131,8 @@ type SystemConfig struct {
|
||||
Title string `json:"title,omitempty"` // 网站标题
|
||||
Slogan string `json:"slogan,omitempty"` // 网站 slogan
|
||||
AdminTitle string `json:"admin_title,omitempty"` // 管理后台标题
|
||||
Logo string `json:"logo,omitempty"` // 方形 Logo
|
||||
Logo string `json:"logo,omitempty"` // 圆形 Logo
|
||||
BarLogo string `json:"bar_logo,omitempty"` // 条形 Logo
|
||||
InitPower int `json:"init_power,omitempty"` // 新用户注册赠送算力值
|
||||
DailyPower int `json:"daily_power,omitempty"` // 每日签到赠送算力
|
||||
InvitePower int `json:"invite_power,omitempty"` // 邀请新用户赠送算力值
|
||||
|
||||
@@ -92,6 +92,8 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
|
||||
if strings.HasPrefix(req.Model, "o1-") {
|
||||
content := fmt.Sprintf("AI 思考结束,耗时:%d 秒。\n\n", time.Now().Unix()-session.Start)
|
||||
contents = append(contents, "> AI 正在思考中...\n")
|
||||
contents = append(contents, content)
|
||||
utils.SendChunkMsg(ws, content)
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,7 @@ func (h *WebsocketHandler) Client(c *gin.Context) {
|
||||
if err != nil {
|
||||
logger.Error(err, chatModel)
|
||||
}
|
||||
session.Model.Id = chatModel.Id
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
h.chatHandler.ReqCancelFunc.Put(clientId, cancel)
|
||||
err = h.chatHandler.sendMessage(ctx, session, chatRole, chatMessage.Content, client)
|
||||
|
||||
@@ -8,4 +8,7 @@ VUE_APP_KEY_PREFIX=GeekAI_DEV_
|
||||
VUE_APP_TITLE="Geek-AI 创作系统"
|
||||
VUE_APP_VERSION=v4.1.9
|
||||
VUE_APP_DOCS_URL=https://docs.geekai.me
|
||||
VUE_APP_GIT_URL=https://github.com/yangjian102621/geekai
|
||||
VUE_APP_GITHUB_URL=https://github.com/yangjian102621/geekai
|
||||
VUE_APP_GITEE_URL=https://gitee.com/blackfox/geekai
|
||||
VUE_APP_GITCODE_URL=https://gitcode.com/yangjian102621/geekai
|
||||
|
||||
|
||||
@@ -3,4 +3,6 @@ VUE_APP_WS_HOST=
|
||||
VUE_APP_KEY_PREFIX=GeekAI_
|
||||
VUE_APP_VERSION=v4.1.9
|
||||
VUE_APP_DOCS_URL=https://docs.geekai.me
|
||||
VUE_APP_GIT_URL=https://github.com/yangjian102621/geekai
|
||||
VUE_APP_GITHUB_URL=https://github.com/yangjian102621/geekai
|
||||
VUE_APP_GITEE_URL=https://gitee.com/blackfox/geekai
|
||||
VUE_APP_GITCODE_URL=https://gitcode.com/yangjian102621/geekai
|
||||
|
||||
BIN
web/public/images/bar_logo.png
Normal file
BIN
web/public/images/bar_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -30,14 +30,6 @@
|
||||
margin-bottom: 10px
|
||||
}
|
||||
|
||||
// 隐藏滚动条
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%
|
||||
overflow-y: scroll
|
||||
@@ -130,7 +122,7 @@
|
||||
.chat-container {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
background-color: var(--el-bg-color)
|
||||
background-color: var(--chat-bg)
|
||||
color var(--text-fb)
|
||||
|
||||
.chat-config {
|
||||
@@ -186,54 +178,33 @@
|
||||
width: 100%;
|
||||
position relative
|
||||
background: var(--chat-bg)
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 12px /* 滚动条宽度 */
|
||||
background #F1F1F1
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #c1c1c1;
|
||||
border-radius 12px
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #A8A8A8;
|
||||
}
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding 0 20px
|
||||
max-width: 960px;
|
||||
|
||||
.chat-box {
|
||||
overflow-y: auto;
|
||||
//border-bottom: 1px solid #4f4f4f
|
||||
scrollbar-width: none; /* 隐藏滚动条(Firefox) */
|
||||
-ms-overflow-style: none; /* 隐藏滚动条(IE、Edge) */
|
||||
|
||||
// 变量定义
|
||||
--content-font-size: 16px;
|
||||
--content-color: #c1c1c1;
|
||||
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
padding: 0 0 50px 0;
|
||||
//padding: 0 0 50px 0;
|
||||
width: 100%;
|
||||
|
||||
.chat-line {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
display: none; /* 隐藏滚动条(Webkit 浏览器) */
|
||||
}
|
||||
}
|
||||
|
||||
.input-box {
|
||||
position absolute
|
||||
bottom 0
|
||||
width 100%
|
||||
max-width: 800px;
|
||||
|
||||
.input-box-inner {
|
||||
display flex
|
||||
@@ -363,11 +334,6 @@
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#container::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
.layout{
|
||||
.layout {
|
||||
display: flex;
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
.big-top-title{
|
||||
|
||||
.big-top-title {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.top-collapse{
|
||||
|
||||
.top-collapse {
|
||||
padding-top: 10px
|
||||
img{
|
||||
|
||||
img {
|
||||
width 24px !important
|
||||
height: 24px !important
|
||||
}
|
||||
}
|
||||
.tab-box{
|
||||
|
||||
.tab-box {
|
||||
align-items: center
|
||||
background-color: var(--card-bg)
|
||||
border-right: 1px solid var(--line-box);
|
||||
// height: 100%
|
||||
// position: fixed;
|
||||
height: 100vh;
|
||||
.title{
|
||||
|
||||
.title {
|
||||
font-size: 28px
|
||||
height: 40px
|
||||
width 120px
|
||||
@@ -26,51 +32,48 @@
|
||||
word-wrap break-all;
|
||||
overflow hidden
|
||||
font-weight: 700
|
||||
color:var(--text-theme-color)
|
||||
color: var(--text-theme-color)
|
||||
}
|
||||
img{
|
||||
|
||||
img {
|
||||
height: 44px
|
||||
object-fit: cover
|
||||
border-radius: 50%
|
||||
border: 2px solid #754ff6;
|
||||
background: #fff
|
||||
}
|
||||
.marr{
|
||||
|
||||
.marr {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.flex-center-col{
|
||||
|
||||
.flex-center-col {
|
||||
display flex
|
||||
align-items center
|
||||
flex-direction column
|
||||
|
||||
.iconfont {
|
||||
font-size 22px
|
||||
}
|
||||
|
||||
.icon-expand {
|
||||
font-size 24px
|
||||
margin-bottom 10px
|
||||
cursor pointer
|
||||
color var(--text-color)
|
||||
}
|
||||
|
||||
.icon-colspan {
|
||||
font-size 18px
|
||||
margin-left 3px
|
||||
cursor pointer
|
||||
color var(--text-color)
|
||||
.icon-new-chat {
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
}
|
||||
.menu-list-collapse{
|
||||
.flex-center-col{
|
||||
|
||||
.menu-list-collapse {
|
||||
.flex-center-col {
|
||||
flex-direction: row;
|
||||
}
|
||||
.menu-list-item{
|
||||
|
||||
.menu-list-item {
|
||||
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
@@ -80,52 +83,49 @@
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.menu-list-item:hover,
|
||||
.active{
|
||||
.active {
|
||||
background: rgba(79, 89, 102, .122);
|
||||
|
||||
border-radius: 8px;
|
||||
.el-icon{
|
||||
|
||||
.el-icon {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
.menu-title{
|
||||
|
||||
.menu-title {
|
||||
font-size: 15px !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
width: 65px;
|
||||
|
||||
.openicon{
|
||||
font-size: 40px;
|
||||
color: #754ff6;
|
||||
}
|
||||
.menuIcon{
|
||||
.openicon{
|
||||
font-size: 28px;
|
||||
color: #754ff6;
|
||||
}
|
||||
}
|
||||
.menu-list{
|
||||
margin-top: 20px;
|
||||
.svg-icon{
|
||||
svg{
|
||||
.svg-icon {
|
||||
svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
.menu-list-item{
|
||||
|
||||
.menu-list-item {
|
||||
// margin-bottom: 10px;
|
||||
margin: 0 8px 8px;
|
||||
cursor: pointer;
|
||||
font-weight: 550;
|
||||
&:hover{
|
||||
.el-icon{
|
||||
color: var(--text-theme-color);
|
||||
|
||||
&:hover {
|
||||
.el-icon {
|
||||
background: rgba(79, 89, 102, .122);
|
||||
}
|
||||
|
||||
}
|
||||
.el-icon{
|
||||
|
||||
.el-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 4px;
|
||||
@@ -138,94 +138,97 @@
|
||||
// }
|
||||
|
||||
}
|
||||
&.active{
|
||||
color: var(--text-color);
|
||||
|
||||
&.active {
|
||||
color: var(--text-theme-color);
|
||||
font-weight: 700;
|
||||
filter: none !important;
|
||||
.el-icon{
|
||||
background: rgba(79, 89, 102, .122);
|
||||
filter: invert(100%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.bot{
|
||||
|
||||
.bot {
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
width 65px;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.bot-line{
|
||||
.bot-line {
|
||||
|
||||
width : 100%;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: var(--line-box)
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
.menu-title{
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.icon-house,
|
||||
.icon-github{
|
||||
font-size: 20px;
|
||||
color: #754ff6;
|
||||
cursor pointer
|
||||
.icon-github {
|
||||
font-size: 20px;
|
||||
color: #754ff6;
|
||||
cursor pointer
|
||||
}
|
||||
.menu-bot-item{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
a{
|
||||
// margin-right: 46px;
|
||||
}
|
||||
|
||||
.menu-bot-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
::v-deep(.theme-box){
|
||||
|
||||
::v-deep(.theme-box) {
|
||||
position: relative !important;
|
||||
right: initial;
|
||||
bottom: initial;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
.iconfont{
|
||||
font-size: 15px !important;}
|
||||
line-height: 18px;
|
||||
|
||||
.iconfont {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
.right-main{
|
||||
|
||||
.right-main {
|
||||
height: 100%;
|
||||
// background: #f5f7fd;
|
||||
background: var(--theme-bg-all);
|
||||
// background-image: linear-gradient(180deg, rgba(247, 232, 255, .54), rgba(191, 223, 255, .35));
|
||||
width: 100%;
|
||||
.loginMask{
|
||||
|
||||
.loginMask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
}
|
||||
.topheader{
|
||||
}
|
||||
|
||||
.topheader {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
right: 8px;
|
||||
z-index : 999;
|
||||
top:0;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
// width 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.btn-go{
|
||||
|
||||
.btn-go {
|
||||
background: #754ff6;
|
||||
margin: 10px 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.el-popper {
|
||||
.more-menus {
|
||||
li {
|
||||
@@ -251,18 +254,21 @@
|
||||
background: rgba(79, 89, 102, 0.1);
|
||||
}
|
||||
}
|
||||
.setting-menus{
|
||||
.title{
|
||||
|
||||
.setting-menus {
|
||||
.title {
|
||||
color: #222226;
|
||||
}
|
||||
|
||||
.el-icon,
|
||||
.iconfont{
|
||||
.iconfont {
|
||||
font-size: 18px
|
||||
margin-right: 6px
|
||||
}
|
||||
color: #222226;
|
||||
}
|
||||
.username{
|
||||
|
||||
.username {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
@@ -271,21 +277,25 @@
|
||||
|
||||
|
||||
}
|
||||
.rightHeightMax{
|
||||
|
||||
.rightHeightMax {
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
.rightHeight{
|
||||
|
||||
.rightHeight {
|
||||
height: calc(100vh - 42px);
|
||||
max-height: calc(100vh - 42px);
|
||||
overflow: hidden;
|
||||
.content{
|
||||
|
||||
.content {
|
||||
padding-top: 42px;
|
||||
}
|
||||
}
|
||||
.content{
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
@@ -43,7 +43,7 @@
|
||||
--el-bg-color-overlay: rgba(17, 28, 68, 1);
|
||||
--el-border-color-light: rgba(255, 255, 255, 0.2);
|
||||
--chat-content-bg:rgba(86, 86, 95, .2);
|
||||
--chat-content-bg-list:rgba(86, 86, 95, .2);
|
||||
--chat-user-content-bg: #762AA4;
|
||||
--hover-deep-color:#30323c;
|
||||
//layout
|
||||
.more-menus li.moreTitle,
|
||||
@@ -52,7 +52,7 @@
|
||||
.setting-menus li .el-icon,
|
||||
.setting-menus li .iconfont,
|
||||
.layout .tab-box .menu-list-item{
|
||||
filter: invert(100%);
|
||||
//filter: invert(100%);
|
||||
}
|
||||
.more-menus span.title{
|
||||
color:#000;
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
--theme-text-primary: #000;
|
||||
--theme-text-color-secondary: #666;
|
||||
--chat-content-bg:#f5f7fc;
|
||||
--chat-user-content-bg: #e0dfff;
|
||||
--chat-list-bg: #0302020a;
|
||||
--chat-content-bg-list:#fff;
|
||||
--chat-wel-bg:rgba(247, 247, 248, 1);
|
||||
--hover-deep-color:#fff;
|
||||
--el-bg-color-overlay: #fff;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4125778 */
|
||||
src: url('iconfont.woff2?t=1734934068681') format('woff2'),
|
||||
url('iconfont.woff?t=1734934068681') format('woff'),
|
||||
url('iconfont.ttf?t=1734934068681') format('truetype');
|
||||
src: url('iconfont.woff2?t=1736144380052') format('woff2'),
|
||||
url('iconfont.woff?t=1736144380052') format('woff'),
|
||||
url('iconfont.ttf?t=1736144380052') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-gitee:before {
|
||||
content: "\e6d0";
|
||||
}
|
||||
|
||||
.icon-redeem:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,13 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "6905420",
|
||||
"name": "码云",
|
||||
"font_class": "gitee",
|
||||
"unicode": "e6d0",
|
||||
"unicode_decimal": 59088
|
||||
},
|
||||
{
|
||||
"icon_id": "3624396",
|
||||
"name": "兑换码",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,22 +2,25 @@
|
||||
<div class="chat-line chat-line-prompt-list" v-if="listStyle === 'list'">
|
||||
<div class="chat-line-inner">
|
||||
<div class="chat-icon">
|
||||
<img :src="data.icon" alt="User" />
|
||||
<img :src="data.icon" alt="User"/>
|
||||
</div>
|
||||
|
||||
<div class="chat-item">
|
||||
<div v-if="files.length > 0" class="file-list-box">
|
||||
<div v-for="file in files">
|
||||
<div class="image" v-if="isImage(file.ext)">
|
||||
<el-image :src="file.url" fit="cover" />
|
||||
<el-image :src="file.url" fit="cover"/>
|
||||
</div>
|
||||
<div class="item" v-else>
|
||||
<div class="icon">
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover" />
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover"/>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="title">
|
||||
<el-link :href="file.url" target="_blank" style="--el-font-weight-primary: bold">{{ file.name }}</el-link>
|
||||
<el-link :href="file.url" target="_blank" style="--el-font-weight-primary: bold">{{
|
||||
file.name
|
||||
}}
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span>{{ GetFileType(file.ext) }}</span>
|
||||
@@ -30,7 +33,7 @@
|
||||
<div class="content" v-html="content"></div>
|
||||
<div class="bar" v-if="data.created_at > 0">
|
||||
<span class="bar-item"
|
||||
><el-icon><Clock /></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
>
|
||||
<span class="bar-item">tokens: {{ finalTokens }}</span>
|
||||
</div>
|
||||
@@ -41,22 +44,25 @@
|
||||
<div class="chat-line chat-line-prompt-chat" v-else>
|
||||
<div class="chat-line-inner">
|
||||
<div class="chat-icon">
|
||||
<img :src="data.icon" alt="User" />
|
||||
<img :src="data.icon" alt="User"/>
|
||||
</div>
|
||||
|
||||
<div class="chat-item">
|
||||
<div v-if="files.length > 0" class="file-list-box">
|
||||
<div v-for="file in files">
|
||||
<div class="image" v-if="isImage(file.ext)">
|
||||
<el-image :src="file.url" fit="cover" />
|
||||
<el-image :src="file.url" fit="cover"/>
|
||||
</div>
|
||||
<div class="item" v-else>
|
||||
<div class="icon">
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover" />
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover"/>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="title">
|
||||
<el-link :href="file.url" target="_blank" style="--el-font-weight-primary: bold">{{ file.name }}</el-link>
|
||||
<el-link :href="file.url" target="_blank" style="--el-font-weight-primary: bold">{{
|
||||
file.name
|
||||
}}
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span>{{ GetFileType(file.ext) }}</span>
|
||||
@@ -71,7 +77,7 @@
|
||||
</div>
|
||||
<div class="bar" v-if="data.created_at > 0">
|
||||
<span class="bar-item"
|
||||
><el-icon><Clock /></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
>
|
||||
<!-- <span class="bar-item">tokens: {{ finalTokens }}</span>-->
|
||||
</div>
|
||||
@@ -81,12 +87,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { Clock } from "@element-plus/icons-vue";
|
||||
import { httpPost } from "@/utils/http";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {Clock} from "@element-plus/icons-vue";
|
||||
import {httpPost} from "@/utils/http";
|
||||
import hl from "highlight.js";
|
||||
import { dateFormat, isImage, processPrompt } from "@/utils/libs";
|
||||
import { FormatFileSize, GetFileIcon, GetFileType } from "@/store/system";
|
||||
import {dateFormat, isImage, processPrompt} from "@/utils/libs";
|
||||
import {FormatFileSize, GetFileIcon, GetFileType} from "@/store/system";
|
||||
import emoji from "markdown-it-emoji";
|
||||
import mathjaxPlugin from "markdown-it-mathjax3";
|
||||
import MarkdownIt from "markdown-it";
|
||||
@@ -101,8 +107,8 @@ const md = new MarkdownIt({
|
||||
// 显示复制代码按钮
|
||||
const copyBtn = `<span class="copy-code-btn" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span>
|
||||
<textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy-target-${codeIndex}">${str.replace(
|
||||
/<\/textarea>/g,
|
||||
"</textarea>"
|
||||
/<\/textarea>/g,
|
||||
"</textarea>"
|
||||
)}</textarea>`;
|
||||
if (lang && hl.getLanguage(lang)) {
|
||||
const langHtml = `<span class="lang-name">${lang}</span>`;
|
||||
@@ -152,17 +158,18 @@ const processFiles = () => {
|
||||
const linkRegex = /(https?:\/\/\S+)/g;
|
||||
const links = props.data.content.match(linkRegex);
|
||||
if (links) {
|
||||
httpPost("/api/upload/list", { urls: links })
|
||||
.then((res) => {
|
||||
files.value = res.data.items;
|
||||
httpPost("/api/upload/list", {urls: links})
|
||||
.then((res) => {
|
||||
files.value = res.data.items;
|
||||
|
||||
for (let link of links) {
|
||||
if (isExternalImg(link, files.value)) {
|
||||
files.value.push({ url: link, ext: ".png" });
|
||||
for (let link of links) {
|
||||
if (isExternalImg(link, files.value)) {
|
||||
files.value.push({url: link, ext: ".png"});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
|
||||
for (let link of links) {
|
||||
content.value = content.value.replace(link, "");
|
||||
@@ -177,11 +184,11 @@ const isExternalImg = (link, files) => {
|
||||
|
||||
<style lang="stylus">
|
||||
@import '@/assets/css/markdown/vue.css';
|
||||
.chat-page,.chat-export {
|
||||
.chat-page, .chat-export {
|
||||
.chat-line-prompt-list {
|
||||
|
||||
background-color:var( --chat-content-bg-list);
|
||||
color:var(--theme-text-color-primary);
|
||||
background-color: var(--chat-content-bg-list);
|
||||
color: var(--theme-text-color-primary);
|
||||
justify-content: center;
|
||||
width 100%
|
||||
padding-bottom: 1.5rem;
|
||||
@@ -213,6 +220,7 @@ const isExternalImg = (link, files) => {
|
||||
.file-list-box {
|
||||
display flex
|
||||
flex-flow column
|
||||
|
||||
.image {
|
||||
display flex
|
||||
flex-flow row
|
||||
@@ -225,13 +233,14 @@ const isExternalImg = (link, files) => {
|
||||
margin-bottom 10px
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
display flex
|
||||
flex-flow row
|
||||
border-radius 10px
|
||||
background-color:var(--chat-content-bg);
|
||||
background-color: var(--chat-content-bg);
|
||||
border 1px solid #e3e3e3
|
||||
color:var(--theme-text-color-primary);
|
||||
color: var(--theme-text-color-primary);
|
||||
padding 6px
|
||||
margin-bottom 10px
|
||||
|
||||
@@ -241,14 +250,17 @@ const isExternalImg = (link, files) => {
|
||||
height 40px
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
margin-left 8px
|
||||
font-size 14px
|
||||
|
||||
.title {
|
||||
font-weight bold
|
||||
line-height 24px
|
||||
color #0D0D0D
|
||||
}
|
||||
|
||||
.info {
|
||||
color #B4B4B4
|
||||
|
||||
@@ -263,7 +275,7 @@ const isExternalImg = (link, files) => {
|
||||
.content {
|
||||
word-break break-word;
|
||||
padding: 0;
|
||||
color:var(--theme-text-color-primary);
|
||||
color: var(--theme-text-color-primary);
|
||||
font-size: var(--content-font-size);
|
||||
border-radius: 5px;
|
||||
overflow: auto;
|
||||
@@ -336,11 +348,12 @@ const isExternalImg = (link, files) => {
|
||||
.chat-item {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
max-width 60%
|
||||
max-width calc(100% - 110px);
|
||||
|
||||
.file-list-box {
|
||||
display flex
|
||||
flex-flow column
|
||||
|
||||
.image {
|
||||
display flex
|
||||
flex-flow row
|
||||
@@ -353,12 +366,13 @@ const isExternalImg = (link, files) => {
|
||||
margin-bottom 10px
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
display flex
|
||||
flex-flow row
|
||||
border-radius 10px
|
||||
background-color:var(--chat-content-bg);
|
||||
color:var(--theme-text-color-primary);
|
||||
background-color: var(--chat-content-bg);
|
||||
color: var(--theme-text-color-primary);
|
||||
border 1px solid #e3e3e3
|
||||
padding 6px
|
||||
margin-bottom 10px
|
||||
@@ -369,14 +383,17 @@ const isExternalImg = (link, files) => {
|
||||
height 40px
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
margin-left 8px
|
||||
font-size 14px
|
||||
|
||||
.title {
|
||||
font-weight bold
|
||||
line-height 24px
|
||||
color #0D0D0D
|
||||
}
|
||||
|
||||
.info {
|
||||
color #B4B4B4
|
||||
|
||||
@@ -392,35 +409,37 @@ const isExternalImg = (link, files) => {
|
||||
.content-wrapper {
|
||||
display flex
|
||||
flex-flow row-reverse
|
||||
|
||||
.content {
|
||||
word-break break-word;
|
||||
padding: 1rem
|
||||
color var(--theme-text-primary);
|
||||
font-size: var(--content-font-size);
|
||||
overflow: auto;
|
||||
background-color :var(--chat-content-bg);
|
||||
border-radius: 10px 0 10px 10px;
|
||||
word-break break-word;
|
||||
padding: 1rem
|
||||
color var(--theme-text-primary);
|
||||
font-size: var(--content-font-size);
|
||||
overflow: auto;
|
||||
background-color: var(--chat-user-content-bg);
|
||||
border-radius: 10px 0 10px 10px;
|
||||
|
||||
img {
|
||||
max-width: 600px;
|
||||
border-radius: 10px;
|
||||
margin 10px 0
|
||||
}
|
||||
|
||||
p {
|
||||
line-height 1.5
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
margin-top 0
|
||||
}
|
||||
img {
|
||||
max-width: 600px;
|
||||
border-radius: 10px;
|
||||
margin 10px 0
|
||||
}
|
||||
|
||||
p {
|
||||
line-height 1.5
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
margin-top 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.bar {
|
||||
padding 10px 10px 10px 0;
|
||||
|
||||
|
||||
@@ -90,9 +90,9 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Clock, DocumentCopy, Refresh } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { dateFormat, processContent } from "@/utils/libs";
|
||||
import {Clock, DocumentCopy, Refresh} from "@element-plus/icons-vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {dateFormat, processContent} from "@/utils/libs";
|
||||
import hl from "highlight.js";
|
||||
import emoji from "markdown-it-emoji";
|
||||
import mathjaxPlugin from "markdown-it-mathjax3";
|
||||
@@ -237,7 +237,6 @@ const reGenerate = (prompt) => {
|
||||
table {
|
||||
width 100%
|
||||
margin-bottom 1rem
|
||||
color #212529
|
||||
border-collapse collapse;
|
||||
border 1px solid #dee2e6;
|
||||
background-color:var(--chat-content-bg);
|
||||
@@ -266,7 +265,7 @@ const reGenerate = (prompt) => {
|
||||
padding: 0.8rem 1.5rem;
|
||||
color: var(--quote-text-color);
|
||||
border-left: 0.4rem solid #6b50e1; /* 紫色边框 */
|
||||
font-size: 1.1rem;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
@@ -275,7 +274,7 @@ const reGenerate = (prompt) => {
|
||||
|
||||
.chat-line-reply-list {
|
||||
justify-content: center;
|
||||
background-color: var(--chat-list-bg);
|
||||
background-color: var(--chat-content-bg);
|
||||
color:var(--theme-text-color-primary);
|
||||
width 100%
|
||||
padding-bottom: 1.5rem;
|
||||
@@ -376,7 +375,8 @@ const reGenerate = (prompt) => {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
max-width 70%
|
||||
width 100%
|
||||
max-width calc(100% - 110px)
|
||||
|
||||
.content-wrapper {
|
||||
display flex
|
||||
@@ -391,6 +391,7 @@ const reGenerate = (prompt) => {
|
||||
// background-color #F5F5F5
|
||||
background-color :var(--chat-content-bg);
|
||||
border-radius: 0 10px 10px 10px;
|
||||
width 100%
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,14 +45,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
import { Delete, Plus } from "@element-plus/icons-vue";
|
||||
import { isImage, removeArrayItem } from "@/utils/libs";
|
||||
import { GetFileIcon } from "@/store/system";
|
||||
import { checkSession } from "@/store/cache";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import {reactive, ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {Delete, Plus} from "@element-plus/icons-vue";
|
||||
import {isImage, removeArrayItem} from "@/utils/libs";
|
||||
import {GetFileIcon} from "@/store/system";
|
||||
import {checkSession} from "@/store/cache";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const props = defineProps({
|
||||
userId: Number,
|
||||
});
|
||||
@@ -151,6 +152,7 @@ const insertURL = (file) => {
|
||||
.file-upload-img {
|
||||
.iconfont {
|
||||
font-size: 19px;
|
||||
cursor pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
</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 {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 gitURL = ref(process.env.VUE_APP_GITHUB_URL);
|
||||
const copyRight = ref("");
|
||||
const license = ref({});
|
||||
const props = defineProps({
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="theme-box" @click="toggleTheme">
|
||||
<span class="iconfont icon-yueliang">{{ themePage === "light" ? "" : "" }}</span>
|
||||
<i class="iconfont" :class="themePage === 'light'?'icon-yueliang':'icon-taiyang'"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import {ref} from "vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
// 定义主题状态,初始值从 localStorage 获取
|
||||
const store = useSharedStore();
|
||||
|
||||
@@ -58,9 +58,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { getSystemInfo } from "@/store/cache";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {getSystemInfo} from "@/store/cache";
|
||||
|
||||
const title = ref(process.env.VUE_APP_TITLE);
|
||||
const version = ref(process.env.VUE_APP_VERSION);
|
||||
@@ -132,7 +132,7 @@ const send = (text) => {
|
||||
line-height: 2.5rem
|
||||
font-weight 600
|
||||
margin-bottom: 4rem
|
||||
color var( --theme-textcolor-normal)
|
||||
color var(--text-color)
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineStore } from "pinia";
|
||||
import {defineStore} from "pinia";
|
||||
import Storage from "good-storage";
|
||||
|
||||
export const useSharedStore = defineStore("shared", {
|
||||
@@ -9,6 +9,7 @@ export const useSharedStore = defineStore("shared", {
|
||||
socket: { conn: null, handlers: {} },
|
||||
theme: Storage.get("theme", "light"),
|
||||
isLogin: false,
|
||||
chatListExtend: Storage.get("chat_list_extend", true),
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
@@ -29,6 +30,10 @@ export const useSharedStore = defineStore("shared", {
|
||||
}
|
||||
this.socket.conn = value;
|
||||
},
|
||||
setChatListExtend(value) {
|
||||
this.chatListExtend = value;
|
||||
Storage.set("chat_list_extend", value);
|
||||
},
|
||||
addMessageHandler(key, callback) {
|
||||
if (!this.socket.handlers[key]) {
|
||||
this.socket.handlers[key] = callback;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<div class="chat-page">
|
||||
<el-container>
|
||||
<el-aside>
|
||||
<el-aside v-show="store.chatListExtend">
|
||||
<div class="flex w-full justify-center pt-3 pb-3">
|
||||
<img :src="logo" style="max-height: 40px" :alt="title" v-if="logo !== ''"/>
|
||||
<h2 v-else>{{ title }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="media-page">
|
||||
<el-button @click="_newChat" type="primary" class="newChat">
|
||||
<el-icon style="margin-right: 5px">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
<i class="iconfont icon-new-chat mr-1"></i>
|
||||
新建对话
|
||||
</el-button>
|
||||
|
||||
@@ -19,7 +22,7 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-scrollbar :height="chatBoxHeight">
|
||||
<el-scrollbar :height="chatListHeight">
|
||||
<div class="content">
|
||||
<el-row v-for="chat in chatList" :key="chat.chat_id">
|
||||
<div :class="chat.chat_id === chatId ? 'chat-list-item active' : 'chat-list-item'" @click="loadChat(chat)">
|
||||
@@ -111,7 +114,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex justify-center">
|
||||
<div id="container" :style="{ height: mainWinHeight + 'px' }">
|
||||
<div class="chat-box" id="chat-box" :style="{ height: chatBoxHeight + 'px' }">
|
||||
<div v-if="showHello">
|
||||
@@ -229,26 +232,29 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import {nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import ChatPrompt from "@/components/ChatPrompt.vue";
|
||||
import ChatReply from "@/components/ChatReply.vue";
|
||||
import { Delete, Edit, InfoFilled, More, Plus, Promotion, Search, Share, VideoPause } from "@element-plus/icons-vue";
|
||||
import {Delete, Edit, InfoFilled, More, Promotion, Search, Share, VideoPause} from "@element-plus/icons-vue";
|
||||
import "highlight.js/styles/a11y-dark.css";
|
||||
import { isMobile, randString, removeArrayItem, UUID } from "@/utils/libs";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
import { useRouter } from "vue-router";
|
||||
import {isMobile, randString, removeArrayItem, UUID} from "@/utils/libs";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {useRouter} from "vue-router";
|
||||
import Clipboard from "clipboard";
|
||||
import { checkSession, getClientId, getSystemInfo } from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import Welcome from "@/components/Welcome.vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import FileSelect from "@/components/FileSelect.vue";
|
||||
import FileList from "@/components/FileList.vue";
|
||||
import ChatSetting from "@/components/ChatSetting.vue";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
import { closeLoading, showLoading, showMessageError } from "@/utils/dialog";
|
||||
import {closeLoading, showLoading, showMessageError} from "@/utils/dialog";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import emoji from "markdown-it-emoji";
|
||||
|
||||
const title = ref("GeekAI-智能助手");
|
||||
const logo = ref("");
|
||||
const models = ref([]);
|
||||
const modelID = ref(0);
|
||||
const chatData = ref([]);
|
||||
@@ -256,7 +262,7 @@ const allChats = ref([]); // 会话列表
|
||||
const chatList = ref(allChats.value);
|
||||
const mainWinHeight = ref(0); // 主窗口高度
|
||||
const chatBoxHeight = ref(0); // 聊天内容框高度
|
||||
const leftBoxHeight = ref(0);
|
||||
const chatListHeight = ref(0); // 聊天列表高度
|
||||
const loading = ref(false);
|
||||
const loginUser = ref(null);
|
||||
const roles = ref([]);
|
||||
@@ -311,6 +317,7 @@ if (!chatId.value) {
|
||||
// 查询对话信息
|
||||
httpGet("/api/chat/detail", { chat_id: chatId.value })
|
||||
.then((res) => {
|
||||
document.title = res.data.title;
|
||||
roleId.value = res.data.role_id;
|
||||
modelID.value = res.data.model_id;
|
||||
})
|
||||
@@ -324,14 +331,12 @@ getSystemInfo()
|
||||
.then((res) => {
|
||||
config.value = res.data;
|
||||
title.value = config.value.title;
|
||||
logo.value = res.data.bar_logo;
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message);
|
||||
});
|
||||
|
||||
import MarkdownIt from "markdown-it";
|
||||
import emoji from "markdown-it-emoji";
|
||||
|
||||
const md = new MarkdownIt({
|
||||
breaks: true,
|
||||
html: true,
|
||||
@@ -540,17 +545,10 @@ const getRoleById = function (rid) {
|
||||
};
|
||||
|
||||
const resizeElement = function () {
|
||||
chatBoxHeight.value = window.innerHeight - 101 - 82 - 38;
|
||||
chatListHeight.value = window.innerHeight - 240;
|
||||
// chatBoxHeight.value = window.innerHeight;
|
||||
|
||||
// mainWinHeight.value = window.innerHeight - 101;
|
||||
mainWinHeight.value = window.innerHeight - 59;
|
||||
|
||||
// mainWinHeight.value = window.innerHeight;
|
||||
|
||||
// leftBoxHeight.value = window.innerHeight - 90 - 45 - 82;
|
||||
// leftBoxHeight.value = window.innerHeight - 90 - 82;
|
||||
leftBoxHeight.value = window.innerHeight - 90 - 100;
|
||||
mainWinHeight.value = window.innerHeight - 50;
|
||||
chatBoxHeight.value = window.innerHeight - 101 - 82 - 38;
|
||||
};
|
||||
|
||||
const _newChat = () => {
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="tab-box">
|
||||
<div class="flex-center-col big-top-title xxx">
|
||||
<div class="flex-center-col" @click="isCollapse = !isCollapse">
|
||||
<el-tooltip content="展开菜单" placement="right" v-if="isCollapse">
|
||||
<i class="iconfont icon-expand"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="flex" :class="{ 'top-collapse': !isCollapse }">
|
||||
<div class="top-avatar flex">
|
||||
<span class="title" v-if="!isCollapse">{{ title }}</span>
|
||||
<el-tooltip :content="title" placement="right">
|
||||
<img :src="logo" alt="" :class="{ marr: !isCollapse }" v-if="isCollapse" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="menuIcon xxx" @click="isCollapse = !isCollapse">
|
||||
<el-tooltip content="关闭菜单" placement="right" v-if="!isCollapse">
|
||||
<div class="flex-center-col pt-2 mb-2">
|
||||
<div class="flex flex-center-col">
|
||||
<div class="menuIcon" @click="store.setChatListExtend(!store.chatListExtend)">
|
||||
<el-tooltip content="隐藏对话列表" placement="right" v-if="store.chatListExtend">
|
||||
<i class="iconfont icon-colspan"></i>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="展开对话列表" placement="right" v-else>
|
||||
<i class="iconfont icon-expand"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-list" :style="{ width: isCollapse ? '65px' : '170px' }" :class="{ 'menu-list-collapse': !isCollapse }">
|
||||
<div class="menu-list">
|
||||
<ul>
|
||||
<li
|
||||
class="menu-list-item flex-center-col"
|
||||
@@ -31,29 +23,28 @@
|
||||
@click="changeNav(item)"
|
||||
:class="item.url === curPath ? 'active' : ''"
|
||||
>
|
||||
<span v-if="item.icon.startsWith('icon')" :class="{ 'mr-1 ml-2': !isCollapse }">
|
||||
<span v-if="item.icon.startsWith('icon')">
|
||||
<i class="iconfont" :class="item.icon"></i>
|
||||
</span>
|
||||
<el-image :src="item.icon" class="el-icon ml-1" v-else />
|
||||
<div class="menu-title" :class="{ 'menu-title-collapse': !isCollapse }">
|
||||
<div class="menu-title">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 更多 -->
|
||||
<div class="bot" :style="{ width: isCollapse ? '65px' : '170px' }">
|
||||
<div class="bot-line"></div>
|
||||
|
||||
<el-popover v-if="moreNavs.length > 0" placement="right-end" trigger="hover">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<el-icon><CirclePlus /></el-icon>
|
||||
<div class="menu-title">更多</div>
|
||||
</li>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus">
|
||||
<li
|
||||
<!-- 更多 -->
|
||||
<div class="bot p-2">
|
||||
<div class="bot-line"></div>
|
||||
<el-popover v-if="moreNavs.length > 0" placement="right-end" trigger="hover">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<i class="iconfont icon-more"/>
|
||||
</li>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus">
|
||||
<li
|
||||
v-for="(item, index) in moreNavs"
|
||||
:key="item.url"
|
||||
:class="{
|
||||
@@ -61,57 +52,52 @@
|
||||
moreTitle: index !== 3 && index !== 4,
|
||||
twoTittle: index === 3 || index === 4,
|
||||
}"
|
||||
>
|
||||
<a @click="changeNav(item)">
|
||||
>
|
||||
<a @click="changeNav(item)">
|
||||
<span v-if="item.icon.startsWith('icon')" class="mr-2">
|
||||
<i class="iconfont" :class="item.icon"></i>
|
||||
</span>
|
||||
<el-image :src="item.icon" style="width: 20px; height: 20px" v-else />
|
||||
<span :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-popover placement="right-end" trigger="hover" v-if="loginUser.id">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<el-icon><Setting /></el-icon>
|
||||
<div v-if="!isCollapse">设置</div>
|
||||
<el-image :src="item.icon" style="width: 20px; height: 20px" v-else/>
|
||||
<span :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus setting-menus">
|
||||
<li>
|
||||
<div @click="showConfigDialog = true" class="flex">
|
||||
<el-icon>
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
<span class="username title">{{ loginUser.nickname }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a @click="logout" class="flex">
|
||||
<i class="iconfont icon-logout"></i>
|
||||
<span class="title">退出登录</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
<li class="menu-bot-item">
|
||||
<a :href="gitURL" class="link-button" target="_blank" v-if="!license.de_copy && !isCollapse">
|
||||
<i class="iconfont icon-github"></i>
|
||||
</a>
|
||||
|
||||
<a @click="router.push('/')" class="link-button">
|
||||
<i class="iconfont icon-house"></i>
|
||||
</a>
|
||||
|
||||
<ThemeChange />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-popover placement="right-end" trigger="hover" v-if="loginUser.id">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<i class="iconfont icon-config"/>
|
||||
</li>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus setting-menus">
|
||||
<li>
|
||||
<div @click="showConfigDialog = true" class="flex">
|
||||
<el-icon>
|
||||
<UserFilled/>
|
||||
</el-icon>
|
||||
<span class="username title">{{ loginUser.nickname }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a @click="logout" class="flex">
|
||||
<i class="iconfont icon-logout"></i>
|
||||
<span class="title">退出登录</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
<div class="menu-bot-item">
|
||||
<a @click="router.push('/')" class="link-button">
|
||||
<i class="iconfont icon-house"></i>
|
||||
</a>
|
||||
<div class="pl-1">
|
||||
<ThemeChange/>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-scrollbar class="right-main">
|
||||
@@ -141,21 +127,19 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { CirclePlus, Setting, UserFilled } from "@element-plus/icons-vue";
|
||||
import {UserFilled} from "@element-plus/icons-vue";
|
||||
import ThemeChange from "@/components/ThemeChange.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { httpGet } from "@/utils/http";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { checkSession, getLicenseInfo, getSystemInfo } from "@/store/cache";
|
||||
import { removeUserToken } from "@/store/session";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import {useRouter} from "vue-router";
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {checkSession, getLicenseInfo, getSystemInfo} from "@/store/cache";
|
||||
import {removeUserToken} from "@/store/session";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import ConfigDialog from "@/components/UserInfoDialog.vue";
|
||||
import { showMessageError } from "@/utils/dialog";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import { substr } from "@/utils/libs";
|
||||
|
||||
const isCollapse = ref(true);
|
||||
const router = useRouter();
|
||||
const logo = ref("");
|
||||
const mainNavs = ref([]);
|
||||
@@ -163,13 +147,11 @@ const moreNavs = ref([]);
|
||||
const curPath = ref();
|
||||
|
||||
const title = ref("");
|
||||
const avatarImg = ref("/images/avatar/default.jpg");
|
||||
const store = useSharedStore();
|
||||
const loginUser = ref({});
|
||||
const routerViewKey = ref(0);
|
||||
const showConfigDialog = ref(false);
|
||||
const license = ref({ de_copy: true });
|
||||
const gitURL = ref(process.env.VUE_APP_GIT_URL);
|
||||
const showLoginDialog = ref(false);
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,25 +9,24 @@
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<span v-if="!license.de_copy">
|
||||
<el-tooltip v-if="!license.de_copy" class="box-item" content="部署文档" placement="bottom">
|
||||
<el-tooltip class="box-item" content="部署文档" placement="bottom">
|
||||
<a :href="docsURL" class="link-button mr-2" target="_blank">
|
||||
<i class="iconfont icon-book"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="!license.de_copy" class="box-item" content="项目源码" placement="bottom">
|
||||
<el-tooltip class="box-item" content="Github 源码" placement="bottom">
|
||||
<a :href="gitURL" class="link-button" target="_blank">
|
||||
<i class="iconfont icon-github"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="box-item" content="Gitee 源码" placement="bottom">
|
||||
<a :href="gitURL" class="link-button" target="_blank">
|
||||
<i class="iconfont icon-gitee"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
|
||||
<span v-if="!isLogin">
|
||||
<!-- <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>
|
||||
@@ -59,14 +58,14 @@
|
||||
</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 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";
|
||||
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();
|
||||
|
||||
@@ -81,7 +80,7 @@ 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 gitURL = ref(process.env.VUE_APP_GITHUB_URL);
|
||||
const navs = ref([]);
|
||||
|
||||
const iconMap = ref({
|
||||
@@ -150,7 +149,6 @@ const setContent = () => {
|
||||
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); // 逐字符追加
|
||||
@@ -164,7 +162,7 @@ const setContent = () => {
|
||||
const rainbowColor = (index) => {
|
||||
const hue = (index * 40) % 360; // 每个字符间隔40度,形成彩虹色
|
||||
return `hsl(${hue}, 90%, 50%)`; // 色调(hue),饱和度(70%),亮度(50%)
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
<el-form-item label="网站Slogan" prop="slogan">
|
||||
<el-input v-model="system['slogan']" />
|
||||
</el-form-item>
|
||||
<el-form-item label="网站 LOGO" prop="logo">
|
||||
<el-input v-model="system['logo']" placeholder="网站LOGO图片">
|
||||
<el-form-item label="圆形 LOGO" prop="logo">
|
||||
<el-input v-model="system['logo']" placeholder="正方形或者圆形 Logo">
|
||||
<template #append>
|
||||
<el-upload :auto-upload="true" :show-file-list="false" @click="beforeUpload('logo')" :http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
@@ -26,7 +26,18 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="条形 LOGO" prop="logo">
|
||||
<el-input v-model="system['bar_logo']" placeholder="长方形 Logo">
|
||||
<template #append>
|
||||
<el-upload :auto-upload="true" :show-file-list="false" @click="beforeUpload('bar_logo')"
|
||||
:http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
<UploadFilled/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<div class="label-title">
|
||||
@@ -359,17 +370,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import Compressor from "compressorjs";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { CloseBold, InfoFilled, Select, UploadFilled } from "@element-plus/icons-vue";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {CloseBold, InfoFilled, Select, UploadFilled} from "@element-plus/icons-vue";
|
||||
import MdEditor from "md-editor-v3";
|
||||
import "md-editor-v3/lib/style.css";
|
||||
import Menu from "@/views/admin/Menu.vue";
|
||||
import { copyObj, dateFormat } from "@/utils/libs";
|
||||
import {copyObj, dateFormat} from "@/utils/libs";
|
||||
import ItemsInput from "@/components/ui/ItemsInput.vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const activeName = ref("basic");
|
||||
const system = ref({ models: [] });
|
||||
|
||||
Reference in New Issue
Block a user