mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-11-09 18:53:43 +08:00
fixed conflicts
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<el-config-provider>
|
||||
<router-view/>
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ElConfigProvider} from 'element-plus';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
|
||||
const debounce = (fn, delay) => {
|
||||
let timer
|
||||
@@ -42,6 +41,8 @@ html, body {
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
--primary-color: #21aa93
|
||||
}
|
||||
|
||||
.el-overlay-dialog {
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
.page-apps {
|
||||
background-color: #282c34;
|
||||
height 100vh
|
||||
|
||||
.title {
|
||||
text-align center
|
||||
background-color #25272d
|
||||
font-size 24px
|
||||
color #ffffff
|
||||
padding 10px
|
||||
border-bottom 1px solid #3c3c3c
|
||||
}
|
||||
height 100%
|
||||
|
||||
.inner {
|
||||
display flex
|
||||
@@ -19,51 +10,71 @@
|
||||
overflow-x hidden
|
||||
|
||||
.list-box {
|
||||
.app-item {
|
||||
border 1px solid #666666
|
||||
border-radius 6px
|
||||
overflow hidden
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
.item {
|
||||
display flex
|
||||
flex-flow row
|
||||
border 1px solid #3c3c3c
|
||||
padding 10px
|
||||
|
||||
.el-image {
|
||||
padding 6px
|
||||
|
||||
.el-image__inner {
|
||||
border-radius 10px
|
||||
}
|
||||
.image {
|
||||
width 80px
|
||||
height 80px
|
||||
min-width 80px
|
||||
border-radius 5px
|
||||
overflow hidden
|
||||
}
|
||||
|
||||
.title {
|
||||
.inner {
|
||||
display flex
|
||||
padding 10px
|
||||
flex-flow column
|
||||
padding 0 0 0 10px
|
||||
width 100%
|
||||
|
||||
.name {
|
||||
width 100%
|
||||
.info {
|
||||
text-align left
|
||||
font-size 16px
|
||||
font-weight bold
|
||||
color #47fff1
|
||||
|
||||
.info-title {
|
||||
color var(--el-text-color)
|
||||
font-size 1.25rem
|
||||
line-height 1.75rem
|
||||
letter-spacing: .025em;
|
||||
font-weight: 600;
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
padding 8px 0
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
word-break: break-all;
|
||||
height 34px
|
||||
font-size: .875rem;
|
||||
color #999999
|
||||
}
|
||||
}
|
||||
|
||||
.opt {
|
||||
position: relative;
|
||||
width 100%
|
||||
top -5px
|
||||
.btn {
|
||||
margin-top 10px
|
||||
display flex
|
||||
justify-content right
|
||||
|
||||
.el-button {
|
||||
margin-left 10px
|
||||
|
||||
.el-icon {
|
||||
margin-right 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hello-msg {
|
||||
height 60px
|
||||
padding 10px
|
||||
font-size 14px
|
||||
color #999999
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,313 +1,378 @@
|
||||
#app {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#app .common-layout {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside {
|
||||
background-color: #252526;
|
||||
background-color: #252526;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .title-box {
|
||||
padding: 6px 10px;
|
||||
display: flex;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
padding: 6px 10px;
|
||||
display: flex;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .title-box span {
|
||||
padding-top: 5px;
|
||||
padding-left: 10px;
|
||||
padding-top: 5px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
background-color: #28292a;
|
||||
border-top: 1px solid #2f3032;
|
||||
border-right: 1px solid #2f3032;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
background-color: #28292a;
|
||||
border-top: 1px solid #2f3032;
|
||||
border-right: 1px solid #2f3032;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .search-box {
|
||||
flex-wrap: wrap;
|
||||
padding: 10px 15px;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .search-box .el-input__wrapper {
|
||||
background-color: #363535;
|
||||
box-shadow: none;
|
||||
background-color: #363535;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list ::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content {
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content .chat-list-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content .chat-list-item:hover {
|
||||
background-color: #343540;
|
||||
background-color: #343540;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
#app .common-layout .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;
|
||||
font-size: 14px;
|
||||
margin-top: 4px;
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 190px;
|
||||
}
|
||||
|
||||
#app .common-layout .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;
|
||||
color: #c1c1c1;
|
||||
padding: 5px 10px;
|
||||
max-width: 220px;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 16px;
|
||||
color: #fff;
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .btn .el-icon {
|
||||
margin-right: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content .chat-list-item.active {
|
||||
background-color: #343540;
|
||||
background-color: #343540;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .chat-list .content .chat-list-item.active .btn {
|
||||
display: inline;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .tool-box {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 0 20px 10px 20px;
|
||||
border-top: 1px solid #3c3c3c;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 0 20px 10px 20px;
|
||||
border-top: 1px solid #3c3c3c;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .tool-box .user-info {
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link .el-image {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link .username {
|
||||
display: flex;
|
||||
line-height: 22px;
|
||||
width: 230px;
|
||||
padding-left: 10px;
|
||||
display: flex;
|
||||
line-height: 22px;
|
||||
width: 230px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link .el-icon {
|
||||
color: #ccc;
|
||||
line-height: 24px;
|
||||
color: #ccc;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main {
|
||||
overflow: hidden;
|
||||
--el-main-padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
--el-main-padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background-color: #28292a;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background-color: #28292a;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .chat-config {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .chat-config .role-select-label {
|
||||
color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .chat-config .el-select {
|
||||
max-width: 150px;
|
||||
margin-right: 10px;
|
||||
max-width: 150px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .chat-config .role-select {
|
||||
max-width: 130px;
|
||||
max-width: 130px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .chat-config .el-button .el-icon {
|
||||
margin-right: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .iconfont {
|
||||
margin-right: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .is-circle {
|
||||
margin-left: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#app .common-layout .el-main .chat-head .is-circle .iconfont {
|
||||
margin-right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
#app .common-layout .el-main .right-box {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-left: 1px solid #4f4f4f;
|
||||
|
||||
#app .common-layout .el-main .chat-box {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-left: 1px solid #4f4f4f;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container ::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container ::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .chat-box {
|
||||
overflow-y: scroll;
|
||||
--content-font-size: 16px;
|
||||
--content-color: #c1c1c1;
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
padding: 0 0 50px 0;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .chat-box {
|
||||
overflow-y: scroll;
|
||||
--content-font-size: 16px;
|
||||
--content-color: #c1c1c1;
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
padding: 0 0 50px 0;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .chat-box .chat-line {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .chat-box .chat-line {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .re-generate {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .re-generate {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .re-generate .btn-box {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .re-generate .btn-box {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .re-generate .btn-box .el-button .el-icon {
|
||||
margin-right: 5px;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .re-generate .btn-box .el-button .el-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .input-box {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
|
||||
padding: 0 15px;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .input-box {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
|
||||
padding: 0 15px;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .input-box .input-container {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .input-box .input-container {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .input-box .input-container .el-textarea .el-textarea__inner::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .input-box .input-container .el-textarea .el-textarea__inner::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .input-box .input-container .select-file {
|
||||
position: absolute;
|
||||
right: 48px;
|
||||
top: 20px;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .input-box .input-container .select-file {
|
||||
position: absolute;
|
||||
right: 48px;
|
||||
top: 20px;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .input-box .input-container .send-btn {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 20px;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .input-box .input-container .send-btn {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 20px;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container .input-box .input-container .send-btn .el-button {
|
||||
padding: 8px 5px;
|
||||
border-radius: 6px;
|
||||
background: #19c37d;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container .input-box .input-container .send-btn .el-button {
|
||||
padding: 8px 5px;
|
||||
border-radius: 6px;
|
||||
background: #19c37d;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
}
|
||||
#app .common-layout .el-main .right-box #container::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
#app .common-layout .el-main .chat-box #container::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#app .el-message-box {
|
||||
width: 90%;
|
||||
max-width: 420px;
|
||||
width: 90%;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
#app .el-message {
|
||||
min-width: 100px;
|
||||
max-width: 600px;
|
||||
min-width: 100px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.el-select-dropdown__wrap .el-select-dropdown__item .role-option {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
margin-top: 8px;
|
||||
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%;
|
||||
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;
|
||||
margin-left: 5px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.account {
|
||||
display: flex;
|
||||
background-color: #90ffc2;
|
||||
color: #000;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
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;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.account .vip-info {
|
||||
padding: 0 10px 0 10px;
|
||||
padding: 0 10px 0 10px;
|
||||
}
|
||||
|
||||
.account .vip-info h4,
|
||||
.account .vip-info p {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.account .vip-info h4 {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.account .vip-info p {
|
||||
color: #333;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.account .pay-btn {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .notice {
|
||||
padding: 0 20px 0 20px;
|
||||
line-height: 1.8;
|
||||
padding: 0 20px 0 20px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .notice .el-text {
|
||||
font-size: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.dialog-service {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dialog-service .el-image {
|
||||
width: 360px;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
@@ -10,35 +10,26 @@ $borderColor = #4676d0;
|
||||
// left side
|
||||
|
||||
.el-aside {
|
||||
background-color: $sideBgColor;
|
||||
height 100vh
|
||||
|
||||
.title-box {
|
||||
padding: 6px 10px;
|
||||
display: flex;
|
||||
color: #ffffff;
|
||||
font-size: 20px;
|
||||
|
||||
span {
|
||||
padding-top: 5px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
//background-color: $sideBgColor;
|
||||
padding 10px
|
||||
width var(--el-aside-width, 320px)
|
||||
|
||||
.chat-list {
|
||||
display: flex
|
||||
flex-flow: column
|
||||
background-color: #28292A
|
||||
border-top: 1px solid #2F3032
|
||||
border-right: 1px solid #2F3032
|
||||
//background-color: $sideBgColor
|
||||
border-radius 10px
|
||||
padding 10px 0
|
||||
|
||||
.search-box {
|
||||
flex-wrap: wrap
|
||||
padding: 10px 15px;
|
||||
//background-color #343540
|
||||
padding: 10px 0;
|
||||
|
||||
.el-input__wrapper {
|
||||
background-color: #363535;
|
||||
.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
|
||||
}
|
||||
}
|
||||
@@ -52,9 +43,6 @@ $borderColor = #4676d0;
|
||||
}
|
||||
|
||||
.content {
|
||||
//display flex
|
||||
//flex-wrap: wrap;
|
||||
//flex-direction column
|
||||
width: 100%
|
||||
overflow-y: scroll
|
||||
|
||||
@@ -65,14 +53,17 @@ $borderColor = #4676d0;
|
||||
padding: 8px 12px
|
||||
//border-bottom: 1px solid #3c3c3c
|
||||
cursor: pointer
|
||||
border: 1px solid #3c3c3c
|
||||
margin-bottom 6px
|
||||
border-radius 5px
|
||||
|
||||
&:hover {
|
||||
background-color #343540
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@@ -96,13 +87,17 @@ $borderColor = #4676d0;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display none
|
||||
|
||||
.chat-opt {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 16px;
|
||||
color #ffffff
|
||||
|
||||
.el-dropdown-link {
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
margin-right 8px;
|
||||
}
|
||||
@@ -111,10 +106,7 @@ $borderColor = #4676d0;
|
||||
|
||||
.chat-list-item.active {
|
||||
background-color: #343540;
|
||||
|
||||
.btn {
|
||||
display inline
|
||||
}
|
||||
border-color #21aa93
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,40 +114,12 @@ $borderColor = #4676d0;
|
||||
|
||||
.tool-box {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding 0 20px 10px 20px;
|
||||
justify-content: center;
|
||||
padding-top 12px
|
||||
border-top 1px solid #3c3c3c;
|
||||
|
||||
.user-info {
|
||||
width 100%
|
||||
padding-top 10px;
|
||||
|
||||
.el-dropdown-link {
|
||||
width 100%;
|
||||
cursor: pointer
|
||||
display flex
|
||||
|
||||
.el-image {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.username {
|
||||
display flex
|
||||
line-height 22px;
|
||||
width 230px;
|
||||
padding-left 10px;
|
||||
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
color: #cccccc;
|
||||
line-height 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
margin-right 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,56 +129,11 @@ $borderColor = #4676d0;
|
||||
--el-main-padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.chat-head {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background-color: #28292A
|
||||
|
||||
.chat-config {
|
||||
display flex
|
||||
flex-direction row
|
||||
align-items: center;
|
||||
justify-content center;
|
||||
padding-top 10px;
|
||||
|
||||
.role-select-label {
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
.el-select {
|
||||
max-width 150px;
|
||||
margin-right 10px;
|
||||
}
|
||||
|
||||
.role-select {
|
||||
max-width 130px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
.el-icon {
|
||||
margin-right 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
margin-right 5px;
|
||||
}
|
||||
|
||||
.is-circle {
|
||||
margin-left 5px
|
||||
|
||||
.iconfont {
|
||||
margin-right 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-box {
|
||||
.chat-box {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
background-color: #ffffff
|
||||
border-left: 1px solid #4f4f4f
|
||||
background-color: var(--el-bg-color)
|
||||
color var(--el-text-color-primary)
|
||||
|
||||
#container {
|
||||
overflow: hidden;
|
||||
@@ -245,23 +164,6 @@ $borderColor = #4676d0;
|
||||
}
|
||||
}
|
||||
|
||||
.re-generate {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.btn-box {
|
||||
position: absolute
|
||||
bottom: 10px;
|
||||
|
||||
.el-button {
|
||||
.el-icon {
|
||||
margin-right 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-box {
|
||||
background-color: #ffffff
|
||||
display: flex;
|
||||
@@ -270,6 +172,26 @@ $borderColor = #4676d0;
|
||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
|
||||
padding 0 15px;
|
||||
|
||||
.tool-item {
|
||||
margin-right 15px
|
||||
border-radius: 6px;
|
||||
color: #19c37d;
|
||||
display flex
|
||||
justify-content center
|
||||
justify-items center
|
||||
padding 6px
|
||||
cursor pointer
|
||||
background #F2F2F2
|
||||
|
||||
&:hover {
|
||||
background #D5FAD3
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-container {
|
||||
width 100%
|
||||
margin: 0;
|
||||
@@ -280,7 +202,6 @@ $borderColor = #4676d0;
|
||||
position relative
|
||||
|
||||
.el-textarea {
|
||||
|
||||
.el-textarea__inner::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
@@ -301,8 +222,6 @@ $borderColor = #4676d0;
|
||||
.el-button {
|
||||
padding 8px 5px;
|
||||
border-radius 6px;
|
||||
background: rgb(25, 195, 125)
|
||||
color #ffffff;
|
||||
font-size 20px;
|
||||
}
|
||||
}
|
||||
@@ -399,12 +318,9 @@ $borderColor = #4676d0;
|
||||
.el-dialog {
|
||||
.el-dialog__body {
|
||||
.notice {
|
||||
padding 0 20px 0 20px
|
||||
//padding 0 20px 0 20px
|
||||
line-height 1.8
|
||||
|
||||
.el-text {
|
||||
font-size 16px
|
||||
}
|
||||
font-size 16px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
188
web/src/assets/css/home.styl
Normal file
188
web/src/assets/css/home.styl
Normal file
@@ -0,0 +1,188 @@
|
||||
.home {
|
||||
display: flex;
|
||||
height 100vh
|
||||
width 100%
|
||||
flex-flow column
|
||||
|
||||
.header {
|
||||
display flex
|
||||
justify-content space-between
|
||||
height 50px
|
||||
line-height 50px
|
||||
background-color #1E1F22
|
||||
padding-right 20px
|
||||
|
||||
.banner {
|
||||
display flex
|
||||
|
||||
.logo {
|
||||
display flex
|
||||
padding 5px
|
||||
cursor pointer
|
||||
|
||||
.el-image {
|
||||
width 40px
|
||||
height 40px
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
color: #ffffff;
|
||||
font-size: 20px;
|
||||
padding 0 10px
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display flex
|
||||
flex-flow row
|
||||
|
||||
.link-button {
|
||||
margin-right 15px
|
||||
color #e1e1e1
|
||||
padding 0 10px
|
||||
|
||||
&:hover {
|
||||
background-color #414141
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size 24px
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
width 100%
|
||||
padding 5px 0;
|
||||
|
||||
.el-dropdown-link {
|
||||
width 100%;
|
||||
cursor: pointer
|
||||
display flex
|
||||
|
||||
.el-image {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
color: #cccccc;
|
||||
line-height 24px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.main {
|
||||
width 100%
|
||||
display flex
|
||||
flex-flow row
|
||||
|
||||
.navigator {
|
||||
display flex
|
||||
flex-flow column
|
||||
width 50px
|
||||
padding 10px 1px
|
||||
border-right: 1px solid #3c3c3c
|
||||
background-color: #1E1F22
|
||||
|
||||
.nav-items {
|
||||
margin-top: 10px;
|
||||
padding 0 5px
|
||||
|
||||
li {
|
||||
margin-bottom 15px
|
||||
|
||||
a {
|
||||
color #DADBDC
|
||||
border-radius 10px
|
||||
width 40px
|
||||
height 40px
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
cursor pointer
|
||||
background-color #414348
|
||||
|
||||
.el-image {
|
||||
border-radius 10px
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size 20px
|
||||
}
|
||||
}
|
||||
|
||||
a:hover, a.active {
|
||||
color #47fff1
|
||||
background-color #0F7A71
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 12px
|
||||
padding-top: 5px
|
||||
color: #e5e7eb;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.active {
|
||||
color #47fff1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.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-image {
|
||||
position: relative
|
||||
top 5px
|
||||
right 5px
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color #f1f1f1
|
||||
}
|
||||
}
|
||||
|
||||
li.active {
|
||||
background-color #f1f1f1
|
||||
}
|
||||
}
|
||||
|
||||
.user-info-menu {
|
||||
li {
|
||||
a {
|
||||
width 100%
|
||||
justify-content left
|
||||
|
||||
&:hover {
|
||||
text-decoration none !important
|
||||
color var(--el-primary-text-color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
.page-mj {
|
||||
background-color: #282c34;
|
||||
height 100vh
|
||||
height 100%
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
@@ -235,7 +235,7 @@
|
||||
|
||||
.task-list-box {
|
||||
width 100%
|
||||
padding 10px
|
||||
padding 0 10px 10px 10px
|
||||
color #ffffff
|
||||
overflow-x hidden
|
||||
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
border 1px solid #454545
|
||||
min-width 300px
|
||||
max-width 300px
|
||||
padding 10px
|
||||
padding 10px 10px 20px 10px
|
||||
border-radius 10px
|
||||
color #ffffff;
|
||||
font-size 14px
|
||||
overflow auto
|
||||
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
@@ -83,7 +84,7 @@
|
||||
|
||||
.task-list-box {
|
||||
width 100%
|
||||
padding 10px
|
||||
padding 0 10px 10px 10px
|
||||
color #ffffff
|
||||
overflow-x hidden
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.page-mark-map {
|
||||
background-color: #282c34;
|
||||
height 100vh
|
||||
height 100%
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
@@ -24,6 +24,7 @@
|
||||
}
|
||||
|
||||
// 隐藏滚动条
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
@@ -64,21 +65,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.right-box {
|
||||
|
||||
.chat-box {
|
||||
width 100%
|
||||
|
||||
.top-bar {
|
||||
display flex
|
||||
justify-content space-between
|
||||
justify-content right
|
||||
align-items center
|
||||
|
||||
h2 {
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-right 20px
|
||||
}
|
||||
padding 10px 20px 10px 10px
|
||||
}
|
||||
|
||||
.markdown {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.member {
|
||||
background-color: #282c34;
|
||||
height 100vh
|
||||
height 100%
|
||||
|
||||
.el-dialog {
|
||||
.el-dialog__body {
|
||||
@@ -65,9 +65,12 @@
|
||||
padding 15px 0 15px 15px;
|
||||
overflow-x hidden
|
||||
overflow-y visible
|
||||
display flex
|
||||
flex-flow row
|
||||
|
||||
.user-profile {
|
||||
padding 10px 20px 20px 20px
|
||||
width 300px
|
||||
background-color #393F4A
|
||||
color #ffffff
|
||||
border-radius 10px
|
||||
@@ -91,12 +94,15 @@
|
||||
|
||||
|
||||
.product-box {
|
||||
padding 0 20px
|
||||
width 100%
|
||||
|
||||
.info {
|
||||
.el-alert__description {
|
||||
font-size 14px !important
|
||||
margin 0
|
||||
}
|
||||
padding 10px 20px 20px 0
|
||||
padding 0 0 20px 0
|
||||
}
|
||||
|
||||
.list-box {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4125778 */
|
||||
src: url('iconfont.woff2?t=1713766977199') format('woff2'),
|
||||
url('iconfont.woff?t=1713766977199') format('woff'),
|
||||
url('iconfont.ttf?t=1713766977199') format('truetype');
|
||||
src: url('iconfont.woff2?t=1715987806624') format('woff2'),
|
||||
url('iconfont.woff?t=1715987806624') format('woff'),
|
||||
url('iconfont.ttf?t=1715987806624') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,42 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-control-simple:before {
|
||||
content: "\e624";
|
||||
}
|
||||
|
||||
.icon-mic-bold:before {
|
||||
content: "\e683";
|
||||
}
|
||||
|
||||
.icon-mic-thin:before {
|
||||
content: "\e8c2";
|
||||
}
|
||||
|
||||
.icon-attachment-cl:before {
|
||||
content: "\e66a";
|
||||
}
|
||||
|
||||
.icon-attachment-st:before {
|
||||
content: "\e63b";
|
||||
}
|
||||
|
||||
.icon-speaker:before {
|
||||
content: "\e607";
|
||||
}
|
||||
|
||||
.icon-clear:before {
|
||||
content: "\e900";
|
||||
}
|
||||
|
||||
.icon-bbs:before {
|
||||
content: "\e623";
|
||||
}
|
||||
|
||||
.icon-license:before {
|
||||
content: "\e65a";
|
||||
}
|
||||
|
||||
.icon-more:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,69 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "12243734",
|
||||
"name": "control",
|
||||
"font_class": "control-simple",
|
||||
"unicode": "e624",
|
||||
"unicode_decimal": 58916
|
||||
},
|
||||
{
|
||||
"icon_id": "6539424",
|
||||
"name": "麦克风",
|
||||
"font_class": "mic-bold",
|
||||
"unicode": "e683",
|
||||
"unicode_decimal": 59011
|
||||
},
|
||||
{
|
||||
"icon_id": "1727442",
|
||||
"name": "213麦克风",
|
||||
"font_class": "mic-thin",
|
||||
"unicode": "e8c2",
|
||||
"unicode_decimal": 59586
|
||||
},
|
||||
{
|
||||
"icon_id": "3730725",
|
||||
"name": "attach-attachment-cl",
|
||||
"font_class": "attachment-cl",
|
||||
"unicode": "e66a",
|
||||
"unicode_decimal": 58986
|
||||
},
|
||||
{
|
||||
"icon_id": "15748474",
|
||||
"name": "st-attachment",
|
||||
"font_class": "attachment-st",
|
||||
"unicode": "e63b",
|
||||
"unicode_decimal": 58939
|
||||
},
|
||||
{
|
||||
"icon_id": "1010",
|
||||
"name": "扬声器",
|
||||
"font_class": "speaker",
|
||||
"unicode": "e607",
|
||||
"unicode_decimal": 58887
|
||||
},
|
||||
{
|
||||
"icon_id": "8094805",
|
||||
"name": "clear",
|
||||
"font_class": "clear",
|
||||
"unicode": "e900",
|
||||
"unicode_decimal": 59648
|
||||
},
|
||||
{
|
||||
"icon_id": "34803640",
|
||||
"name": "论坛",
|
||||
"font_class": "bbs",
|
||||
"unicode": "e623",
|
||||
"unicode_decimal": 58915
|
||||
},
|
||||
{
|
||||
"icon_id": "310708",
|
||||
"name": "license",
|
||||
"font_class": "license",
|
||||
"unicode": "e65a",
|
||||
"unicode_decimal": 58970
|
||||
},
|
||||
{
|
||||
"icon_id": "1421807",
|
||||
"name": "更多",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,9 +7,9 @@
|
||||
|
||||
<div class="chat-item">
|
||||
<div class="content" v-html="content"></div>
|
||||
<div class="bar" v-if="createdAt !== ''">
|
||||
<div class="bar" v-if="createdAt">
|
||||
<span class="bar-item"><el-icon><Clock/></el-icon> {{ createdAt }}</span>
|
||||
<span class="bar-item">Tokens: {{ finalTokens }}</span>
|
||||
<!-- <span class="bar-item">Tokens: {{ finalTokens }}</span>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,67 +2,98 @@
|
||||
<div class="chat-line chat-line-reply">
|
||||
<div class="chat-line-inner">
|
||||
<div class="chat-icon">
|
||||
<img :src="icon" alt="ChatGPT">
|
||||
<img :src="data.icon" alt="ChatGPT">
|
||||
</div>
|
||||
|
||||
<div class="chat-item">
|
||||
<div class="content" v-html="content"></div>
|
||||
<div class="bar" v-if="createdAt !== ''">
|
||||
<span class="bar-item"><el-icon><Clock/></el-icon> {{ createdAt }}</span>
|
||||
<span class="bar-item">Tokens: {{ tokens }}</span>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="复制回答"
|
||||
placement="bottom"
|
||||
>
|
||||
<el-button type="info" class="copy-reply" :data-clipboard-text="orgContent">
|
||||
<el-icon>
|
||||
<DocumentCopy/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<div class="content" v-html="data.content"></div>
|
||||
<div class="bar" v-if="data.created_at">
|
||||
<span class="bar-item"><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span>
|
||||
<!-- <span class="bar-item">Tokens: {{ tokens }}</span>-->
|
||||
<span class="bar-item">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="复制回答"
|
||||
placement="bottom"
|
||||
>
|
||||
<el-icon class="copy-reply" :data-clipboard-text="data.orgContent">
|
||||
<DocumentCopy/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span v-if="!readOnly">
|
||||
<span class="bar-item" @click="reGenerate(data.prompt)">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="重新生成"
|
||||
placement="bottom"
|
||||
>
|
||||
<el-icon><Refresh/></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
|
||||
<span class="bar-item" @click="synthesis(data.orgContent)">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="生成语音朗读"
|
||||
placement="bottom"
|
||||
>
|
||||
<i class="iconfont icon-speaker"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
<!-- <span class="bar-item">-->
|
||||
<!-- <el-dropdown trigger="click">-->
|
||||
<!-- <span class="el-dropdown-link">-->
|
||||
<!-- <el-icon><More/></el-icon>-->
|
||||
<!-- </span>-->
|
||||
<!-- <template #dropdown>-->
|
||||
<!-- <el-dropdown-menu>-->
|
||||
<!-- <el-dropdown-item :icon="Headset" @click="synthesis(orgContent)">生成语音</el-dropdown-item>-->
|
||||
<!-- </el-dropdown-menu>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-dropdown>-->
|
||||
<!-- </span>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {defineComponent} from "vue"
|
||||
import {Clock, DocumentCopy, Position} from "@element-plus/icons-vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ChatReply',
|
||||
components: {Position, Clock, DocumentCopy},
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
orgContent: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
createdAt: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
tokens: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'images/gpt-icon.png',
|
||||
}
|
||||
<script setup>
|
||||
import {Clock, DocumentCopy, Refresh} from "@element-plus/icons-vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {dateFormat} from "@/utils/libs";
|
||||
// eslint-disable-next-line no-undef,no-unused-vars
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
finalTokens: this.tokens
|
||||
}
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['regen']);
|
||||
|
||||
if (!props.data.icon) {
|
||||
props.data.icon = "images/gpt-icon.png"
|
||||
}
|
||||
|
||||
const synthesis = (text) => {
|
||||
console.log(text)
|
||||
ElMessage.info("语音合成功能暂不可用")
|
||||
}
|
||||
|
||||
// 重新生成
|
||||
const reGenerate = (prompt) => {
|
||||
emits('regen', prompt)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@@ -218,10 +249,12 @@ export default defineComponent({
|
||||
padding 3px 5px;
|
||||
margin-right 10px;
|
||||
border-radius 5px;
|
||||
cursor pointer
|
||||
|
||||
.el-icon {
|
||||
position relative
|
||||
top 2px;
|
||||
cursor pointer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
<template>
|
||||
<el-container class="file-list-box">
|
||||
<el-tooltip class="box-item" effect="dark" content="打开文件管理中心">
|
||||
<el-button class="file-upload-img" @click="fetchFiles">
|
||||
<el-icon>
|
||||
<PictureFilled/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<a class="file-upload-img" @click="fetchFiles">
|
||||
<i class="iconfont icon-attachment-st"></i>
|
||||
</a>
|
||||
<el-dialog
|
||||
v-model="show"
|
||||
:close-on-click-modal="true"
|
||||
@@ -58,7 +53,7 @@
|
||||
import {ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {Delete, PictureFilled, Plus} from "@element-plus/icons-vue";
|
||||
import {Delete, Plus} from "@element-plus/icons-vue";
|
||||
import {isImage, removeArrayItem} from "@/utils/libs";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -132,11 +127,9 @@ const insertURL = (url) => {
|
||||
|
||||
.file-list-box {
|
||||
.file-upload-img {
|
||||
padding: 8px 5px;
|
||||
border-radius: 6px;
|
||||
background: #19c37d;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
.iconfont {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
<template>
|
||||
<div class="list-box" ref="container">
|
||||
<div class="list-inner">
|
||||
<div
|
||||
class="list-item"
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
:style="{width:itemWidth + 'px'}"
|
||||
>
|
||||
<div class="item-inner" :style="{padding: gap/2+'px'}">
|
||||
<div class="item-wrapper">
|
||||
<slot :item="item" :index="index" :width="itemWidth"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-box" ref="containerRef">
|
||||
<el-row :gutter="gap">
|
||||
<el-col v-for="item in items" :key="item.id" :span="span" :style="{marginBottom:gap+'px'} ">
|
||||
<slot :item="item"></slot>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +20,7 @@ const props = defineProps({
|
||||
},
|
||||
gap: {
|
||||
type: Number,
|
||||
default: 12
|
||||
default: 10
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
@@ -37,45 +28,35 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const container = ref(null)
|
||||
const itemWidth = ref(props.width)
|
||||
const containerRef = ref(null)
|
||||
const span = ref(12)
|
||||
|
||||
onMounted(() => {
|
||||
computeSize()
|
||||
calcSpan()
|
||||
})
|
||||
|
||||
const computeSize = () => {
|
||||
const w = container.value.offsetWidth - 10 // 减去滚动条的宽度
|
||||
let cols = Math.floor(w / props.width)
|
||||
itemWidth.value = Math.floor(w / cols)
|
||||
}
|
||||
|
||||
window.onresize = () => {
|
||||
computeSize()
|
||||
const calcSpan = () => {
|
||||
let cols = Math.floor(containerRef.value.offsetWidth / props.width)
|
||||
if (cols >= 12) {
|
||||
span.value = 1
|
||||
return
|
||||
}
|
||||
console.log(cols)
|
||||
while (cols > 1) {
|
||||
if (24 % cols === 0) {
|
||||
span.value = 24 / cols
|
||||
return
|
||||
}
|
||||
cols -= 1
|
||||
}
|
||||
span.value = 12
|
||||
}
|
||||
window.onresize = () => calcSpan()
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
|
||||
.list-box {
|
||||
|
||||
.list-inner {
|
||||
display flex
|
||||
flex-wrap wrap
|
||||
|
||||
.list-item {
|
||||
.item-inner {
|
||||
display flex
|
||||
|
||||
.item-wrapper {
|
||||
height 100%
|
||||
width 100%
|
||||
display flex
|
||||
justify-content center
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -221,7 +221,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed, ref} from "vue"
|
||||
import {ref, watch} from "vue"
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {setUserToken} from "@/store/session";
|
||||
@@ -234,8 +234,9 @@ import {arrayContains} from "@/utils/libs";
|
||||
const props = defineProps({
|
||||
show: Boolean,
|
||||
});
|
||||
const showDialog = computed(() => {
|
||||
return props.show
|
||||
const showDialog = ref(false)
|
||||
watch(() => props.show, (newValue) => {
|
||||
showDialog.value = newValue
|
||||
})
|
||||
|
||||
const login = ref(true)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="user-info" id="user-info">
|
||||
<el-form :model="user" label-width="150px">
|
||||
<el-form :model="user" label-width="100px">
|
||||
<el-row>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
@@ -26,7 +26,7 @@
|
||||
content="您已经是 VIP 会员"
|
||||
placement="right"
|
||||
>
|
||||
<el-image v-if="user.vip" :src="vipImg" style="height: 25px;margin-left: 10px"/>
|
||||
<span class="vip-icon"><el-image v-if="user.vip" :src="vipImg" style="height: 25px;margin-left: 10px"/></span>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="剩余算力">
|
||||
@@ -37,7 +37,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-row class="opt-line">
|
||||
<el-button color="#47fff1" :dark="false" round @click="save">保存</el-button>
|
||||
<el-button color="#47fff1" :dark="false" @click="save">保存</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -107,13 +107,18 @@ const save = () => {
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.user-info {
|
||||
padding 20px
|
||||
padding 20px 0
|
||||
|
||||
.el-row {
|
||||
justify-content center
|
||||
margin-bottom 10px
|
||||
}
|
||||
|
||||
.vip-icon {
|
||||
position relative
|
||||
top 5px
|
||||
}
|
||||
|
||||
.opt-line {
|
||||
padding-top 20px
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import {createApp} from 'vue'
|
||||
import ElementPlus from "element-plus"
|
||||
import "element-plus/dist/index.css"
|
||||
import '@/assets/iconfont/iconfont.css';
|
||||
import 'vant/lib/index.css';
|
||||
import App from './App.vue'
|
||||
import {createPinia} from "pinia";
|
||||
|
||||
@@ -81,6 +81,11 @@ const routes = [
|
||||
meta: {title: 'DALLE-3'},
|
||||
component: () => import('@/views/Dalle.vue'),
|
||||
},
|
||||
{
|
||||
name: 'ExternalLink',
|
||||
path: '/external',
|
||||
component: () => import('@/views/ExternalPage.vue'),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
13
web/src/store/sharedata.js
Normal file
13
web/src/store/sharedata.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import {defineStore} from 'pinia';
|
||||
|
||||
export const useSharedStore = defineStore('shared', {
|
||||
state: () => ({
|
||||
showLoginDialog: false
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
setShowLoginDialog(value) {
|
||||
this.showLoginDialog = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,19 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="page-apps custom-scroll">
|
||||
<div class="title">
|
||||
AI 助手应用中心
|
||||
</div>
|
||||
<div class="inner" :style="{height: listBoxHeight + 'px'}">
|
||||
<ItemList :items="list" v-if="list.length > 0" :gap="20" :width="250">
|
||||
<ItemList :items="list" v-if="list.length > 0" :gap="15" :width="300">
|
||||
<template #default="scope">
|
||||
<div class="app-item" :style="{width: scope.width+'px'}">
|
||||
<el-image :src="scope.item.icon" fit="cover" :style="{height: scope.width+'px'}"/>
|
||||
<div class="title">
|
||||
<span class="name">{{ scope.item.name }}</span>
|
||||
<div class="opt">
|
||||
<div class="item">
|
||||
<div class="image">
|
||||
<el-image :src="scope.item.icon" fit="cover"/>
|
||||
</div>
|
||||
|
||||
<div class="inner">
|
||||
<div class="info">
|
||||
<div class="info-title">{{ scope.item.name }}</div>
|
||||
<div class="info-text">{{ scope.item.hello_msg }}</div>
|
||||
</div>
|
||||
<div class="btn">
|
||||
<div v-if="hasRole(scope.item.key)">
|
||||
<el-button size="small" type="success" @click="useRole(scope.item)">使用</el-button>
|
||||
<el-button size="small" color="#21aa93" @click="useRole(scope.item)">使用</el-button>
|
||||
<el-button size="small" type="danger" @click="updateRole(scope.item,'remove')">移除</el-button>
|
||||
</div>
|
||||
<el-button v-else size="small"
|
||||
@@ -26,15 +29,34 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hello-msg" ref="elements">{{ scope.item.intro }}</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- <div class="app-item">-->
|
||||
<!-- <el-image :src="scope.item.icon" fit="cover"/>-->
|
||||
<!-- <div class="title">-->
|
||||
<!-- <span class="name">{{ scope.item.name }}</span>-->
|
||||
<!-- <div class="opt">-->
|
||||
<!-- <div v-if="hasRole(scope.item.key)">-->
|
||||
<!-- <el-button size="small" type="success" @click="useRole(scope.item)">使用</el-button>-->
|
||||
<!-- <el-button size="small" type="danger" @click="updateRole(scope.item,'remove')">移除</el-button>-->
|
||||
<!-- </div>-->
|
||||
<!-- <el-button v-else size="small"-->
|
||||
<!-- style="--el-color-primary:#009999"-->
|
||||
<!-- @click="updateRole(scope.item, 'add')">-->
|
||||
<!-- <el-icon>-->
|
||||
<!-- <Plus/>-->
|
||||
<!-- </el-icon>-->
|
||||
<!-- <span>添加应用</span>-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="hello-msg" ref="elements">{{ scope.item.intro }}</div>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
</ItemList>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<login-dialog :show="showLoginDialog" @hide="getRoles" @success=""/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -42,18 +64,18 @@
|
||||
import {onMounted, ref} from "vue"
|
||||
import {ElMessage} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import ItemList from "@/components/ItemList.vue";
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {checkSession} from "@/action/session";
|
||||
import {arrayContains, removeArrayItem, substr} from "@/utils/libs";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import ItemList from "@/components/ItemList.vue";
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
|
||||
const listBoxHeight = window.innerHeight - 97
|
||||
const listBoxHeight = window.innerHeight - 87
|
||||
const list = ref([])
|
||||
const showLoginDialog = ref(false)
|
||||
const roles = ref([])
|
||||
const elements = ref(null)
|
||||
const store = useSharedStore();
|
||||
|
||||
onMounted(() => {
|
||||
httpGet("/api/role/list?all=true").then((res) => {
|
||||
const items = res.data
|
||||
@@ -70,7 +92,6 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
const getRoles = () => {
|
||||
showLoginDialog.value = false
|
||||
checkSession().then(user => {
|
||||
roles.value = user.chat_roles
|
||||
}).catch(() => {
|
||||
@@ -101,7 +122,7 @@ const updateRole = (row, opt) => {
|
||||
ElMessage.error(title.value + "失败:" + e.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
<div class="chat-box" id="chat-box">
|
||||
<div class="title">
|
||||
<h2>{{ chatTitle }}</h2>
|
||||
<el-button type="success" @click="exportChat" :icon="Promotion">
|
||||
导出 PDF 文档
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-for="item in chatData" :key="item.id">
|
||||
@@ -14,17 +11,10 @@
|
||||
:icon="item.icon"
|
||||
:created-at="dateFormat(item['created_at'])"
|
||||
:tokens="item['tokens']"
|
||||
:model="item['model']"
|
||||
:content="item.content"/>
|
||||
<chat-reply v-else-if="item.type==='reply'"
|
||||
:icon="item.icon"
|
||||
:org-content="item.orgContent"
|
||||
:created-at="dateFormat(item['created_at'])"
|
||||
:tokens="item['tokens']"
|
||||
:content="item.content"/>
|
||||
<chat-mid-journey v-else-if="item.type==='mj'"
|
||||
:content="item.content"
|
||||
:icon="item.icon"
|
||||
:created-at="dateFormat(item['created_at'])"/>
|
||||
:data="item" :read-only="true"/>
|
||||
</div>
|
||||
</div><!-- end chat box -->
|
||||
</div>
|
||||
@@ -34,14 +24,13 @@
|
||||
import {dateFormat} from "@/utils/libs";
|
||||
import ChatReply from "@/components/ChatReply.vue";
|
||||
import ChatPrompt from "@/components/ChatPrompt.vue";
|
||||
import {nextTick, ref} from "vue";
|
||||
import {nextTick, onMounted, ref} from "vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import 'highlight.js/styles/a11y-dark.css'
|
||||
import hl from "highlight.js";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {Promotion} from "@element-plus/icons-vue";
|
||||
import ChatMidJourney from "@/components/ChatMidJourney.vue";
|
||||
import Clipboard from "clipboard";
|
||||
|
||||
const chatData = ref([])
|
||||
const router = useRouter()
|
||||
@@ -91,9 +80,16 @@ httpGet('/api/chat/detail?chat_id=' + chatId).then(res => {
|
||||
ElMessage.error("加载会失败: " + e.message)
|
||||
})
|
||||
|
||||
const exportChat = () => {
|
||||
window.print()
|
||||
}
|
||||
onMounted(() => {
|
||||
const clipboard = new Clipboard('.copy-reply');
|
||||
clipboard.on('success', () => {
|
||||
ElMessage.success('复制成功!');
|
||||
})
|
||||
|
||||
clipboard.on('error', () => {
|
||||
ElMessage.error('复制失败!');
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style lang="stylus">
|
||||
.chat-export {
|
||||
@@ -115,12 +111,15 @@ const exportChat = () => {
|
||||
}
|
||||
|
||||
|
||||
.chat-line {
|
||||
.chat-line-prompt {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.chat-line-inner {
|
||||
.chat-icon {
|
||||
margin-right: 0
|
||||
}
|
||||
.content {
|
||||
padding-top: 0
|
||||
font-size 16px;
|
||||
@@ -138,10 +137,6 @@ const exportChat = () => {
|
||||
.chat-line-inner {
|
||||
display flex
|
||||
|
||||
.copy-reply {
|
||||
display none
|
||||
}
|
||||
|
||||
.bar-item {
|
||||
background-color: #f7f7f8;
|
||||
color: #888;
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
<template>
|
||||
<div class="common-layout theme-white">
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-aside>
|
||||
<div class="title-box">
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
<div class="chat-list">
|
||||
<el-button @click="newChat" color="#21aa93">
|
||||
<el-icon style="margin-right: 5px">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
新建对话
|
||||
</el-button>
|
||||
|
||||
<div class="search-box">
|
||||
<el-input v-model="chatName" class="w-50 m-2" size="small" placeholder="搜索会话" @keyup="searchChat">
|
||||
<el-input v-model="chatName" placeholder="搜索会话" @keyup="searchChat($event)" style=""
|
||||
class="search-input">
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon">
|
||||
<Search/>
|
||||
@@ -22,111 +27,45 @@
|
||||
@click="changeChat(chat)">
|
||||
<el-image :src="chat.icon" class="avatar"/>
|
||||
<span class="chat-title-input" v-if="chat.edit">
|
||||
<el-input v-model="tmpChatTitle" size="small" @keydown="titleKeydown($event, chat)"
|
||||
placeholder="请输入会话标题"/>
|
||||
</span>
|
||||
<el-input v-model="tmpChatTitle" size="small" @keydown="titleKeydown($event, chat)"
|
||||
:id="'chat-'+chat.chat_id"
|
||||
@blur="editConfirm(chat)"
|
||||
@click="stopPropagation($event)"
|
||||
placeholder="请输入标题"/>
|
||||
</span>
|
||||
<span v-else class="chat-title">{{ chat.title }}</span>
|
||||
<span class="btn btn-check" v-if="chat.edit || chat.removing">
|
||||
<el-icon @click="confirm($event, chat)"><Check/></el-icon>
|
||||
<el-icon @click="cancel($event, chat)"><Close/></el-icon>
|
||||
</span>
|
||||
<span class="btn" v-else>
|
||||
<el-icon title="编辑" @click="editChatTitle($event, chat)"><Edit/></el-icon>
|
||||
<el-icon title="删除会话" @click="removeChat($event, chat)"><Delete/></el-icon>
|
||||
</span>
|
||||
|
||||
<span class="chat-opt">
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link" @click="stopPropagation($event)">
|
||||
<el-icon><More/></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :icon="Edit" @click="editChatTitle(chat)">重命名</el-dropdown-item>
|
||||
<el-dropdown-item :icon="Delete"
|
||||
style="--el-text-color-regular: var(--el-color-danger);
|
||||
--el-dropdown-menuItem-hover-fill:#F8E1DE;
|
||||
--el-dropdown-menuItem-hover-color: var(--el-color-danger)"
|
||||
@click="removeChat(chat)">删除</el-dropdown-item>
|
||||
<el-dropdown-item :icon="Share" @click="shareChat(chat)">分享</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tool-box">
|
||||
<el-dropdown :hide-on-click="true" class="user-info" trigger="click" v-if="isLogin">
|
||||
<span class="el-dropdown-link">
|
||||
<el-image :src="loginUser.avatar"/>
|
||||
<span class="username">{{ loginUser.nickname }}</span>
|
||||
<el-icon><ArrowDown/></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu style="width: 296px;">
|
||||
<el-dropdown-item @click="showConfig">
|
||||
<el-icon>
|
||||
<Tools/>
|
||||
</el-icon>
|
||||
<span>账户信息</span>
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item @click="clearAllChats">
|
||||
<el-icon>
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
<span>清除所有会话</span>
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item @click="logout">
|
||||
<i class="iconfont icon-logout"></i>
|
||||
<span>注销</span>
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item>
|
||||
<i class="iconfont icon-github"></i>
|
||||
<span>
|
||||
powered by
|
||||
<el-link type="primary" href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">chatgpt-plus-v3</el-link>
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button type="danger" size="small" @click="clearAllChats">
|
||||
<i class="iconfont icon-clear"></i> 清空聊天记录
|
||||
</el-button>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.3)">
|
||||
<div class="chat-head">
|
||||
<div class="chat-config">
|
||||
<el-select v-model="roleId" filterable placeholder="角色" class="role-select" @change="_newChat"
|
||||
style="width:150px">
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
<div class="role-option">
|
||||
<el-image :src="item.icon"></el-image>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-select v-model="modelID" placeholder="模型" @change="_newChat" :disabled="disableModel"
|
||||
style="width:150px">
|
||||
<el-option
|
||||
v-for="item in models"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
<el-tag style="margin-left: 5px; position: relative; top:-2px" type="info" size="small">{{
|
||||
item.power
|
||||
}}算力
|
||||
</el-tag>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="newChat">
|
||||
<el-icon>
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
新建对话
|
||||
</el-button>
|
||||
|
||||
<el-button type="success" @click="exportChat" plain>
|
||||
<i class="iconfont icon-export"></i>
|
||||
<span>导出会话</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-box" :style="{height: mainWinHeight+'px'}">
|
||||
<div class="chat-box" :style="{height: mainWinHeight+'px'}">
|
||||
<div>
|
||||
<div id="container">
|
||||
<div class="chat-box" id="chat-box" :style="{height: chatBoxHeight+'px'}">
|
||||
@@ -141,63 +80,103 @@
|
||||
:tokens="item['tokens']"
|
||||
:model="getModelValue(modelID)"
|
||||
:content="item.content"/>
|
||||
<chat-reply v-else-if="item.type==='reply'"
|
||||
:icon="item.icon"
|
||||
:org-content="item.orgContent"
|
||||
:created-at="dateFormat(item['created_at'])"
|
||||
:tokens="item['tokens']"
|
||||
:content="item.content"/>
|
||||
<chat-mid-journey v-else-if="item.type==='mj'"
|
||||
:content="item.content"
|
||||
:role-id="item.role_id"
|
||||
:chat-id="item.chat_id"
|
||||
:icon="item.icon"
|
||||
@disable-input="disableInput(true)"
|
||||
@enable-input="enableInput"
|
||||
:created-at="dateFormat(item['created_at'])"/>
|
||||
<chat-reply v-else-if="item.type==='reply'" :data="item" @regen="reGenerate" :read-only="false"/>
|
||||
</div>
|
||||
</div><!-- end chat box -->
|
||||
|
||||
<div class="re-generate">
|
||||
<div class="btn-box">
|
||||
<el-button type="info" v-if="showStopGenerate" @click="stopGenerate" plain>
|
||||
<el-icon>
|
||||
<VideoPause/>
|
||||
</el-icon>
|
||||
停止生成
|
||||
</el-button>
|
||||
<el-affix position="bottom" :offset="0">
|
||||
<div class="input-box">
|
||||
<span class="tool-item">
|
||||
<el-popover
|
||||
:width="300"
|
||||
trigger="click"
|
||||
placement="top-start"
|
||||
>
|
||||
<template #reference>
|
||||
<div>
|
||||
<el-tooltip effect="dark" content="模型选择">
|
||||
<i class="iconfont icon-model"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-button type="primary" v-if="showReGenerate" @click="reGenerate" plain>
|
||||
<el-icon>
|
||||
<RefreshRight/>
|
||||
</el-icon>
|
||||
重新生成
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #default>
|
||||
<div class="chat-config">
|
||||
<el-select v-model="roleId" filterable placeholder="角色" @change="_newChat"
|
||||
class="role-select"
|
||||
style="width:150px">
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
<div class="role-option">
|
||||
<el-image :src="item.icon"></el-image>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<div class="input-box">
|
||||
<div class="input-container">
|
||||
<el-input
|
||||
ref="textInput"
|
||||
v-model="prompt"
|
||||
v-on:keydown="inputKeyDown"
|
||||
autofocus
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="按 Enter 键发送消息,使用 Ctrl + Enter 换行"
|
||||
/>
|
||||
<span class="select-file">
|
||||
<file-select v-if="isLogin" :user-id="loginUser.id" @selected="insertURL"/>
|
||||
<el-select v-model="modelID" filterable placeholder="模型" @change="_newChat"
|
||||
:disabled="disableModel"
|
||||
style="width:150px">
|
||||
<el-option
|
||||
v-for="item in models"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
<el-tag style="margin-left: 5px; position: relative; top:-2px" type="info" size="small">{{
|
||||
item.power
|
||||
}}算力
|
||||
</el-tag>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
</span>
|
||||
<span class="send-btn">
|
||||
<el-button @click="sendMessage">
|
||||
|
||||
<span class="tool-item" @click="ElMessage.info('暂时不支持语音输入')">
|
||||
<el-tooltip class="box-item" effect="dark" content="语音输入">
|
||||
<i class="iconfont icon-mic-bold"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
|
||||
<span class="tool-item" v-if="isLogin">
|
||||
<el-tooltip class="box-item" effect="dark" content="上传附件">
|
||||
<file-select v-if="isLogin" :user-id="loginUser.id" @selected="insertURL"/>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
|
||||
<div class="input-container">
|
||||
<el-input
|
||||
ref="textInput"
|
||||
v-model="prompt"
|
||||
v-on:keydown="inputKeyDown"
|
||||
autofocus
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
style="--el-input-focus-border-color:#21AA93;
|
||||
border: 1px solid #21AA93;--el-input-border-color:#21AA93;
|
||||
border-radius: 5px; --el-input-hover-border-color:#21AA93;"
|
||||
placeholder="按 Enter 键发送消息,使用 Ctrl + Enter 换行"
|
||||
/>
|
||||
<span class="send-btn">
|
||||
<el-button type="info" v-if="showStopGenerate" @click="stopGenerate" plain>
|
||||
<el-icon>
|
||||
<VideoPause/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button @click="sendMessage" color="#19c37d" style="color:#ffffff" v-else>
|
||||
<el-icon><Promotion/></el-icon>
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div><!-- end input box -->
|
||||
|
||||
</div>
|
||||
</div><!-- end input box -->
|
||||
</el-affix>
|
||||
</div><!-- end container -->
|
||||
</div><!-- end loading -->
|
||||
</div>
|
||||
@@ -207,23 +186,17 @@
|
||||
<el-dialog
|
||||
v-model="showNotice"
|
||||
:show-close="true"
|
||||
custom-class="notice-dialog"
|
||||
class="notice-dialog"
|
||||
title="网站公告"
|
||||
>
|
||||
<div class="notice">
|
||||
<el-text type="primary">
|
||||
<div v-html="notice"></div>
|
||||
</el-text>
|
||||
<div v-html="notice"></div>
|
||||
|
||||
<p style="text-align: right">
|
||||
<el-button @click="notShow" type="success" plain>我知道了,不再显示</el-button>
|
||||
</p>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<config-dialog v-if="isLogin" :show="showConfigDialog" :models="models" @hide="showConfigDialog = false"/>
|
||||
|
||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -232,19 +205,7 @@
|
||||
import {nextTick, onMounted, onUnmounted, ref} from 'vue'
|
||||
import ChatPrompt from "@/components/ChatPrompt.vue";
|
||||
import ChatReply from "@/components/ChatReply.vue";
|
||||
import {
|
||||
ArrowDown,
|
||||
Check,
|
||||
Close,
|
||||
Delete,
|
||||
Edit,
|
||||
Plus,
|
||||
Promotion,
|
||||
RefreshRight,
|
||||
Search,
|
||||
Tools,
|
||||
VideoPause
|
||||
} from '@element-plus/icons-vue'
|
||||
import {Delete, Edit, More, Plus, Promotion, Search, Share, VideoPause} from '@element-plus/icons-vue'
|
||||
import 'highlight.js/styles/a11y-dark.css'
|
||||
import {dateFormat, escapeHTML, isMobile, processContent, randString, removeArrayItem, UUID} from "@/utils/libs";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
@@ -253,12 +214,10 @@ import {getSessionId, getUserToken, removeUserToken} from "@/store/session";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {useRouter} from "vue-router";
|
||||
import Clipboard from "clipboard";
|
||||
import ConfigDialog from "@/components/ConfigDialog.vue";
|
||||
import {checkSession} from "@/action/session";
|
||||
import Welcome from "@/components/Welcome.vue";
|
||||
import ChatMidJourney from "@/components/ChatMidJourney.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import FileSelect from "@/components/FileSelect.vue";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
|
||||
const title = ref('ChatGPT-智能助手');
|
||||
const models = ref([])
|
||||
@@ -276,14 +235,13 @@ const roles = ref([]);
|
||||
const router = useRouter();
|
||||
const roleId = ref(0)
|
||||
const newChatItem = ref(null);
|
||||
const showConfigDialog = ref(false);
|
||||
const showLoginDialog = ref(false)
|
||||
const isLogin = ref(false)
|
||||
const showHello = ref(true)
|
||||
const textInput = ref(null)
|
||||
const showNotice = ref(false)
|
||||
const notice = ref("")
|
||||
const noticeKey = ref("SYSTEM_NOTICE")
|
||||
const store = useSharedStore();
|
||||
|
||||
if (isMobile()) {
|
||||
router.replace("/mobile/chat")
|
||||
@@ -414,9 +372,9 @@ const getRoleById = function (rid) {
|
||||
}
|
||||
|
||||
const resizeElement = function () {
|
||||
chatBoxHeight.value = window.innerHeight - 51 - 82 - 38;
|
||||
mainWinHeight.value = window.innerHeight - 51;
|
||||
leftBoxHeight.value = window.innerHeight - 43 - 47 - 45;
|
||||
chatBoxHeight.value = window.innerHeight - 50 - 82 - 38;
|
||||
mainWinHeight.value = window.innerHeight - 50;
|
||||
leftBoxHeight.value = window.innerHeight - 90 - 45 - 82;
|
||||
};
|
||||
|
||||
const _newChat = () => {
|
||||
@@ -424,17 +382,17 @@ const _newChat = () => {
|
||||
newChat()
|
||||
}
|
||||
}
|
||||
|
||||
const disableModel = ref(false)
|
||||
// 新建会话
|
||||
const newChat = () => {
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return;
|
||||
}
|
||||
const role = getRoleById(roleId.value)
|
||||
showHello.value = role.key === 'gpt';
|
||||
// if the role bind a model, disable model change
|
||||
disableModel.value = false
|
||||
if (role.model_id > 0) {
|
||||
modelID.value = role.model_id
|
||||
disableModel.value = true
|
||||
@@ -462,7 +420,6 @@ const newChat = () => {
|
||||
};
|
||||
activeChat.value = {} //取消激活的会话高亮
|
||||
showStopGenerate.value = false;
|
||||
showReGenerate.value = false;
|
||||
connect(null, roleId.value)
|
||||
}
|
||||
|
||||
@@ -474,7 +431,7 @@ const changeChat = (chat) => {
|
||||
|
||||
const loadChat = function (chat) {
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -487,69 +444,77 @@ const loadChat = function (chat) {
|
||||
roleId.value = chat.role_id;
|
||||
modelID.value = chat.model_id;
|
||||
showStopGenerate.value = false;
|
||||
showReGenerate.value = false;
|
||||
connect(chat.chat_id, chat.role_id)
|
||||
}
|
||||
|
||||
// 编辑会话标题
|
||||
const curOpt = ref('')
|
||||
const tmpChatTitle = ref('');
|
||||
const editChatTitle = function (event, chat) {
|
||||
event.stopPropagation();
|
||||
const editChatTitle = (chat) => {
|
||||
chat.edit = true;
|
||||
curOpt.value = 'edit';
|
||||
tmpChatTitle.value = chat.title;
|
||||
console.log(chat.chat_id)
|
||||
nextTick(() => {
|
||||
document.getElementById('chat-' + chat.chat_id).focus()
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
const titleKeydown = (e, chat) => {
|
||||
if (e.keyCode === 13) {
|
||||
e.stopPropagation();
|
||||
confirm(e, chat)
|
||||
editConfirm(chat)
|
||||
}
|
||||
}
|
||||
|
||||
const stopPropagation = (e) => {
|
||||
e.stopPropagation();
|
||||
}
|
||||
// 确认修改
|
||||
const confirm = function (event, chat) {
|
||||
event.stopPropagation();
|
||||
if (curOpt.value === 'edit') {
|
||||
if (tmpChatTitle.value === '') {
|
||||
return ElMessage.error("请输入会话标题!");
|
||||
}
|
||||
if (!chat.chat_id) {
|
||||
return ElMessage.error("对话 ID 为空,请刷新页面再试!");
|
||||
}
|
||||
httpPost('/api/chat/update', {chat_id: chat.chat_id, title: tmpChatTitle.value}).then(() => {
|
||||
chat.title = tmpChatTitle.value;
|
||||
chat.edit = false;
|
||||
}).catch(e => {
|
||||
ElMessage.error("操作失败:" + e.message);
|
||||
})
|
||||
} else if (curOpt.value === 'remove') {
|
||||
httpGet('/api/chat/remove?chat_id=' + chat.chat_id).then(() => {
|
||||
chatList.value = removeArrayItem(chatList.value, chat, function (e1, e2) {
|
||||
return e1.id === e2.id
|
||||
})
|
||||
// 重置会话
|
||||
newChat();
|
||||
}).catch(e => {
|
||||
ElMessage.error("操作失败:" + e.message);
|
||||
})
|
||||
|
||||
const editConfirm = function (chat) {
|
||||
if (tmpChatTitle.value === '') {
|
||||
return ElMessage.error("请输入会话标题!");
|
||||
}
|
||||
if (!chat.chat_id) {
|
||||
return ElMessage.error("对话 ID 为空,请刷新页面再试!");
|
||||
}
|
||||
if (tmpChatTitle.value === chat.title) {
|
||||
chat.edit = false;
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
// 取消修改
|
||||
const cancel = function (event, chat) {
|
||||
event.stopPropagation();
|
||||
chat.edit = false;
|
||||
chat.removing = false;
|
||||
}
|
||||
httpPost('/api/chat/update', {chat_id: chat.chat_id, title: tmpChatTitle.value}).then(() => {
|
||||
chat.title = tmpChatTitle.value;
|
||||
chat.edit = false;
|
||||
}).catch(e => {
|
||||
ElMessage.error("操作失败:" + e.message);
|
||||
})
|
||||
|
||||
}
|
||||
// 删除会话
|
||||
const removeChat = function (event, chat) {
|
||||
event.stopPropagation();
|
||||
chat.removing = true;
|
||||
curOpt.value = 'remove';
|
||||
const removeChat = function (chat) {
|
||||
ElMessageBox.confirm(
|
||||
`该操作会删除"${chat.title}"`,
|
||||
'删除聊天',
|
||||
{
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
httpGet('/api/chat/remove?chat_id=' + chat.chat_id).then(() => {
|
||||
chatList.value = removeArrayItem(chatList.value, chat, function (e1, e2) {
|
||||
return e1.id === e2.id
|
||||
})
|
||||
// 重置会话
|
||||
newChat();
|
||||
}).catch(e => {
|
||||
ElMessage.error("操作失败:" + e.message);
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const latexPlugin = require('markdown-it-latex2img')
|
||||
@@ -584,8 +549,6 @@ md.use(mathjaxPlugin)
|
||||
// 创建 socket 连接
|
||||
const prompt = ref('');
|
||||
const showStopGenerate = ref(false); // 停止生成
|
||||
const showReGenerate = ref(false); // 重新生成
|
||||
const previousText = ref(''); // 上一次提问
|
||||
const lineBuffer = ref(''); // 输出缓冲行
|
||||
const socket = ref(null);
|
||||
const activelyClose = ref(false); // 主动关闭
|
||||
@@ -630,7 +593,6 @@ const connect = function (chat_id, role_id) {
|
||||
const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${role_id}&chat_id=${chat_id}&model_id=${modelID.value}&token=${getUserToken()}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
chatData.value = []; // 初始化聊天数据
|
||||
previousText.value = '';
|
||||
enableInput()
|
||||
activelyClose.value = false;
|
||||
|
||||
@@ -670,7 +632,7 @@ const connect = function (chat_id, role_id) {
|
||||
} else if (data.type === 'end') { // 消息接收完毕
|
||||
// 追加当前会话到会话列表
|
||||
if (isNewChat && newChatItem.value !== null) {
|
||||
newChatItem.value['title'] = previousText.value;
|
||||
newChatItem.value['title'] = tmpChatTitle.value;
|
||||
newChatItem.value['chat_id'] = chat_id;
|
||||
chatList.value.unshift(newChatItem.value);
|
||||
activeChat.value = newChatItem.value;
|
||||
@@ -735,13 +697,11 @@ const connect = function (chat_id, role_id) {
|
||||
|
||||
const disableInput = (force) => {
|
||||
canSend.value = false;
|
||||
showReGenerate.value = false;
|
||||
showStopGenerate.value = !force;
|
||||
}
|
||||
|
||||
const enableInput = () => {
|
||||
canSend.value = true;
|
||||
showReGenerate.value = previousText.value !== "";
|
||||
showStopGenerate.value = false;
|
||||
}
|
||||
|
||||
@@ -766,7 +726,7 @@ const autofillPrompt = (text) => {
|
||||
// 发送消息
|
||||
const sendMessage = function () {
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -784,7 +744,7 @@ const sendMessage = function () {
|
||||
id: randString(32),
|
||||
icon: loginUser.value.avatar,
|
||||
content: md.render(escapeHTML(processContent(prompt.value))),
|
||||
created_at: new Date().getTime(),
|
||||
created_at: new Date().getTime() / 1000,
|
||||
});
|
||||
|
||||
nextTick(() => {
|
||||
@@ -794,15 +754,11 @@ const sendMessage = function () {
|
||||
showHello.value = false
|
||||
disableInput(false)
|
||||
socket.value.send(JSON.stringify({type: "chat", content: prompt.value}));
|
||||
previousText.value = prompt.value;
|
||||
tmpChatTitle.value = prompt.value
|
||||
prompt.value = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
const showConfig = function () {
|
||||
showConfigDialog.value = true;
|
||||
}
|
||||
|
||||
const clearAllChats = function () {
|
||||
ElMessageBox.confirm(
|
||||
'确认要清空所有的会话历史记录吗?<br/>此操作不可以撤销!',
|
||||
@@ -850,6 +806,9 @@ const loadChatHistory = function (chatId) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
data[i].orgContent = data[i].content;
|
||||
data[i].content = md.render(processContent(data[i].content))
|
||||
if (i > 0 && data[i].type === 'reply') {
|
||||
data[i].prompt = data[i - 1].orgContent
|
||||
}
|
||||
chatData.value.push(data[i]);
|
||||
}
|
||||
|
||||
@@ -871,9 +830,9 @@ const stopGenerate = function () {
|
||||
}
|
||||
|
||||
// 重新生成
|
||||
const reGenerate = function () {
|
||||
const reGenerate = function (prompt) {
|
||||
disableInput(false)
|
||||
const text = '重新生成上述问题的答案:' + previousText.value;
|
||||
const text = '重新生成下面问题的答案:' + prompt;
|
||||
// 追加消息
|
||||
chatData.value.push({
|
||||
type: "prompt",
|
||||
@@ -881,32 +840,34 @@ const reGenerate = function () {
|
||||
icon: loginUser.value.avatar,
|
||||
content: md.render(text)
|
||||
});
|
||||
socket.value.send(JSON.stringify({type: "chat", content: previousText.value}));
|
||||
socket.value.send(JSON.stringify({type: "chat", content: prompt}));
|
||||
}
|
||||
|
||||
const chatName = ref('')
|
||||
// 搜索会话
|
||||
const searchChat = function () {
|
||||
const searchChat = function (e) {
|
||||
if (chatName.value === '') {
|
||||
chatList.value = allChats.value
|
||||
return
|
||||
}
|
||||
const items = [];
|
||||
for (let i = 0; i < allChats.value.length; i++) {
|
||||
if (allChats.value[i].title.toLowerCase().indexOf(chatName.value.toLowerCase()) !== -1) {
|
||||
items.push(allChats.value[i]);
|
||||
if (e.keyCode === 13) {
|
||||
const items = [];
|
||||
for (let i = 0; i < allChats.value.length; i++) {
|
||||
if (allChats.value[i].title.toLowerCase().indexOf(chatName.value.toLowerCase()) !== -1) {
|
||||
items.push(allChats.value[i]);
|
||||
}
|
||||
}
|
||||
chatList.value = items;
|
||||
}
|
||||
chatList.value = items;
|
||||
}
|
||||
|
||||
// 导出会话
|
||||
const exportChat = () => {
|
||||
if (!activeChat.value['chat_id']) {
|
||||
const shareChat = (chat) => {
|
||||
if (!chat.chat_id) {
|
||||
return ElMessage.error("请先选中一个会话")
|
||||
}
|
||||
|
||||
const url = location.protocol + '//' + location.host + '/chat/export?chat_id=' + activeChat.value['chat_id']
|
||||
const url = location.protocol + '//' + location.host + '/chat/export?chat_id=' + chat.chat_id
|
||||
// console.log(url)
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
@@ -939,8 +900,58 @@ const insertURL = (url) => {
|
||||
|
||||
<style lang="stylus">
|
||||
.notice-dialog {
|
||||
.el-dialog__header {
|
||||
padding-bottom 0
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding 0 20px
|
||||
|
||||
ol, ul {
|
||||
padding-left 10px
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style decimal-leading-zero
|
||||
padding-left 20px
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style disc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-container {
|
||||
.el-textarea {
|
||||
.el-textarea__inner {
|
||||
padding-right 40px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-config {
|
||||
display flex
|
||||
flex-direction row
|
||||
padding-top 10px;
|
||||
|
||||
.role-select-label {
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
.el-select {
|
||||
max-width 150px;
|
||||
margin-right 10px;
|
||||
}
|
||||
|
||||
.role-select {
|
||||
max-width 130px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
.el-icon {
|
||||
margin-right 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="sd-box">
|
||||
<h2>DALL-E 创作中心</h2>
|
||||
|
||||
<div class="sd-params" :style="{ height: paramBoxHeight + 'px' }">
|
||||
<div class="sd-params">
|
||||
<el-form :model="params" label-width="80px" label-position="left">
|
||||
<div class="param-line" style="padding-top: 10px">
|
||||
<el-form-item label="图片质量">
|
||||
@@ -88,7 +88,7 @@
|
||||
<h2>任务列表</h2>
|
||||
<div class="running-job-list">
|
||||
<div class="running-job-box" v-if="runningJobs.length > 0">
|
||||
<div class="job-item" v-for="item in runningJobs">
|
||||
<div class="job-item" v-for="item in runningJobs" :key="item.id">
|
||||
<div v-if="item.progress > 0" class="job-item-inner">
|
||||
<el-image :src="item['img_url']" fit="cover" loading="lazy">
|
||||
<template #placeholder>
|
||||
@@ -221,32 +221,35 @@
|
||||
|
||||
</div>
|
||||
|
||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
||||
<el-image-viewer @close="() => { previewURL = '' }" v-if="previewURL !== ''" :url-list="[previewURL]"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, onUnmounted, ref} from "vue"
|
||||
import {Delete, InfoFilled} from "@element-plus/icons-vue";
|
||||
import {Delete, InfoFilled, Picture} from "@element-plus/icons-vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession} from "@/action/session";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const listBoxHeight = ref(window.innerHeight - 40)
|
||||
const paramBoxHeight = ref(window.innerHeight - 150)
|
||||
const showLoginDialog = ref(false)
|
||||
const listBoxHeight = ref(0)
|
||||
// const paramBoxHeight = ref(0)
|
||||
const isLogin = ref(false)
|
||||
const loading = ref(true)
|
||||
const colWidth = ref(240)
|
||||
const colWidth = ref(220)
|
||||
const isOver = ref(false)
|
||||
const previewURL = ref("")
|
||||
const store = useSharedStore();
|
||||
|
||||
const resizeElement = function () {
|
||||
listBoxHeight.value = window.innerHeight - 90
|
||||
// paramBoxHeight.value = window.innerHeight - 110
|
||||
};
|
||||
resizeElement()
|
||||
window.onresize = () => {
|
||||
listBoxHeight.value = window.innerHeight - 40
|
||||
paramBoxHeight.value = window.innerHeight - 150
|
||||
resizeElement()
|
||||
}
|
||||
const qualities = [
|
||||
{name: "标准", value: "standard"},
|
||||
@@ -351,8 +354,8 @@ const connect = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH") {
|
||||
page.value = 0
|
||||
fetchFinishJobs(page.value)
|
||||
isOver.value = false
|
||||
fetchFinishJobs(page.value)
|
||||
}
|
||||
fetchRunningJobs()
|
||||
}
|
||||
@@ -434,7 +437,7 @@ const generate = () => {
|
||||
}
|
||||
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
httpPost("/api/dall/image", params.value).then(() => {
|
||||
@@ -458,7 +461,9 @@ const removeImage = (event, item) => {
|
||||
).then(() => {
|
||||
httpPost("/api/dall/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
|
||||
ElMessage.success("任务删除成功")
|
||||
fetchFinishJobs(1)
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error("任务删除失败:" + e.message)
|
||||
})
|
||||
@@ -480,6 +485,9 @@ const publishImage = (event, item, action) => {
|
||||
httpPost("/api/dall/publish", {id: item.id, action: action}).then(() => {
|
||||
ElMessage.success(text + "成功")
|
||||
item.publish = action
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error(text + "失败:" + e.message)
|
||||
})
|
||||
|
||||
38
web/src/views/ExternalPage.vue
Normal file
38
web/src/views/ExternalPage.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="page-iframe" v-loading="loading"
|
||||
style="--el-color-primary:#47fff1"
|
||||
element-loading-text="页面正在加载中..."
|
||||
element-loading-background="rgba(122, 122, 122, 0.8)">
|
||||
<iframe :src="externalUrl" class="iframe" @load="onIframeLoad"></iframe>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {useRouter} from "vue-router";
|
||||
import {computed, ref} from "vue";
|
||||
|
||||
const loading = ref(true)
|
||||
const router = useRouter()
|
||||
const externalUrl = computed(() => {
|
||||
loading.value = true
|
||||
return router.currentRoute.value.query.url || 'about:blank'
|
||||
})
|
||||
|
||||
const onIframeLoad = () => {
|
||||
loading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.page-iframe {
|
||||
width 100%
|
||||
height 100%
|
||||
overflow hidden
|
||||
|
||||
.iframe {
|
||||
width 100%
|
||||
height 100%
|
||||
border none
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,200 +1,225 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="navigator">
|
||||
<div class="logo">
|
||||
<el-image :src="logo" @click="router.push('/')"/>
|
||||
<div class="divider"></div>
|
||||
<div class="header">
|
||||
<div class="banner">
|
||||
<div class="logo">
|
||||
<el-image :src="logo" @click="router.push('/')"/>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="nav-items">
|
||||
<li v-for="item in mainNavs" :key="item.url">
|
||||
<a @click="changeNav(item)" :class="item.url === curPath ? 'active' : ''">
|
||||
<el-image :src="item.icon" style="width: 30px;height: 30px"/>
|
||||
</a>
|
||||
<div :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</div>
|
||||
</li>
|
||||
|
||||
<el-popover
|
||||
v-if="moreNavs.length > 0"
|
||||
placement="right-end"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #reference>
|
||||
<li>
|
||||
<a class="active">
|
||||
<el-image src="/images/menu/more.png" style="width: 30px;height: 30px"/>
|
||||
</a>
|
||||
</li>
|
||||
<div class="navbar">
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="light"
|
||||
content="部署文档"
|
||||
placement="bottom">
|
||||
<a href="https://ai.r9it.com/docs/install/" class="link-button" target="_blank">
|
||||
<i class="iconfont icon-book"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="light"
|
||||
content="项目源码"
|
||||
placement="bottom">
|
||||
<a href="https://github.com/yangjian102621/chatgpt-plus" class="link-button" target="_blank">
|
||||
<i class="iconfont icon-github"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
|
||||
<el-dropdown :hide-on-click="true" class="user-info" trigger="click" v-if="loginUser.id">
|
||||
<span class="el-dropdown-link">
|
||||
<el-image :src="loginUser.avatar"/>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="user-info-menu">
|
||||
<el-dropdown-item @click="showConfigDialog = true">
|
||||
<el-icon>
|
||||
<UserFilled/>
|
||||
</el-icon>
|
||||
<span class="username">{{ loginUser.nickname }}</span>
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item>
|
||||
<i class="iconfont icon-book"></i>
|
||||
<a href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">
|
||||
用户手册
|
||||
</a>
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item>
|
||||
<i class="iconfont icon-github"></i>
|
||||
<a href="https://ai.r9it.com/docs/" target="_blank">
|
||||
Geek-AI {{ version }}
|
||||
</a>
|
||||
</el-dropdown-item>
|
||||
<el-divider style="margin: 2px 0"/>
|
||||
<el-dropdown-item @click="logout">
|
||||
<i class="iconfont icon-logout"></i>
|
||||
<span>退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus">
|
||||
<li v-for="item in moreNavs" :key="item.url" :class="item.url === curPath ? 'active' : ''">
|
||||
<a @click="changeNav(item)">
|
||||
<el-image :src="item.icon" style="width: 20px;height: 20px"/>
|
||||
<span :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</span>
|
||||
</el-dropdown>
|
||||
|
||||
<div v-else>
|
||||
<el-button size="small" color="#21aa93" @click="store.setShowLoginDialog(true)" round>登录</el-button>
|
||||
<el-button size="small" @click="router.push('/register')" round>注册</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="navigator">
|
||||
<ul class="nav-items">
|
||||
<li v-for="item in mainNavs" :key="item.url">
|
||||
<el-tooltip
|
||||
effect="light"
|
||||
:content="item.name"
|
||||
placement="right">
|
||||
<a @click="changeNav(item)" :class="item.url === curPath ? 'active' : ''">
|
||||
<el-image :src="item.icon" style="width: 30px;height: 30px"/>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
</li>
|
||||
|
||||
<el-popover
|
||||
v-if="moreNavs.length > 0"
|
||||
placement="right-end"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #reference>
|
||||
<li>
|
||||
<a class="active">
|
||||
<el-image src="/images/menu/more.png" style="width: 30px;height: 30px"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<component :is="Component"></component>
|
||||
</transition>
|
||||
</router-view>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus">
|
||||
<li v-for="item in moreNavs" :key="item.url" :class="item.url === curPath ? 'active' : ''">
|
||||
<a @click="changeNav(item)">
|
||||
<el-image :src="item.icon" style="width: 20px;height: 20px"/>
|
||||
<span :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content custom-scroll" :style="{height: mainWinHeight+'px'}">
|
||||
<router-view :key="routerViewKey" v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<component :is="Component"></component>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<login-dialog :show="show" @hide="store.setShowLoginDialog(false)" @success="loginCallback"/>
|
||||
<config-dialog v-if="loginUser.id" :show="showConfigDialog" @hide="showConfigDialog = false"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import {useRouter} from "vue-router";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {UserFilled} from "@element-plus/icons-vue";
|
||||
import {checkSession} from "@/action/session";
|
||||
import {removeUserToken} from "@/store/session";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import ConfigDialog from "@/components/ConfigDialog.vue";
|
||||
|
||||
const router = useRouter();
|
||||
const logo = ref('/images/logo.png');
|
||||
const mainNavs = ref([])
|
||||
const moreNavs = ref([])
|
||||
const curPath = ref(router.currentRoute.value.path)
|
||||
const title = ref("")
|
||||
const mainWinHeight = window.innerHeight - 50
|
||||
const loginUser = ref({})
|
||||
const version = ref(process.env.VUE_APP_VERSION)
|
||||
const routerViewKey = ref(0)
|
||||
const showConfigDialog = ref(false)
|
||||
|
||||
const store = useSharedStore();
|
||||
const show = ref(false)
|
||||
watch(() => store.showLoginDialog, (newValue) => {
|
||||
show.value = newValue
|
||||
});
|
||||
|
||||
if (curPath.value === "/external") {
|
||||
curPath.value = router.currentRoute.value.query.url
|
||||
}
|
||||
const changeNav = (item) => {
|
||||
curPath.value = item.url
|
||||
router.push(item.url)
|
||||
if (item.url.indexOf("http") !== -1) { // 外部链接
|
||||
router.push({name: 'ExternalLink', query: {url: item.url}})
|
||||
} else {
|
||||
router.push(item.url)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
httpGet("/api/config/get?key=system").then(res => {
|
||||
logo.value = res.data['logo']
|
||||
logo.value = res.data.logo
|
||||
title.value = res.data.title
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message)
|
||||
})
|
||||
// 获取菜单
|
||||
httpGet("/api/menu/list").then(res => {
|
||||
mainNavs.value = res.data
|
||||
if (res.data.length > 7) {
|
||||
mainNavs.value = res.data.slice(0, 7)
|
||||
moreNavs.value = res.data.slice(7)
|
||||
// 根据窗口的高度计算应该显示多少菜单
|
||||
const rows = Math.floor((window.innerHeight - 90) / 60)
|
||||
if (res.data.length > rows) {
|
||||
mainNavs.value = res.data.slice(0, rows)
|
||||
moreNavs.value = res.data.slice(rows)
|
||||
}
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取系统菜单失败:" + e.message)
|
||||
})
|
||||
|
||||
init()
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
checkSession().then(user => {
|
||||
loginUser.value = user
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
|
||||
const logout = function () {
|
||||
httpGet('/api/user/logout').then(() => {
|
||||
removeUserToken()
|
||||
store.setShowLoginDialog(true)
|
||||
loginUser.value = {}
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1
|
||||
}).catch(() => {
|
||||
ElMessage.error('注销失败!');
|
||||
})
|
||||
}
|
||||
|
||||
const loginCallback = () => {
|
||||
init()
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '@/assets/iconfont/iconfont.css';
|
||||
.home {
|
||||
display: flex;
|
||||
height 100vh
|
||||
width 100%
|
||||
|
||||
.navigator {
|
||||
display flex
|
||||
flex-flow column
|
||||
width 60px
|
||||
padding 10px 6px
|
||||
border-right: 1px solid #3c3c3c
|
||||
background-color: #25272D
|
||||
|
||||
.logo {
|
||||
display flex
|
||||
flex-flow column
|
||||
align-items center
|
||||
cursor pointer
|
||||
|
||||
.el-image {
|
||||
width 50px
|
||||
height 50px
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-bottom 1px solid #4A4A4A
|
||||
width 80%
|
||||
height 10px
|
||||
}
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
margin-top: 10px;
|
||||
padding 0 5px
|
||||
|
||||
li {
|
||||
margin-bottom 15px
|
||||
|
||||
a {
|
||||
color #DADBDC
|
||||
border-radius 10px
|
||||
width 48px
|
||||
height 48px
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
cursor pointer
|
||||
|
||||
.el-image {
|
||||
border-radius 10px
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size 20px
|
||||
}
|
||||
}
|
||||
|
||||
a:hover, a.active {
|
||||
color #47fff1
|
||||
background-color #0F7A71
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 12px
|
||||
padding-top: 5px
|
||||
color: #e5e7eb;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.active {
|
||||
color #47fff1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%
|
||||
height: 100vh
|
||||
box-sizing: border-box
|
||||
background-color #282c34
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.el-popper {
|
||||
.more-menus {
|
||||
li {
|
||||
padding 10px 15px
|
||||
cursor pointer
|
||||
border-radius 5px
|
||||
margin 5px 0
|
||||
|
||||
.el-image {
|
||||
position: relative
|
||||
top 5px
|
||||
right 5px
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color #f1f1f1
|
||||
}
|
||||
}
|
||||
|
||||
li.active {
|
||||
background-color #f1f1f1
|
||||
}
|
||||
}
|
||||
}
|
||||
@import "@/assets/css/custom-scroll.styl"
|
||||
@import "@/assets/css/home.styl"
|
||||
</style>
|
||||
|
||||
@@ -600,8 +600,6 @@
|
||||
</div>
|
||||
|
||||
<el-image-viewer @close="() => { previewURL = '' }" v-if="previewURL !== ''" :url-list="[previewURL]"/>
|
||||
|
||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -616,19 +614,24 @@ import {checkSession} from "@/action/session";
|
||||
import {useRouter} from "vue-router";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import {copyObj, removeArrayItem} from "@/utils/libs";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const listBoxHeight = ref(window.innerHeight - 40)
|
||||
const paramBoxHeight = ref(window.innerHeight - 150)
|
||||
const showLoginDialog = ref(false)
|
||||
const listBoxHeight = ref(0)
|
||||
const paramBoxHeight = ref(0)
|
||||
const loading = ref(true)
|
||||
const colWidth = ref(240)
|
||||
const colWidth = ref(220)
|
||||
const previewURL = ref("")
|
||||
const store = useSharedStore();
|
||||
|
||||
const resizeElement = function () {
|
||||
listBoxHeight.value = window.innerHeight - 80
|
||||
paramBoxHeight.value = window.innerHeight - 160
|
||||
};
|
||||
resizeElement()
|
||||
window.onresize = () => {
|
||||
listBoxHeight.value = window.innerHeight - 40
|
||||
paramBoxHeight.value = window.innerHeight - 150
|
||||
resizeElement()
|
||||
}
|
||||
|
||||
const rates = [
|
||||
{css: "square", value: "1:1", text: "1:1", img: "/images/mj/rate_1_1.png"},
|
||||
{css: "size1-2", value: "1:2", text: "1:2", img: "/images/mj/rate_1_2.png"},
|
||||
@@ -746,8 +749,8 @@ const connect = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH") {
|
||||
page.value = 0
|
||||
fetchFinishJobs(page.value)
|
||||
isOver.value = false
|
||||
fetchFinishJobs(page.value)
|
||||
}
|
||||
fetchRunningJobs()
|
||||
}
|
||||
@@ -898,7 +901,7 @@ const beforeUpload = (key) => {
|
||||
// 图片上传
|
||||
const uploadImg = (file) => {
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -931,7 +934,7 @@ const uploadImg = (file) => {
|
||||
const promptRef = ref(null)
|
||||
const generate = () => {
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -993,6 +996,9 @@ const removeImage = (item) => {
|
||||
).then(() => {
|
||||
httpPost("/api/mj/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
|
||||
ElMessage.success("任务删除成功")
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error("任务删除失败:" + e.message)
|
||||
})
|
||||
@@ -1009,6 +1015,9 @@ const publishImage = (item, action) => {
|
||||
httpPost("/api/mj/publish", {id: item.id, action: action}).then(() => {
|
||||
ElMessage.success(text + "成功")
|
||||
item.publish = action
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error(text + "失败:" + e.message)
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="sd-box">
|
||||
<h2>Stable Diffusion 创作中心</h2>
|
||||
|
||||
<div class="sd-params" :style="{ height: paramBoxHeight + 'px' }">
|
||||
<div class="sd-params">
|
||||
<el-form :model="params" label-width="80px" label-position="left">
|
||||
<div class="param-line" style="padding-top: 10px">
|
||||
<el-form-item label="采样方法">
|
||||
@@ -497,8 +497,6 @@
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -511,21 +509,25 @@ import Clipboard from "clipboard";
|
||||
import {checkSession} from "@/action/session";
|
||||
import {useRouter} from "vue-router";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const listBoxHeight = ref(window.innerHeight - 40)
|
||||
const paramBoxHeight = ref(window.innerHeight - 150)
|
||||
const listBoxHeight = ref(0)
|
||||
// const paramBoxHeight = ref(0)
|
||||
const fullImgHeight = ref(window.innerHeight - 60)
|
||||
const showTaskDialog = ref(false)
|
||||
const item = ref({})
|
||||
const showLoginDialog = ref(false)
|
||||
const isLogin = ref(false)
|
||||
const loading = ref(true)
|
||||
const colWidth = ref(240)
|
||||
const colWidth = ref(220)
|
||||
const store = useSharedStore();
|
||||
|
||||
const resizeElement = function () {
|
||||
listBoxHeight.value = window.innerHeight - 80
|
||||
// paramBoxHeight.value = window.innerHeight - 200
|
||||
};
|
||||
resizeElement()
|
||||
window.onresize = () => {
|
||||
listBoxHeight.value = window.innerHeight - 40
|
||||
paramBoxHeight.value = window.innerHeight - 150
|
||||
resizeElement()
|
||||
}
|
||||
const samplers = ["Euler a", "DPM++ 2S a", "DPM++ 2M", "DPM++ SDE", "DPM++ 2M SDE", "UniPC", "Restart"]
|
||||
const schedulers = ["Automatic", "Karras", "Exponential", "Uniform"]
|
||||
@@ -600,8 +602,8 @@ const connect = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH") {
|
||||
page.value = 0
|
||||
fetchFinishJobs()
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}
|
||||
fetchRunningJobs()
|
||||
}
|
||||
@@ -726,7 +728,7 @@ const generate = () => {
|
||||
}
|
||||
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -765,6 +767,9 @@ const removeImage = (event, item) => {
|
||||
).then(() => {
|
||||
httpPost("/api/sd/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
|
||||
ElMessage.success("任务删除成功")
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error("任务删除失败:" + e.message)
|
||||
})
|
||||
@@ -782,6 +787,9 @@ const publishImage = (event, item, action) => {
|
||||
httpPost("/api/sd/publish", {id: item.id, action: action}).then(() => {
|
||||
ElMessage.success(text + "成功")
|
||||
item.publish = action
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error(text + "失败:" + e.message)
|
||||
})
|
||||
|
||||
@@ -310,8 +310,8 @@ const data = ref({
|
||||
const loading = ref(true)
|
||||
const isOver = ref(false)
|
||||
const imgType = ref("mj") // 图片类别
|
||||
const listBoxHeight = window.innerHeight - 74
|
||||
const colWidth = ref(240)
|
||||
const listBoxHeight = window.innerHeight - 124
|
||||
const colWidth = ref(220)
|
||||
const fullImgHeight = ref(window.innerHeight - 60)
|
||||
const showTaskDialog = ref(false)
|
||||
const item = ref({})
|
||||
@@ -395,6 +395,7 @@ onUnmounted(() => {
|
||||
})
|
||||
|
||||
const changeImgType = () => {
|
||||
console.log(imgType.value)
|
||||
document.getElementById('waterfall-box').scrollTo(0, 0)
|
||||
page.value = 0
|
||||
data.value = {
|
||||
|
||||
@@ -84,8 +84,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -97,17 +95,17 @@ import {ElMessage} from "element-plus";
|
||||
import Clipboard from "clipboard";
|
||||
import InviteList from "@/components/InviteList.vue";
|
||||
import {checkSession} from "@/action/session";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const inviteURL = ref("")
|
||||
const qrImg = ref("")
|
||||
const qrImg = ref("/images/wx.png")
|
||||
const inviteChatCalls = ref(0)
|
||||
const inviteImgCalls = ref(0)
|
||||
const hits = ref(0)
|
||||
const regNum = ref(0)
|
||||
const rate = ref(0)
|
||||
const isLogin = ref(false)
|
||||
const showLoginDialog = ref(false)
|
||||
const store = useSharedStore()
|
||||
|
||||
onMounted(() => {
|
||||
initData()
|
||||
@@ -152,7 +150,7 @@ const initData = () => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -163,7 +161,7 @@ const initData = () => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: #282c34;
|
||||
height 100vh
|
||||
height 100%
|
||||
overflow-x hidden
|
||||
overflow-y visible
|
||||
|
||||
@@ -176,6 +174,7 @@ const initData = () => {
|
||||
|
||||
h2 {
|
||||
color #ffffff;
|
||||
text-align center
|
||||
}
|
||||
|
||||
.share-box {
|
||||
|
||||
@@ -36,8 +36,12 @@
|
||||
|
||||
<el-row class="opt" :gutter="20">
|
||||
<el-col :span="8"><el-link type="primary" @click="router.push('/register')">注册</el-link></el-col>
|
||||
<el-col :span="8"><el-link @click="showResetPass = true">重置密码</el-link></el-col>
|
||||
<el-col :span="8"><el-link @click="router.push('/')">首页</el-link></el-col>
|
||||
<el-col :span="8">
|
||||
<el-link type="info" @click="showResetPass = true">重置密码</el-link>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-link type="info" @click="router.push('/')">首页</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<div>
|
||||
<div class="page-mark-map">
|
||||
<div class="inner custom-scroll">
|
||||
<div class="mark-map-box">
|
||||
<div class="mark-map-box" :style="{ height: leftBoxHeight + 'px' }">
|
||||
<h2>思维导图创作中心</h2>
|
||||
|
||||
<div class="mark-map-params" :style="{ height: leftBoxHeight + 'px' }">
|
||||
<div class="mark-map-params">
|
||||
<el-form label-width="80px" label-position="left">
|
||||
<div class="param-line">
|
||||
你的需求?
|
||||
@@ -69,9 +69,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-box">
|
||||
<div class="chat-box">
|
||||
<div class="top-bar">
|
||||
<h2>思维导图</h2>
|
||||
<el-button @click="downloadImage" type="primary">
|
||||
<el-icon>
|
||||
<Download/>
|
||||
@@ -91,13 +90,10 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {nextTick, onMounted, onUnmounted, ref} from 'vue';
|
||||
import {Markmap} from 'markmap-view';
|
||||
import {Transformer} from 'markmap-lib';
|
||||
@@ -106,9 +102,10 @@ import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {Download} from "@element-plus/icons-vue";
|
||||
import {Toolbar} from 'markmap-toolbar';
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const leftBoxHeight = ref(window.innerHeight - 105)
|
||||
const rightBoxHeight = ref(window.innerHeight - 85)
|
||||
const rightBoxHeight = ref(window.innerHeight - 115)
|
||||
|
||||
const prompt = ref("")
|
||||
const text = ref(`# Geek-AI 助手
|
||||
@@ -126,10 +123,10 @@ const md = require('markdown-it')({breaks: true});
|
||||
const content = ref(text.value)
|
||||
const html = ref("")
|
||||
|
||||
const showLoginDialog = ref(false)
|
||||
const isLogin = ref(false)
|
||||
const loginUser = ref({power: 0})
|
||||
const transformer = new Transformer();
|
||||
const store = useSharedStore();
|
||||
|
||||
|
||||
const svgRef = ref(null)
|
||||
@@ -151,21 +148,21 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const initData = () => {
|
||||
httpGet("/api/model/list").then(res => {
|
||||
for (let v of res.data) {
|
||||
if (v.platform === "OpenAI" && v.value.indexOf("gpt-4-gizmo") === -1) {
|
||||
models.value.push(v)
|
||||
}
|
||||
}
|
||||
modelID.value = models.value[0].id
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取模型失败:" + e.message)
|
||||
})
|
||||
|
||||
checkSession().then(user => {
|
||||
loginUser.value = user
|
||||
isLogin.value = true
|
||||
|
||||
httpGet("/api/model/list").then(res => {
|
||||
for (let v of res.data) {
|
||||
if (v.platform === "OpenAI" && v.value.indexOf("gpt-4-gizmo") === -1) {
|
||||
models.value.push(v)
|
||||
}
|
||||
}
|
||||
modelID.value = models.value[0].id
|
||||
connect(user.id)
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取模型失败:" + e.message)
|
||||
})
|
||||
connect(user.id)
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
@@ -206,7 +203,7 @@ window.onresize = () => {
|
||||
}
|
||||
|
||||
const socket = ref(null)
|
||||
const heartbeatHandle = ref(null)
|
||||
const heartbeatHandle = ref(0)
|
||||
const connect = (userId) => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
@@ -293,7 +290,7 @@ const generateAI = () => {
|
||||
return ElMessage.error("请输入你的需求")
|
||||
}
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
|
||||
@@ -1,102 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="member custom-scroll">
|
||||
<div class="title">
|
||||
会员充值中心
|
||||
</div>
|
||||
<div class="inner" :style="{height: listBoxHeight + 'px'}">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="7">
|
||||
<div class="user-profile">
|
||||
<user-profile/>
|
||||
<div class="inner">
|
||||
<div class="user-profile">
|
||||
<user-profile/>
|
||||
|
||||
<el-row class="user-opt" :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showPasswordDialog = true">修改密码</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showBindMobileDialog = true">更改账号</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" v-if="enableReward" @click="showRewardDialog = true">加入众筹</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" v-if="enableReward" @click="showRewardVerifyDialog = true">众筹核销
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-row class="user-opt" :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showPasswordDialog = true">修改密码</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showBindMobileDialog = true">更改账号</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" v-if="enableReward" @click="showRewardDialog = true">加入众筹</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" v-if="enableReward" @click="showRewardVerifyDialog = true">众筹核销
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" style="padding-top: 30px" v-if="isLogin">
|
||||
<el-button type="danger" round @click="logout">退出登录</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-top: 30px" v-if="isLogin">
|
||||
<el-button type="danger" round @click="logout">退出登录</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-col :span="17">
|
||||
<div class="product-box">
|
||||
<div class="info" v-if="orderPayInfoText !== ''">
|
||||
<el-alert type="success" show-icon :closable="false" effect="dark">
|
||||
<strong>说明:</strong> {{ vipInfoText }}
|
||||
</el-alert>
|
||||
</div>
|
||||
<div class="product-box">
|
||||
<div class="info" v-if="orderPayInfoText !== ''">
|
||||
<el-alert type="success" show-icon :closable="false" effect="dark">
|
||||
<strong>说明:</strong> {{ vipInfoText }}
|
||||
</el-alert>
|
||||
</div>
|
||||
|
||||
<ItemList :items="list" v-if="list.length > 0" :gap="30" :width="240">
|
||||
<template #default="scope">
|
||||
<div class="product-item" :style="{width: scope.width+'px'}">
|
||||
<div class="image-container">
|
||||
<el-image :src="vipImg" fit="cover"/>
|
||||
</div>
|
||||
<div class="product-title">
|
||||
<span class="name">{{ scope.item.name }}</span>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<div class="info-line">
|
||||
<span class="label">商品原价:</span>
|
||||
<span class="price">¥{{ scope.item.price }}</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="label">促销立减:</span>
|
||||
<span class="price">¥{{ scope.item.discount }}</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="label">有效期:</span>
|
||||
<span class="expire" v-if="scope.item.days > 0">{{ scope.item.days }}天</span>
|
||||
<span class="expire" v-else>长期有效</span>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<span class="label">算力值:</span>
|
||||
<span class="power" v-if="scope.item.power > 0">{{ scope.item.power }}</span>
|
||||
<span class="power" v-else>{{ vipMonthPower }}</span>
|
||||
</div>
|
||||
|
||||
<div class="pay-way">
|
||||
<el-button type="primary" @click="alipay(scope.item)" size="small" v-if="payWays['alipay']">
|
||||
<i class="iconfont icon-alipay"></i> 支付宝
|
||||
</el-button>
|
||||
<el-button type="success" @click="huPiPay(scope.item)" size="small" v-if="payWays['hupi']">
|
||||
<span v-if="payWays['hupi']['name'] === 'wechat'"><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
<span v-else><i class="iconfont icon-alipay"></i> 支付宝</span>
|
||||
</el-button>
|
||||
|
||||
<el-button type="success" @click="PayJs(scope.item)" size="small" v-if="payWays['payjs']">
|
||||
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<ItemList :items="list" v-if="list.length > 0" :gap="15" :width="240">
|
||||
<template #default="scope">
|
||||
<div class="product-item">
|
||||
<div class="image-container">
|
||||
<el-image :src="vipImg" fit="cover"/>
|
||||
</div>
|
||||
<div class="product-title">
|
||||
<span class="name">{{ scope.item.name }}</span>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<div class="info-line">
|
||||
<span class="label">商品原价:</span>
|
||||
<span class="price">¥{{ scope.item.price }}</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="label">促销立减:</span>
|
||||
<span class="price">¥{{ scope.item.discount }}</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="label">有效期:</span>
|
||||
<span class="expire" v-if="scope.item.days > 0">{{ scope.item.days }}天</span>
|
||||
<span class="expire" v-else>长期有效</span>
|
||||
</div>
|
||||
</template>
|
||||
</ItemList>
|
||||
|
||||
<h2 class="headline">消费账单</h2>
|
||||
<div class="info-line">
|
||||
<span class="label">算力值:</span>
|
||||
<span class="power" v-if="scope.item.power > 0">{{ scope.item.power }}</span>
|
||||
<span class="power" v-else>{{ vipMonthPower }}</span>
|
||||
</div>
|
||||
|
||||
<div class="user-order">
|
||||
<user-order v-if="isLogin"/>
|
||||
<div class="pay-way">
|
||||
<el-button type="primary" @click="alipay(scope.item)" size="small" v-if="payWays['alipay']">
|
||||
<i class="iconfont icon-alipay"></i> 支付宝
|
||||
</el-button>
|
||||
<el-button type="success" @click="huPiPay(scope.item)" size="small" v-if="payWays['hupi']">
|
||||
<span v-if="payWays['hupi']['name'] === 'wechat'"><i
|
||||
class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
<span v-else><i class="iconfont icon-alipay"></i> 支付宝</span>
|
||||
</el-button>
|
||||
|
||||
<el-button type="success" @click="PayJs(scope.item)" size="small" v-if="payWays['payjs']">
|
||||
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</ItemList>
|
||||
|
||||
<h2 class="headline">消费账单</h2>
|
||||
|
||||
<div class="user-order">
|
||||
<user-order v-if="isLogin"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<password-dialog v-if="isLogin" :show="showPasswordDialog" @hide="showPasswordDialog = false"
|
||||
@@ -157,7 +148,6 @@
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="loginSuccess"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -167,7 +157,6 @@ import {ElMessage} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import ItemList from "@/components/ItemList.vue";
|
||||
import {InfoFilled, SuccessFilled} from "@element-plus/icons-vue";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import {checkSession} from "@/action/session";
|
||||
import UserProfile from "@/components/UserProfile.vue";
|
||||
import PasswordDialog from "@/components/PasswordDialog.vue";
|
||||
@@ -177,10 +166,9 @@ import {useRouter} from "vue-router";
|
||||
import {removeUserToken} from "@/store/session";
|
||||
import UserOrder from "@/components/UserOrder.vue";
|
||||
import CountDown from "@/components/CountDown.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const listBoxHeight = window.innerHeight - 97
|
||||
const list = ref([])
|
||||
const showLoginDialog = ref(false)
|
||||
const showPayDialog = ref(false)
|
||||
const vipImg = ref("/images/vip.png")
|
||||
const enableReward = ref(false) // 是否启用众筹功能
|
||||
@@ -208,6 +196,7 @@ const amount = ref(0)
|
||||
const payName = ref("支付宝")
|
||||
const curPay = ref("alipay") // 当前支付方式
|
||||
const vipInfoText = ref("")
|
||||
const store = useSharedStore()
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
@@ -215,7 +204,7 @@ onMounted(() => {
|
||||
user.value = _user
|
||||
isLogin.value = true
|
||||
}).catch(() => {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
})
|
||||
|
||||
httpGet("/api/product/list").then((res) => {
|
||||
@@ -281,7 +270,7 @@ const alipay = (row) => {
|
||||
curPay.value = "alipay"
|
||||
amount.value = (row.price - row.discount).toFixed(2)
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -297,7 +286,7 @@ const huPiPay = (row) => {
|
||||
curPay.value = "hupi"
|
||||
amount.value = (row.price - row.discount).toFixed(2)
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -313,7 +302,7 @@ const PayJs = (row) => {
|
||||
curPay.value = "payjs"
|
||||
amount.value = (row.price - row.discount).toFixed(2)
|
||||
if (!isLogin.value) {
|
||||
showLoginDialog.value = true
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<div class="power-log" v-loading="loading">
|
||||
<div class="inner">
|
||||
<h2>消费日志</h2>
|
||||
|
||||
<div class="list-box" :style="{height: listBoxHeight + 'px'}">
|
||||
<div class="power-log custom-scroll" v-loading="loading">
|
||||
<div class="inner" :style="{height: listBoxHeight + 'px'}">
|
||||
<div class="list-box">
|
||||
<div class="handle-box">
|
||||
<el-input v-model="query.model" placeholder="模型" class="handle-input mr10" clearable></el-input>
|
||||
<el-date-picker
|
||||
@@ -13,9 +11,9 @@
|
||||
end-placeholder="结束日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="margin: 0 10px;width: 200px; position: relative;top:3px;"
|
||||
style="margin: 0 10px;width: 200px;"
|
||||
/>
|
||||
<el-button type="primary" :icon="Search" @click="fetchData">搜索</el-button>
|
||||
<el-button color="#21aa93" :icon="Search" @click="fetchData">搜索</el-button>
|
||||
</div>
|
||||
|
||||
<el-row v-if="items.length > 0">
|
||||
@@ -73,21 +71,25 @@ import {Search} from "@element-plus/icons-vue";
|
||||
import Clipboard from "clipboard";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {httpPost} from "@/utils/http";
|
||||
import {checkSession} from "@/action/session";
|
||||
|
||||
const items = ref([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const loading = ref(false)
|
||||
const listBoxHeight = window.innerHeight - 117
|
||||
const listBoxHeight = window.innerHeight - 87
|
||||
const query = ref({
|
||||
model: "",
|
||||
date: []
|
||||
})
|
||||
const tagColors = ref(["", "success", "", "danger", "info", "warning"])
|
||||
const tagColors = ref(["primary", "success", "primary", "danger", "info", "warning"])
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
checkSession().then(() => {
|
||||
fetchData()
|
||||
}).catch(() => {
|
||||
})
|
||||
const clipboard = new Clipboard('.copy-order-no');
|
||||
clipboard.on('success', () => {
|
||||
ElMessage.success("复制成功!");
|
||||
@@ -123,33 +125,12 @@ const fetchData = () => {
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import "@/assets/css/custom-scroll.styl"
|
||||
.power-log {
|
||||
color #ffffff
|
||||
.inner {
|
||||
padding 0 20px 20px 20px
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px; /* 滚动条宽度 */
|
||||
}
|
||||
|
||||
/* 修改滚动条轨道的背景颜色 */
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #cccccc;
|
||||
border-radius 8px
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块的悬停颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #999999;
|
||||
}
|
||||
overflow auto
|
||||
|
||||
.list-box {
|
||||
overflow-x hidden
|
||||
|
||||
@@ -528,6 +528,10 @@ const onUploadImg = (files, callback) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input {
|
||||
width 100%
|
||||
}
|
||||
}
|
||||
|
||||
.el-descriptions {
|
||||
|
||||
Reference in New Issue
Block a user