mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	refactor: refactor mobile pages for the chat model updating
This commit is contained in:
		@@ -76,7 +76,7 @@ func (h *UserHandler) Register(c *gin.Context) {
 | 
			
		||||
	var item model.User
 | 
			
		||||
	res := h.db.Where("mobile = ?", data.Mobile).First(&item)
 | 
			
		||||
	if res.RowsAffected > 0 {
 | 
			
		||||
		resp.ERROR(c, "该手机号码以及被注册,请更换其他手机号")
 | 
			
		||||
		resp.ERROR(c, "该手机号码已经被注册,请更换其他手机号")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ const items = [
 | 
			
		||||
  {
 | 
			
		||||
    icon: 'api-key',
 | 
			
		||||
    index: '/admin/apikey',
 | 
			
		||||
    title: 'API-KEY 管理',
 | 
			
		||||
    title: 'API-KEY',
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    icon: 'model',
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,7 @@
 | 
			
		||||
          placeholder="请输入短信验证码"
 | 
			
		||||
      >
 | 
			
		||||
        <template #button>
 | 
			
		||||
          <!--          <van-button size="small" type="primary">发送验证码</van-button>-->
 | 
			
		||||
          <send-msg size="small" :mobile="form.mobile"/>
 | 
			
		||||
          <send-msg-mobile size="small" :mobile="form.mobile"/>
 | 
			
		||||
        </template>
 | 
			
		||||
      </van-field>
 | 
			
		||||
    </van-cell-group>
 | 
			
		||||
@@ -28,11 +27,10 @@
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {computed, ref} from "vue";
 | 
			
		||||
import SendMsg from "@/components/mobile/SendMsg.vue";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import {httpPost} from "@/utils/http";
 | 
			
		||||
import {validateMobile} from "@/utils/validate";
 | 
			
		||||
import {showNotify} from "vant";
 | 
			
		||||
import SendMsgMobile from "@/components/mobile/SendMsgMobile.vue";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  show: Boolean,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										265
									
								
								web/src/components/mobile/ChatMidJourney.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								web/src/components/mobile/ChatMidJourney.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,265 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="mobile-message-mj">
 | 
			
		||||
    <div class="chat-icon">
 | 
			
		||||
      <van-image :src="icon"/>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="chat-item">
 | 
			
		||||
      <div class="triangle"></div>
 | 
			
		||||
      <div class="content-box">
 | 
			
		||||
        <div class="content">
 | 
			
		||||
          <div class="content-inner">
 | 
			
		||||
            <div class="text" v-html="data.html"></div>
 | 
			
		||||
            <div class="images" v-if="data.image?.url !== ''">
 | 
			
		||||
              <el-image :src="data.image?.url"
 | 
			
		||||
                        :zoom-rate="1.2"
 | 
			
		||||
                        :preview-src-list="[data.image?.url]"
 | 
			
		||||
                        fit="cover"
 | 
			
		||||
                        :initial-index="0" loading="lazy">
 | 
			
		||||
                <template #placeholder>
 | 
			
		||||
                  <div class="image-slot"
 | 
			
		||||
                       :style="{height: height+'px', lineHeight:height+'px'}">
 | 
			
		||||
                    正在加载图片<span class="dot">...</span></div>
 | 
			
		||||
                </template>
 | 
			
		||||
 | 
			
		||||
                <template #error>
 | 
			
		||||
                  <div class="image-slot">
 | 
			
		||||
                    <el-icon>
 | 
			
		||||
                      <Picture/>
 | 
			
		||||
                    </el-icon>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </template>
 | 
			
		||||
              </el-image>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="opt" v-if="data.showOpt &&data.image?.hash !== ''">
 | 
			
		||||
            <div class="opt-line">
 | 
			
		||||
              <ul>
 | 
			
		||||
                <li><a @click="upscale(1)">U1</a></li>
 | 
			
		||||
                <li><a @click="upscale(2)">U2</a></li>
 | 
			
		||||
                <li><a @click="upscale(3)">U3</a></li>
 | 
			
		||||
                <li><a @click="upscale(4)">U4</a></li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="opt-line">
 | 
			
		||||
              <ul>
 | 
			
		||||
                <li><a @click="variation(1)">V1</a></li>
 | 
			
		||||
                <li><a @click="variation(2)">V2</a></li>
 | 
			
		||||
                <li><a @click="variation(3)">V3</a></li>
 | 
			
		||||
                <li><a @click="variation(4)">V4</a></li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {ref, watch} from "vue";
 | 
			
		||||
import {Picture} from "@element-plus/icons-vue";
 | 
			
		||||
import {httpPost} from "@/utils/http";
 | 
			
		||||
import {getSessionId} from "@/store/session";
 | 
			
		||||
import {showNotify} from "vant";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  content: Object,
 | 
			
		||||
  icon: String,
 | 
			
		||||
  createdAt: String
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const data = ref(props.content)
 | 
			
		||||
const cacheKey = "img_placeholder_height"
 | 
			
		||||
const item = localStorage.getItem(cacheKey);
 | 
			
		||||
const loading = ref(false)
 | 
			
		||||
const height = ref(0)
 | 
			
		||||
if (item) {
 | 
			
		||||
  height.value = parseInt(item)
 | 
			
		||||
}
 | 
			
		||||
if (data.value["image"]?.width > 0) {
 | 
			
		||||
  height.value = 350 * data.value["image"]?.height / data.value["image"]?.width
 | 
			
		||||
  localStorage.setItem(cacheKey, height.value)
 | 
			
		||||
}
 | 
			
		||||
data.value["showOpt"] = data.value["content"]?.indexOf("- Image #") === -1;
 | 
			
		||||
// console.log(data.value)
 | 
			
		||||
 | 
			
		||||
watch(() => props.content, (newVal) => {
 | 
			
		||||
  data.value = newVal;
 | 
			
		||||
});
 | 
			
		||||
const emits = defineEmits(['disable-input', 'disable-input']);
 | 
			
		||||
const upscale = (index) => {
 | 
			
		||||
  send('/api/mj/upscale', index)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const variation = (index) => {
 | 
			
		||||
  send('/api/mj/variation', index)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const send = (url, index) => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  emits('disable-input')
 | 
			
		||||
  httpPost(url, {
 | 
			
		||||
    index: index,
 | 
			
		||||
    message_id: data.value?.["message_id"],
 | 
			
		||||
    message_hash: data.value?.["image"]?.hash,
 | 
			
		||||
    session_id: getSessionId(),
 | 
			
		||||
    key: data.value?.["key"],
 | 
			
		||||
    prompt: data.value?.["prompt"],
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    showNotify({type: "success", message: "任务推送成功,请耐心等待任务执行..."})
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showNotify({type: "danger", message: "任务推送失败:" + e.message})
 | 
			
		||||
    emits('disable-input')
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
.mobile-message-mj {
 | 
			
		||||
  display flex
 | 
			
		||||
  justify-content: flex-start;
 | 
			
		||||
 | 
			
		||||
  .chat-icon {
 | 
			
		||||
    margin-right 5px
 | 
			
		||||
 | 
			
		||||
    .van-image {
 | 
			
		||||
      width 32px
 | 
			
		||||
 | 
			
		||||
      img {
 | 
			
		||||
        border-radius 5px
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .chat-item {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    padding: 0 0 0 5px;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
    .triangle {
 | 
			
		||||
      width: 0;
 | 
			
		||||
      height: 0;
 | 
			
		||||
      border-top: 5px solid transparent;
 | 
			
		||||
      border-bottom: 5px solid transparent;
 | 
			
		||||
      border-right: 5px solid #fff;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      top: 13px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .content-box {
 | 
			
		||||
 | 
			
		||||
      display flex
 | 
			
		||||
      flex-direction row
 | 
			
		||||
 | 
			
		||||
      .content {
 | 
			
		||||
        text-align left
 | 
			
		||||
        width 100%
 | 
			
		||||
        overflow-x auto
 | 
			
		||||
        min-height 20px;
 | 
			
		||||
        word-break break-word;
 | 
			
		||||
        padding: 5px 10px;
 | 
			
		||||
        color #444444
 | 
			
		||||
        background-color: #ffffff;
 | 
			
		||||
        font-size: 16px
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
 | 
			
		||||
        .content-inner {
 | 
			
		||||
          word-break break-word;
 | 
			
		||||
          padding: 6px 10px;
 | 
			
		||||
          color #374151;
 | 
			
		||||
          font-size: var(--content-font-size);
 | 
			
		||||
          border-radius: 5px;
 | 
			
		||||
          overflow: auto;
 | 
			
		||||
 | 
			
		||||
          .text {
 | 
			
		||||
            p:first-child {
 | 
			
		||||
              margin-top 0
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          .images {
 | 
			
		||||
            max-width 350px;
 | 
			
		||||
 | 
			
		||||
            .el-image {
 | 
			
		||||
              border-radius 10px;
 | 
			
		||||
 | 
			
		||||
              .image-slot {
 | 
			
		||||
                color #c1c1c1
 | 
			
		||||
                width 350px
 | 
			
		||||
                text-align center
 | 
			
		||||
                border-radius 10px;
 | 
			
		||||
                border 1px solid #e1e1e1
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .opt {
 | 
			
		||||
          .opt-line {
 | 
			
		||||
            margin 6px 0
 | 
			
		||||
 | 
			
		||||
            ul {
 | 
			
		||||
              display flex
 | 
			
		||||
              flex-flow row
 | 
			
		||||
              padding-left 10px
 | 
			
		||||
 | 
			
		||||
              li {
 | 
			
		||||
                margin-right 10px
 | 
			
		||||
 | 
			
		||||
                a {
 | 
			
		||||
                  padding 3px 0
 | 
			
		||||
                  width 50px
 | 
			
		||||
                  text-align center
 | 
			
		||||
                  border-radius 5px
 | 
			
		||||
                  display block
 | 
			
		||||
                  cursor pointer
 | 
			
		||||
                  background-color #4E5058
 | 
			
		||||
                  color #ffffff
 | 
			
		||||
 | 
			
		||||
                  &:hover {
 | 
			
		||||
                    background-color #6D6F78
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.van-theme-dark {
 | 
			
		||||
  .mobile-message-reply {
 | 
			
		||||
    .chat-item {
 | 
			
		||||
      .triangle {
 | 
			
		||||
        border-right: 5px solid #404042;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .content-box {
 | 
			
		||||
        .content {
 | 
			
		||||
          color #c1c1c1
 | 
			
		||||
          background-color: #404042;
 | 
			
		||||
 | 
			
		||||
          p > code {
 | 
			
		||||
            color #c1c1c1
 | 
			
		||||
            background-color #2b2b2b
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <van-button size="small"
 | 
			
		||||
              type="primary"
 | 
			
		||||
              :disabled="!canSend"
 | 
			
		||||
              :size="props.size"
 | 
			
		||||
              @click="sendMsg">{{ btnText }}
 | 
			
		||||
  </van-button>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
// 发送短信验证码组件
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
import {validateMobile} from "@/utils/validate";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import {showNotify} from "vant";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  mobile: String,
 | 
			
		||||
  size: String,
 | 
			
		||||
});
 | 
			
		||||
const btnText = ref('发送验证码')
 | 
			
		||||
const canSend = ref(true)
 | 
			
		||||
 | 
			
		||||
const sendMsg = () => {
 | 
			
		||||
  if (!canSend.value) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!validateMobile(props.mobile)) {
 | 
			
		||||
    return showNotify({type: 'danger', message: '请输入合法的手机号'})
 | 
			
		||||
  }
 | 
			
		||||
  canSend.value = false
 | 
			
		||||
  httpGet('/api/verify/token').then(res => {
 | 
			
		||||
    httpPost('/api/verify/sms', {token: res.data, mobile: props.mobile}).then(() => {
 | 
			
		||||
      showNotify({type: 'success', message: '短信发送成功'})
 | 
			
		||||
      let time = 120
 | 
			
		||||
      btnText.value = time
 | 
			
		||||
      const handler = setInterval(() => {
 | 
			
		||||
        time = time - 1
 | 
			
		||||
        if (time <= 0) {
 | 
			
		||||
          clearInterval(handler)
 | 
			
		||||
          btnText.value = '重新发送'
 | 
			
		||||
          canSend.value = true
 | 
			
		||||
        } else {
 | 
			
		||||
          btnText.value = time
 | 
			
		||||
        }
 | 
			
		||||
      }, 1000)
 | 
			
		||||
    }).catch(e => {
 | 
			
		||||
      canSend.value = true
 | 
			
		||||
      showNotify({type: 'danger', message: '短信发送失败:' + e.message})
 | 
			
		||||
    })
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    console.log('failed to fetch token: ' + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										137
									
								
								web/src/components/mobile/SendMsgMobile.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								web/src/components/mobile/SendMsgMobile.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <el-container class="captcha-box">
 | 
			
		||||
    <el-button type="primary" class="send-btn" :size="props.size" :disabled="!canSend" @click="loadCaptcha" plain>
 | 
			
		||||
      {{ btnText }}
 | 
			
		||||
    </el-button>
 | 
			
		||||
 | 
			
		||||
    <el-dialog
 | 
			
		||||
        v-model="showCaptcha"
 | 
			
		||||
        :close-on-click-modal="true"
 | 
			
		||||
        :show-close="false"
 | 
			
		||||
        style="width:90%;max-width: 800px;"
 | 
			
		||||
    >
 | 
			
		||||
      <captcha-plus
 | 
			
		||||
          :max-dot="maxDot"
 | 
			
		||||
          :image-base64="imageBase64"
 | 
			
		||||
          :thumb-base64="thumbBase64"
 | 
			
		||||
          width="100%"
 | 
			
		||||
          @close="showCaptcha = false"
 | 
			
		||||
          @refresh="handleRequestCaptCode"
 | 
			
		||||
          @confirm="handleConfirm"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
    </el-dialog>
 | 
			
		||||
  </el-container>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
// 发送短信验证码组件
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
import lodash from 'lodash'
 | 
			
		||||
import {validateMobile} from "@/utils/validate";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import CaptchaPlus from "@/components/CaptchaPlus.vue";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  mobile: String,
 | 
			
		||||
  size: String,
 | 
			
		||||
});
 | 
			
		||||
const btnText = ref('发送验证码')
 | 
			
		||||
const canSend = ref(true)
 | 
			
		||||
const showCaptcha = ref(false)
 | 
			
		||||
const maxDot = ref(5)
 | 
			
		||||
const imageBase64 = ref('')
 | 
			
		||||
const thumbBase64 = ref('')
 | 
			
		||||
const captKey = ref('')
 | 
			
		||||
const dots = ref(null)
 | 
			
		||||
 | 
			
		||||
const handleRequestCaptCode = () => {
 | 
			
		||||
 | 
			
		||||
  httpGet('/api/captcha/get').then(res => {
 | 
			
		||||
    const data = res.data
 | 
			
		||||
    imageBase64.value = data.image
 | 
			
		||||
    thumbBase64.value = data.thumb
 | 
			
		||||
    captKey.value = data.key
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    ElMessage.error('获取人机验证数据失败:' + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const handleConfirm = (dots) => {
 | 
			
		||||
  if (lodash.size(dots) <= 0) {
 | 
			
		||||
    return ElMessage.error('请进行人机验证再操作')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let dotArr = []
 | 
			
		||||
  lodash.forEach(dots, (dot) => {
 | 
			
		||||
    dotArr.push(dot.x, dot.y)
 | 
			
		||||
  })
 | 
			
		||||
  dots.value = dotArr.join(',')
 | 
			
		||||
  httpPost('/api/captcha/check', {
 | 
			
		||||
    dots: dots.value,
 | 
			
		||||
    key: captKey.value
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    showCaptcha.value = false
 | 
			
		||||
    sendMsg()
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    ElMessage.error('人机验证失败')
 | 
			
		||||
    handleRequestCaptCode()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const loadCaptcha = () => {
 | 
			
		||||
  if (!validateMobile(props.mobile)) {
 | 
			
		||||
    return ElMessage.error("请输入合法的手机号")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  showCaptcha.value = true
 | 
			
		||||
  handleRequestCaptCode() // 每次点开都刷新验证码
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const sendMsg = () => {
 | 
			
		||||
  if (!canSend.value) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  canSend.value = false
 | 
			
		||||
  httpPost('/api/sms/code', {mobile: props.mobile, key: captKey.value, dots: dots.value}).then(() => {
 | 
			
		||||
    ElMessage.success('短信发送成功')
 | 
			
		||||
    let time = 120
 | 
			
		||||
    btnText.value = time
 | 
			
		||||
    const handler = setInterval(() => {
 | 
			
		||||
      time = time - 1
 | 
			
		||||
      if (time <= 0) {
 | 
			
		||||
        clearInterval(handler)
 | 
			
		||||
        btnText.value = '重新发送'
 | 
			
		||||
        canSend.value = true
 | 
			
		||||
      } else {
 | 
			
		||||
        btnText.value = time
 | 
			
		||||
      }
 | 
			
		||||
    }, 1000)
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    canSend.value = true
 | 
			
		||||
    ElMessage.error('短信发送失败:' + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
 | 
			
		||||
.captcha-box {
 | 
			
		||||
  .send-btn {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .el-dialog {
 | 
			
		||||
    .el-dialog__header {
 | 
			
		||||
      padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .el-dialog__body {
 | 
			
		||||
      //padding 0
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -55,12 +55,22 @@ import {ElMessage} from "element-plus";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import FooterBar from "@/components/FooterBar.vue";
 | 
			
		||||
import {isMobile} from "@/utils/libs";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const title = ref('ChatGPT-PLUS 用户登录');
 | 
			
		||||
const username = ref(process.env.VUE_APP_USER);
 | 
			
		||||
const password = ref(process.env.VUE_APP_PASS);
 | 
			
		||||
 | 
			
		||||
checkSession().then(() => {
 | 
			
		||||
  if (isMobile()) {
 | 
			
		||||
    router.replace('mobile')
 | 
			
		||||
  } else {
 | 
			
		||||
    router.replace('chat')
 | 
			
		||||
  }
 | 
			
		||||
}).catch(() => {
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  document.addEventListener('keyup', (e) => {
 | 
			
		||||
    if (e.key === 'Enter') {
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,8 @@
 | 
			
		||||
                    </el-input>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                  <el-col :span="12">
 | 
			
		||||
                    <send-msg size="large" :mobile="formData.mobile"/>
 | 
			
		||||
                    <send-msg-mobile size="large" :mobile="formData.mobile" v-if="isMobile()"/>
 | 
			
		||||
                    <send-msg size="large" :mobile="formData.mobile" v-else/>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                </el-row>
 | 
			
		||||
              </div>
 | 
			
		||||
@@ -107,6 +108,8 @@ import {useRouter} from "vue-router";
 | 
			
		||||
import FooterBar from "@/components/FooterBar.vue";
 | 
			
		||||
import SendMsg from "@/components/SendMsg.vue";
 | 
			
		||||
import {validateMobile} from "@/utils/validate";
 | 
			
		||||
import {isMobile} from "@/utils/libs";
 | 
			
		||||
import SendMsgMobile from "@/components/mobile/SendMsgMobile.vue";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const title = ref('ChatGPT-PLUS 用户注册');
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,10 @@
 | 
			
		||||
      </van-picker>
 | 
			
		||||
    </van-popup>
 | 
			
		||||
 | 
			
		||||
    <van-dialog v-model:show="showEditChat" title="修改对话标题" show-cancel-button @confirm="saveTitle">
 | 
			
		||||
      <van-field v-model="tmpChatTitle" label="" placeholder="请输入对话标题"/>
 | 
			
		||||
    </van-dialog>
 | 
			
		||||
 | 
			
		||||
    <bind-mobile v-if="isLogin" :show="showBindMobileDialog" :mobile="loginUser.mobile"
 | 
			
		||||
                 @hide="showBindMobileDialog = false"/>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -71,8 +75,8 @@
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
import {httpGet} from "@/utils/http";
 | 
			
		||||
import {showConfirmDialog, showFailToast, showSuccessToast, showToast} from "vant";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import {showConfirmDialog, showFailToast, showSuccessToast} from "vant";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
import router from "@/router";
 | 
			
		||||
import {setChatConfig} from "@/store/chat";
 | 
			
		||||
@@ -93,6 +97,9 @@ const models = ref([])
 | 
			
		||||
const showPicker = ref(false)
 | 
			
		||||
const columns = ref([roles.value, models.value])
 | 
			
		||||
const showBindMobileDialog = ref(false)
 | 
			
		||||
const showEditChat = ref(false)
 | 
			
		||||
const item = ref({})
 | 
			
		||||
const tmpChatTitle = ref("")
 | 
			
		||||
 | 
			
		||||
checkSession().then((user) => {
 | 
			
		||||
  loginUser.value = user
 | 
			
		||||
@@ -115,17 +122,18 @@ checkSession().then((user) => {
 | 
			
		||||
    showFailToast("加载聊天角色失败")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // 加载系统配置
 | 
			
		||||
  httpGet('/api/admin/config/get?key=system').then(res => {
 | 
			
		||||
  // 加载模型
 | 
			
		||||
  httpGet('/api/model/list?enable=1').then(res => {
 | 
			
		||||
    if (res.data) {
 | 
			
		||||
      const items = res.data.models
 | 
			
		||||
      const items = res.data
 | 
			
		||||
      for (let i = 0; i < items.length; i++) {
 | 
			
		||||
        models.value.push({text: items[i].toUpperCase(), value: items[i]})
 | 
			
		||||
        models.value.push({text: items[i].name, value: items[i].id})
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    showFailToast("加载系统配置失败")
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast("加载模型失败: " + e.message)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
}).catch(() => {
 | 
			
		||||
  router.push("/login")
 | 
			
		||||
})
 | 
			
		||||
@@ -144,6 +152,15 @@ const onLoad = () => {
 | 
			
		||||
  })
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getModelValue = (model_id) => {
 | 
			
		||||
  for (let i = 0; i < models.value.length; i++) {
 | 
			
		||||
    if (models.value[i].value === model_id) {
 | 
			
		||||
      return models.value[i].text
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const search = () => {
 | 
			
		||||
  if (chatName.value === '') {
 | 
			
		||||
    chats.value = allChats.value
 | 
			
		||||
@@ -185,6 +202,7 @@ const newChat = (item) => {
 | 
			
		||||
      helloMsg: options[0].helloMsg
 | 
			
		||||
    },
 | 
			
		||||
    model: options[1].value,
 | 
			
		||||
    modelValue: getModelValue(options[1].value),
 | 
			
		||||
    title: '新建会话',
 | 
			
		||||
    chatId: 0
 | 
			
		||||
  })
 | 
			
		||||
@@ -205,7 +223,8 @@ const changeChat = (chat) => {
 | 
			
		||||
      name: role.text,
 | 
			
		||||
      icon: role.icon
 | 
			
		||||
    },
 | 
			
		||||
    model: chat.model,
 | 
			
		||||
    model: chat.model_id,
 | 
			
		||||
    modelValue: getModelValue(chat.model_id),
 | 
			
		||||
    title: chat.title,
 | 
			
		||||
    chatId: chat.chat_id,
 | 
			
		||||
    helloMsg: chat.hello_msg,
 | 
			
		||||
@@ -213,9 +232,18 @@ const changeChat = (chat) => {
 | 
			
		||||
  router.push('/mobile/chat/session')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const editChat = (item) => {
 | 
			
		||||
  showToast('修改会话标题')
 | 
			
		||||
 | 
			
		||||
const editChat = (row) => {
 | 
			
		||||
  showEditChat.value = true
 | 
			
		||||
  item.value = row
 | 
			
		||||
  tmpChatTitle.value = row.title
 | 
			
		||||
}
 | 
			
		||||
const saveTitle = () => {
 | 
			
		||||
  httpPost('/api/chat/update', {id: item.value.id, title: tmpChatTitle.value}).then(() => {
 | 
			
		||||
    showSuccessToast("操作成功!");
 | 
			
		||||
    item.value.title = tmpChatTitle.value;
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast("操作失败:" + e.message);
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const removeChat = (item) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <van-config-provider :theme="getMobileTheme()">
 | 
			
		||||
    <div class="mobile-chat">
 | 
			
		||||
    <div class="mobile-chat" v-loading="loading" element-loading-text="正在连接会话...">
 | 
			
		||||
      <van-sticky ref="navBarRef" :offset-top="0" position="top">
 | 
			
		||||
        <van-nav-bar left-arrow left-text="返回" @click-left="router.back()">
 | 
			
		||||
          <template #title>
 | 
			
		||||
            <van-dropdown-menu>
 | 
			
		||||
              <van-dropdown-item :title="title">
 | 
			
		||||
                <van-cell center title="角色"> {{ role.name }}</van-cell>
 | 
			
		||||
                <van-cell center title="模型">{{ model }}</van-cell>
 | 
			
		||||
                <van-cell center title="模型">{{ modelValue }}</van-cell>
 | 
			
		||||
              </van-dropdown-item>
 | 
			
		||||
            </van-dropdown-menu>
 | 
			
		||||
          </template>
 | 
			
		||||
@@ -28,7 +28,6 @@
 | 
			
		||||
      <div id="message-list-box" :style="{height: winHeight+'px'}" class="message-list-box">
 | 
			
		||||
        <van-list
 | 
			
		||||
            v-model:error="error"
 | 
			
		||||
            v-model:loading="loading"
 | 
			
		||||
            :finished="finished"
 | 
			
		||||
            error-text="请求失败,点击重新加载"
 | 
			
		||||
            @load="onLoad"
 | 
			
		||||
@@ -47,6 +46,12 @@
 | 
			
		||||
                        :icon="item.icon"
 | 
			
		||||
                        :org-content="item.orgContent"
 | 
			
		||||
                        :tokens="item['tokens']"/>
 | 
			
		||||
            <chat-mid-journey v-else-if="item.type==='mj'"
 | 
			
		||||
                              :content="item.content"
 | 
			
		||||
                              :icon="item.icon"
 | 
			
		||||
                              @disable-input="disableInput(true)"
 | 
			
		||||
                              @enable-input="enableInput"
 | 
			
		||||
                              :created-at="dateFormat(item['created_at'])"/>
 | 
			
		||||
          </van-cell>
 | 
			
		||||
        </van-list>
 | 
			
		||||
      </div>
 | 
			
		||||
@@ -80,7 +85,7 @@
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {nextTick, onMounted, ref} from "vue";
 | 
			
		||||
import {showToast, showDialog} from "vant";
 | 
			
		||||
import {showToast} from "vant";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import {dateFormat, randString, renderInputText, UUID} from "@/utils/libs";
 | 
			
		||||
import {getChatConfig} from "@/store/chat";
 | 
			
		||||
@@ -89,9 +94,10 @@ import hl from "highlight.js";
 | 
			
		||||
import 'highlight.js/styles/a11y-dark.css'
 | 
			
		||||
import ChatPrompt from "@/components/mobile/ChatPrompt.vue";
 | 
			
		||||
import ChatReply from "@/components/mobile/ChatReply.vue";
 | 
			
		||||
import {getSessionId} from "@/store/session";
 | 
			
		||||
import {getSessionId, getUserToken} from "@/store/session";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
import {getMobileTheme} from "@/store/system";
 | 
			
		||||
import ChatMidJourney from "@/components/mobile/ChatMidJourney.vue";
 | 
			
		||||
 | 
			
		||||
const winHeight = ref(0)
 | 
			
		||||
const navBarRef = ref(null)
 | 
			
		||||
@@ -101,6 +107,7 @@ const router = useRouter()
 | 
			
		||||
const chatConfig = getChatConfig()
 | 
			
		||||
const role = chatConfig.role
 | 
			
		||||
const model = chatConfig.model
 | 
			
		||||
const modelValue = chatConfig.modelValue
 | 
			
		||||
const title = chatConfig.title
 | 
			
		||||
const chatId = chatConfig.chatId
 | 
			
		||||
const loginUser = ref(null)
 | 
			
		||||
@@ -123,7 +130,6 @@ checkSession().then(user => {
 | 
			
		||||
const onLoad = () => {
 | 
			
		||||
  httpGet('/api/chat/history?chat_id=' + chatId).then(res => {
 | 
			
		||||
    // 加载状态结束
 | 
			
		||||
    loading.value = false;
 | 
			
		||||
    finished.value = true;
 | 
			
		||||
    const data = res.data
 | 
			
		||||
    if (data && data.length > 0) {
 | 
			
		||||
@@ -132,6 +138,11 @@ const onLoad = () => {
 | 
			
		||||
        if (data[i].type === "prompt") {
 | 
			
		||||
          chatData.value.push(data[i]);
 | 
			
		||||
          continue;
 | 
			
		||||
        } else if (data[i].type === "mj") {
 | 
			
		||||
          data[i].content = JSON.parse(data[i].content)
 | 
			
		||||
          data[i].content.html = md.render(data[i].content?.content)
 | 
			
		||||
          chatData.value.push(data[i]);
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        data[i].orgContent = data[i].content;
 | 
			
		||||
@@ -189,14 +200,14 @@ const connect = function (chat_id, role_id) {
 | 
			
		||||
      host = 'ws://' + location.host;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${role_id}&chat_id=${chat_id}&model=${model}`);
 | 
			
		||||
  const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${role_id}&chat_id=${chat_id}&model_id=${model}`);
 | 
			
		||||
  _socket.addEventListener('open', () => {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
    previousText.value = '';
 | 
			
		||||
    canSend.value = true;
 | 
			
		||||
    activelyClose.value = false;
 | 
			
		||||
 | 
			
		||||
    if (isNewChat) { // 加载打招呼信息
 | 
			
		||||
      loading.value = false;
 | 
			
		||||
      chatData.value.push({
 | 
			
		||||
        type: "reply",
 | 
			
		||||
        id: randString(32),
 | 
			
		||||
@@ -220,10 +231,39 @@ const connect = function (chat_id, role_id) {
 | 
			
		||||
            icon: role.icon,
 | 
			
		||||
            content: ""
 | 
			
		||||
          });
 | 
			
		||||
        } else if (data.type === "mj") {
 | 
			
		||||
          disableInput(true)
 | 
			
		||||
          const content = data.content;
 | 
			
		||||
          const md = require('markdown-it')({breaks: true});
 | 
			
		||||
          content.html = md.render(content.content)
 | 
			
		||||
          let key = content.key
 | 
			
		||||
          // fixed bug: 执行 Upscale 和 Variation 操作的时候覆盖之前的绘画
 | 
			
		||||
          if (content.status === "Finished") {
 | 
			
		||||
            key = randString(32)
 | 
			
		||||
            enableInput()
 | 
			
		||||
          }
 | 
			
		||||
          // console.log(content)
 | 
			
		||||
          // check if the message is in chatData
 | 
			
		||||
          let flag = false
 | 
			
		||||
          for (let i = 0; i < chatData.value.length; i++) {
 | 
			
		||||
            if (chatData.value[i].id === content.key) {
 | 
			
		||||
              flag = true
 | 
			
		||||
              chatData.value[i].content = content
 | 
			
		||||
              chatData.value[i].id = key
 | 
			
		||||
              break
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          if (flag === false) {
 | 
			
		||||
            chatData.value.push({
 | 
			
		||||
              type: "mj",
 | 
			
		||||
              id: key,
 | 
			
		||||
              icon: "/images/avatar/mid_journey.png",
 | 
			
		||||
              content: content
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
        } else if (data.type === 'end') { // 消息接收完毕
 | 
			
		||||
          canSend.value = true;
 | 
			
		||||
          showReGenerate.value = true;
 | 
			
		||||
          showStopGenerate.value = false;
 | 
			
		||||
          enableInput()
 | 
			
		||||
          lineBuffer.value = ''; // 清空缓冲
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -250,7 +290,6 @@ const connect = function (chat_id, role_id) {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  _socket.addEventListener('close', () => {
 | 
			
		||||
    console.log(activelyClose.value)
 | 
			
		||||
    if (activelyClose.value) { // 忽略主动关闭
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -260,18 +299,26 @@ const connect = function (chat_id, role_id) {
 | 
			
		||||
    checkSession().then(() => {
 | 
			
		||||
      connect(chat_id, role_id)
 | 
			
		||||
    }).catch(() => {
 | 
			
		||||
      showDialog({
 | 
			
		||||
        title: '会话提示',
 | 
			
		||||
        message: '当前会话已经失效,请重新登录!',
 | 
			
		||||
      }).then(() => {
 | 
			
		||||
        router.push('/login')
 | 
			
		||||
      });
 | 
			
		||||
      loading.value = true
 | 
			
		||||
      setTimeout(() => connect(chat_id, role_id), 3000)
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  socket.value = _socket;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const disableInput = (force) => {
 | 
			
		||||
  canSend.value = false;
 | 
			
		||||
  showReGenerate.value = false;
 | 
			
		||||
  showStopGenerate.value = !force;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const enableInput = () => {
 | 
			
		||||
  canSend.value = true;
 | 
			
		||||
  showReGenerate.value = previousText.value !== "";
 | 
			
		||||
  showStopGenerate.value = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 将聊天框的滚动条滑动到最底部
 | 
			
		||||
const scrollListBox = () => {
 | 
			
		||||
  document.getElementById('message-list-box').scrollTo(0, document.getElementById('message-list-box').scrollHeight + 46)
 | 
			
		||||
@@ -300,9 +347,7 @@ const sendMessage = () => {
 | 
			
		||||
    scrollListBox()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  canSend.value = false;
 | 
			
		||||
  showStopGenerate.value = true;
 | 
			
		||||
  showReGenerate.value = false;
 | 
			
		||||
  disableInput(false)
 | 
			
		||||
  socket.value.send(prompt.value);
 | 
			
		||||
  previousText.value = prompt.value;
 | 
			
		||||
  prompt.value = '';
 | 
			
		||||
@@ -312,17 +357,12 @@ const sendMessage = () => {
 | 
			
		||||
const stopGenerate = () => {
 | 
			
		||||
  showStopGenerate.value = false;
 | 
			
		||||
  httpGet("/api/chat/stop?session_id=" + getSessionId()).then(() => {
 | 
			
		||||
    canSend.value = true;
 | 
			
		||||
    if (previousText.value !== '') {
 | 
			
		||||
      showReGenerate.value = true;
 | 
			
		||||
    }
 | 
			
		||||
    enableInput()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const reGenerate = () => {
 | 
			
		||||
  canSend.value = false;
 | 
			
		||||
  showStopGenerate.value = true;
 | 
			
		||||
  showReGenerate.value = false;
 | 
			
		||||
  disableInput(false)
 | 
			
		||||
  const text = '重新生成上述问题的答案:' + previousText.value;
 | 
			
		||||
  // 追加消息
 | 
			
		||||
  chatData.value.push({
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,6 @@
 | 
			
		||||
    <div class="content">
 | 
			
		||||
      <van-form @submit="save">
 | 
			
		||||
        <van-cell-group inset v-model="form">
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="form.username"
 | 
			
		||||
              name="用户名"
 | 
			
		||||
              label="用户名"
 | 
			
		||||
              readonly
 | 
			
		||||
              disabled
 | 
			
		||||
              placeholder="用户名"
 | 
			
		||||
          />
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="form.mobile"
 | 
			
		||||
              name="手机号"
 | 
			
		||||
@@ -21,13 +13,6 @@
 | 
			
		||||
              disabled
 | 
			
		||||
              placeholder="手机号"
 | 
			
		||||
          />
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="form.nickname"
 | 
			
		||||
              name="昵称"
 | 
			
		||||
              label="昵称"
 | 
			
		||||
              placeholder="昵称"
 | 
			
		||||
              :rules="[{ required: true, message: '请填写用户昵称' }]"
 | 
			
		||||
          />
 | 
			
		||||
          <van-field label="头像">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-uploader v-model="fileList"
 | 
			
		||||
@@ -37,15 +22,21 @@
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
 | 
			
		||||
          <van-field label="剩余次数">
 | 
			
		||||
          <van-field label="剩余对话次数">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-tag type="success">{{ form.calls }}</van-tag>
 | 
			
		||||
              <van-tag type="primary">{{ form.calls }}</van-tag>
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
 | 
			
		||||
          <van-field label="消耗 Tokens">
 | 
			
		||||
          <van-field label="剩余绘图次数">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-tag type="primary">{{ form.tokens }}</van-tag>
 | 
			
		||||
              <van-tag type="primary">{{ form.img_calls }}</van-tag>
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
 | 
			
		||||
          <van-field label="累计消耗tokens">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-tag type="primary">{{ form.total_tokens }}</van-tag>
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
        </van-cell-group>
 | 
			
		||||
@@ -125,6 +116,13 @@ const save = () => {
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
.mobile-user-profile {
 | 
			
		||||
  .content {
 | 
			
		||||
    .van-field__label {
 | 
			
		||||
      width 100px
 | 
			
		||||
      text-align right
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -6,45 +6,19 @@
 | 
			
		||||
      <van-form @submit="save" v-model="form">
 | 
			
		||||
        <van-cell-group inset>
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="form.chat_config.model"
 | 
			
		||||
              readonly
 | 
			
		||||
              label="默认模型"
 | 
			
		||||
              placeholder=""
 | 
			
		||||
              @click="showPicker = true"
 | 
			
		||||
              v-model="form.chat_config.api_keys.OpenAI"
 | 
			
		||||
              label="OpenAI KEY"
 | 
			
		||||
              placeholder="OpenAI API KEY"
 | 
			
		||||
          />
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model.number="form.chat_config.max_tokens"
 | 
			
		||||
              type="number"
 | 
			
		||||
              name="MaxTokens"
 | 
			
		||||
              label="MaxTokens"
 | 
			
		||||
              placeholder="每次请求最大 token 数量"
 | 
			
		||||
              :rules="[{ required: true, message: '请填写 MaxTokens' }]"
 | 
			
		||||
              v-model="form.chat_config.api_keys.Azure"
 | 
			
		||||
              label="Azure KEY"
 | 
			
		||||
              placeholder="Azure API KEY"
 | 
			
		||||
          />
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model.number="form.chat_config.temperature"
 | 
			
		||||
              type="number"
 | 
			
		||||
              name="Temperature"
 | 
			
		||||
              label="Temperature"
 | 
			
		||||
              placeholder="模型温度"
 | 
			
		||||
              :rules="[{ required: true, message: '请填写 Temperature' }]"
 | 
			
		||||
          />
 | 
			
		||||
 | 
			
		||||
          <van-field name="switch" label="聊天记录">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-switch v-model="form.chat_config.enable_history"/>
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
 | 
			
		||||
          <van-field name="switch" label="聊天上下文">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-switch v-model="form.chat_config.enable_context"/>
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="form.chat_config.api_key"
 | 
			
		||||
              name="API KEY"
 | 
			
		||||
              label="API KEY"
 | 
			
		||||
              placeholder="配置自己的 api key"
 | 
			
		||||
              v-model="form.chat_config.api_keys.ChatGLM"
 | 
			
		||||
              label="ChatGLM KEY"
 | 
			
		||||
              placeholder="ChatGLM API KEY"
 | 
			
		||||
          />
 | 
			
		||||
        </van-cell-group>
 | 
			
		||||
        <div style="margin: 16px;">
 | 
			
		||||
@@ -74,12 +48,7 @@ import {ElMessage} from "element-plus";
 | 
			
		||||
const title = ref('聊天设置')
 | 
			
		||||
const form = ref({
 | 
			
		||||
  chat_config: {
 | 
			
		||||
    model: '',
 | 
			
		||||
    max_tokens: 0,
 | 
			
		||||
    enable_context: false,
 | 
			
		||||
    enable_history: false,
 | 
			
		||||
    temperature: false,
 | 
			
		||||
    api_key: ''
 | 
			
		||||
    api_keys: {OpenAI: "", Azure: "", ChatGLM: ""}
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
const showPicker = ref(false)
 | 
			
		||||
@@ -89,6 +58,7 @@ onMounted(() => {
 | 
			
		||||
  // 获取最新用户信息
 | 
			
		||||
  httpGet('/api/user/profile').then(res => {
 | 
			
		||||
    form.value = res.data
 | 
			
		||||
    form.value.chat_config.api_keys = res.data.chat_config.api_keys ?? {OpenAI: "", Azure: "", ChatGLM: ""}
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    showFailToast('获取用户信息失败')
 | 
			
		||||
  });
 | 
			
		||||
@@ -119,10 +89,15 @@ const save = () => {
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="stylus">
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
.mobile-setting {
 | 
			
		||||
  .content {
 | 
			
		||||
    padding-top 60px
 | 
			
		||||
 | 
			
		||||
    .van-field__label {
 | 
			
		||||
      width 100px
 | 
			
		||||
      text-align right
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user