mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	feat: optimize chat page data list style, support list style and chat style
This commit is contained in:
		@@ -1,5 +1,5 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
  presets: [
 | 
			
		||||
    '@vue.css/cli-plugin-babel/preset'
 | 
			
		||||
    '@vue/cli-plugin-babel/preset'
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,27 +2,27 @@
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout {
 | 
			
		||||
#app .chat-page {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside {
 | 
			
		||||
#app .chat-page .el-aside {
 | 
			
		||||
    background-color: #252526;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .title-box {
 | 
			
		||||
#app .chat-page .el-aside .title-box {
 | 
			
		||||
    padding: 6px 10px;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .title-box span {
 | 
			
		||||
#app .chat-page .el-aside .title-box span {
 | 
			
		||||
    padding-top: 5px;
 | 
			
		||||
    padding-left: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list {
 | 
			
		||||
#app .chat-page .el-aside .chat-list {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-flow: column;
 | 
			
		||||
    background-color: #28292a;
 | 
			
		||||
@@ -30,28 +30,28 @@
 | 
			
		||||
    border-right: 1px solid #2f3032;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .search-box {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .search-box {
 | 
			
		||||
    flex-wrap: wrap;
 | 
			
		||||
    padding: 10px 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .search-box .el-input__wrapper {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .search-box .el-input__wrapper {
 | 
			
		||||
    background-color: #363535;
 | 
			
		||||
    box-shadow: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list ::-webkit-scrollbar {
 | 
			
		||||
#app .chat-page .el-aside .chat-list ::-webkit-scrollbar {
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    background-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    justify-content: flex-start;
 | 
			
		||||
@@ -59,17 +59,17 @@
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item:hover {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item:hover {
 | 
			
		||||
    background-color: #343540;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .avatar {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item .avatar {
 | 
			
		||||
    width: 28px;
 | 
			
		||||
    height: 28px;
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .chat-title-input {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item .chat-title-input {
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    margin-top: 4px;
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
@@ -79,7 +79,7 @@
 | 
			
		||||
    width: 190px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .chat-title {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item .chat-title {
 | 
			
		||||
    color: #c1c1c1;
 | 
			
		||||
    padding: 5px 10px;
 | 
			
		||||
    max-width: 220px;
 | 
			
		||||
@@ -89,7 +89,7 @@
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .btn {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item .btn {
 | 
			
		||||
    display: none;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: 2px;
 | 
			
		||||
@@ -97,19 +97,19 @@
 | 
			
		||||
    color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item .btn .el-icon {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item .btn .el-icon {
 | 
			
		||||
    margin-right: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item.active {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item.active {
 | 
			
		||||
    background-color: #343540;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .chat-list .content .chat-list-item.active .btn {
 | 
			
		||||
#app .chat-page .el-aside .chat-list .content .chat-list-item.active .btn {
 | 
			
		||||
    display: inline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .tool-box {
 | 
			
		||||
#app .chat-page .el-aside .tool-box {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: flex-end;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
@@ -117,48 +117,48 @@
 | 
			
		||||
    border-top: 1px solid #3c3c3c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .tool-box .user-info {
 | 
			
		||||
#app .chat-page .el-aside .tool-box .user-info {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    padding-top: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link {
 | 
			
		||||
#app .chat-page .el-aside .tool-box .user-info .el-dropdown-link {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    display: flex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link .el-image {
 | 
			
		||||
#app .chat-page .el-aside .tool-box .user-info .el-dropdown-link .el-image {
 | 
			
		||||
    width: 20px;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link .username {
 | 
			
		||||
#app .chat-page .el-aside .tool-box .user-info .el-dropdown-link .username {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    line-height: 22px;
 | 
			
		||||
    width: 230px;
 | 
			
		||||
    padding-left: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-aside .tool-box .user-info .el-dropdown-link .el-icon {
 | 
			
		||||
#app .chat-page .el-aside .tool-box .user-info .el-dropdown-link .el-icon {
 | 
			
		||||
    color: #ccc;
 | 
			
		||||
    line-height: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main {
 | 
			
		||||
#app .chat-page .el-main {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    --el-main-padding: 0;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head {
 | 
			
		||||
#app .chat-page .el-main .chat-head {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 50px;
 | 
			
		||||
    background-color: #28292a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .chat-config {
 | 
			
		||||
#app .chat-page .el-main .chat-head .chat-config {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
@@ -166,54 +166,54 @@
 | 
			
		||||
    padding-top: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .chat-config .role-select-label {
 | 
			
		||||
#app .chat-page .el-main .chat-head .chat-config .role-select-label {
 | 
			
		||||
    color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .chat-config .el-select {
 | 
			
		||||
#app .chat-page .el-main .chat-head .chat-config .el-select {
 | 
			
		||||
    max-width: 150px;
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .chat-config .role-select {
 | 
			
		||||
#app .chat-page .el-main .chat-head .chat-config .role-select {
 | 
			
		||||
    max-width: 130px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .chat-config .el-button .el-icon {
 | 
			
		||||
#app .chat-page .el-main .chat-head .chat-config .el-button .el-icon {
 | 
			
		||||
    margin-right: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .iconfont {
 | 
			
		||||
#app .chat-page .el-main .chat-head .iconfont {
 | 
			
		||||
    margin-right: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .is-circle {
 | 
			
		||||
#app .chat-page .el-main .chat-head .is-circle {
 | 
			
		||||
    margin-left: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-head .is-circle .iconfont {
 | 
			
		||||
#app .chat-page .el-main .chat-head .is-circle .iconfont {
 | 
			
		||||
    margin-right: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box {
 | 
			
		||||
#app .chat-page .el-main .chat-box {
 | 
			
		||||
    min-width: 0;
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    border-left: 1px solid #4f4f4f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container ::-webkit-scrollbar {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container ::-webkit-scrollbar {
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    background-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .chat-box {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .chat-box {
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
    --content-font-size: 16px;
 | 
			
		||||
    --content-color: #c1c1c1;
 | 
			
		||||
@@ -221,28 +221,28 @@
 | 
			
		||||
    padding: 0 0 50px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .chat-box .chat-line {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .chat-box .chat-line {
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: flex-start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .re-generate {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .re-generate {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .re-generate .btn-box {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .re-generate .btn-box {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    bottom: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .re-generate .btn-box .el-button .el-icon {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .re-generate .btn-box .el-button .el-icon {
 | 
			
		||||
    margin-right: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .input-box {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .input-box {
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
@@ -251,7 +251,7 @@
 | 
			
		||||
    padding: 0 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .input-box .input-container {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .input-box .input-container {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    border: none;
 | 
			
		||||
@@ -261,24 +261,24 @@
 | 
			
		||||
    position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container .input-box .input-container .el-textarea .el-textarea__inner::-webkit-scrollbar {
 | 
			
		||||
#app .chat-page .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 .chat-box #container .input-box .input-container .select-file {
 | 
			
		||||
#app .chat-page .el-main .chat-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 .send-btn {
 | 
			
		||||
#app .chat-page .el-main .chat-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 .el-button {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container .input-box .input-container .send-btn .el-button {
 | 
			
		||||
    padding: 8px 5px;
 | 
			
		||||
    border-radius: 6px;
 | 
			
		||||
    background: #19c37d;
 | 
			
		||||
@@ -286,7 +286,7 @@
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app .common-layout .el-main .chat-box #container::-webkit-scrollbar {
 | 
			
		||||
#app .chat-page .el-main .chat-box #container::-webkit-scrollbar {
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ $borderColor = #4676d0;
 | 
			
		||||
 | 
			
		||||
  height: 100%;
 | 
			
		||||
 | 
			
		||||
  .common-layout {
 | 
			
		||||
  .chat-page {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
 | 
			
		||||
    // left side
 | 
			
		||||
@@ -156,6 +156,20 @@ $borderColor = #4676d0;
 | 
			
		||||
            max-width 130px;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          .setting {
 | 
			
		||||
            padding 5px
 | 
			
		||||
            border-radius 5px
 | 
			
		||||
            cursor pointer
 | 
			
		||||
            .iconfont {
 | 
			
		||||
              font-size 18px
 | 
			
		||||
              color #19c37d
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &:hover {
 | 
			
		||||
              background #D5FAD3
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          .el-button {
 | 
			
		||||
            .el-icon {
 | 
			
		||||
              margin-right 5px;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,49 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="chat-line chat-line-prompt">
 | 
			
		||||
  <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"/>
 | 
			
		||||
        </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"/>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="item" v-else>
 | 
			
		||||
                <div class="icon">
 | 
			
		||||
                  <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>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="info">
 | 
			
		||||
                    <span>{{GetFileType(file.ext)}}</span>
 | 
			
		||||
                    <span>{{FormatFileSize(file.size)}}</span>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <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>
 | 
			
		||||
            <span class="bar-item">tokens: {{ finalTokens }}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="chat-line chat-line-prompt-chat" v-else>
 | 
			
		||||
    <div class="chat-line-inner">
 | 
			
		||||
      <div class="chat-icon">
 | 
			
		||||
        <img :src="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)">
 | 
			
		||||
@@ -27,14 +65,15 @@
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <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: {{ finalTokens }}</span>-->
 | 
			
		||||
        <div class="content-wrapper">
 | 
			
		||||
          <div class="content" v-html="content"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="bar" v-if="data.created_at > 0">
 | 
			
		||||
          <span class="bar-item"><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span>
 | 
			
		||||
          <span class="bar-item">tokens: {{ finalTokens }}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -43,7 +82,7 @@ import {onMounted, ref} from "vue"
 | 
			
		||||
import {Clock} from "@element-plus/icons-vue";
 | 
			
		||||
import {httpPost} from "@/utils/http";
 | 
			
		||||
import hl from "highlight.js";
 | 
			
		||||
import {isImage, processPrompt, substr} from "@/utils/libs";
 | 
			
		||||
import {dateFormat, isImage, processPrompt} from "@/utils/libs";
 | 
			
		||||
import {FormatFileSize, GetFileIcon, GetFileType} from "@/store/system";
 | 
			
		||||
 | 
			
		||||
const mathjaxPlugin = require('markdown-it-mathjax3')
 | 
			
		||||
@@ -73,41 +112,35 @@ const md = require('markdown-it')({
 | 
			
		||||
});
 | 
			
		||||
md.use(mathjaxPlugin)
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  content: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '',
 | 
			
		||||
  data: {
 | 
			
		||||
    type: Object,
 | 
			
		||||
    default: {
 | 
			
		||||
      content: '',
 | 
			
		||||
      created_at: '',
 | 
			
		||||
      tokens: 0,
 | 
			
		||||
      model: '',
 | 
			
		||||
      icon: '',
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  icon: {
 | 
			
		||||
  listStyle: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: 'images/user-icon.png',
 | 
			
		||||
  },
 | 
			
		||||
  createdAt: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '',
 | 
			
		||||
  },
 | 
			
		||||
  tokens: {
 | 
			
		||||
    type: Number,
 | 
			
		||||
    default: 0,
 | 
			
		||||
  },
 | 
			
		||||
  model: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '',
 | 
			
		||||
    default: 'list',
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
const finalTokens = ref(props.tokens)
 | 
			
		||||
const content =ref(processPrompt(props.content))
 | 
			
		||||
const finalTokens = ref(props.data.tokens)
 | 
			
		||||
const content =ref(processPrompt(props.data.content))
 | 
			
		||||
const files = ref([])
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  if (!finalTokens.value) {
 | 
			
		||||
    httpPost("/api/chat/tokens", {text: props.content, model: props.model}).then(res => {
 | 
			
		||||
    httpPost("/api/chat/tokens", {text: props.data.content, model: props.data.model}).then(res => {
 | 
			
		||||
      finalTokens.value = res.data;
 | 
			
		||||
    }).catch(() => {
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const linkRegex = /(https?:\/\/\S+)/g;
 | 
			
		||||
  const links = props.content.match(linkRegex);
 | 
			
		||||
  const links = props.data.content.match(linkRegex);
 | 
			
		||||
  if (links) {
 | 
			
		||||
    httpPost("/api/upload/list", {urls: links}).then(res => {
 | 
			
		||||
      files.value = res.data
 | 
			
		||||
@@ -124,131 +157,263 @@ onMounted(() => {
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
@import '@/assets/css/markdown/vue.css';
 | 
			
		||||
.chat-line-prompt {
 | 
			
		||||
  background-color #ffffff;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  width 100%
 | 
			
		||||
  padding-bottom: 1.5rem;
 | 
			
		||||
  padding-top: 1.5rem;
 | 
			
		||||
  border-bottom: 1px solid #d9d9e3;
 | 
			
		||||
.chat-page,.chat-export {
 | 
			
		||||
  .chat-line-prompt-list {
 | 
			
		||||
    background-color #ffffff;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    width 100%
 | 
			
		||||
    padding-bottom: 1.5rem;
 | 
			
		||||
    padding-top: 1.5rem;
 | 
			
		||||
    border-bottom: 1px solid #d9d9e3;
 | 
			
		||||
 | 
			
		||||
  .chat-line-inner {
 | 
			
		||||
    display flex;
 | 
			
		||||
    width 100%;
 | 
			
		||||
    max-width 900px;
 | 
			
		||||
    padding-left 10px;
 | 
			
		||||
    .chat-line-inner {
 | 
			
		||||
      display flex;
 | 
			
		||||
      width 100%;
 | 
			
		||||
      max-width 900px;
 | 
			
		||||
      padding-left 10px;
 | 
			
		||||
 | 
			
		||||
    .chat-icon {
 | 
			
		||||
      margin-right 20px;
 | 
			
		||||
      .chat-icon {
 | 
			
		||||
        margin-right 20px;
 | 
			
		||||
 | 
			
		||||
      img {
 | 
			
		||||
        width: 36px;
 | 
			
		||||
        height: 36px;
 | 
			
		||||
        border-radius: 10px;
 | 
			
		||||
        padding: 1px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .chat-item {
 | 
			
		||||
      width 100%
 | 
			
		||||
      padding: 0 5px 0 0;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
 | 
			
		||||
      .file-list-box {
 | 
			
		||||
        display flex
 | 
			
		||||
        flex-flow column
 | 
			
		||||
        .image {
 | 
			
		||||
          display flex
 | 
			
		||||
          flex-flow row
 | 
			
		||||
          margin-right 10px
 | 
			
		||||
          position relative
 | 
			
		||||
 | 
			
		||||
          .el-image {
 | 
			
		||||
            border 1px solid #e3e3e3
 | 
			
		||||
            border-radius 10px
 | 
			
		||||
            margin-bottom 10px
 | 
			
		||||
          }
 | 
			
		||||
        img {
 | 
			
		||||
          width: 36px;
 | 
			
		||||
          height: 36px;
 | 
			
		||||
          border-radius: 10px;
 | 
			
		||||
          padding: 1px;
 | 
			
		||||
        }
 | 
			
		||||
        .item {
 | 
			
		||||
          display flex
 | 
			
		||||
          flex-flow row
 | 
			
		||||
          border-radius 10px
 | 
			
		||||
          background-color #ffffff
 | 
			
		||||
          border 1px solid #e3e3e3
 | 
			
		||||
          padding 6px
 | 
			
		||||
          margin-bottom 10px
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .chat-item {
 | 
			
		||||
        width 100%
 | 
			
		||||
        padding: 0 5px 0 0;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        .file-list-box {
 | 
			
		||||
          display flex
 | 
			
		||||
          flex-flow column
 | 
			
		||||
          .image {
 | 
			
		||||
            display flex
 | 
			
		||||
            flex-flow row
 | 
			
		||||
            margin-right 10px
 | 
			
		||||
            position relative
 | 
			
		||||
 | 
			
		||||
          .icon {
 | 
			
		||||
            .el-image {
 | 
			
		||||
              width 40px
 | 
			
		||||
              height 40px
 | 
			
		||||
              border 1px solid #e3e3e3
 | 
			
		||||
              border-radius 10px
 | 
			
		||||
              margin-bottom 10px
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          .body {
 | 
			
		||||
            margin-left 8px
 | 
			
		||||
            font-size 14px
 | 
			
		||||
            .title {
 | 
			
		||||
              font-weight bold
 | 
			
		||||
              line-height 24px
 | 
			
		||||
              color #0D0D0D
 | 
			
		||||
            }
 | 
			
		||||
            .info {
 | 
			
		||||
              color #B4B4B4
 | 
			
		||||
          .item {
 | 
			
		||||
            display flex
 | 
			
		||||
            flex-flow row
 | 
			
		||||
            border-radius 10px
 | 
			
		||||
            background-color #ffffff
 | 
			
		||||
            border 1px solid #e3e3e3
 | 
			
		||||
            padding 6px
 | 
			
		||||
            margin-bottom 10px
 | 
			
		||||
 | 
			
		||||
              span {
 | 
			
		||||
                margin-right 10px
 | 
			
		||||
            .icon {
 | 
			
		||||
              .el-image {
 | 
			
		||||
                width 40px
 | 
			
		||||
                height 40px
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            .body {
 | 
			
		||||
              margin-left 8px
 | 
			
		||||
              font-size 14px
 | 
			
		||||
              .title {
 | 
			
		||||
                font-weight bold
 | 
			
		||||
                line-height 24px
 | 
			
		||||
                color #0D0D0D
 | 
			
		||||
              }
 | 
			
		||||
              .info {
 | 
			
		||||
                color #B4B4B4
 | 
			
		||||
 | 
			
		||||
                span {
 | 
			
		||||
                  margin-right 10px
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .content {
 | 
			
		||||
        word-break break-word;
 | 
			
		||||
        padding: 6px 10px;
 | 
			
		||||
        color #374151;
 | 
			
		||||
        font-size: var(--content-font-size);
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        .content {
 | 
			
		||||
          word-break break-word;
 | 
			
		||||
          padding: 0;
 | 
			
		||||
          color #374151;
 | 
			
		||||
          font-size: var(--content-font-size);
 | 
			
		||||
          border-radius: 5px;
 | 
			
		||||
          overflow: auto;
 | 
			
		||||
 | 
			
		||||
        img {
 | 
			
		||||
          max-width: 600px;
 | 
			
		||||
          border-radius: 10px;
 | 
			
		||||
          margin 10px 0
 | 
			
		||||
          img {
 | 
			
		||||
            max-width: 600px;
 | 
			
		||||
            border-radius: 10px;
 | 
			
		||||
            margin 10px 0
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          p {
 | 
			
		||||
            line-height 1.5
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          p:last-child {
 | 
			
		||||
            margin-bottom: 0
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          p:first-child {
 | 
			
		||||
            margin-top 0
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        p {
 | 
			
		||||
          line-height 1.5
 | 
			
		||||
        }
 | 
			
		||||
        .bar {
 | 
			
		||||
          padding 10px 10px 10px 0;
 | 
			
		||||
 | 
			
		||||
        p:last-child {
 | 
			
		||||
          margin-bottom: 0
 | 
			
		||||
        }
 | 
			
		||||
          .bar-item {
 | 
			
		||||
            background-color #f7f7f8;
 | 
			
		||||
            color #888
 | 
			
		||||
            padding 3px 5px;
 | 
			
		||||
            margin-right 10px;
 | 
			
		||||
            border-radius 5px;
 | 
			
		||||
 | 
			
		||||
        p:first-child {
 | 
			
		||||
          margin-top 0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .bar {
 | 
			
		||||
        padding 10px;
 | 
			
		||||
 | 
			
		||||
        .bar-item {
 | 
			
		||||
          background-color #f7f7f8;
 | 
			
		||||
          color #888
 | 
			
		||||
          padding 3px 5px;
 | 
			
		||||
          margin-right 10px;
 | 
			
		||||
          border-radius 5px;
 | 
			
		||||
 | 
			
		||||
          .el-icon {
 | 
			
		||||
            position relative
 | 
			
		||||
            top 2px;
 | 
			
		||||
            .el-icon {
 | 
			
		||||
              position relative
 | 
			
		||||
              top 2px;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .chat-line-prompt-chat {
 | 
			
		||||
    background-color #ffffff;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    width 100%
 | 
			
		||||
    padding-bottom: 1.5rem;
 | 
			
		||||
    padding-top: 1.5rem;
 | 
			
		||||
 | 
			
		||||
    .chat-line-inner {
 | 
			
		||||
      display flex;
 | 
			
		||||
      width 100%;
 | 
			
		||||
      padding 0 25px;
 | 
			
		||||
 | 
			
		||||
      .chat-icon {
 | 
			
		||||
        margin-right 20px;
 | 
			
		||||
 | 
			
		||||
        img {
 | 
			
		||||
          width: 36px;
 | 
			
		||||
          height: 36px;
 | 
			
		||||
          border-radius: 50%;
 | 
			
		||||
          padding: 1px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .chat-item {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        .file-list-box {
 | 
			
		||||
          display flex
 | 
			
		||||
          flex-flow column
 | 
			
		||||
          .image {
 | 
			
		||||
            display flex
 | 
			
		||||
            flex-flow row
 | 
			
		||||
            margin-right 10px
 | 
			
		||||
            position relative
 | 
			
		||||
 | 
			
		||||
            .el-image {
 | 
			
		||||
              border 1px solid #e3e3e3
 | 
			
		||||
              border-radius 10px
 | 
			
		||||
              margin-bottom 10px
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          .item {
 | 
			
		||||
            display flex
 | 
			
		||||
            flex-flow row
 | 
			
		||||
            border-radius 10px
 | 
			
		||||
            background-color #ffffff
 | 
			
		||||
            border 1px solid #e3e3e3
 | 
			
		||||
            padding 6px
 | 
			
		||||
            margin-bottom 10px
 | 
			
		||||
 | 
			
		||||
            .icon {
 | 
			
		||||
              .el-image {
 | 
			
		||||
                width 40px
 | 
			
		||||
                height 40px
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            .body {
 | 
			
		||||
              margin-left 8px
 | 
			
		||||
              font-size 14px
 | 
			
		||||
              .title {
 | 
			
		||||
                font-weight bold
 | 
			
		||||
                line-height 24px
 | 
			
		||||
                color #0D0D0D
 | 
			
		||||
              }
 | 
			
		||||
              .info {
 | 
			
		||||
                color #B4B4B4
 | 
			
		||||
 | 
			
		||||
                span {
 | 
			
		||||
                  margin-right 10px
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        .content-wrapper {
 | 
			
		||||
          display flex
 | 
			
		||||
          .content {
 | 
			
		||||
              word-break break-word;
 | 
			
		||||
              padding: 1rem
 | 
			
		||||
              color #222222;
 | 
			
		||||
              font-size: var(--content-font-size);
 | 
			
		||||
              overflow: auto;
 | 
			
		||||
              background-color #98e165
 | 
			
		||||
              border-radius: 0 10px 10px 10px;
 | 
			
		||||
 | 
			
		||||
              img {
 | 
			
		||||
                max-width: 600px;
 | 
			
		||||
                border-radius: 10px;
 | 
			
		||||
                margin 10px 0
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              p {
 | 
			
		||||
                line-height 1.5
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              p:last-child {
 | 
			
		||||
                margin-bottom: 0
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              p:first-child {
 | 
			
		||||
                margin-top 0
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        .bar {
 | 
			
		||||
          padding 10px 10px 10px 0;
 | 
			
		||||
 | 
			
		||||
          .bar-item {
 | 
			
		||||
            color #888
 | 
			
		||||
            padding 3px 5px;
 | 
			
		||||
            margin-right 10px;
 | 
			
		||||
            border-radius 5px;
 | 
			
		||||
 | 
			
		||||
            .el-icon {
 | 
			
		||||
              position relative
 | 
			
		||||
              top 2px;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="chat-line chat-line-reply">
 | 
			
		||||
  <div class="chat-line chat-line-reply-list" v-if="listStyle === 'list'">
 | 
			
		||||
    <div class="chat-line-inner">
 | 
			
		||||
      <div class="chat-icon">
 | 
			
		||||
        <img :src="data.icon" alt="ChatGPT">
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
        <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">tokens: {{ data.tokens }}</span>
 | 
			
		||||
          <span class="bar-item">
 | 
			
		||||
              <el-tooltip
 | 
			
		||||
                  class="box-item"
 | 
			
		||||
@@ -61,6 +61,59 @@
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="chat-line chat-line-reply-chat" v-else>
 | 
			
		||||
    <div class="chat-line-inner">
 | 
			
		||||
      <div class="chat-icon">
 | 
			
		||||
        <img :src="data.icon" alt="ChatGPT">
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="chat-item">
 | 
			
		||||
        <div class="content-wrapper">
 | 
			
		||||
          <div class="content" v-html="data.content"></div>
 | 
			
		||||
        </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: {{ data.tokens }}</span>
 | 
			
		||||
          <span class="bar-item bg">
 | 
			
		||||
              <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 bg" @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 bg" @click="synthesis(data.orgContent)">
 | 
			
		||||
            <el-tooltip
 | 
			
		||||
                class="box-item"
 | 
			
		||||
                effect="dark"
 | 
			
		||||
                content="生成语音朗读"
 | 
			
		||||
                placement="bottom"
 | 
			
		||||
            >
 | 
			
		||||
              <i class="iconfont icon-speaker"></i>
 | 
			
		||||
            </el-tooltip>
 | 
			
		||||
          </span>
 | 
			
		||||
          </span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
@@ -71,12 +124,22 @@ import {dateFormat} from "@/utils/libs";
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  data: {
 | 
			
		||||
    type: Object,
 | 
			
		||||
    default: {},
 | 
			
		||||
    default: {
 | 
			
		||||
      icon: "",
 | 
			
		||||
      content: "",
 | 
			
		||||
      created_at: "",
 | 
			
		||||
      tokens: 0,
 | 
			
		||||
      orgContent: ""
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  readOnly: {
 | 
			
		||||
    type: Boolean,
 | 
			
		||||
    default: false
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  listStyle: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: 'list',
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const emits = defineEmits(['regen']);
 | 
			
		||||
@@ -98,8 +161,8 @@ const reGenerate = (prompt) => {
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
@import '@/assets/css/markdown/vue.css';
 | 
			
		||||
.common-layout {
 | 
			
		||||
  .chat-line-reply {
 | 
			
		||||
.chat-page,.chat-export {
 | 
			
		||||
  .chat-line-reply-list {
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    background-color: rgba(247, 247, 248, 1);
 | 
			
		||||
    width 100%
 | 
			
		||||
@@ -127,13 +190,13 @@ const reGenerate = (prompt) => {
 | 
			
		||||
      .chat-item {
 | 
			
		||||
        width 100%
 | 
			
		||||
        position: relative;
 | 
			
		||||
        padding: 0 0 0 5px;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        .content {
 | 
			
		||||
          min-height 20px;
 | 
			
		||||
          word-break break-word;
 | 
			
		||||
          padding: 0 10px;
 | 
			
		||||
          padding: 0
 | 
			
		||||
          color #374151;
 | 
			
		||||
          font-size: var(--content-font-size);
 | 
			
		||||
          border-radius: 5px;
 | 
			
		||||
@@ -237,7 +300,7 @@ const reGenerate = (prompt) => {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        .bar {
 | 
			
		||||
          padding 10px;
 | 
			
		||||
          padding 10px 10px 10px 0;
 | 
			
		||||
 | 
			
		||||
          .bar-item {
 | 
			
		||||
            background-color #e7e7e8;
 | 
			
		||||
@@ -273,6 +336,186 @@ const reGenerate = (prompt) => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .chat-line-reply-chat {
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    width 100%
 | 
			
		||||
    padding-bottom: 1.5rem;
 | 
			
		||||
    padding-top: 1.5rem;
 | 
			
		||||
 | 
			
		||||
    .chat-line-inner {
 | 
			
		||||
      display flex;
 | 
			
		||||
      padding 0 25px;
 | 
			
		||||
      width 100%
 | 
			
		||||
      flex-flow row-reverse
 | 
			
		||||
 | 
			
		||||
      .chat-icon {
 | 
			
		||||
        margin-left 20px;
 | 
			
		||||
 | 
			
		||||
        img {
 | 
			
		||||
          width: 36px;
 | 
			
		||||
          height: 36px;
 | 
			
		||||
          border-radius: 50%
 | 
			
		||||
          padding: 1px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .chat-item {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        .content-wrapper {
 | 
			
		||||
          display flex
 | 
			
		||||
          flex-flow row-reverse
 | 
			
		||||
          .content {
 | 
			
		||||
            min-height 20px;
 | 
			
		||||
            word-break break-word;
 | 
			
		||||
            padding: 1rem
 | 
			
		||||
            color #374151;
 | 
			
		||||
            font-size: var(--content-font-size);
 | 
			
		||||
            overflow auto;
 | 
			
		||||
            background-color #F5F5F5
 | 
			
		||||
            border-radius: 10px 0 10px 10px;
 | 
			
		||||
 | 
			
		||||
            img {
 | 
			
		||||
              max-width: 600px;
 | 
			
		||||
              border-radius: 10px;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            p {
 | 
			
		||||
              line-height 1.5
 | 
			
		||||
 | 
			
		||||
              code {
 | 
			
		||||
                color #374151
 | 
			
		||||
                background-color #e7e7e8
 | 
			
		||||
                padding 0 3px;
 | 
			
		||||
                border-radius 5px;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            p:last-child {
 | 
			
		||||
              margin-bottom: 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            p:first-child {
 | 
			
		||||
              margin-top 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .code-container {
 | 
			
		||||
              position relative
 | 
			
		||||
              display flex
 | 
			
		||||
 | 
			
		||||
              .hljs {
 | 
			
		||||
                border-radius 10px
 | 
			
		||||
                width 100%
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              .copy-code-btn {
 | 
			
		||||
                position: absolute;
 | 
			
		||||
                right 10px
 | 
			
		||||
                top 10px
 | 
			
		||||
                cursor pointer
 | 
			
		||||
                font-size 12px
 | 
			
		||||
                color #c1c1c1
 | 
			
		||||
 | 
			
		||||
                &:hover {
 | 
			
		||||
                  color #20a0ff
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .lang-name {
 | 
			
		||||
              position absolute;
 | 
			
		||||
              right 10px
 | 
			
		||||
              bottom 20px
 | 
			
		||||
              padding 2px 6px 4px 6px
 | 
			
		||||
              background-color #444444
 | 
			
		||||
              border-radius 10px
 | 
			
		||||
              color #00e0e0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // 设置表格边框
 | 
			
		||||
 | 
			
		||||
            table {
 | 
			
		||||
              width 100%
 | 
			
		||||
              margin-bottom 1rem
 | 
			
		||||
              color #212529
 | 
			
		||||
              border-collapse collapse;
 | 
			
		||||
              border 1px solid #dee2e6;
 | 
			
		||||
              background-color #ffffff
 | 
			
		||||
 | 
			
		||||
              thead {
 | 
			
		||||
                th {
 | 
			
		||||
                  border 1px solid #dee2e6
 | 
			
		||||
                  vertical-align: bottom
 | 
			
		||||
                  border-bottom: 2px solid #dee2e6
 | 
			
		||||
                  padding 10px
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              td {
 | 
			
		||||
                border 1px solid #dee2e6
 | 
			
		||||
                padding 10px
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 代码快
 | 
			
		||||
 | 
			
		||||
            blockquote {
 | 
			
		||||
              margin 0
 | 
			
		||||
              background-color: #ebfffe;
 | 
			
		||||
              padding: 0.8rem 1.5rem;
 | 
			
		||||
              border-left: 0.5rem solid;
 | 
			
		||||
              border-color: #026863;
 | 
			
		||||
              color: #2c3e50;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .bar {
 | 
			
		||||
          padding 10px 10px 10px 0;
 | 
			
		||||
 | 
			
		||||
          .bar-item {
 | 
			
		||||
            color #888
 | 
			
		||||
            padding 3px 5px;
 | 
			
		||||
            margin-right 10px;
 | 
			
		||||
            border-radius 5px;
 | 
			
		||||
 | 
			
		||||
            .el-icon {
 | 
			
		||||
              position relative
 | 
			
		||||
              top 2px;
 | 
			
		||||
              cursor pointer
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          .bar-item.bg {
 | 
			
		||||
            background-color #e7e7e8
 | 
			
		||||
            cursor pointer
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          .el-button {
 | 
			
		||||
            height 20px
 | 
			
		||||
            padding 5px 2px;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .tool-box {
 | 
			
		||||
        font-size 16px;
 | 
			
		||||
 | 
			
		||||
        .el-button {
 | 
			
		||||
          height 20px
 | 
			
		||||
          padding 5px 2px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								web/src/components/ChatSetting.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								web/src/components/ChatSetting.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <el-dialog
 | 
			
		||||
      class="config-dialog"
 | 
			
		||||
      v-model="showDialog"
 | 
			
		||||
      :close-on-click-modal="true"
 | 
			
		||||
      :before-close="close"
 | 
			
		||||
      style="max-width: 600px"
 | 
			
		||||
      title="聊天配置"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="chat-setting">
 | 
			
		||||
      <el-form :model="data" label-width="100px" label-position="left">
 | 
			
		||||
        <el-form-item label="聊天样式:">
 | 
			
		||||
          <el-radio-group v-model="data.style" @change="(val) => {store.setChatListStyle(val)}">
 | 
			
		||||
            <el-radio value="list">列表样式</el-radio>
 | 
			
		||||
            <el-radio value="chat">对话样式</el-radio>
 | 
			
		||||
          </el-radio-group>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
 | 
			
		||||
      </el-form>
 | 
			
		||||
    </div>
 | 
			
		||||
  </el-dialog>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {computed, ref} from "vue"
 | 
			
		||||
import {useSharedStore} from "@/store/sharedata";
 | 
			
		||||
const store = useSharedStore();
 | 
			
		||||
 | 
			
		||||
const data = ref({
 | 
			
		||||
  style: store.chatListStyle,
 | 
			
		||||
})
 | 
			
		||||
// eslint-disable-next-line no-undef
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  show: Boolean,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const showDialog = computed(() => {
 | 
			
		||||
  return props.show
 | 
			
		||||
})
 | 
			
		||||
const emits = defineEmits(['hide']);
 | 
			
		||||
const close = function () {
 | 
			
		||||
  emits('hide', false);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.chat-setting {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,13 +1,19 @@
 | 
			
		||||
import {defineStore} from 'pinia';
 | 
			
		||||
import Storage from 'good-storage'
 | 
			
		||||
 | 
			
		||||
export const useSharedStore = defineStore('shared', {
 | 
			
		||||
    state: () => ({
 | 
			
		||||
        showLoginDialog: false
 | 
			
		||||
        showLoginDialog: false,
 | 
			
		||||
        chatListStyle: Storage.get("chat_list_style","chat")
 | 
			
		||||
    }),
 | 
			
		||||
    getters: {},
 | 
			
		||||
    actions: {
 | 
			
		||||
        setShowLoginDialog(value) {
 | 
			
		||||
            this.showLoginDialog = value;
 | 
			
		||||
        },
 | 
			
		||||
        setChatListStyle(value) {
 | 
			
		||||
            this.chatListStyle = value;
 | 
			
		||||
            Storage.set("chat_list_style", value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -6,22 +6,14 @@
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div v-for="item in chatData" :key="item.id">
 | 
			
		||||
        <chat-prompt
 | 
			
		||||
            v-if="item.type==='prompt'"
 | 
			
		||||
            :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'"
 | 
			
		||||
                    :data="item" :read-only="true"/>
 | 
			
		||||
        <chat-prompt v-if="item.type==='prompt'" :data="item" list-style="list"/>
 | 
			
		||||
        <chat-reply v-else-if="item.type==='reply'" :data="item" :read-only="true" list-style="list"/>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div><!-- end chat box -->
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup>
 | 
			
		||||
 | 
			
		||||
import {dateFormat} from "@/utils/libs";
 | 
			
		||||
import ChatReply from "@/components/ChatReply.vue";
 | 
			
		||||
import ChatPrompt from "@/components/ChatPrompt.vue";
 | 
			
		||||
import {nextTick, onMounted, ref} from "vue";
 | 
			
		||||
@@ -98,7 +90,7 @@ onMounted(() => {
 | 
			
		||||
  padding 0 20px
 | 
			
		||||
 | 
			
		||||
  .chat-box {
 | 
			
		||||
    width 800px;
 | 
			
		||||
    width 100%;
 | 
			
		||||
    // 变量定义
 | 
			
		||||
    --content-font-size: 16px;
 | 
			
		||||
    --content-color: #c1c1c1;
 | 
			
		||||
@@ -110,57 +102,13 @@ onMounted(() => {
 | 
			
		||||
      text-align center
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    .chat-line-prompt {
 | 
			
		||||
    .chat-line {
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      display: flex;
 | 
			
		||||
      align-items: flex-start;
 | 
			
		||||
      align-items: center;
 | 
			
		||||
 | 
			
		||||
      .chat-line-inner {
 | 
			
		||||
        .chat-icon {
 | 
			
		||||
          margin-right: 0
 | 
			
		||||
        }
 | 
			
		||||
        .content {
 | 
			
		||||
          padding-top: 0
 | 
			
		||||
          font-size 16px;
 | 
			
		||||
 | 
			
		||||
          p:first-child {
 | 
			
		||||
            margin-top 0
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .chat-line-reply {
 | 
			
		||||
      padding-top: 1.5rem;
 | 
			
		||||
 | 
			
		||||
      .chat-line-inner {
 | 
			
		||||
        display flex
 | 
			
		||||
 | 
			
		||||
        .bar-item {
 | 
			
		||||
          background-color: #f7f7f8;
 | 
			
		||||
          color: #888;
 | 
			
		||||
          padding: 3px 5px;
 | 
			
		||||
          margin-right: 10px;
 | 
			
		||||
          border-radius: 5px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .chat-icon {
 | 
			
		||||
          margin-right: 20px
 | 
			
		||||
 | 
			
		||||
          img {
 | 
			
		||||
            width 30px
 | 
			
		||||
            height 30px
 | 
			
		||||
            border-radius: 10px;
 | 
			
		||||
            padding: 1px
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .chat-item {
 | 
			
		||||
          img {
 | 
			
		||||
            max-width 90%
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        max-width 800px
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="common-layout">
 | 
			
		||||
  <div class="chat-page">
 | 
			
		||||
    <el-container>
 | 
			
		||||
      <el-aside>
 | 
			
		||||
        <div class="chat-list">
 | 
			
		||||
@@ -99,6 +99,12 @@
 | 
			
		||||
                </el-tag>
 | 
			
		||||
              </el-option>
 | 
			
		||||
            </el-select>
 | 
			
		||||
 | 
			
		||||
            <span class="setting" @click="showChatSetting = true">
 | 
			
		||||
                    <el-tooltip class="box-item" effect="dark" content="对话设置">
 | 
			
		||||
                      <i class="iconfont icon-config"></i>
 | 
			
		||||
                    </el-tooltip>
 | 
			
		||||
                  </span>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div>
 | 
			
		||||
@@ -109,13 +115,8 @@
 | 
			
		||||
                </div>
 | 
			
		||||
                <div v-for="item in chatData" :key="item.id" v-else>
 | 
			
		||||
                  <chat-prompt
 | 
			
		||||
                      v-if="item.type==='prompt'"
 | 
			
		||||
                      :icon="item.icon"
 | 
			
		||||
                      :created-at="dateFormat(item['created_at'])"
 | 
			
		||||
                      :tokens="item['tokens']"
 | 
			
		||||
                      :model="getModelValue(modelID)"
 | 
			
		||||
                      :content="item.content"/>
 | 
			
		||||
                  <chat-reply v-else-if="item.type==='reply'" :data="item" @regen="reGenerate" :read-only="false"/>
 | 
			
		||||
                      v-if="item.type==='prompt'" :data="item" :list-style="listStyle"/>
 | 
			
		||||
                  <chat-reply v-else-if="item.type==='reply'" :data="item" @regen="reGenerate" :read-only="false" :list-style="listStyle"/>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div><!-- end chat box -->
 | 
			
		||||
 | 
			
		||||
@@ -187,21 +188,21 @@
 | 
			
		||||
        </p>
 | 
			
		||||
      </div>
 | 
			
		||||
    </el-dialog>
 | 
			
		||||
 | 
			
		||||
    <ChatSetting :show="showChatSetting" @hide="showChatSetting = false"/>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</template>
 | 
			
		||||
<script setup>
 | 
			
		||||
import {nextTick, onMounted, onUnmounted, ref} 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, More, Plus, Promotion, Search, Share, VideoPause} from '@element-plus/icons-vue'
 | 
			
		||||
import 'highlight.js/styles/a11y-dark.css'
 | 
			
		||||
import {
 | 
			
		||||
  dateFormat,
 | 
			
		||||
  isMobile,
 | 
			
		||||
  processContent,
 | 
			
		||||
  processPrompt,
 | 
			
		||||
  randString,
 | 
			
		||||
  removeArrayItem,
 | 
			
		||||
  UUID
 | 
			
		||||
@@ -217,6 +218,7 @@ import Welcome from "@/components/Welcome.vue";
 | 
			
		||||
import {useSharedStore} from "@/store/sharedata";
 | 
			
		||||
import FileSelect from "@/components/FileSelect.vue";
 | 
			
		||||
import FileList from "@/components/FileList.vue";
 | 
			
		||||
import ChatSetting from "@/components/ChatSetting.vue";
 | 
			
		||||
 | 
			
		||||
const title = ref('ChatGPT-智能助手');
 | 
			
		||||
const models = ref([])
 | 
			
		||||
@@ -243,6 +245,12 @@ const notice = ref("")
 | 
			
		||||
const noticeKey = ref("SYSTEM_NOTICE")
 | 
			
		||||
const store = useSharedStore();
 | 
			
		||||
const row = ref(1)
 | 
			
		||||
const showChatSetting = ref(false)
 | 
			
		||||
const listStyle = ref(store.chatListStyle)
 | 
			
		||||
watch(() => store.chatListStyle, (newValue) => {
 | 
			
		||||
  listStyle.value = newValue
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if (isMobile()) {
 | 
			
		||||
  router.replace("/mobile/chat")
 | 
			
		||||
@@ -757,6 +765,7 @@ const sendMessage = function () {
 | 
			
		||||
    id: randString(32),
 | 
			
		||||
    icon: loginUser.value.avatar,
 | 
			
		||||
    content: content,
 | 
			
		||||
    model: getModelValue(modelID.value),
 | 
			
		||||
    created_at: new Date().getTime() / 1000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@ 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";
 | 
			
		||||
import ConfigDialog from "@/components/UserInfoDialog.vue";
 | 
			
		||||
import {showMessageError} from "@/utils/dialog";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
 
 | 
			
		||||
@@ -153,7 +153,7 @@
 | 
			
		||||
        v-model="showChatItemDialog"
 | 
			
		||||
        title="对话详情"
 | 
			
		||||
    >
 | 
			
		||||
      <div class="chat-box common-layout">
 | 
			
		||||
      <div class="chat-box chat-page">
 | 
			
		||||
        <div v-for="item in messages" :key="item.id">
 | 
			
		||||
          <chat-prompt
 | 
			
		||||
              v-if="item.type==='prompt'"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user