Merge branch 'main' into husm_2024-09-02

This commit is contained in:
胡双明
2024-09-10 14:29:08 +08:00
60 changed files with 1229 additions and 1263 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 337 KiB

View File

@@ -1,334 +0,0 @@
#app {
height: 100%;
}
#app .chat-page {
height: 100%;
}
#app .chat-page .el-aside {
padding: 10px;
width: var(--el-aside-width, 320px);
}
#app .chat-page .el-aside .chat-list {
display: flex;
flex-flow: column;
border-radius: 10px;
padding: 10px 0;
}
#app .chat-page .el-aside .chat-list .search-box {
flex-wrap: wrap;
padding: 10px 0;
}
#app .chat-page .el-aside .chat-list .search-box .search-input {
--el-input-bg-color: #363535;
--el-input-border-color: #464545;
--el-input-focus-border-color: #47fff1;
--el-input-hover-border-color: #2da39a;
box-shadow: none;
}
#app .chat-page .el-aside .chat-list ::-webkit-scrollbar {
width: 0;
height: 0;
background-color: transparent;
}
#app .chat-page .el-aside .chat-list .content {
width: 100%;
overflow-y: scroll;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item {
display: flex;
width: 100%;
justify-content: flex-start;
padding: 8px 12px;
cursor: pointer;
border: 1px solid #3c3c3c;
margin-bottom: 6px;
border-radius: 5px;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item:hover {
background-color: #343540;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item .avatar {
width: 32px;
height: 32px;
border-radius: 50%;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item .chat-title-input {
font-size: 14px;
margin-top: 4px;
margin-left: 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 190px;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item .chat-title {
color: #c1c1c1;
padding: 5px 10px;
max-width: 220px;
font-size: 14px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item .chat-opt {
position: absolute;
right: 2px;
top: 16px;
color: #fff;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item .chat-opt .el-dropdown-link {
color: #fff;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item .chat-opt .el-icon {
margin-right: 8px;
}
#app .chat-page .el-aside .chat-list .content .chat-list-item.active {
background-color: #343540;
border-color: #21aa93;
}
#app .chat-page .el-aside .tool-box {
display: flex;
justify-content: center;
padding-top: 12px;
border-top: 1px solid #3c3c3c;
}
#app .chat-page .el-aside .tool-box .iconfont {
margin-right: 5px;
}
#app .chat-page .el-main {
overflow: hidden;
--el-main-padding: 0;
margin: 0;
}
#app .chat-page .el-main .chat-container {
min-width: 0;
flex: 1;
background-color: var(--el-bg-color);
color: var(--el-text-color-primary);
}
#app .chat-page .el-main .chat-container .chat-config {
height: 30px;
padding: 10px 30px;
display: flex;
justify-content: center;
justify-items: center;
border-bottom: 1px solid #d9d9e3;
}
#app .chat-page .el-main .chat-container .chat-config .role-select-label {
color: #fff;
}
#app .chat-page .el-main .chat-container .chat-config .el-select {
max-width: 150px;
margin-right: 10px;
}
#app .chat-page .el-main .chat-container .chat-config .role-select {
max-width: 130px;
}
#app .chat-page .el-main .chat-container .chat-config .setting {
padding: 5px;
border-radius: 5px;
cursor: pointer;
}
#app .chat-page .el-main .chat-container .chat-config .setting .iconfont {
font-size: 18px;
color: #19c37d;
}
#app .chat-page .el-main .chat-container .chat-config .setting:hover {
background: #d5fad3;
}
#app .chat-page .el-main .chat-container .chat-config .el-button .el-icon {
margin-right: 5px;
}
#app .chat-page .el-main .chat-container #container {
overflow: hidden;
width: 100%;
position: relative;
}
#app .chat-page .el-main .chat-container #container ::-webkit-scrollbar {
width: 12px /* 滚动条宽度 */;
background: #f1f1f1;
}
#app .chat-page .el-main .chat-container #container ::-webkit-scrollbar-track {
background-color: #e1e1e1;
}
#app .chat-page .el-main .chat-container #container ::-webkit-scrollbar-thumb {
background-color: #c1c1c1;
border-radius: 12px;
}
#app .chat-page .el-main .chat-container #container ::-webkit-scrollbar-thumb:hover {
background-color: #a8a8a8;
}
#app .chat-page .el-main .chat-container #container .chat-box {
overflow-y: auto;
--content-font-size: 16px;
--content-color: #c1c1c1;
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
padding: 0 0 50px 0;
}
#app .chat-page .el-main .chat-container #container .chat-box .chat-line {
font-size: 14px;
display: flex;
align-items: flex-start;
}
#app .chat-page .el-main .chat-container #container .input-box {
position: absolute;
bottom: 0;
width: 100%;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner {
display: flex;
background-color: #fff;
justify-content: center;
align-items: center;
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
padding: 0 15px;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .tool-item {
margin-right: 15px;
border-radius: 6px;
color: #19c37d;
display: flex;
justify-content: center;
justify-items: center;
padding: 6px;
cursor: pointer;
background: #f2f2f2;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .tool-item:hover {
background: #d5fad3;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .tool-item .iconfont {
font-size: 24px;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body {
width: 100%;
margin: 0;
border: none;
padding: 10px 0;
display: flex;
justify-content: center;
position: relative;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .hide-div {
white-space: pre-wrap; /* 保持文本换行 */
visibility: hidden; /* 隐藏 div */
position: absolute; /* 脱离文档流 */
line-height: 24px;
font-size: 14px;
word-wrap: break-word; /* 允许单词换行 */
overflow-wrap: break-word; /* 允许长单词换行,适用于现代浏览器 */
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .input-border {
display: flex;
width: 100%;
overflow: hidden;
border: 2px solid #21aa93;
border-radius: 10px;
padding: 10px;
background-color: #f4f4f4;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .input-border .input-inner {
display: flex;
flex-flow: column;
width: 100%;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .input-border .input-inner .file-list {
padding-bottom: 10px;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .input-border .input-inner .prompt-input::-webkit-scrollbar {
width: 0;
height: 0;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .input-border .input-inner .prompt-input {
width: 100%;
line-height: 24px;
border: none;
font-size: 14px;
background: none;
resize: none;
white-space: pre-wrap; /* 保持文本换行 */
word-wrap: break-word; /* 允许单词换行 */
overflow-wrap: break-word; /* 允许长单词换行,适用于现代浏览器 */
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .input-border .send-btn {
width: 32px;
margin-left: 10px;
}
#app .chat-page .el-main .chat-container #container .input-box .input-box-inner .input-body .input-border .send-btn .el-button {
padding: 8px 5px;
border-radius: 6px;
font-size: 20px;
}
#app .chat-page .el-main .chat-container #container::-webkit-scrollbar {
width: 0;
height: 0;
}
#app .el-message-box {
width: 90%;
max-width: 420px;
}
#app .el-message {
min-width: 100px;
max-width: 600px;
}
.el-select-dropdown__wrap .el-select-dropdown__item .role-option {
display: flex;
flex-flow: row;
margin-top: 8px;
}
.el-select-dropdown__wrap .el-select-dropdown__item .role-option .el-image {
width: 20px;
height: 20px;
border-radius: 50%;
}
.el-select-dropdown__wrap .el-select-dropdown__item .role-option span {
margin-left: 5px;
height: 20px;
line-height: 20px;
}
.account {
display: flex;
background-color: #90ffc2;
color: #000;
width: 100%;
border-radius: 10px;
padding: 10px;
}
.account .vip-logo .el-image {
width: 40px;
height: 40px;
border-radius: 100%;
background-color: #fff;
}
.account .vip-info {
padding: 0 10px 0 10px;
}
.account .vip-info h4,
.account .vip-info p {
margin: 0;
}
.account .vip-info h4 {
font-weight: bold;
font-size: 16px;
}
.account .vip-info p {
color: #333;
}
.account .pay-btn {
width: 100%;
display: flex;
justify-content: right;
align-items: center;
}
.el-overlay-dialog .el-dialog .el-dialog__body .notice {
line-height: 1.8;
font-size: 16px;
overflow: auto;
height: 100%;
}
.dialog-service {
text-align: center;
}
.dialog-service .el-image {
width: 360px;
}

View File

@@ -160,13 +160,15 @@ $borderColor = #4676d0;
padding 5px
border-radius 5px
cursor pointer
background-color #f2f2f2
margin-right 10px
.iconfont {
font-size 18px
color #19c37d
}
&:hover {
background #D5FAD3
background-color #D5FAD3
}
}
@@ -427,4 +429,11 @@ $borderColor = #4676d0;
.el-image {
width 360px;
}
}
.tools-dropdown {
width auto
.el-icon {
margin-left 5px;
}
}

View File

@@ -1,154 +0,0 @@
.home {
display: flex;
height: 100vh;
width: 100%;
flex-flow: column;
}
.home .header {
display: flex;
justify-content: space-between;
height: 50px;
line-height: 50px;
background-color: #1e1f22;
padding-right: 20px;
}
.home .header .banner {
display: flex;
}
.home .header .banner .logo {
display: flex;
padding: 5px;
cursor: pointer;
}
.home .header .banner .logo .el-image {
width: 48px;
height: 48px;
background-color: #fff;
border-radius: 50%;
}
.home .header .banner .title {
display: flex;
color: #fff;
font-size: 20px;
padding: 0 10px;
}
.home .header .navbar {
display: flex;
flex-flow: row;
}
.home .header .navbar .link-button {
margin-right: 15px;
color: #e1e1e1;
padding: 0 10px;
}
.home .header .navbar .link-button:hover {
background-color: #414141;
}
.home .header .navbar .link-button .iconfont {
font-size: 24px;
}
.home .header .navbar .user-info {
width: 100%;
padding: 5px 0;
}
.home .header .navbar .user-info .el-dropdown-link {
width: 100%;
cursor: pointer;
display: flex;
}
.home .header .navbar .user-info .el-dropdown-link .el-image {
width: 36px;
height: 36px;
border-radius: 50%;
}
.home .header .navbar .user-info .el-dropdown-link .el-icon {
color: #ccc;
line-height: 24px;
}
.home .main {
width: 100%;
display: flex;
flex-flow: row;
}
.home .main .navigator {
display: flex;
flex-flow: column;
width: 60px;
padding: 10px 1px;
border-right: 1px solid #3c3c3c;
background-color: #1e1f22;
}
.home .main .navigator .nav-items {
margin-top: 10px;
padding: 0 5px;
}
.home .main .navigator .nav-items li {
margin-bottom: 15px;
display: flex;
flex-flow: column;
}
.home .main .navigator .nav-items li a {
color: #dadbdc;
border-radius: 10px;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
background-color: #414348;
}
.home .main .navigator .nav-items li a .el-image {
border-radius: 10px;
}
.home .main .navigator .nav-items li a .iconfont {
font-size: 20px;
}
.home .main .navigator .nav-items li a:hover,
.home .main .navigator .nav-items li a.active {
color: #47fff1;
background-color: #0f7a71;
}
.home .main .navigator .nav-items li .title {
font-size: 12px;
padding-top: 6px;
color: #e5e7eb;
text-align: center;
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: unset; /* 使用省略号表示溢出内容 */
}
.home .main .navigator .nav-items li .active {
color: #47fff1;
}
.home .main .content {
width: 100%;
overflow: auto;
box-sizing: border-box;
background-color: #282c34;
}
.el-popper .more-menus li {
padding: 10px 15px;
cursor: pointer;
border-radius: 5px;
margin: 5px 0;
}
.el-popper .more-menus li .el-image {
position: relative;
top: 5px;
right: 5px;
}
.el-popper .more-menus li:hover {
background-color: #f1f1f1;
}
.el-popper .more-menus li.active {
background-color: #f1f1f1;
}
.el-popper .user-info-menu li a {
width: 100%;
justify-content: left;
}
.el-popper .user-info-menu li a:hover {
text-decoration: none !important;
color: var(--el-primary-text-color);
}

View File

@@ -23,7 +23,6 @@
.el-image {
width 48px
height 48px
background-color #ffffff
border-radius 50%
}
}

View File

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

View File

@@ -54,9 +54,9 @@
padding 10px 10px 0 10px
}
.el-image {
.logo {
height 50px
background-color #ffffff
border-radius 50%
}
.el-button {

View File

@@ -1,99 +0,0 @@
.bg {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #313237;
background-image: url("~@/assets/img/login-bg.jpg");
background-size: cover;
background-position: center;
background-repeat: repeat-y;
}
.main .contain {
position: fixed;
left: 50%;
top: 40%;
width: 90%;
max-width: 400px;
transform: translate(-50%, -50%);
padding: 20px 10px;
color: #fff;
border-radius: 10px;
}
.main .contain .logo {
text-align: center;
}
.main .contain .logo .el-image {
width: 120px;
cursor: pointer;
background-color: #fff;
border-radius: 50%;
}
.main .contain .header {
width: 100%;
margin-bottom: 24px;
font-size: 24px;
color: $white_v1;
letter-space: 2px;
text-align: center;
padding-top: 10px;
}
.main .contain .content {
width: 100%;
height: auto;
border-radius: 3px;
}
.main .contain .content .block {
margin-bottom: 16px;
}
.main .contain .content .block .el-input__inner {
border: 1px solid $gray-v6 !important;
}
.main .contain .content .block .el-input__inner .el-icon-user,
.main .contain .content .block .el-input__inner .el-icon-lock {
font-size: 20px;
}
.main .contain .content .btn-row {
padding-top: 10px;
}
.main .contain .content .btn-row .login-btn {
width: 100%;
font-size: 16px;
letter-spacing: 2px;
}
.main .contain .content .text-line {
justify-content: center;
padding-top: 10px;
font-size: 14px;
}
.main .contain .content .opt {
padding: 15px;
}
.main .contain .content .opt .el-col {
text-align: center;
}
.main .contain .content .divider {
border-top: 2px solid #c1c1c1;
}
.main .contain .content .clogin {
padding: 15px;
display: flex;
justify-content: center;
}
.main .contain .content .clogin .iconfont {
font-size: 20px;
background: #e9f1f6;
padding: 8px;
border-radius: 50%;
cursor: pointer;
}
.main .contain .content .clogin .iconfont.icon-wechat {
color: #0bc15f;
}
.main .footer {
color: #fff;
}
.main .footer .container {
padding: 20px;
}

View File

@@ -30,7 +30,6 @@
.el-image {
width 120px;
cursor pointer
background-color #ffffff
border-radius 50%
}
}

View File

@@ -41,7 +41,7 @@ import {computed, ref, watch} from "vue";
import SendMsg from "@/components/SendMsg.vue";
import {ElMessage} from "element-plus";
import {httpPost} from "@/utils/http";
import {checkSession, removeUserInfo} from "@/store/cache";
import {checkSession} from "@/store/cache";
const props = defineProps({
show: Boolean,
@@ -76,7 +76,6 @@ const save = () => {
}
httpPost('/api/user/bind/email', form.value).then(() => {
removeUserInfo()
ElMessage.success("绑定成功")
emits('hide')
}).catch(e => {

View File

@@ -41,7 +41,7 @@ import {computed, ref, watch} from "vue";
import SendMsg from "@/components/SendMsg.vue";
import {ElMessage} from "element-plus";
import {httpPost} from "@/utils/http";
import {checkSession, removeUserInfo} from "@/store/cache";
import {checkSession} from "@/store/cache";
const props = defineProps({
show: Boolean,
@@ -79,7 +79,6 @@ const save = () => {
}
httpPost('/api/user/bind/mobile', form.value).then(() => {
removeUserInfo()
ElMessage.success("绑定成功")
emits('hide')
}).catch(e => {

View File

@@ -26,6 +26,12 @@ const routes = [
meta: {title: '创作中心'},
component: () => import('@/views/ChatPlus.vue'),
},
{
name: 'chat-id',
path: '/chat/:id',
meta: {title: '创作中心'},
component: () => import('@/views/ChatPlus.vue'),
},
{
name: 'image-mj',
path: '/mj',

View File

@@ -6,29 +6,15 @@ const adminDataKey = "ADMIN_INFO_CACHE_KEY"
const systemInfoKey = "SYSTEM_INFO_CACHE_KEY"
const licenseInfoKey = "LICENSE_INFO_CACHE_KEY"
export function checkSession() {
const item = Storage.get(userDataKey) ?? {expire:0, data:null}
if (item.expire > Date.now()) {
return Promise.resolve(item.data)
}
return new Promise((resolve, reject) => {
httpGet('/api/user/session').then(res => {
item.data = res.data
// cache expires after 10 secs
item.expire = Date.now() + 1000 * 30
Storage.set(userDataKey, item)
resolve(item.data)
resolve(res.data)
}).catch(e => {
Storage.remove(userDataKey)
reject(e)
})
})
}
export function removeUserInfo() {
Storage.remove(userDataKey)
}
export function checkAdminSession() {
const item = Storage.get(adminDataKey) ?? {expire:0, data:null}
if (item.expire > Date.now()) {
@@ -63,7 +49,7 @@ export function getSystemInfo() {
Storage.set(systemInfoKey, item)
resolve(item.data)
}).catch(err => {
resolve(err)
reject(err)
})
})
}

View File

@@ -1,6 +1,6 @@
import {randString} from "@/utils/libs";
import Storage from "good-storage";
import {checkAdminSession, checkSession, removeAdminInfo, removeUserInfo} from "@/store/cache";
import {removeAdminInfo} from "@/store/cache";
/**
* storage handler
@@ -24,7 +24,6 @@ export function setUserToken(token) {
export function removeUserToken() {
Storage.remove(UserTokenKey)
removeUserInfo()
}
export function getAdminToken() {

View File

@@ -3,7 +3,7 @@
<el-container>
<el-aside>
<div class="chat-list">
<el-button @click="newChat" color="#21aa93">
<el-button @click="_newChat" color="#21aa93">
<el-icon style="margin-right: 5px">
<Plus/>
</el-icon>
@@ -23,7 +23,7 @@
<div class="content" :style="{height: leftBoxHeight+'px'}">
<el-row v-for="chat in chatList" :key="chat.chat_id">
<div :class="chat.chat_id === activeChat.chat_id?'chat-list-item active':'chat-list-item'"
<div :class="chat.chat_id === chatId?'chat-list-item active':'chat-list-item'"
@click="loadChat(chat)">
<el-image :src="chat.icon" class="avatar"/>
<span class="chat-title-input" v-if="chat.edit">
@@ -100,11 +100,25 @@
</el-option>
</el-select>
<el-dropdown :hide-on-click="false" trigger="click">
<span class="setting"><i class="iconfont icon-plugin"></i></span>
<template #dropdown>
<el-dropdown-menu class="tools-dropdown">
<el-checkbox-group v-model="toolSelected">
<el-dropdown-item v-for="item in tools" :key="item.id">
<el-checkbox :value="item.id" :label="item.label" @change="changeTool" />
<el-tooltip :content="item.description" placement="right">
<el-icon><InfoFilled /></el-icon>
</el-tooltip>
</el-dropdown-item>
</el-checkbox-group>
</el-dropdown-menu>
</template>
</el-dropdown>
<span class="setting" @click="showChatSetting = true">
<el-tooltip class="box-item" effect="dark" content="对话设置">
<i class="iconfont icon-config"></i>
</el-tooltip>
</span>
<i class="iconfont icon-config"></i>
</span>
</div>
<div>
@@ -202,7 +216,7 @@
import {nextTick, onMounted, onUnmounted, ref, watch} from 'vue'
import ChatPrompt from "@/components/ChatPrompt.vue";
import ChatReply from "@/components/ChatReply.vue";
import {Delete, Edit, More, Plus, Promotion, Search, Share, VideoPause} from '@element-plus/icons-vue'
import {Delete, Edit, InfoFilled, More, Plus, Promotion, Search, Share, VideoPause} from '@element-plus/icons-vue'
import 'highlight.js/styles/a11y-dark.css'
import {
isMobile,
@@ -211,7 +225,7 @@ import {
UUID
} from "@/utils/libs";
import {ElMessage, ElMessageBox} from "element-plus";
import {getSessionId, getUserToken, removeUserToken} from "@/store/session";
import {getSessionId, getUserToken} from "@/store/session";
import {httpGet, httpPost} from "@/utils/http";
import {useRouter} from "vue-router";
import Clipboard from "clipboard";
@@ -230,15 +244,15 @@ const modelID = ref(0)
const chatData = ref([]);
const allChats = ref([]); // 会话列表
const chatList = ref(allChats.value);
const activeChat = ref({});
const mainWinHeight = ref(0); // 主窗口高度
const chatBoxHeight = ref(0); // 聊天内容框高度
const leftBoxHeight = ref(0);
const loading = ref(true);
const loading = ref(false);
const loginUser = ref(null);
const roles = ref([]);
const router = useRouter();
const roleId = ref(0)
const chatId = ref();
const newChatItem = ref(null);
const isLogin = ref(false)
const showHello = ref(true)
@@ -254,7 +268,15 @@ const listStyle = ref(store.chatListStyle)
watch(() => store.chatListStyle, (newValue) => {
listStyle.value = newValue
});
const tools = ref([])
const toolSelected = ref([])
const loadHistory = ref(false)
// 初始化 ChatID
chatId.value = router.currentRoute.value.params.id
if (!chatId.value) {
chatId.value = UUID()
}
if (isMobile()) {
router.replace("/mobile/chat")
@@ -290,6 +312,13 @@ httpGet("/api/config/get?key=notice").then(res => {
ElMessage.error("获取系统配置失败:" + e.message)
})
// 获取工具函数
httpGet("/api/function/list").then(res => {
tools.value = res.data
}).catch(e => {
showMessageError("获取工具函数失败:" + e.message)
})
onMounted(() => {
resizeElement();
initData()
@@ -351,7 +380,6 @@ const initData = () => {
ElMessage.error("加载会话列表失败!")
})
}).catch(() => {
loading.value = false
// 加载模型
httpGet('/api/model/list',{id:roleId.value}).then(res => {
models.value = res.data
@@ -418,6 +446,7 @@ const resizeElement = function () {
const _newChat = () => {
if (isLogin.value) {
chatId.value = UUID()
newChat()
}
}
@@ -428,6 +457,7 @@ const newChat = () => {
store.setShowLoginDialog(true)
return;
}
const role = getRoleById(roleId.value)
showHello.value = role.key === 'gpt';
// if the role bind a model, disable model change
@@ -457,9 +487,19 @@ const newChat = () => {
edit: false,
removing: false,
};
activeChat.value = {} //取消激活的会话高亮
showStopGenerate.value = false;
connect(null, roleId.value)
router.push(`/chat/${chatId.value}`)
loadHistory.value = true
connect()
}
// 切换工具
const changeTool = () => {
if (!isLogin.value) {
return;
}
loadHistory.value = false
socket.value.close()
}
@@ -470,16 +510,18 @@ const loadChat = function (chat) {
return;
}
if (activeChat.value['chat_id'] === chat.chat_id) {
if (chatId.value === chat.chat_id) {
return;
}
activeChat.value = chat
newChatItem.value = null;
roleId.value = chat.role_id;
modelID.value = chat.model_id;
chatId.value = chat.chat_id;
showStopGenerate.value = false;
connect(chat.chat_id, chat.role_id)
router.push(`/chat/${chatId.value}`)
loadHistory.value = true
socket.value.close()
}
// 编辑会话标题
@@ -487,7 +529,6 @@ const tmpChatTitle = ref('');
const editChatTitle = (chat) => {
chat.edit = true;
tmpChatTitle.value = chat.title;
console.log(chat.chat_id)
nextTick(() => {
document.getElementById('chat-' + chat.chat_id).focus()
})
@@ -542,7 +583,7 @@ const removeChat = function (chat) {
return e1.id === e2.id
})
// 重置会话
newChat();
_newChat();
}).catch(e => {
ElMessage.error("操作失败:" + e.message);
})
@@ -557,23 +598,10 @@ const prompt = ref('');
const showStopGenerate = ref(false); // 停止生成
const lineBuffer = ref(''); // 输出缓冲行
const socket = ref(null);
const activelyClose = ref(false); // 主动关闭
const canSend = ref(true);
const heartbeatHandle = ref(null)
const sessionId = ref("")
const connect = function (chat_id, role_id) {
let isNewChat = false;
if (!chat_id) {
isNewChat = true;
chat_id = UUID();
}
// 先关闭已有连接
if (socket.value !== null) {
activelyClose.value = true;
socket.value.close();
}
const _role = getRoleById(role_id);
const connect = function () {
const chatRole = getRoleById(roleId.value);
// 初始化 WebSocket 对象
sessionId.value = getSessionId();
let host = process.env.VUE_APP_WS_HOST
@@ -585,26 +613,15 @@ const connect = function (chat_id, role_id) {
}
}
const _socket = new WebSocket(host + `/api/chat/new?session_id=${sessionId.value}&role_id=${role_id}&chat_id=${chat_id}&model_id=${modelID.value}&token=${getUserToken()}`);
loading.value = true
const toolIds = toolSelected.value.join(',')
const _socket = new WebSocket(host + `/api/chat/new?session_id=${sessionId.value}&role_id=${roleId.value}&chat_id=${chatId.value}&model_id=${modelID.value}&token=${getUserToken()}&tools=${toolIds}`);
_socket.addEventListener('open', () => {
chatData.value = []; // 初始化聊天数据
enableInput()
activelyClose.value = false;
if (isNewChat) { // 加载打招呼信息
loading.value = false;
chatData.value.push({
chat_id: chat_id,
role_id: role_id,
type: "reply",
id: randString(32),
icon: _role['icon'],
content: _role['hello_msg'],
})
ElMessage.success({message: "对话连接成功!", duration: 1000})
} else { // 加载聊天记录
loadChatHistory(chat_id);
if (loadHistory.value) {
loadChatHistory(chatId.value)
}
loading.value = false
});
_socket.addEventListener('message', event => {
@@ -619,17 +636,16 @@ const connect = function (chat_id, role_id) {
chatData.value.push({
type: "reply",
id: randString(32),
icon: _role['icon'],
icon: chatRole['icon'],
prompt:prePrompt,
content: "",
});
} else if (data.type === 'end') { // 消息接收完毕
// 追加当前会话到会话列表
if (isNewChat && newChatItem.value !== null) {
if (newChatItem.value !== null) {
newChatItem.value['title'] = tmpChatTitle.value;
newChatItem.value['chat_id'] = chat_id;
newChatItem.value['chat_id'] = chatId.value;
chatList.value.unshift(newChatItem.value);
activeChat.value = newChatItem.value;
newChatItem.value = null; // 只追加一次
}
@@ -641,7 +657,7 @@ const connect = function (chat_id, role_id) {
httpPost("/api/chat/tokens", {
text: "",
model: getModelValue(modelID.value),
chat_id: chat_id
chat_id: chatId.value,
}).then(res => {
reply['created_at'] = new Date().getTime();
reply['tokens'] = res.data;
@@ -662,7 +678,7 @@ const connect = function (chat_id, role_id) {
// 将聊天框的滚动条滑动到最底部
nextTick(() => {
document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)
localStorage.setItem("chat_id", chat_id)
localStorage.setItem("chat_id", chatId.value)
})
};
}
@@ -673,18 +689,8 @@ const connect = function (chat_id, role_id) {
});
_socket.addEventListener('close', () => {
if (activelyClose.value || socket.value === null) { // 忽略主动关闭
return;
}
// 停止发送消息
disableInput(true)
loading.value = true;
checkSession().then(() => {
connect(chat_id, role_id)
}).catch(() => {
loading.value = true
showMessageError("会话已断开,刷新页面...")
});
connect()
});
socket.value = _socket;
@@ -801,21 +807,20 @@ const clearAllChats = function () {
})
}
const logout = function () {
activelyClose.value = true;
httpGet('/api/user/logout').then(() => {
removeUserToken()
router.push("/login")
}).catch(() => {
ElMessage.error('注销失败!');
})
}
const loadChatHistory = function (chatId) {
chatData.value = []
httpGet('/api/chat/history?chat_id=' + chatId).then(res => {
const data = res.data
if (!data) {
loading.value = false
if (!data || data.length === 0) { // 加载打招呼信息
const _role = getRoleById(roleId.value)
chatData.value.push({
chat_id: chatId,
role_id: roleId.value,
type: "reply",
id: randString(32),
icon: _role['icon'],
content: _role['hello_msg'],
})
return
}
showHello.value = false
@@ -829,7 +834,6 @@ const loadChatHistory = function (chatId) {
nextTick(() => {
document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)
})
loading.value = false
}).catch(e => {
// TODO: 显示重新加载按钮
ElMessage.error('加载聊天记录失败:' + e.message);
@@ -882,7 +886,6 @@ const shareChat = (chat) => {
}
const url = location.protocol + '//' + location.host + '/chat/export?chat_id=' + chat.chat_id
// console.log(url)
window.open(url, '_blank');
}

View File

@@ -206,7 +206,7 @@
import {nextTick, onMounted, onUnmounted, ref} from "vue"
import {Delete, InfoFilled, Picture} from "@element-plus/icons-vue";
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
import {ElMessage, ElMessageBox} from "element-plus";
import Clipboard from "clipboard";
import {checkSession, getSystemInfo} from "@/store/cache";
import {useSharedStore} from "@/store/sharedata";
@@ -338,7 +338,7 @@ const fetchRunningJobs = () => {
}
// 获取运行中的任务
httpGet(`/api/dall/jobs?finish=false`).then(res => {
runningJobs.value = res.data
runningJobs.value = res.data.items
}).catch(e => {
ElMessage.error("获取任务失败:" + e.message)
})
@@ -356,10 +356,10 @@ const fetchFinishJobs = () => {
page.value = page.value + 1
httpGet(`/api/dall/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`).then(res => {
if (res.data.length < pageSize.value) {
if (res.data.items.length < pageSize.value) {
isOver.value = true
}
const imageList = res.data
const imageList = res.data.items
for (let i = 0; i < imageList.length; i++) {
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75"
}

View File

@@ -816,7 +816,7 @@ const fetchRunningJobs = () => {
}
httpGet(`/api/mj/jobs?finish=false`).then(res => {
const jobs = res.data
const jobs = res.data.items
const _jobs = []
for (let i = 0; i < jobs.length; i++) {
if (jobs[i].progress === 101) {
@@ -853,7 +853,7 @@ const fetchFinishJobs = () => {
page.value = page.value + 1
// 获取已完成的任务
httpGet(`/api/mj/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`).then(res => {
const jobs = res.data
const jobs = res.data.items
for (let i = 0; i < jobs.length; i++) {
if (jobs[i]['img_url'] !== "") {
if (jobs[i].type === 'upscale' || jobs[i].type === 'swapFace') {

View File

@@ -549,7 +549,6 @@ const sdPower = ref(0) // 画一张 SD 图片消耗算力
const socket = ref(null)
const userId = ref(0)
const heartbeatHandle = ref(null)
const connect = () => {
let host = process.env.VUE_APP_WS_HOST
if (host === '') {
@@ -637,7 +636,7 @@ const fetchRunningJobs = () => {
// 获取运行中的任务
httpGet(`/api/sd/jobs?finish=0`).then(res => {
runningJobs.value = res.data
runningJobs.value = res.data.items
}).catch(e => {
ElMessage.error("获取任务失败:" + e.message)
})
@@ -655,10 +654,10 @@ const fetchFinishJobs = () => {
page.value = page.value + 1
httpGet(`/api/sd/jobs?finish=1&page=${page.value}&page_size=${pageSize.value}`).then(res => {
if (res.data.length < pageSize.value) {
if (res.data.items.length < pageSize.value) {
isOver.value = true
}
const imageList = res.data
const imageList = res.data.items
for (let i = 0; i < imageList.length; i++) {
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75"
}

View File

@@ -355,13 +355,13 @@ const getNext = () => {
}
httpGet(`${url}?page=${page.value}&page_size=${pageSize.value}`).then(res => {
loading.value = false
if (!res.data || res.data.length === 0) {
if (!res.data.items || res.data.items.length === 0) {
isOver.value = true
return
}
// 生成缩略图
const imageList = res.data
const imageList = res.data.items
for (let i = 0; i < imageList.length; i++) {
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75"
}

View File

@@ -7,7 +7,7 @@
:ellipsis="false"
>
<div class="menu-item">
<el-image :src="logo" alt="Geek-AI"/>
<el-image :src="logo" class="logo" alt="Geek-AI"/>
<div class="title" :style="{color:theme.textColor}">{{ title }}</div>
</div>
<div class="menu-item">

View File

@@ -42,7 +42,7 @@ import {setUserToken} from "@/store/session";
import Clipboard from "clipboard";
import {showMessageError, showMessageOK} from "@/utils/dialog";
import {getRoute} from "@/store/system";
import {checkSession, removeUserInfo} from "@/store/cache";
import {checkSession} from "@/store/cache";
const winHeight = ref(window.innerHeight)
const loading = ref(true)
@@ -68,7 +68,6 @@ if (code === "") {
const doLogin = (userId) => {
// 发送请求获取用户信息
httpGet("/api/user/clogin/callback",{login_type: "wx",code: code, action:action, user_id: userId}).then(res => {
removeUserInfo()
if (res.data.token) {
setUserToken(res.data.token)
}

View File

@@ -340,7 +340,6 @@ const doSubmitRegister = (verifyData) => {
.el-image {
width 120px;
cursor pointer
background-color #ffffff
border-radius 50%
}
}

View File

@@ -145,7 +145,6 @@ const doLogin = function (verifyData) {
.el-image {
width 120px;
cursor pointer
background-color #ffffff
border-radius 50%
}
}

View File

@@ -302,6 +302,9 @@
<el-form-item label="Suno 算力" prop="suno_power">
<el-input v-model.number="system['suno_power']" placeholder="使用 Suno 生成一首音乐消耗算力"/>
</el-form-item>
<el-form-item label="Luma 算力" prop="luma_power">
<el-input v-model.number="system['luma_power']" placeholder="使用 Luma 生成一段视频消耗算力"/>
</el-form-item>
</el-tab-pane>
</el-tabs>

View File

@@ -11,7 +11,8 @@
<el-table :data="users.items" border class="table" :row-key="row => row.id"
@selection-change="handleSelectionChange" table-layout="auto">
<el-table-column type="selection" width="38"></el-table-column>
<el-table-column prop="mobile" label="账号">
<el-table-column prop="id" label="ID"/>
<el-table-column label="账号">
<template #default="scope">
<span>{{ scope.row.username }}</span>
<el-image v-if="scope.row.vip" :src="vipImg" style="height: 20px;position: relative; top:5px; left: 5px"/>

View File

@@ -225,10 +225,7 @@ const newChat = (item) => {
}
showPicker.value = false
const options = item.selectedOptions
router.push({
name: "mobile-chat-session",
params: {role_id: options[0].value, model_id: options[1].value, title: '新建会话', chat_id: 0}
})
router.push(`/mobile/chat/session?title=新对话&role_id=${options[0].value}&model_id=${options[1].value}&chat_id=0}`)
}
const changeChat = (chat) => {

View File

@@ -102,6 +102,7 @@
<van-popup v-model:show="showPicker" position="bottom" class="popup">
<van-picker
:columns="columns"
v-model="selectedValues"
title="选择模型和角色"
@cancel="showPicker = false"
@confirm="newChat"
@@ -153,6 +154,7 @@ const loginUser = ref(null)
// const showMic = ref(false)
const showPicker = ref(false)
const columns = ref([roles.value, models.value])
const selectedValues = ref([roleId.value, modelId.value])
checkSession().then(user => {
loginUser.value = user

View File

@@ -1,7 +1,7 @@
<template>
<div class="mobile-user-profile container">
<div class="content">
<van-form>
<van-form v-if="isLogin">
<div class="avatar">
<van-image :src="fileList[0].url" size="80" width="80" fit="cover" round />
<!-- <van-uploader v-model="fileList"-->

View File

@@ -317,7 +317,7 @@ const initData = () => {
const fetchRunningJobs = () => {
// 获取运行中的任务
httpGet(`/api/dall/jobs?finish=0`).then(res => {
const jobs = res.data
const jobs = res.data.items
const _jobs = []
for (let i = 0; i < jobs.length; i++) {
if (jobs[i].progress === -1) {
@@ -345,13 +345,14 @@ const pageSize = ref(10)
const fetchFinishJobs = (page) => {
loading.value = true
httpGet(`/api/dall/jobs?finish=1&page=${page}&page_size=${pageSize.value}`).then(res => {
if (res.data.length < pageSize.value) {
const jobs = res.data.items
if (jobs.length < pageSize.value) {
finished.value = true
}
if (page === 1) {
finishedJobs.value = res.data
finishedJobs.value = jobs
} else {
finishedJobs.value = finishedJobs.value.concat(res.data)
finishedJobs.value = finishedJobs.value.concat(jobs)
}
loading.value = false
}).catch(e => {

View File

@@ -430,7 +430,7 @@ const connect = () => {
// 获取运行中的任务
const fetchRunningJobs = (userId) => {
httpGet(`/api/mj/jobs?finish=0&user_id=${userId}`).then(res => {
const jobs = res.data
const jobs = res.data.items
const _jobs = []
for (let i = 0; i < jobs.length; i++) {
if (jobs[i].progress === -1) {
@@ -462,7 +462,7 @@ const fetchFinishJobs = (page) => {
loading.value = true
// 获取已完成的任务
httpGet(`/api/mj/jobs?finish=1&page=${page}&page_size=${pageSize.value}`).then(res => {
const jobs = res.data
const jobs = res.data.items
for (let i = 0; i < jobs.length; i++) {
if (jobs[i].progress === 101) {
showNotify({

View File

@@ -382,7 +382,7 @@ const initData = () => {
const fetchRunningJobs = () => {
// 获取运行中的任务
httpGet(`/api/sd/jobs?finish=0`).then(res => {
const jobs = res.data
const jobs = res.data.items
const _jobs = []
for (let i = 0; i < jobs.length; i++) {
if (jobs[i].progress === -1) {
@@ -410,13 +410,14 @@ const pageSize = ref(10)
const fetchFinishJobs = (page) => {
loading.value = true
httpGet(`/api/sd/jobs?finish=1&page=${page}&page_size=${pageSize.value}`).then(res => {
if (res.data.length < pageSize.value) {
const jobs = res.data.items
if (jobs.length < pageSize.value) {
finished.value = true
}
if (page === 1) {
finishedJobs.value = res.data
finishedJobs.value = jobs
} else {
finishedJobs.value = finishedJobs.value.concat(res.data)
finishedJobs.value = finishedJobs.value.concat(jobs)
}
loading.value = false
}).catch(e => {

View File

@@ -134,13 +134,13 @@ const onLoad = () => {
const d = data.value[activeName.value]
httpGet(`${d.url}?status=1&page=${d.page}&page_size=${d.pageSize}&publish=true`).then(res => {
d.loading = false
if (res.data.length === 0) {
if (res.data.items.length === 0) {
d.finished = true
return
}
// 生成缩略图
const imageList = res.data
const imageList = res.data.items
for (let i = 0; i < imageList.length; i++) {
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75"
}