mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-11-14 13:13:43 +08:00
merge v4.2.0
This commit is contained in:
@@ -6,7 +6,7 @@ VUE_APP_ADMIN_USER=admin
|
||||
VUE_APP_ADMIN_PASS=admin123
|
||||
VUE_APP_KEY_PREFIX=GeekAI_DEV_
|
||||
VUE_APP_TITLE="Geek-AI 创作系统"
|
||||
VUE_APP_VERSION=v4.1.9
|
||||
VUE_APP_VERSION=v4.2.0
|
||||
VUE_APP_DOCS_URL=https://docs.geekai.me
|
||||
VUE_APP_GITHUB_URL=https://github.com/yangjian102621/geekai
|
||||
VUE_APP_GITEE_URL=https://gitee.com/blackfox/geekai
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
VUE_APP_API_HOST=
|
||||
VUE_APP_WS_HOST=
|
||||
VUE_APP_KEY_PREFIX=GeekAI_
|
||||
VUE_APP_VERSION=v4.2.0
|
||||
VUE_APP_TITLE="Geek-AI 创作系统"
|
||||
VUE_APP_VERSION=v4.1.9
|
||||
VUE_APP_DOCS_URL=https://docs.geekai.me
|
||||
VUE_APP_GITHUB_URL=https://github.com/yangjian102621/geekai
|
||||
VUE_APP_GITEE_URL=https://gitee.com/blackfox/geekai
|
||||
|
||||
@@ -92,6 +92,29 @@ const connect = () => {
|
||||
});
|
||||
store.setSocket(_socket);
|
||||
};
|
||||
|
||||
// 打印 banner
|
||||
const banner = `
|
||||
|
||||
.oooooo. oooo .o. ooooo
|
||||
d8P' 'Y8b 888 .888. 888
|
||||
888 .ooooo. .ooooo. 888 oooo .8"888. 888
|
||||
888 d88' 88b d88' 88b 888 .8P' .8' 888. 888
|
||||
888 ooooo 888ooo888 888ooo888 888888. .88ooo8888. 888
|
||||
'88. .88' 888 .o 888 .o 888 88b. .8' 888. 888
|
||||
Y8bood8P' Y8bod8P' Y8bod8P' o888o o888o o88o o8888o o888o
|
||||
|
||||
`;
|
||||
console.log("%c" + banner + "", "color: purple;font-size: 18px;");
|
||||
|
||||
console.log("%c感谢大家为 GeekAI 做出的卓越贡献!", "color: green;font-size: 40px;font-family: '微软雅黑';");
|
||||
console.log(
|
||||
"%c项目源码:https://github.com/yangjian102621/geekai %c 您的 star 对我们非常重要!",
|
||||
"color: green;font-size: 20px;font-family: '微软雅黑';",
|
||||
"color: red;font-size: 20px;font-family: '微软雅黑';"
|
||||
);
|
||||
|
||||
console.log("%c 愿你出走半生,归来仍是少年!大奉武夫许七安,前来凿阵!", "color: #7c39ed;font-size: 18px;font-family: '微软雅黑';");
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
@@ -132,11 +132,6 @@
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
font-size: 20px;
|
||||
// img{
|
||||
// width: 24px;
|
||||
// height: 24px;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
&.active {
|
||||
@@ -183,19 +178,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.theme-box) {
|
||||
position: relative !important;
|
||||
right: initial;
|
||||
bottom: initial;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 18px;
|
||||
|
||||
.iconfont {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.right-main {
|
||||
height: 100%;
|
||||
// background: #f5f7fd;
|
||||
@@ -246,18 +228,18 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(79, 89, 102, 0.1);
|
||||
background: rgba(183, 176, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
li.active {
|
||||
background: rgba(79, 89, 102, 0.1);
|
||||
background: rgba(183, 176, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.setting-menus {
|
||||
.title {
|
||||
color: #222226;
|
||||
color: var(--text-theme-color);
|
||||
}
|
||||
|
||||
.el-icon,
|
||||
@@ -265,7 +247,7 @@
|
||||
font-size: 18px
|
||||
margin-right: 6px
|
||||
}
|
||||
color: #222226;
|
||||
color: var(--text-theme-color);
|
||||
}
|
||||
|
||||
.username {
|
||||
|
||||
@@ -1,238 +1,239 @@
|
||||
.chat-line {
|
||||
ol, ul {
|
||||
margin: 0.8em 0;
|
||||
list-style: normal;
|
||||
}
|
||||
a {
|
||||
|
||||
color :var(--a-link-color);
|
||||
text-decoration: underline;
|
||||
|
||||
padding: 0 2px;
|
||||
}
|
||||
.chat-line,
|
||||
.notice-dialog {
|
||||
ol,
|
||||
ul {
|
||||
margin: 0.8em 0;
|
||||
list-style: normal;
|
||||
}
|
||||
a {
|
||||
color: var(--a-link-color);
|
||||
text-decoration: underline;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
cursor: text;
|
||||
}
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
h1:hover a.anchor,
|
||||
h2:hover a.anchor,
|
||||
h3:hover a.anchor,
|
||||
h4:hover a.anchor,
|
||||
h5:hover a.anchor,
|
||||
h6:hover a.anchor {
|
||||
text-decoration: none;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
h1 tt,
|
||||
h1 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
h1:hover a.anchor,
|
||||
h2:hover a.anchor,
|
||||
h3:hover a.anchor,
|
||||
h4:hover a.anchor,
|
||||
h5:hover a.anchor,
|
||||
h6:hover a.anchor {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h2 tt,
|
||||
h2 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
h1 tt,
|
||||
h1 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
|
||||
h3 tt,
|
||||
h3 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
h2 tt,
|
||||
h2 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
|
||||
h4 tt,
|
||||
h4 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
h3 tt,
|
||||
h3 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
|
||||
h5 tt,
|
||||
h5 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
h4 tt,
|
||||
h4 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
|
||||
h6 tt,
|
||||
h6 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
h5 tt,
|
||||
h5 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
|
||||
h2 a,
|
||||
h3 a {
|
||||
color: #34495e;
|
||||
}
|
||||
h6 tt,
|
||||
h6 code {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-bottom: .4rem;
|
||||
font-size: 2.2rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
h2 a,
|
||||
h3 a {
|
||||
color: #34495e;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem;
|
||||
line-height: 1.225;
|
||||
margin: 35px 0 15px;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
h1 {
|
||||
padding-bottom: 0.4rem;
|
||||
font-size: 2.2rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.43;
|
||||
margin: 20px 0 7px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.75rem;
|
||||
line-height: 1.225;
|
||||
margin: 35px 0 15px;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.43;
|
||||
margin: 20px 0 7px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
color: #777;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p,
|
||||
blockquote,
|
||||
ul,
|
||||
ol,
|
||||
dl,
|
||||
table {
|
||||
margin: 0.8em 0;
|
||||
}
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
li > ol,
|
||||
li > ul {
|
||||
margin: 0 0;
|
||||
}
|
||||
p,
|
||||
blockquote,
|
||||
ul,
|
||||
ol,
|
||||
dl,
|
||||
table {
|
||||
margin: 0.8em 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 2px;
|
||||
padding: 0;
|
||||
margin: 16px 0;
|
||||
background-color: #e7e7e7;
|
||||
border: 0 none;
|
||||
overflow: hidden;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
li > ol,
|
||||
li > ul {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
body > h2:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
hr {
|
||||
height: 2px;
|
||||
padding: 0;
|
||||
margin: 16px 0;
|
||||
background-color: #e7e7e7;
|
||||
border: 0 none;
|
||||
overflow: hidden;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
body > h1:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
body > h2:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
body > h1:first-child + h2 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
body > h1:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
body > h3:first-child,
|
||||
body > h4:first-child,
|
||||
body > h5:first-child,
|
||||
body > h6:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
body > h1:first-child + h2 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
a:first-child h1,
|
||||
a:first-child h2,
|
||||
a:first-child h3,
|
||||
a:first-child h4,
|
||||
a:first-child h5,
|
||||
a:first-child h6 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
body > h3:first-child,
|
||||
body > h4:first-child,
|
||||
body > h5:first-child,
|
||||
body > h6:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
h1 p,
|
||||
h2 p,
|
||||
h3 p,
|
||||
h4 p,
|
||||
h5 p,
|
||||
h6 p {
|
||||
margin-top: 0;
|
||||
}
|
||||
a:first-child h1,
|
||||
a:first-child h2,
|
||||
a:first-child h3,
|
||||
a:first-child h4,
|
||||
a:first-child h5,
|
||||
a:first-child h6 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
li p.first {
|
||||
display: inline-block;
|
||||
}
|
||||
h1 p,
|
||||
h2 p,
|
||||
h3 p,
|
||||
h4 p,
|
||||
h5 p,
|
||||
h6 p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
li p.first {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ul:first-child,
|
||||
ol:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
ul,
|
||||
ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
ul:last-child,
|
||||
ol:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ul:first-child,
|
||||
ol:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid #42b983;
|
||||
padding: 10px 15px;
|
||||
color: #777;
|
||||
background-color: rgba(66, 185, 131, .1);
|
||||
}
|
||||
ul:last-child,
|
||||
ol:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table {
|
||||
padding: 0;
|
||||
word-break: initial;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 4px solid #42b983;
|
||||
padding: 10px 15px;
|
||||
color: #777;
|
||||
background-color: rgba(66, 185, 131, 0.1);
|
||||
}
|
||||
|
||||
table tr {
|
||||
border-top: 1px solid #dfe2e5;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
table {
|
||||
padding: 0;
|
||||
word-break: initial;
|
||||
}
|
||||
|
||||
table tr:nth-child(2n),
|
||||
thead {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
table tr {
|
||||
border-top: 1px solid #dfe2e5;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table tr th {
|
||||
font-weight: bold;
|
||||
border: 1px solid #dfe2e5;
|
||||
border-bottom: 0;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
table tr:nth-child(2n),
|
||||
thead {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
table tr td {
|
||||
border: 1px solid #dfe2e5;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
table tr th {
|
||||
font-weight: bold;
|
||||
border: 1px solid #dfe2e5;
|
||||
border-bottom: 0;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
|
||||
table tr th:first-child,
|
||||
table tr td:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
table tr td {
|
||||
border: 1px solid #dfe2e5;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
|
||||
table tr th:last-child,
|
||||
table tr td:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
table tr th:first-child,
|
||||
table tr td:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
table tr th:last-child,
|
||||
table tr td:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +243,9 @@
|
||||
opacity 0
|
||||
transform: translate(-50%, 0px);
|
||||
transition opacity 0.3s ease 0s
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
//filter: invert(100%);
|
||||
}
|
||||
.more-menus span.title{
|
||||
color:#000;
|
||||
color: var(--text-theme-color);
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
@@ -89,4 +89,7 @@
|
||||
// 引用快样式
|
||||
--quote-bg-color: #1F243F;
|
||||
--quote-text-color: #fff;
|
||||
|
||||
// el-dialog 阴影
|
||||
--el-box-shadow: 0 0 15px rgba(107, 80, 225, 0.8);
|
||||
}
|
||||
|
||||
@@ -2,27 +2,25 @@
|
||||
<div class="chat-line chat-line-prompt-list" v-if="listStyle === 'list'">
|
||||
<div class="chat-line-inner">
|
||||
<div class="chat-icon">
|
||||
<img :src="data.icon" alt="User" />
|
||||
<img :src="data.icon" alt="User"/>
|
||||
</div>
|
||||
|
||||
<div class="chat-item">
|
||||
<div v-if="files.length > 0" class="file-list-box">
|
||||
<div v-for="file in files">
|
||||
<div class="image" v-if="isImage(file.ext)">
|
||||
<el-image :src="file.url" fit="cover" />
|
||||
<el-image :src="file.url" fit="cover"/>
|
||||
</div>
|
||||
<div class="item" v-else>
|
||||
<div class="icon">
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover" />
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover"/>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="title">
|
||||
<el-link
|
||||
:href="file.url"
|
||||
target="_blank"
|
||||
style="--el-font-weight-primary: bold"
|
||||
>{{ file.name }}</el-link
|
||||
>
|
||||
<el-link :href="file.url" target="_blank" style="--el-font-weight-primary: bold">{{
|
||||
file.name
|
||||
}}
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span>{{ GetFileType(file.ext) }}</span>
|
||||
@@ -35,7 +33,7 @@
|
||||
<div class="content" v-html="content"></div>
|
||||
<div class="bar" v-if="data.created_at > 0">
|
||||
<span class="bar-item"
|
||||
><el-icon><Clock /></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
>
|
||||
<span class="bar-item">tokens: {{ finalTokens }}</span>
|
||||
</div>
|
||||
@@ -46,27 +44,25 @@
|
||||
<div class="chat-line chat-line-prompt-chat" v-else>
|
||||
<div class="chat-line-inner">
|
||||
<div class="chat-icon">
|
||||
<img :src="data.icon" alt="User" />
|
||||
<img :src="data.icon" alt="User"/>
|
||||
</div>
|
||||
|
||||
<div class="chat-item">
|
||||
<div v-if="files.length > 0" class="file-list-box">
|
||||
<div v-for="file in files">
|
||||
<div class="image" v-if="isImage(file.ext)">
|
||||
<el-image :src="file.url" fit="cover" />
|
||||
<el-image :src="file.url" fit="cover"/>
|
||||
</div>
|
||||
<div class="item" v-else>
|
||||
<div class="icon">
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover" />
|
||||
<el-image :src="GetFileIcon(file.ext)" fit="cover"/>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="title">
|
||||
<el-link
|
||||
:href="file.url"
|
||||
target="_blank"
|
||||
style="--el-font-weight-primary: bold"
|
||||
>{{ file.name }}</el-link
|
||||
>
|
||||
<el-link :href="file.url" target="_blank" style="--el-font-weight-primary: bold">{{
|
||||
file.name
|
||||
}}
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span>{{ GetFileType(file.ext) }}</span>
|
||||
@@ -81,7 +77,7 @@
|
||||
</div>
|
||||
<div class="bar" v-if="data.created_at > 0">
|
||||
<span class="bar-item"
|
||||
><el-icon><Clock /></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span
|
||||
>
|
||||
<!-- <span class="bar-item">tokens: {{ finalTokens }}</span>-->
|
||||
</div>
|
||||
@@ -91,15 +87,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { Clock } from '@element-plus/icons-vue'
|
||||
import { httpPost } from '@/utils/http'
|
||||
import hl from 'highlight.js'
|
||||
import { dateFormat, isImage, processPrompt } from '@/utils/libs'
|
||||
import { FormatFileSize, GetFileIcon, GetFileType } from '@/store/system'
|
||||
import emoji from 'markdown-it-emoji'
|
||||
import mathjaxPlugin from 'markdown-it-mathjax3'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import {onMounted, ref} from "vue";
|
||||
import {Clock} from "@element-plus/icons-vue";
|
||||
import {httpPost} from "@/utils/http";
|
||||
import hl from "highlight.js";
|
||||
import {dateFormat, isImage, processPrompt} from "@/utils/libs";
|
||||
import {FormatFileSize, GetFileIcon, GetFileType} from "@/store/system";
|
||||
import emoji from "markdown-it-emoji";
|
||||
import mathjaxPlugin from "markdown-it-mathjax3";
|
||||
import MarkdownIt from "markdown-it";
|
||||
|
||||
const md = new MarkdownIt({
|
||||
breaks: true,
|
||||
@@ -107,91 +103,83 @@ const md = new MarkdownIt({
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
highlight: function (str, lang) {
|
||||
const codeIndex = parseInt(Date.now()) + Math.floor(Math.random() * 10000000)
|
||||
const codeIndex = parseInt(Date.now()) + Math.floor(Math.random() * 10000000);
|
||||
// 显示复制代码按钮
|
||||
const copyBtn = `<span class="copy-code-btn" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span>
|
||||
<textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy-target-${codeIndex}">${str.replace(
|
||||
/<\/textarea>/g,
|
||||
'</textarea>'
|
||||
)}</textarea>`
|
||||
/<\/textarea>/g,
|
||||
"</textarea>"
|
||||
)}</textarea>`;
|
||||
if (lang && hl.getLanguage(lang)) {
|
||||
const langHtml = `<span class="lang-name">${lang}</span>`
|
||||
const langHtml = `<span class="lang-name">${lang}</span>`;
|
||||
// 处理代码高亮
|
||||
const preCode = hl.highlight(lang, str, true).value
|
||||
const preCode = hl.highlight(lang, str, true).value;
|
||||
// 将代码包裹在 pre 中
|
||||
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn} ${langHtml}</pre>`
|
||||
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn} ${langHtml}</pre>`;
|
||||
}
|
||||
|
||||
// 处理代码高亮
|
||||
const preCode = md.utils.escapeHtml(str)
|
||||
const preCode = md.utils.escapeHtml(str);
|
||||
// 将代码包裹在 pre 中
|
||||
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn}</pre>`
|
||||
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn}</pre>`;
|
||||
},
|
||||
})
|
||||
md.use(mathjaxPlugin)
|
||||
md.use(emoji)
|
||||
});
|
||||
md.use(mathjaxPlugin);
|
||||
md.use(emoji);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {
|
||||
content: '',
|
||||
created_at: '',
|
||||
content: "",
|
||||
created_at: "",
|
||||
tokens: 0,
|
||||
model: '',
|
||||
icon: '',
|
||||
model: "",
|
||||
icon: "",
|
||||
},
|
||||
},
|
||||
listStyle: {
|
||||
type: String,
|
||||
default: 'list',
|
||||
default: "list",
|
||||
},
|
||||
})
|
||||
const finalTokens = ref(props.data.tokens)
|
||||
const content = ref(processPrompt(props.data.content))
|
||||
const files = ref([])
|
||||
});
|
||||
const finalTokens = ref(props.data.tokens);
|
||||
const content = ref(processPrompt(props.data.content));
|
||||
const files = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
processFiles()
|
||||
})
|
||||
processFiles();
|
||||
});
|
||||
|
||||
const processFiles = () => {
|
||||
if (!props.data.content) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const linkRegex = /(https?:\/\/\S+)/g
|
||||
const links = props.data.content.match(linkRegex)
|
||||
const urlPrefix = `${window.location.protocol}//${window.location.host}`
|
||||
const linkRegex = /(https?:\/\/\S+)/g;
|
||||
const links = props.data.content.match(linkRegex);
|
||||
if (links) {
|
||||
const _links = links.map((link) => {
|
||||
if (link.startsWith(urlPrefix)) {
|
||||
return link.replace(urlPrefix, '')
|
||||
}
|
||||
return link
|
||||
})
|
||||
// 合并数组并去重
|
||||
const urls = [...new Set([...links, ..._links])]
|
||||
httpPost('/api/upload/list', { urls: urls })
|
||||
.then((res) => {
|
||||
files.value = res.data.items
|
||||
httpPost("/api/upload/list", {urls: links})
|
||||
.then((res) => {
|
||||
files.value = res.data.items;
|
||||
|
||||
for (let link of links) {
|
||||
if (isExternalImg(link, files.value)) {
|
||||
files.value.push({ url: link, ext: '.png' })
|
||||
for (let link of links) {
|
||||
if (isExternalImg(link, files.value)) {
|
||||
files.value.push({url: link, ext: ".png"});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
|
||||
for (let link of links) {
|
||||
content.value = content.value.replace(link, '')
|
||||
content.value = content.value.replace(link, "");
|
||||
}
|
||||
}
|
||||
content.value = md.render(content.value.trim())
|
||||
}
|
||||
content.value = md.render(content.value.trim());
|
||||
};
|
||||
const isExternalImg = (link, files) => {
|
||||
return isImage(link) && !files.find((file) => file.url === link)
|
||||
}
|
||||
return isImage(link) && !files.find((file) => file.url === link);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
<template>
|
||||
<div class="theme-box" @click="toggleTheme">
|
||||
<div class="theme-box" @click="toggleTheme" :class="size">
|
||||
<i class="iconfont" :class="themePage === 'light' ? 'icon-yueliang' : 'icon-taiyang'"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useSharedStore } from '@/store/sharedata'
|
||||
import { ref } from "vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
// 定义主题状态,初始值从 localStorage 获取
|
||||
const store = useSharedStore()
|
||||
const themePage = ref(store.theme || 'light')
|
||||
const store = useSharedStore();
|
||||
const themePage = ref(store.theme || "light");
|
||||
|
||||
// 切换主题函数
|
||||
const toggleTheme = () => {
|
||||
themePage.value = themePage.value === 'light' ? 'dark' : 'light'
|
||||
store.setTheme(themePage.value) // 保存主题
|
||||
}
|
||||
themePage.value = themePage.value === "light" ? "dark" : "light";
|
||||
store.setTheme(themePage.value); // 保存主题
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '@/assets/iconfont/iconfont.css'
|
||||
.theme-box{
|
||||
z-index :111
|
||||
position: fixed;
|
||||
@@ -49,4 +55,17 @@ const toggleTheme = () => {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-box.small {
|
||||
position: relative !important;
|
||||
right: initial;
|
||||
bottom: initial;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 18px;
|
||||
|
||||
.iconfont {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-dialog class="config-dialog" v-model="showDialog" :close-on-click-modal="true" :before-close="close" style="max-width: 400px" title="账户信息">
|
||||
<div class="flex-center-col p-4 pt-0" id="user-info">
|
||||
<div class="flex-center-col pl-4 pr-4" id="user-info">
|
||||
<user-profile @hide="close" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
<el-form-item label="剩余算力">
|
||||
<el-text type="warning">{{ user["power"] }}</el-text>
|
||||
<el-tag type="info" size="small" class="ml-2 cursor-pointer" @click="gotoLog">算力日志</el-tag>
|
||||
<el-tooltip :content="`每日签到可获得 ${systemConfig.daily_power} 算力`" placement="top" v-if="systemConfig.daily_power > 0">
|
||||
<el-button type="primary" size="small" @click="signIn" class="ml-2">签到</el-button>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="会员到期时间" v-if="user['expired_time'] > 0">
|
||||
<el-tag type="danger">{{ dateFormat(user["expired_time"]) }}</el-tag>
|
||||
@@ -44,8 +47,9 @@ import { ElMessage } from "element-plus";
|
||||
import { Plus } from "@element-plus/icons-vue";
|
||||
import Compressor from "compressorjs";
|
||||
import { dateFormat } from "@/utils/libs";
|
||||
import { checkSession } from "@/store/cache";
|
||||
import { checkSession, getSystemInfo } from "@/store/cache";
|
||||
import { useRouter } from "vue-router";
|
||||
import { showMessageError, showMessageOK } from "@/utils/dialog";
|
||||
const user = ref({
|
||||
vip: false,
|
||||
username: "演示数据",
|
||||
@@ -56,6 +60,7 @@ const user = ref({
|
||||
});
|
||||
|
||||
const vipImg = ref("/images/menu/member.png");
|
||||
const systemConfig = ref({});
|
||||
const router = useRouter();
|
||||
const emits = defineEmits(["hide"]);
|
||||
onMounted(() => {
|
||||
@@ -73,6 +78,10 @@ onMounted(() => {
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
getSystemInfo().then((res) => {
|
||||
systemConfig.value = res.data;
|
||||
});
|
||||
});
|
||||
|
||||
const afterRead = (file) => {
|
||||
@@ -112,6 +121,17 @@ const gotoLog = () => {
|
||||
router.push("/powerLog");
|
||||
emits("hide", false);
|
||||
};
|
||||
|
||||
const signIn = () => {
|
||||
httpGet("/api/user/signin")
|
||||
.then(() => {
|
||||
showMessageOK("签到成功");
|
||||
user.value.power += systemConfig.value.daily_power;
|
||||
})
|
||||
.catch((e) => {
|
||||
showMessageError(e.message);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
@@ -8,236 +8,248 @@
|
||||
/**
|
||||
* Util lib functions
|
||||
*/
|
||||
import {showConfirmDialog} from "vant";
|
||||
import { showConfirmDialog } from "vant";
|
||||
|
||||
// generate a random string
|
||||
export function randString(length) {
|
||||
const str = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
const size = str.length
|
||||
let buf = []
|
||||
for (let i = 0; i < length; i++) {
|
||||
const rand = Math.random() * size
|
||||
buf.push(str.charAt(rand))
|
||||
}
|
||||
return buf.join("")
|
||||
const str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
const size = str.length;
|
||||
let buf = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
const rand = Math.random() * size;
|
||||
buf.push(str.charAt(rand));
|
||||
}
|
||||
return buf.join("");
|
||||
}
|
||||
|
||||
export function UUID() {
|
||||
let d = new Date().getTime();
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
let d = new Date().getTime();
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
||||
const r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
// 判断是否是移动设备
|
||||
export function isMobile() {
|
||||
const userAgent = navigator.userAgent;
|
||||
const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
|
||||
return mobileRegex.test(userAgent);
|
||||
const userAgent = navigator.userAgent;
|
||||
const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
|
||||
return mobileRegex.test(userAgent);
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
export function dateFormat(timestamp, format) {
|
||||
if (!timestamp) {
|
||||
return '';
|
||||
} else if (timestamp < 9680917502) {
|
||||
timestamp = timestamp * 1000;
|
||||
}
|
||||
let year, month, day, HH, mm, ss;
|
||||
let time = new Date(timestamp);
|
||||
let timeDate;
|
||||
year = time.getFullYear(); // 年
|
||||
month = time.getMonth() + 1; // 月
|
||||
day = time.getDate(); // 日
|
||||
HH = time.getHours(); // 时
|
||||
mm = time.getMinutes(); // 分
|
||||
ss = time.getSeconds(); // 秒
|
||||
if (!timestamp) {
|
||||
return "";
|
||||
} else if (timestamp < 9680917502) {
|
||||
timestamp = timestamp * 1000;
|
||||
}
|
||||
let year, month, day, HH, mm, ss;
|
||||
let time = new Date(timestamp);
|
||||
let timeDate;
|
||||
year = time.getFullYear(); // 年
|
||||
month = time.getMonth() + 1; // 月
|
||||
day = time.getDate(); // 日
|
||||
HH = time.getHours(); // 时
|
||||
mm = time.getMinutes(); // 分
|
||||
ss = time.getSeconds(); // 秒
|
||||
|
||||
month = month < 10 ? '0' + month : month;
|
||||
day = day < 10 ? '0' + day : day;
|
||||
HH = HH < 10 ? '0' + HH : HH; // 时
|
||||
mm = mm < 10 ? '0' + mm : mm; // 分
|
||||
ss = ss < 10 ? '0' + ss : ss; // 秒
|
||||
month = month < 10 ? "0" + month : month;
|
||||
day = day < 10 ? "0" + day : day;
|
||||
HH = HH < 10 ? "0" + HH : HH; // 时
|
||||
mm = mm < 10 ? "0" + mm : mm; // 分
|
||||
ss = ss < 10 ? "0" + ss : ss; // 秒
|
||||
|
||||
switch (format) {
|
||||
case 'yyyy':
|
||||
timeDate = String(year);
|
||||
break;
|
||||
case 'yyyy-MM':
|
||||
timeDate = year + '-' + month;
|
||||
break;
|
||||
case 'yyyy-MM-dd':
|
||||
timeDate = year + '-' + month + '-' + day;
|
||||
break;
|
||||
case 'yyyy/MM/dd':
|
||||
timeDate = year + '/' + month + '/' + day;
|
||||
break;
|
||||
case 'yyyy-MM-dd HH:mm:ss':
|
||||
timeDate = year + '-' + month + '-' + day + ' ' + HH + ':' + mm + ':' + ss;
|
||||
break;
|
||||
case 'HH:mm:ss':
|
||||
timeDate = HH + ':' + mm + ':' + ss;
|
||||
break;
|
||||
case 'MM':
|
||||
timeDate = String(month);
|
||||
break;
|
||||
default:
|
||||
timeDate = year + '-' + month + '-' + day + ' ' + HH + ':' + mm + ':' + ss;
|
||||
break;
|
||||
}
|
||||
return timeDate;
|
||||
switch (format) {
|
||||
case "yyyy":
|
||||
timeDate = String(year);
|
||||
break;
|
||||
case "yyyy-MM":
|
||||
timeDate = year + "-" + month;
|
||||
break;
|
||||
case "yyyy-MM-dd":
|
||||
timeDate = year + "-" + month + "-" + day;
|
||||
break;
|
||||
case "yyyy/MM/dd":
|
||||
timeDate = year + "/" + month + "/" + day;
|
||||
break;
|
||||
case "yyyy-MM-dd HH:mm:ss":
|
||||
timeDate = year + "-" + month + "-" + day + " " + HH + ":" + mm + ":" + ss;
|
||||
break;
|
||||
case "HH:mm:ss":
|
||||
timeDate = HH + ":" + mm + ":" + ss;
|
||||
break;
|
||||
case "MM":
|
||||
timeDate = String(month);
|
||||
break;
|
||||
default:
|
||||
timeDate = year + "-" + month + "-" + day + " " + HH + ":" + mm + ":" + ss;
|
||||
break;
|
||||
}
|
||||
return timeDate;
|
||||
}
|
||||
|
||||
export function formatTime(time) {
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`;
|
||||
}
|
||||
|
||||
// 判断数组中是否包含某个元素
|
||||
export function arrayContains(array, value, compare) {
|
||||
if (!array) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof compare !== 'function') {
|
||||
compare = function (v1, v2) {
|
||||
return v1 === v2;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (compare(array[i], value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!array) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof compare !== "function") {
|
||||
compare = function (v1, v2) {
|
||||
return v1 === v2;
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (compare(array[i], value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 删除数组中指定的元素
|
||||
export function removeArrayItem(array, value, compare) {
|
||||
if (typeof compare !== 'function') {
|
||||
compare = function (v1, v2) {
|
||||
return v1 === v2;
|
||||
}
|
||||
if (typeof compare !== "function") {
|
||||
compare = function (v1, v2) {
|
||||
return v1 === v2;
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (compare(array[i], value)) {
|
||||
array.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (compare(array[i], value)) {
|
||||
array.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// 渲染输入的换行符
|
||||
export function renderInputText(text) {
|
||||
const replaceRegex = /(\n\r|\r\n|\r|\n)/g;
|
||||
text = text || '';
|
||||
return text.replace(replaceRegex, "<br/>");
|
||||
const replaceRegex = /(\n\r|\r\n|\r|\n)/g;
|
||||
text = text || "";
|
||||
return text.replace(replaceRegex, "<br/>");
|
||||
}
|
||||
|
||||
// 拷贝对象
|
||||
export function copyObj(origin) {
|
||||
return JSON.parse(JSON.stringify(origin));
|
||||
return JSON.parse(JSON.stringify(origin));
|
||||
}
|
||||
|
||||
export function disabledDate(time) {
|
||||
return time.getTime() < Date.now()
|
||||
return time.getTime() < Date.now();
|
||||
}
|
||||
|
||||
// 字符串截取
|
||||
export function substr(str, length) {
|
||||
let result = ''
|
||||
let count = 0
|
||||
let result = "";
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charAt(i)
|
||||
const charCode = str.charCodeAt(i);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charAt(i);
|
||||
const charCode = str.charCodeAt(i);
|
||||
|
||||
// 判断字符是否为中文字符
|
||||
if (charCode >= 0x4e00 && charCode <= 0x9fff) {
|
||||
// 中文字符算两个字符
|
||||
count += 2
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
|
||||
if (count <= length) {
|
||||
result += char
|
||||
} else {
|
||||
result += " ..."
|
||||
break
|
||||
}
|
||||
// 判断字符是否为中文字符
|
||||
if (charCode >= 0x4e00 && charCode <= 0x9fff) {
|
||||
// 中文字符算两个字符
|
||||
count += 2;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
|
||||
return result
|
||||
if (count <= length) {
|
||||
result += char;
|
||||
} else {
|
||||
result += " ...";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isImage(url) {
|
||||
const expr = /\.(jpg|jpeg|png|gif|bmp|svg)$/i;
|
||||
return expr.test(url);
|
||||
const expr = /\.(jpg|jpeg|png|gif|bmp|svg)$/i;
|
||||
return expr.test(url);
|
||||
}
|
||||
|
||||
export function processContent(content) {
|
||||
if (!content) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 如果是图片链接地址,则直接替换成图片标签
|
||||
const linkRegex = /(https?:\/\/\S+)/g;
|
||||
const links = content.match(linkRegex);
|
||||
if (links) {
|
||||
for (let link of links) {
|
||||
if (isImage(link)) {
|
||||
const index = content.indexOf(link)
|
||||
if (content.substring(index - 1, 2) !== "]") {
|
||||
content = content.replace(link, "\n\n")
|
||||
}
|
||||
}
|
||||
if (!content) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 如果是图片链接地址,则直接替换成图片标签
|
||||
const linkRegex = /(https?:\/\/\S+)/g;
|
||||
const links = content.match(linkRegex);
|
||||
if (links) {
|
||||
for (let link of links) {
|
||||
if (isImage(link)) {
|
||||
const index = content.indexOf(link);
|
||||
if (content.substring(index - 1, 2) !== "]") {
|
||||
content = content.replace(link, "\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return content
|
||||
}
|
||||
// 处理推理标签
|
||||
if (content.includes("<think>")) {
|
||||
content = content.replace(/<think>(.*?)<\/think>/gs, (match, content) => {
|
||||
if (content.length > 10) {
|
||||
return `<blockquote>${content}</blockquote>`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
content = content.replace(/<think>(.*?)$/gs, (match, content) => {
|
||||
if (content.length > 10) {
|
||||
return `<blockquote>${content}</blockquote>`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
export function processPrompt(prompt) {
|
||||
return prompt.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
return prompt.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
|
||||
// 判断是否为微信浏览器
|
||||
export function isWeChatBrowser() {
|
||||
return /MicroMessenger/i.test( navigator.userAgent);
|
||||
return /MicroMessenger/i.test(navigator.userAgent);
|
||||
}
|
||||
|
||||
export function showLoginDialog(router) {
|
||||
showConfirmDialog({
|
||||
title: '登录',
|
||||
message:
|
||||
'此操作需要登录才能进行,前往登录?',
|
||||
}).then(() => {
|
||||
router.push("/mobile/login")
|
||||
}).catch(() => {
|
||||
// on cancel
|
||||
showConfirmDialog({
|
||||
title: "登录",
|
||||
message: "此操作需要登录才能进行,前往登录?",
|
||||
})
|
||||
.then(() => {
|
||||
router.push("/mobile/login");
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
}
|
||||
|
||||
export const replaceImg =(img) => {
|
||||
if (!img.startsWith("http")) {
|
||||
img = `${location.protocol}//${location.host}/${img}`
|
||||
}
|
||||
const devHost = process.env.VUE_APP_API_HOST
|
||||
const localhost = "http://localhost:5678"
|
||||
if (img.includes(localhost)) {
|
||||
return img?.replace(localhost, devHost)
|
||||
}
|
||||
return img
|
||||
}
|
||||
export const replaceImg = (img) => {
|
||||
if (!img.startsWith("http")) {
|
||||
img = `${location.protocol}//${location.host}/${img}`;
|
||||
}
|
||||
const devHost = process.env.VUE_APP_API_HOST;
|
||||
const localhost = "http://localhost:5678";
|
||||
if (img.includes(localhost)) {
|
||||
return img?.replace(localhost, devHost);
|
||||
}
|
||||
return img;
|
||||
};
|
||||
export function isChrome() {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
return /chrome/.test(userAgent) && !/edg/.test(userAgent);
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
return /chrome/.test(userAgent) && !/edg/.test(userAgent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-container>
|
||||
<el-aside v-show="store.chatListExtend">
|
||||
<div class="flex w-full justify-center pt-3 pb-3">
|
||||
<img :src="logo" style="max-height: 40px" :alt="title" v-if="logo !== ''"/>
|
||||
<img :src="logo" style="max-height: 40px" :alt="title" v-if="logo !== ''" />
|
||||
<h2 v-else>{{ title }}</h2>
|
||||
</div>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
</el-container>
|
||||
|
||||
<el-dialog v-model="showNotice" :show-close="true" class="notice-dialog" title="网站公告">
|
||||
<div class="notice p-4">
|
||||
<div class="notice">
|
||||
<div v-html="notice"></div>
|
||||
</div>
|
||||
|
||||
@@ -232,24 +232,24 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import { nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import ChatPrompt from "@/components/ChatPrompt.vue";
|
||||
import ChatReply from "@/components/ChatReply.vue";
|
||||
import {Delete, Edit, InfoFilled, More, Promotion, Search, Share, VideoPause} from "@element-plus/icons-vue";
|
||||
import { Delete, Edit, InfoFilled, More, Promotion, Search, Share, VideoPause } from "@element-plus/icons-vue";
|
||||
import "highlight.js/styles/a11y-dark.css";
|
||||
import {isMobile, randString, removeArrayItem, UUID} from "@/utils/libs";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {useRouter} from "vue-router";
|
||||
import { isMobile, randString, removeArrayItem, UUID } from "@/utils/libs";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
import { useRouter } from "vue-router";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import { checkSession, getClientId, getSystemInfo } from "@/store/cache";
|
||||
import Welcome from "@/components/Welcome.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import FileSelect from "@/components/FileSelect.vue";
|
||||
import FileList from "@/components/FileList.vue";
|
||||
import ChatSetting from "@/components/ChatSetting.vue";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
import {closeLoading, showLoading, showMessageError} from "@/utils/dialog";
|
||||
import { closeLoading, showLoading, showMessageError } from "@/utils/dialog";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import emoji from "markdown-it-emoji";
|
||||
|
||||
@@ -977,6 +977,7 @@ const realtimeChat = () => {
|
||||
</style>
|
||||
|
||||
<style lang="stylus">
|
||||
@import '@/assets/css/markdown/vue.css';
|
||||
.notice-dialog {
|
||||
.el-dialog__header {
|
||||
padding-bottom 0
|
||||
@@ -985,6 +986,10 @@ const realtimeChat = () => {
|
||||
.el-dialog__body {
|
||||
padding 0 20px
|
||||
|
||||
h2 {
|
||||
margin: 20px 0 15px 0;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
padding-left 10px
|
||||
}
|
||||
@@ -995,7 +1000,7 @@ const realtimeChat = () => {
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style disc
|
||||
list-style inside
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +320,7 @@ onMounted(() => {
|
||||
models.value = res.data;
|
||||
selectedModel.value = models.value[0];
|
||||
params.value.model_id = selectedModel.value.id;
|
||||
changeModel(selectedModel.value);
|
||||
})
|
||||
.catch((e) => {
|
||||
showMessageError("获取模型列表失败:" + e.message);
|
||||
|
||||
@@ -39,25 +39,25 @@
|
||||
<el-popover v-if="moreNavs.length > 0" placement="right-end" trigger="hover">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<i class="iconfont icon-more"/>
|
||||
<i class="iconfont icon-more" />
|
||||
</li>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus">
|
||||
<li
|
||||
v-for="(item, index) in moreNavs"
|
||||
:key="item.url"
|
||||
:class="{
|
||||
active: item.url === curPath,
|
||||
moreTitle: index !== 3 && index !== 4,
|
||||
twoTittle: index === 3 || index === 4,
|
||||
}"
|
||||
v-for="(item, index) in moreNavs"
|
||||
:key="item.url"
|
||||
:class="{
|
||||
active: item.url === curPath,
|
||||
moreTitle: index !== 3 && index !== 4,
|
||||
twoTittle: index === 3 || index === 4,
|
||||
}"
|
||||
>
|
||||
<a @click="changeNav(item)">
|
||||
<span v-if="item.icon.startsWith('icon')" class="mr-2">
|
||||
<i class="iconfont" :class="item.icon"></i>
|
||||
</span>
|
||||
<el-image :src="item.icon" style="width: 20px; height: 20px" v-else/>
|
||||
<span v-if="item.icon.startsWith('icon')" class="mr-2">
|
||||
<i class="iconfont" :class="item.icon"></i>
|
||||
</span>
|
||||
<el-image :src="item.icon" style="width: 20px; height: 20px" v-else />
|
||||
<span :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -67,7 +67,7 @@
|
||||
<el-popover placement="right-end" trigger="hover" v-if="loginUser.id">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<i class="iconfont icon-config"/>
|
||||
<i class="iconfont icon-config" />
|
||||
</li>
|
||||
</template>
|
||||
<template #default>
|
||||
@@ -75,7 +75,7 @@
|
||||
<li>
|
||||
<div @click="showConfigDialog = true" class="flex">
|
||||
<el-icon>
|
||||
<UserFilled/>
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
<span class="username title">{{ loginUser.nickname }}</span>
|
||||
</div>
|
||||
@@ -100,7 +100,7 @@
|
||||
<i class="iconfont icon-house"></i>
|
||||
</a>
|
||||
<div class="pl-1">
|
||||
<ThemeChange/>
|
||||
<ThemeChange size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,17 +133,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {UserFilled} from "@element-plus/icons-vue";
|
||||
import { UserFilled } from "@element-plus/icons-vue";
|
||||
import ThemeChange from "@/components/ThemeChange.vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import {computed, onMounted, ref, watch} from "vue";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {checkSession, getLicenseInfo, getSystemInfo} from "@/store/cache";
|
||||
import {removeUserToken} from "@/store/session";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import { useRouter } from "vue-router";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { httpGet } from "@/utils/http";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { checkSession, getLicenseInfo, getSystemInfo } from "@/store/cache";
|
||||
import { removeUserToken } from "@/store/session";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import ConfigDialog from "@/components/UserInfoDialog.vue";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
import { showMessageError } from "@/utils/dialog";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
|
||||
const router = useRouter();
|
||||
@@ -188,7 +188,7 @@ const getFirstPathSegment = (url) => {
|
||||
|
||||
const stars = computed(() => {
|
||||
return 1000;
|
||||
})
|
||||
});
|
||||
|
||||
watch(
|
||||
() => store.showLoginDialog,
|
||||
|
||||
@@ -141,6 +141,7 @@ const types = ref([
|
||||
{ label: "Suno文生歌", value: "suno" },
|
||||
{ label: "Luma视频", value: "luma" },
|
||||
{ label: "Realtime API", value: "realtime" },
|
||||
{ label: "其他", value: "other" },
|
||||
]);
|
||||
const isEdit = ref(false);
|
||||
const clipboard = ref(null);
|
||||
|
||||
@@ -195,7 +195,7 @@ const type = ref([
|
||||
|
||||
// 获取 API KEY
|
||||
const apiKeys = ref([]);
|
||||
httpGet("/api/admin/apikey/list?type=chat|dalle")
|
||||
httpGet("/api/admin/apikey/list")
|
||||
.then((res) => {
|
||||
apiKeys.value = res.data;
|
||||
})
|
||||
@@ -345,13 +345,6 @@ const remove = function (row) {
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
.copy-model {
|
||||
margin-left 6px
|
||||
cursor pointer
|
||||
}
|
||||
}
|
||||
|
||||
.el-select {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user