支持在 Chat 页面显示,隐藏对话列表

This commit is contained in:
RockYang 2025-01-06 19:17:18 +08:00
parent cffc722622
commit 7da5b7163c
27 changed files with 404 additions and 393 deletions

View File

@ -131,7 +131,8 @@ type SystemConfig struct {
Title string `json:"title,omitempty"` // 网站标题 Title string `json:"title,omitempty"` // 网站标题
Slogan string `json:"slogan,omitempty"` // 网站 slogan Slogan string `json:"slogan,omitempty"` // 网站 slogan
AdminTitle string `json:"admin_title,omitempty"` // 管理后台标题 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"` // 新用户注册赠送算力值 InitPower int `json:"init_power,omitempty"` // 新用户注册赠送算力值
DailyPower int `json:"daily_power,omitempty"` // 每日签到赠送算力 DailyPower int `json:"daily_power,omitempty"` // 每日签到赠送算力
InvitePower int `json:"invite_power,omitempty"` // 邀请新用户赠送算力值 InvitePower int `json:"invite_power,omitempty"` // 邀请新用户赠送算力值

View File

@ -92,6 +92,8 @@ func (h *ChatHandler) sendOpenAiMessage(
if strings.HasPrefix(req.Model, "o1-") { if strings.HasPrefix(req.Model, "o1-") {
content := fmt.Sprintf("AI 思考结束,耗时:%d 秒。\n\n", time.Now().Unix()-session.Start) 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) utils.SendChunkMsg(ws, content)
} }

View File

@ -134,6 +134,7 @@ func (h *WebsocketHandler) Client(c *gin.Context) {
if err != nil { if err != nil {
logger.Error(err, chatModel) logger.Error(err, chatModel)
} }
session.Model.Id = chatModel.Id
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
h.chatHandler.ReqCancelFunc.Put(clientId, cancel) h.chatHandler.ReqCancelFunc.Put(clientId, cancel)
err = h.chatHandler.sendMessage(ctx, session, chatRole, chatMessage.Content, client) err = h.chatHandler.sendMessage(ctx, session, chatRole, chatMessage.Content, client)

View File

@ -8,4 +8,7 @@ VUE_APP_KEY_PREFIX=GeekAI_DEV_
VUE_APP_TITLE="Geek-AI 创作系统" VUE_APP_TITLE="Geek-AI 创作系统"
VUE_APP_VERSION=v4.1.9 VUE_APP_VERSION=v4.1.9
VUE_APP_DOCS_URL=https://docs.geekai.me 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

View File

@ -3,4 +3,6 @@ VUE_APP_WS_HOST=
VUE_APP_KEY_PREFIX=GeekAI_ VUE_APP_KEY_PREFIX=GeekAI_
VUE_APP_VERSION=v4.1.9 VUE_APP_VERSION=v4.1.9
VUE_APP_DOCS_URL=https://docs.geekai.me 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -30,14 +30,6 @@
margin-bottom: 10px margin-bottom: 10px
} }
//
::-webkit-scrollbar {
width: 0;
height: 0;
background-color: transparent;
}
.content { .content {
width: 100% width: 100%
overflow-y: scroll overflow-y: scroll
@ -130,7 +122,7 @@
.chat-container { .chat-container {
min-width: 0; min-width: 0;
flex: 1; flex: 1;
background-color: var(--el-bg-color) background-color: var(--chat-bg)
color var(--text-fb) color var(--text-fb)
.chat-config { .chat-config {
@ -186,47 +178,25 @@
width: 100%; width: 100%;
position relative position relative
background: var(--chat-bg) background: var(--chat-bg)
display: flex;
::-webkit-scrollbar { justify-content: center;
width: 12px /* */ padding 0 20px
background #F1F1F1 max-width: 960px;
}
::-webkit-scrollbar-track {
background-color: #e1e1e1;
}
::-webkit-scrollbar-thumb {
background-color: #c1c1c1;
border-radius 12px
}
::-webkit-scrollbar-thumb:hover {
background-color: #A8A8A8;
}
.chat-box { .chat-box {
overflow-y: auto; overflow-y: auto;
//border-bottom: 1px solid #4f4f4f
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IEEdge */
// //
--content-font-size: 16px; --content-font-size: 16px;
--content-color: #c1c1c1; --content-color: #c1c1c1;
font-family: 'Microsoft YaHei', '', Arial, sans-serif; font-family: 'Microsoft YaHei', '', Arial, sans-serif;
padding: 0 0 50px 0; //padding: 0 0 50px 0;
width: 100%;
.chat-line { .chat-line {
font-size: 14px; font-size: 14px;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
}
::-webkit-scrollbar {
display: none; /* Webkit */
} }
} }
@ -234,6 +204,7 @@
position absolute position absolute
bottom 0 bottom 0
width 100% width 100%
max-width: 800px;
.input-box-inner { .input-box-inner {
display flex display flex
@ -363,11 +334,6 @@
} }
} }
#container::-webkit-scrollbar {
width: 0;
height: 0;
}
} }
} }
} }

