mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	feat: vue-mobile => 完成移动端聊天配置功能
This commit is contained in:
		
							
								
								
									
										1
									
								
								api/go/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								api/go/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -16,3 +16,4 @@ tmp
 | 
			
		||||
bin
 | 
			
		||||
data
 | 
			
		||||
config.toml
 | 
			
		||||
static/upload
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,9 @@ func (s *AppServer) Init(debug bool) {
 | 
			
		||||
	s.Engine.Use(authorizeMiddleware(s))
 | 
			
		||||
	s.Engine.Use(errorHandler)
 | 
			
		||||
	//gob.Register(model.User{})
 | 
			
		||||
 | 
			
		||||
	// 添加静态资源访问
 | 
			
		||||
	s.Engine.Static("/static", s.AppConfig.StaticDir)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *AppServer) Run(db *gorm.DB) error {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,10 @@ var logger = logger2.GetLogger()
 | 
			
		||||
 | 
			
		||||
func NewDefaultConfig() *types.AppConfig {
 | 
			
		||||
	return &types.AppConfig{
 | 
			
		||||
		Listen:   "0.0.0.0:5678",
 | 
			
		||||
		ProxyURL: "",
 | 
			
		||||
		Manager:  types.Manager{Username: "admin", Password: "admin123"},
 | 
			
		||||
		Listen:    "0.0.0.0:5678",
 | 
			
		||||
		ProxyURL:  "",
 | 
			
		||||
		Manager:   types.Manager{Username: "admin", Password: "admin123"},
 | 
			
		||||
		StaticDir: "./static",
 | 
			
		||||
 | 
			
		||||
		Session: types.Session{
 | 
			
		||||
			SecretKey: utils.RandString(64),
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,13 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AppConfig struct {
 | 
			
		||||
	Path     string `toml:"-"`
 | 
			
		||||
	Listen   string
 | 
			
		||||
	Session  Session
 | 
			
		||||
	ProxyURL string
 | 
			
		||||
	MysqlDns string  // mysql 连接地址
 | 
			
		||||
	Manager  Manager // 后台管理员账户信息
 | 
			
		||||
	Path      string `toml:"-"`
 | 
			
		||||
	Listen    string
 | 
			
		||||
	Session   Session
 | 
			
		||||
	ProxyURL  string
 | 
			
		||||
	MysqlDns  string  // mysql 连接地址
 | 
			
		||||
	Manager   Manager // 后台管理员账户信息
 | 
			
		||||
	StaticDir string  // 静态资源目录
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Manager 管理员
 | 
			
		||||
 
 | 
			
		||||
@@ -289,8 +289,6 @@ func (h *UserHandler) Profile(c *gin.Context) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userVo.Id = user.Id
 | 
			
		||||
	userVo.CreatedAt = user.CreatedAt.Unix()
 | 
			
		||||
	userVo.UpdatedAt = user.UpdatedAt.Unix()
 | 
			
		||||
	resp.SUCCESS(c, userVo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								api/go/static/hello.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								api/go/static/hello.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
hello, world!
 | 
			
		||||
@@ -4,16 +4,16 @@ import "chatplus/core/types"
 | 
			
		||||
 | 
			
		||||
type User struct {
 | 
			
		||||
	BaseVo
 | 
			
		||||
	Username    string           `json:"username"`
 | 
			
		||||
	Nickname    string           `json:"nickname"`
 | 
			
		||||
	Avatar      string           `json:"avatar"`
 | 
			
		||||
	Salt        string           `json:"salt"`          // 密码盐
 | 
			
		||||
	Tokens      int64            `json:"tokens"`        // 剩余tokens
 | 
			
		||||
	Calls       int              `json:"calls"`         // 剩余对话次数
 | 
			
		||||
	ChatConfig  types.ChatConfig `json:"chat_config"`   // 聊天配置
 | 
			
		||||
	ChatRoles   []string         `json:"chat_roles"`    // 聊天角色集合
 | 
			
		||||
	ExpiredTime int64            `json:"expired_time"`  // 账户到期时间
 | 
			
		||||
	Status      bool             `json:"status"`        // 当前状态
 | 
			
		||||
	LastLoginAt int64            `json:"last_login_at"` // 最后登录时间
 | 
			
		||||
	LastLoginIp string           `json:"last_login_ip"` // 最后登录 IP
 | 
			
		||||
	Username    string           `json:"username,omitempty"`
 | 
			
		||||
	Nickname    string           `json:"nickname,omitempty"`
 | 
			
		||||
	Avatar      string           `json:"avatar,omitempty"`
 | 
			
		||||
	Salt        string           `json:"salt,omitempty"`          // 密码盐
 | 
			
		||||
	Tokens      int64            `json:"tokens,omitempty"`        // 剩余tokens
 | 
			
		||||
	Calls       int              `json:"calls,omitempty"`         // 剩余对话次数
 | 
			
		||||
	ChatConfig  types.ChatConfig `json:"chat_config,omitempty"`   // 聊天配置
 | 
			
		||||
	ChatRoles   []string         `json:"chat_roles,omitempty"`    // 聊天角色集合
 | 
			
		||||
	ExpiredTime int64            `json:"expired_time,omitempty"`  // 账户到期时间
 | 
			
		||||
	Status      bool             `json:"status,omitempty"`        // 当前状态
 | 
			
		||||
	LastLoginAt int64            `json:"last_login_at,omitempty"` // 最后登录时间
 | 
			
		||||
	LastLoginIp string           `json:"last_login_ip,omitempty"` // 最后登录 IP
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ onMounted(() => {
 | 
			
		||||
    margin-left 5px
 | 
			
		||||
 | 
			
		||||
    .van-image {
 | 
			
		||||
      width 25px
 | 
			
		||||
      width 32px
 | 
			
		||||
 | 
			
		||||
      img {
 | 
			
		||||
        border-radius 5px
 | 
			
		||||
@@ -73,7 +73,7 @@ onMounted(() => {
 | 
			
		||||
 | 
			
		||||
    .content {
 | 
			
		||||
      word-break break-word;
 | 
			
		||||
      padding: 6px 10px;
 | 
			
		||||
      padding: 5px 10px;
 | 
			
		||||
      background-color: #98E165;
 | 
			
		||||
      color #444444
 | 
			
		||||
      font-size: 16px
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ onMounted(() => {
 | 
			
		||||
    margin-right 5px
 | 
			
		||||
 | 
			
		||||
    .van-image {
 | 
			
		||||
      width 25px
 | 
			
		||||
      width 32px
 | 
			
		||||
 | 
			
		||||
      img {
 | 
			
		||||
        border-radius 5px
 | 
			
		||||
@@ -92,7 +92,7 @@ onMounted(() => {
 | 
			
		||||
        overflow-x auto
 | 
			
		||||
        min-height 20px;
 | 
			
		||||
        word-break break-word;
 | 
			
		||||
        padding: 6px 10px;
 | 
			
		||||
        padding: 5px 10px;
 | 
			
		||||
        color #444444
 | 
			
		||||
        background-color: #ffffff;
 | 
			
		||||
        font-size: 16px
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import {
 | 
			
		||||
    ShareSheet,
 | 
			
		||||
    Sticky,
 | 
			
		||||
    SwipeCell,
 | 
			
		||||
    Switch,
 | 
			
		||||
    Tabbar,
 | 
			
		||||
    TabbarItem,
 | 
			
		||||
    TextEllipsis
 | 
			
		||||
@@ -56,6 +57,7 @@ app.use(Sticky);
 | 
			
		||||
app.use(SwipeCell);
 | 
			
		||||
app.use(Dialog);
 | 
			
		||||
app.use(ShareSheet);
 | 
			
		||||
app.use(Switch);
 | 
			
		||||
app.use(router).use(ElementPlus).mount('#app')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -227,20 +227,22 @@ const removeChat = (item) => {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
 | 
			
		||||
$fontSize = 16px;
 | 
			
		||||
.mobile-chat-list {
 | 
			
		||||
 | 
			
		||||
  .content {
 | 
			
		||||
    .van-cell__value {
 | 
			
		||||
      .chat-list-item {
 | 
			
		||||
        display flex
 | 
			
		||||
        font-size $fontSize
 | 
			
		||||
 | 
			
		||||
        .van-image {
 | 
			
		||||
          width 30px
 | 
			
		||||
          height 30px
 | 
			
		||||
          width 32px
 | 
			
		||||
          height 32px
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .van-ellipsis {
 | 
			
		||||
          margin-top 4px;
 | 
			
		||||
          margin-top 5px;
 | 
			
		||||
          margin-left 10px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,33 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
  <div class="mobile-user-profile container">
 | 
			
		||||
    <van-nav-bar :title="title"/>
 | 
			
		||||
 | 
			
		||||
    <div class="content">
 | 
			
		||||
      <van-form @submit="onSubmit">
 | 
			
		||||
        <van-cell-group inset>
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="username"
 | 
			
		||||
              name="用户名"
 | 
			
		||||
              label="用户名"
 | 
			
		||||
              placeholder="用户名"
 | 
			
		||||
              :rules="[{ required: true, message: '请填写用户名' }]"
 | 
			
		||||
          />
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="password"
 | 
			
		||||
              type="password"
 | 
			
		||||
              name="密码"
 | 
			
		||||
              label="密码"
 | 
			
		||||
              placeholder="密码"
 | 
			
		||||
              :rules="[{ required: true, message: '请填写密码' }]"
 | 
			
		||||
          />
 | 
			
		||||
        </van-cell-group>
 | 
			
		||||
        <div style="margin: 16px;">
 | 
			
		||||
          <van-button round block type="primary" native-type="submit">
 | 
			
		||||
            提交
 | 
			
		||||
          </van-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </van-form>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,132 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
  <div class="mobile-setting container">
 | 
			
		||||
    <van-nav-bar :title="title"/>
 | 
			
		||||
 | 
			
		||||
    <div class="content">
 | 
			
		||||
      <van-form @submit="save" v-model="form">
 | 
			
		||||
        <van-cell-group inset>
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="form.chat_config.model"
 | 
			
		||||
              is-link
 | 
			
		||||
              readonly
 | 
			
		||||
              label="默认模型"
 | 
			
		||||
              placeholder=""
 | 
			
		||||
              @click="showPicker = true"
 | 
			
		||||
          />
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model.number="form.chat_config.max_tokens"
 | 
			
		||||
              name="MaxTokens"
 | 
			
		||||
              type="number"
 | 
			
		||||
              label="MaxTokens"
 | 
			
		||||
              placeholder="每次请求最大 token 数量"
 | 
			
		||||
              :rules="[{ required: true, message: '请填写 MaxTokens' }]"
 | 
			
		||||
          />
 | 
			
		||||
          <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"
 | 
			
		||||
              :rules="[{ required: true, message: '请填写 API KEY' }]"
 | 
			
		||||
          />
 | 
			
		||||
        </van-cell-group>
 | 
			
		||||
        <div style="margin: 16px;">
 | 
			
		||||
          <van-button round block type="primary" native-type="submit">
 | 
			
		||||
            保存
 | 
			
		||||
          </van-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </van-form>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <van-popup v-model:show="showPicker" round position="bottom">
 | 
			
		||||
      <van-picker
 | 
			
		||||
          :columns="models"
 | 
			
		||||
          @cancel="showPicker = false"
 | 
			
		||||
          @confirm="selectModel"
 | 
			
		||||
      />
 | 
			
		||||
    </van-popup>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
import {onMounted, ref} from "vue";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import {showFailToast, showSuccessToast} from "vant";
 | 
			
		||||
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: ''
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
const showPicker = ref(false)
 | 
			
		||||
const models = ref([])
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  // 获取最新用户信息
 | 
			
		||||
  httpGet('/api/user/profile').then(res => {
 | 
			
		||||
    console.log(res.data)
 | 
			
		||||
    form.value = res.data
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    showFailToast('获取用户信息失败')
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // 加载系统配置
 | 
			
		||||
  httpGet('/api/admin/config/get?key=system').then(res => {
 | 
			
		||||
    const mds = res.data.models;
 | 
			
		||||
    console.log(mds)
 | 
			
		||||
    mds.forEach(item => {
 | 
			
		||||
      models.value.push({text: item, value: item})
 | 
			
		||||
    })
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    ElMessage.error("加载系统配置失败: " + e.message)
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const selectModel = (item) => {
 | 
			
		||||
  showPicker.value = false
 | 
			
		||||
  form.value.chat_config.model = item.selectedValues[0]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const save = () => {
 | 
			
		||||
  httpPost('/api/user/profile/update', form.value).then(() => {
 | 
			
		||||
    showSuccessToast('保存成功')
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    showFailToast('保存失败')
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="stylus">
 | 
			
		||||
.mobile-setting {
 | 
			
		||||
  .content {
 | 
			
		||||
    padding-top 60px
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
		Reference in New Issue
	
	Block a user