View File

@ -2,22 +2,28 @@
display: flex; display: flex;
position: relative; position: relative;
height: 100vh; height: 100vh;
.big-top-title { .big-top-title {
padding-top: 10px; padding-top: 10px;
} }
.top-collapse { .top-collapse {
padding-top: 10px padding-top: 10px
img { img {
width 24px !important width 24px !important
height: 24px !important height: 24px !important
} }
} }
.tab-box { .tab-box {
align-items: center align-items: center
background-color: var(--card-bg) background-color: var(--card-bg)
border-right: 1px solid var(--line-box);
// height: 100% // height: 100%
// position: fixed; // position: fixed;
height: 100vh; height: 100vh;
.title { .title {
font-size: 28px font-size: 28px
height: 40px height: 40px
@ -28,6 +34,7 @@
font-weight: 700 font-weight: 700
color: var(--text-theme-color) color: var(--text-theme-color)
} }
img { img {
height: 44px height: 44px
object-fit: cover object-fit: cover
@ -35,6 +42,7 @@
border: 2px solid #754ff6; border: 2px solid #754ff6;
background: #fff background: #fff
} }
.marr { .marr {
margin-right: 4px; margin-right: 4px;
} }
@ -42,34 +50,29 @@
} }
} }
.flex-center-col { .flex-center-col {
display flex display flex
align-items center align-items center
flex-direction column flex-direction column
.iconfont { .iconfont {
font-size 22px
}
.icon-expand {
font-size 24px font-size 24px
margin-bottom 10px
cursor pointer cursor pointer
color var(--text-color) color var(--text-color)
} }
.icon-colspan { .icon-new-chat {
font-size 18px color #ffffff
margin-left 3px
cursor pointer
color var(--text-color)
} }
} }
.menu-list-collapse { .menu-list-collapse {
.flex-center-col { .flex-center-col {
flex-direction: row; flex-direction: row;
} }
.menu-list-item { .menu-list-item {
height: 38px; height: 38px;
@ -80,51 +83,48 @@
} }
} }
.menu-list-item:hover, .menu-list-item:hover,
.active { .active {
background: rgba(79, 89, 102, .122); background: rgba(79, 89, 102, .122);
border-radius: 8px; border-radius: 8px;
.el-icon { .el-icon {
background: transparent !important; background: transparent !important;
} }
} }
.menu-title { .menu-title {
font-size: 15px !important; font-size: 15px !important;
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
} }
.openicon{
font-size: 40px;
color: #754ff6;
}
.menuIcon{
.openicon{
font-size: 28px;
color: #754ff6;
}
}
.menu-list { .menu-list {
margin-top: 20px; width: 65px;
.svg-icon { .svg-icon {
svg { svg {
width: 30px; width: 30px;
height: 30px; height: 30px;
} }
} }
.menu-list-item { .menu-list-item {
// margin-bottom: 10px; // margin-bottom: 10px;
margin: 0 8px 8px; margin: 0 8px 8px;
cursor: pointer; cursor: pointer;
font-weight: 550; font-weight: 550;
color: var(--text-theme-color);
&:hover { &:hover {
.el-icon { .el-icon {
background: rgba(79, 89, 102, .122); background: rgba(79, 89, 102, .122);
} }
} }
.el-icon { .el-icon {
width: 24px; width: 24px;
height: 24px; height: 24px;
@ -138,24 +138,22 @@
// } // }
} }
&.active { &.active {
color: var(--text-color); color: var(--text-theme-color);
font-weight: 700; font-weight: 700;
filter: none !important;
.el-icon{
background: rgba(79, 89, 102, .122);
filter: invert(100%);
}
} }
} }
.bot { .bot {
position: absolute; position: absolute;
bottom: 6px; bottom: 6px;
width 65px;
} }
.bot-line { .bot-line {
width: 100%; width: 100%;
@ -163,44 +161,48 @@
background: var(--line-box) background: var(--line-box)
margin: 20px 0 10px 0; margin: 20px 0 10px 0;
} }
.menu-title { .menu-title {
font-size: 12px; font-size: 12px;
margin-bottom: 6px; margin-bottom: 6px;
} }
.icon-house, .icon-house,
.icon-github { .icon-github {
font-size: 20px; font-size: 20px;
color: #754ff6; color: #754ff6;
cursor pointer cursor pointer
} }
.menu-bot-item { .menu-bot-item {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
align-items: center;
a{
// margin-right: 46px;
}
} }
} }
::v-deep(.theme-box) { ::v-deep(.theme-box) {
position: relative !important; position: relative !important;
right: initial; right: initial;
bottom: initial; bottom: initial;
width: 20px; width: 20px;
height: 20px; height: 20px;
line-height: 20px; line-height: 18px;
.iconfont { .iconfont {
font-size: 15px !important;} font-size: 15px !important;
} }
}
.right-main { .right-main {
height: 100%; height: 100%;
// background: #f5f7fd; // background: #f5f7fd;
background: var(--theme-bg-all); background: var(--theme-bg-all);
// background-image: linear-gradient(180deg, rgba(247, 232, 255, .54), rgba(191, 223, 255, .35)); // background-image: linear-gradient(180deg, rgba(247, 232, 255, .54), rgba(191, 223, 255, .35));
width: 100%; width: 100%;
.loginMask { .loginMask {
position: absolute; position: absolute;
top: 0; top: 0;
@ -208,6 +210,7 @@
height: 100%; height: 100%;
z-index: 999; z-index: 999;
} }
.topheader { .topheader {
display: flex; display: flex;
position: fixed; position: fixed;
@ -218,6 +221,7 @@
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
} }
.btn-go { .btn-go {
background: #754ff6; background: #754ff6;
margin: 10px 10px 0; margin: 10px 10px 0;
@ -225,7 +229,6 @@
} }
.el-popper { .el-popper {
.more-menus { .more-menus {
li { li {
@ -251,10 +254,12 @@
background: rgba(79, 89, 102, 0.1); background: rgba(79, 89, 102, 0.1);
} }
} }
.setting-menus { .setting-menus {
.title { .title {
color: #222226; color: #222226;
} }
.el-icon, .el-icon,
.iconfont { .iconfont {
font-size: 18px font-size: 18px
@ -262,6 +267,7 @@
} }
color: #222226; color: #222226;
} }
.username { .username {
display: -webkit-box; display: -webkit-box;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
@ -271,20 +277,24 @@
} }
.rightHeightMax { .rightHeightMax {
height: 100vh; height: 100vh;
max-height: 100vh; max-height: 100vh;
overflow: hidden; overflow: hidden;
} }
.rightHeight { .rightHeight {
height: calc(100vh - 42px); height: calc(100vh - 42px);
max-height: calc(100vh - 42px); max-height: calc(100vh - 42px);
overflow: hidden; overflow: hidden;
.content { .content {
padding-top: 42px; padding-top: 42px;
} }
} }
.content { .content {
height: 100%; height: 100%;
overflow: scroll; overflow: scroll;

View File

@ -43,7 +43,7 @@
--el-bg-color-overlay: rgba(17, 28, 68, 1); --el-bg-color-overlay: rgba(17, 28, 68, 1);
--el-border-color-light: rgba(255, 255, 255, 0.2); --el-border-color-light: rgba(255, 255, 255, 0.2);
--chat-content-bg:rgba(86, 86, 95, .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; --hover-deep-color:#30323c;
//layout //layout
.more-menus li.moreTitle, .more-menus li.moreTitle,
@ -52,7 +52,7 @@
.setting-menus li .el-icon, .setting-menus li .el-icon,
.setting-menus li .iconfont, .setting-menus li .iconfont,
.layout .tab-box .menu-list-item{ .layout .tab-box .menu-list-item{
filter: invert(100%); //filter: invert(100%);
} }
.more-menus span.title{ .more-menus span.title{
color:#000; color:#000;

View File

@ -31,8 +31,8 @@
--theme-text-primary: #000; --theme-text-primary: #000;
--theme-text-color-secondary: #666; --theme-text-color-secondary: #666;
--chat-content-bg:#f5f7fc; --chat-content-bg:#f5f7fc;
--chat-user-content-bg: #e0dfff;
--chat-list-bg: #0302020a; --chat-list-bg: #0302020a;
--chat-content-bg-list:#fff;
--chat-wel-bg:rgba(247, 247, 248, 1); --chat-wel-bg:rgba(247, 247, 248, 1);
--hover-deep-color:#fff; --hover-deep-color:#fff;
--el-bg-color-overlay: #fff; --el-bg-color-overlay: #fff;

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=1734934068681') format('woff2'), src: url('iconfont.woff2?t=1736144380052') format('woff2'),
url('iconfont.woff?t=1734934068681') format('woff'), url('iconfont.woff?t=1736144380052') format('woff'),
url('iconfont.ttf?t=1734934068681') format('truetype'); url('iconfont.ttf?t=1736144380052') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-gitee:before {
content: "\e6d0";
}
.icon-redeem:before { .icon-redeem:before {
content: "\e61a"; content: "\e61a";
} }

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": "6905420",
"name": "码云",
"font_class": "gitee",
"unicode": "e6d0",
"unicode_decimal": 59088
},
{ {
"icon_id": "3624396", "icon_id": "3624396",
"name": "兑换码", "name": "兑换码",

Binary file not shown.

View File

@ -17,7 +17,10 @@
</div> </div>
<div class="body"> <div class="body">
<div class="title"> <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>
<div class="info"> <div class="info">
<span>{{ GetFileType(file.ext) }}</span> <span>{{ GetFileType(file.ext) }}</span>
@ -56,7 +59,10 @@
</div> </div>
<div class="body"> <div class="body">
<div class="title"> <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>
<div class="info"> <div class="info">
<span>{{ GetFileType(file.ext) }}</span> <span>{{ GetFileType(file.ext) }}</span>
@ -162,7 +168,8 @@ const processFiles = () => {
} }
} }
}) })
.catch(() => {}); .catch(() => {
});
for (let link of links) { for (let link of links) {
content.value = content.value.replace(link, ""); content.value = content.value.replace(link, "");
@ -213,6 +220,7 @@ const isExternalImg = (link, files) => {
.file-list-box { .file-list-box {
display flex display flex
flex-flow column flex-flow column
.image { .image {
display flex display flex
flex-flow row flex-flow row
@ -225,6 +233,7 @@ const isExternalImg = (link, files) => {
margin-bottom 10px margin-bottom 10px
} }
} }
.item { .item {
display flex display flex
flex-flow row flex-flow row
@ -241,14 +250,17 @@ const isExternalImg = (link, files) => {
height 40px height 40px
} }
} }
.body { .body {
margin-left 8px margin-left 8px
font-size 14px font-size 14px
.title { .title {
font-weight bold font-weight bold
line-height 24px line-height 24px
color #0D0D0D color #0D0D0D
} }
.info { .info {
color #B4B4B4 color #B4B4B4
@ -336,11 +348,12 @@ const isExternalImg = (link, files) => {
.chat-item { .chat-item {
padding: 0; padding: 0;
overflow: hidden; overflow: hidden;
max-width 60% max-width calc(100% - 110px);
.file-list-box { .file-list-box {
display flex display flex
flex-flow column flex-flow column
.image { .image {
display flex display flex
flex-flow row flex-flow row
@ -353,6 +366,7 @@ const isExternalImg = (link, files) => {
margin-bottom 10px margin-bottom 10px
} }
} }
.item { .item {
display flex display flex
flex-flow row flex-flow row
@ -369,14 +383,17 @@ const isExternalImg = (link, files) => {
height 40px height 40px
} }
} }
.body { .body {
margin-left 8px margin-left 8px
font-size 14px font-size 14px
.title { .title {
font-weight bold font-weight bold
line-height 24px line-height 24px
color #0D0D0D color #0D0D0D
} }
.info { .info {
color #B4B4B4 color #B4B4B4
@ -392,13 +409,14 @@ const isExternalImg = (link, files) => {
.content-wrapper { .content-wrapper {
display flex display flex
flex-flow row-reverse flex-flow row-reverse
.content { .content {
word-break break-word; word-break break-word;
padding: 1rem padding: 1rem
color var(--theme-text-primary); color var(--theme-text-primary);
font-size: var(--content-font-size); font-size: var(--content-font-size);
overflow: auto; overflow: auto;
background-color :var(--chat-content-bg); background-color: var(--chat-user-content-bg);
border-radius: 10px 0 10px 10px; border-radius: 10px 0 10px 10px;
img { img {
@ -421,6 +439,7 @@ const isExternalImg = (link, files) => {
} }
} }
.bar { .bar {
padding 10px 10px 10px 0; padding 10px 10px 10px 0;

View File

@ -237,7 +237,6 @@ const reGenerate = (prompt) => {
table { table {
width 100% width 100%
margin-bottom 1rem margin-bottom 1rem
color #212529
border-collapse collapse; border-collapse collapse;
border 1px solid #dee2e6; border 1px solid #dee2e6;
background-color:var(--chat-content-bg); background-color:var(--chat-content-bg);
@ -266,7 +265,7 @@ const reGenerate = (prompt) => {
padding: 0.8rem 1.5rem; padding: 0.8rem 1.5rem;
color: var(--quote-text-color); color: var(--quote-text-color);
border-left: 0.4rem solid #6b50e1; /* 紫色边框 */ border-left: 0.4rem solid #6b50e1; /* 紫色边框 */
font-size: 1.1rem; font-size: 16px;
line-height: 1.6; line-height: 1.6;
} }
} }
@ -275,7 +274,7 @@ const reGenerate = (prompt) => {
.chat-line-reply-list { .chat-line-reply-list {
justify-content: center; justify-content: center;
background-color: var(--chat-list-bg); background-color: var(--chat-content-bg);
color:var(--theme-text-color-primary); color:var(--theme-text-color-primary);
width 100% width 100%
padding-bottom: 1.5rem; padding-bottom: 1.5rem;
@ -376,7 +375,8 @@ const reGenerate = (prompt) => {
position: relative; position: relative;
padding: 0; padding: 0;
overflow: hidden; overflow: hidden;
max-width 70% width 100%
max-width calc(100% - 110px)
.content-wrapper { .content-wrapper {
display flex display flex
@ -391,6 +391,7 @@ const reGenerate = (prompt) => {
// background-color #F5F5F5 // background-color #F5F5F5
background-color :var(--chat-content-bg); background-color :var(--chat-content-bg);
border-radius: 0 10px 10px 10px; border-radius: 0 10px 10px 10px;
width 100%
} }
} }

View File

@ -53,6 +53,7 @@ import { isImage, removeArrayItem } from "@/utils/libs";
import {GetFileIcon} from "@/store/system"; import {GetFileIcon} from "@/store/system";
import {checkSession} from "@/store/cache"; import {checkSession} from "@/store/cache";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
const props = defineProps({ const props = defineProps({
userId: Number, userId: Number,
}); });
@ -151,6 +152,7 @@ const insertURL = (file) => {
.file-upload-img { .file-upload-img {
.iconfont { .iconfont {
font-size: 19px; font-size: 19px;
cursor pointer;
} }
} }

View File

@ -15,13 +15,12 @@
</template> </template>
<script setup> <script setup>
import {ref} from "vue"; import {ref} from "vue";
import { httpGet } from "@/utils/http";
import {showMessageError} from "@/utils/dialog"; import {showMessageError} from "@/utils/dialog";
import {getLicenseInfo, getSystemInfo} from "@/store/cache"; import {getLicenseInfo, getSystemInfo} from "@/store/cache";
const title = ref(""); const title = ref("");
const version = ref(process.env.VUE_APP_VERSION); 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 copyRight = ref("");
const license = ref({}); const license = ref({});
const props = defineProps({ const props = defineProps({

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="theme-box" @click="toggleTheme"> <div class="theme-box" @click="toggleTheme">
<span class="iconfont icon-yueliang">{{ themePage === "light" ? "&#xe679;" : "&#xe60b;" }}</span> <i class="iconfont" :class="themePage === 'light'?'icon-yueliang':'icon-taiyang'"></i>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from "vue"; import {ref} from "vue";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
// localStorage // localStorage

View File

@ -132,7 +132,7 @@ const send = (text) => {
line-height: 2.5rem line-height: 2.5rem
font-weight 600 font-weight 600
margin-bottom: 4rem margin-bottom: 4rem
color var( --theme-textcolor-normal) color var(--text-color)
} }
.grid-content { .grid-content {

View File

@ -9,6 +9,7 @@ export const useSharedStore = defineStore("shared", {
socket: { conn: null, handlers: {} }, socket: { conn: null, handlers: {} },
theme: Storage.get("theme", "light"), theme: Storage.get("theme", "light"),
isLogin: false, isLogin: false,
chatListExtend: Storage.get("chat_list_extend", true),
}), }),
getters: {}, getters: {},
actions: { actions: {
@ -29,6 +30,10 @@ export const useSharedStore = defineStore("shared", {
} }
this.socket.conn = value; this.socket.conn = value;
}, },
setChatListExtend(value) {
this.chatListExtend = value;
Storage.set("chat_list_extend", value);
},
addMessageHandler(key, callback) { addMessageHandler(key, callback) {
if (!this.socket.handlers[key]) { if (!this.socket.handlers[key]) {
this.socket.handlers[key] = callback; this.socket.handlers[key] = callback;

View File

@ -1,12 +1,15 @@
<template> <template>
<div class="chat-page"> <div class="chat-page">
<el-container> <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"> <div class="media-page">
<el-button @click="_newChat" type="primary" class="newChat"> <el-button @click="_newChat" type="primary" class="newChat">
<el-icon style="margin-right: 5px"> <i class="iconfont icon-new-chat mr-1"></i>
<Plus />
</el-icon>
新建对话 新建对话
</el-button> </el-button>
@ -19,7 +22,7 @@
</template> </template>
</el-input> </el-input>
</div> </div>
<el-scrollbar :height="chatBoxHeight"> <el-scrollbar :height="chatListHeight">
<div class="content"> <div class="content">
<el-row v-for="chat in chatList" :key="chat.chat_id"> <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)"> <div :class="chat.chat_id === chatId ? 'chat-list-item active' : 'chat-list-item'" @click="loadChat(chat)">
@ -111,7 +114,7 @@
</div> </div>
</div> </div>
<div> <div class="flex justify-center">
<div id="container" :style="{ height: mainWinHeight + 'px' }"> <div id="container" :style="{ height: mainWinHeight + 'px' }">
<div class="chat-box" id="chat-box" :style="{ height: chatBoxHeight + 'px' }"> <div class="chat-box" id="chat-box" :style="{ height: chatBoxHeight + 'px' }">
<div v-if="showHello"> <div v-if="showHello">
@ -232,7 +235,7 @@
import {nextTick, onMounted, onUnmounted, ref, watch} from "vue"; import {nextTick, onMounted, onUnmounted, ref, watch} from "vue";
import ChatPrompt from "@/components/ChatPrompt.vue"; import ChatPrompt from "@/components/ChatPrompt.vue";
import ChatReply from "@/components/ChatReply.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 "highlight.js/styles/a11y-dark.css";
import {isMobile, randString, removeArrayItem, UUID} from "@/utils/libs"; import {isMobile, randString, removeArrayItem, UUID} from "@/utils/libs";
import {ElMessage, ElMessageBox} from "element-plus"; import {ElMessage, ElMessageBox} from "element-plus";
@ -247,8 +250,11 @@ import FileList from "@/components/FileList.vue";
import ChatSetting from "@/components/ChatSetting.vue"; import ChatSetting from "@/components/ChatSetting.vue";
import BackTop from "@/components/BackTop.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 title = ref("GeekAI-智能助手");
const logo = ref("");
const models = ref([]); const models = ref([]);
const modelID = ref(0); const modelID = ref(0);
const chatData = ref([]); const chatData = ref([]);
@ -256,7 +262,7 @@ const allChats = ref([]); // 会话列表
const chatList = ref(allChats.value); const chatList = ref(allChats.value);
const mainWinHeight = ref(0); // const mainWinHeight = ref(0); //
const chatBoxHeight = ref(0); // const chatBoxHeight = ref(0); //
const leftBoxHeight = ref(0); const chatListHeight = ref(0); //
const loading = ref(false); const loading = ref(false);
const loginUser = ref(null); const loginUser = ref(null);
const roles = ref([]); const roles = ref([]);
@ -311,6 +317,7 @@ if (!chatId.value) {
// //
httpGet("/api/chat/detail", { chat_id: chatId.value }) httpGet("/api/chat/detail", { chat_id: chatId.value })
.then((res) => { .then((res) => {
document.title = res.data.title;
roleId.value = res.data.role_id; roleId.value = res.data.role_id;
modelID.value = res.data.model_id; modelID.value = res.data.model_id;
}) })
@ -324,14 +331,12 @@ getSystemInfo()
.then((res) => { .then((res) => {
config.value = res.data; config.value = res.data;
title.value = config.value.title; title.value = config.value.title;
logo.value = res.data.bar_logo;
}) })
.catch((e) => { .catch((e) => {
ElMessage.error("获取系统配置失败:" + e.message); ElMessage.error("获取系统配置失败:" + e.message);
}); });
import MarkdownIt from "markdown-it";
import emoji from "markdown-it-emoji";
const md = new MarkdownIt({ const md = new MarkdownIt({
breaks: true, breaks: true,
html: true, html: true,
@ -540,17 +545,10 @@ const getRoleById = function (rid) {
}; };
const resizeElement = function () { const resizeElement = function () {
chatBoxHeight.value = window.innerHeight - 101 - 82 - 38; chatListHeight.value = window.innerHeight - 240;
// chatBoxHeight.value = window.innerHeight; // chatBoxHeight.value = window.innerHeight;
mainWinHeight.value = window.innerHeight - 50;
// mainWinHeight.value = window.innerHeight - 101; chatBoxHeight.value = window.innerHeight - 101 - 82 - 38;
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;
}; };
const _newChat = () => { const _newChat = () => {

View File

@ -1,28 +1,20 @@
<template> <template>
<div class="layout"> <div class="layout">
<div class="tab-box"> <div class="tab-box">
<div class="flex-center-col big-top-title xxx"> <div class="flex-center-col pt-2 mb-2">
<div class="flex-center-col" @click="isCollapse = !isCollapse"> <div class="flex flex-center-col">
<el-tooltip content="展开菜单" placement="right" v-if="isCollapse"> <div class="menuIcon" @click="store.setChatListExtend(!store.chatListExtend)">
<i class="iconfont icon-expand"></i> <el-tooltip content="隐藏对话列表" placement="right" v-if="store.chatListExtend">
</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">
<i class="iconfont icon-colspan"></i> <i class="iconfont icon-colspan"></i>
</el-tooltip> </el-tooltip>
<el-tooltip content="展开对话列表" placement="right" v-else>
<i class="iconfont icon-expand"></i>
</el-tooltip>
</div> </div>
</div> </div>
</div> </div>
<div class="menu-list" :style="{ width: isCollapse ? '65px' : '170px' }" :class="{ 'menu-list-collapse': !isCollapse }"> <div class="menu-list">
<ul> <ul>
<li <li
class="menu-list-item flex-center-col" class="menu-list-item flex-center-col"
@ -31,24 +23,23 @@
@click="changeNav(item)" @click="changeNav(item)"
:class="item.url === curPath ? 'active' : ''" :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> <i class="iconfont" :class="item.icon"></i>
</span> </span>
<el-image :src="item.icon" class="el-icon ml-1" v-else /> <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 }} {{ item.name }}
</div> </div>
</li> </li>
</ul>
<!-- 更多 --> <!-- 更多 -->
<div class="bot" :style="{ width: isCollapse ? '65px' : '170px' }"> <div class="bot p-2">
<div class="bot-line"></div> <div class="bot-line"></div>
<el-popover v-if="moreNavs.length > 0" placement="right-end" trigger="hover"> <el-popover v-if="moreNavs.length > 0" placement="right-end" trigger="hover">
<template #reference> <template #reference>
<li class="menu-list-item flex-center-col"> <li class="menu-list-item flex-center-col">
<el-icon><CirclePlus /></el-icon> <i class="iconfont icon-more"/>
<div class="menu-title">更多</div>
</li> </li>
</template> </template>
<template #default> <template #default>
@ -76,8 +67,7 @@
<el-popover placement="right-end" trigger="hover" v-if="loginUser.id"> <el-popover placement="right-end" trigger="hover" v-if="loginUser.id">
<template #reference> <template #reference>
<li class="menu-list-item flex-center-col"> <li class="menu-list-item flex-center-col">
<el-icon><Setting /></el-icon> <i class="iconfont icon-config"/>
<div v-if="!isCollapse">设置</div>
</li> </li>
</template> </template>
<template #default> <template #default>
@ -99,19 +89,15 @@
</ul> </ul>
</template> </template>
</el-popover> </el-popover>
<li class="menu-bot-item"> <div 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"> <a @click="router.push('/')" class="link-button">
<i class="iconfont icon-house"></i> <i class="iconfont icon-house"></i>
</a> </a>
<div class="pl-1">
<ThemeChange/> <ThemeChange/>
</li>
</div> </div>
</ul> </div>
</div>
</div> </div>
</div> </div>
<el-scrollbar class="right-main"> <el-scrollbar class="right-main">
@ -141,7 +127,7 @@
</template> </template>
<script setup> <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 ThemeChange from "@/components/ThemeChange.vue";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import {onMounted, ref, watch} from "vue"; import {onMounted, ref, watch} from "vue";
@ -153,9 +139,7 @@ import { useSharedStore } from "@/store/sharedata";
import ConfigDialog from "@/components/UserInfoDialog.vue"; import ConfigDialog from "@/components/UserInfoDialog.vue";
import {showMessageError} from "@/utils/dialog"; import {showMessageError} from "@/utils/dialog";
import LoginDialog from "@/components/LoginDialog.vue"; import LoginDialog from "@/components/LoginDialog.vue";
import { substr } from "@/utils/libs";
const isCollapse = ref(true);
const router = useRouter(); const router = useRouter();
const logo = ref(""); const logo = ref("");
const mainNavs = ref([]); const mainNavs = ref([]);
@ -163,13 +147,11 @@ const moreNavs = ref([]);
const curPath = ref(); const curPath = ref();
const title = ref(""); const title = ref("");
const avatarImg = ref("/images/avatar/default.jpg");
const store = useSharedStore(); const store = useSharedStore();
const loginUser = ref({}); const loginUser = ref({});
const routerViewKey = ref(0); const routerViewKey = ref(0);
const showConfigDialog = ref(false); const showConfigDialog = ref(false);
const license = ref({ de_copy: true }); const license = ref({ de_copy: true });
const gitURL = ref(process.env.VUE_APP_GIT_URL);
const showLoginDialog = ref(false); const showLoginDialog = ref(false);
/** /**

View File

@ -9,25 +9,24 @@
</div> </div>
<div class="menu-item"> <div class="menu-item">
<span v-if="!license.de_copy"> <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"> <a :href="docsURL" class="link-button mr-2" target="_blank">
<i class="iconfont icon-book"></i> <i class="iconfont icon-book"></i>
</a> </a>
</el-tooltip> </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"> <a :href="gitURL" class="link-button" target="_blank">
<i class="iconfont icon-github"></i> <i class="iconfont icon-github"></i>
</a> </a>
</el-tooltip> </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>
<span v-if="!isLogin"> <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> <el-button @click="router.push('/login')" class="btn-go animate__animated animate__pulse animate__infinite" round>登录/注册</el-button>
</span> </span>
</div> </div>
@ -81,7 +80,7 @@ const license = ref({ de_copy: true });
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_GITHUB_URL);
const navs = ref([]); const navs = ref([]);
const iconMap = ref({ const iconMap = ref({
@ -150,7 +149,6 @@ const setContent = () => {
displayedChars.value = []; displayedChars.value = [];
if (timer) clearInterval(timer); if (timer) clearInterval(timer);
timer = setInterval(setContent, interTime.value); timer = setInterval(setContent, interTime.value);
return;
} else { } else {
const nextChar = slogan.value.charAt(initAnimation.value.length); const nextChar = slogan.value.charAt(initAnimation.value.length);
initAnimation.value += slogan.value.charAt(initAnimation.value.length); // initAnimation.value += slogan.value.charAt(initAnimation.value.length); //
@ -164,7 +162,7 @@ const setContent = () => {
const rainbowColor = (index) => { const rainbowColor = (index) => {
const hue = (index * 40) % 360; // 40 const hue = (index * 40) % 360; // 40
return `hsl(${hue}, 90%, 50%)`; // (hue)(70%)(50%) return `hsl(${hue}, 90%, 50%)`; // (hue)(70%)(50%)
}; }
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>

View File

@ -15,8 +15,8 @@
<el-form-item label="网站Slogan" prop="slogan"> <el-form-item label="网站Slogan" prop="slogan">
<el-input v-model="system['slogan']" /> <el-input v-model="system['slogan']" />
</el-form-item> </el-form-item>
<el-form-item label="网站 LOGO" prop="logo"> <el-form-item label="圆形 LOGO" prop="logo">
<el-input v-model="system['logo']" placeholder="网站LOGO图片"> <el-input v-model="system['logo']" placeholder="正方形或者圆形 Logo">
<template #append> <template #append>
<el-upload :auto-upload="true" :show-file-list="false" @click="beforeUpload('logo')" :http-request="uploadImg"> <el-upload :auto-upload="true" :show-file-list="false" @click="beforeUpload('logo')" :http-request="uploadImg">
<el-icon class="uploader-icon"> <el-icon class="uploader-icon">
@ -26,7 +26,18 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </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> <el-form-item>
<template #label> <template #label>
<div class="label-title"> <div class="label-title">