From e5e33996d3385a9954aa8670398f15b46082cc28 Mon Sep 17 00:00:00 2001 From: Eg <1711788888@qq.com> Date: Tue, 24 Feb 2026 20:54:26 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20=E4=BC=98=E5=8C=96=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=A0=8F=EF=BC=88=E4=BF=AE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=8A=BD=E5=B1=89=E6=A8=A1=E5=BC=8F=EF=BC=89&&=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Eai=E9=A2=84=E8=AE=BE=E5=8A=9F=E8=83=BD=20&&=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96ai=E5=AF=B9=E8=AF=9D=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- server/api/v1/app/ai_preset.go | 256 +++++++ server/api/v1/app/enter.go | 1 + server/initialize/gorm.go | 1 + server/initialize/router.go | 1 + server/model/app/request/ai_preset.go | 24 + server/model/app/response/ai_preset.go | 42 ++ server/router/app/ai_preset.go | 23 + server/router/app/enter.go | 1 + server/service/app/ai_preset.go | 198 ++++++ server/service/app/enter.go | 1 + web-app-vue/package-lock.json | 17 +- web-app-vue/package.json | 1 + web-app-vue/src/api/preset.ts | 74 ++ web-app-vue/src/components.d.ts | 8 + .../src/components/DrawerContentWrapper.vue | 152 ++++ .../src/components/MessageRenderer.vue | 156 ++++ web-app-vue/src/components/VariableNode.vue | 166 +++++ web-app-vue/src/components/VariableViewer.vue | 110 +++ .../components/preset/GenericPresetForm.vue | 57 ++ .../components/preset/OpenAIPresetForm.vue | 326 +++++++++ web-app-vue/src/layouts/DefaultLayout.vue | 216 ++++-- web-app-vue/src/types/preset.d.ts | 41 ++ web-app-vue/src/views/chat/ChatView.vue | 141 +++- web-app-vue/src/views/preset/PresetEdit.vue | 340 +++++++++ web-app-vue/src/views/preset/PresetList.vue | 304 ++++++++ .../src/views/script/ScriptManager.vue | 38 + .../src/views/worldbook/WorldBookEdit.vue | 94 ++- .../views/worldbook/WorldBookListDrawer.vue | 293 ++++++++ word_info/word1.json | 664 ++++++++++++++++++ 30 files changed, 3681 insertions(+), 68 deletions(-) create mode 100644 server/api/v1/app/ai_preset.go create mode 100644 server/model/app/request/ai_preset.go create mode 100644 server/model/app/response/ai_preset.go create mode 100644 server/router/app/ai_preset.go create mode 100644 server/service/app/ai_preset.go create mode 100644 web-app-vue/src/api/preset.ts create mode 100644 web-app-vue/src/components/DrawerContentWrapper.vue create mode 100644 web-app-vue/src/components/MessageRenderer.vue create mode 100644 web-app-vue/src/components/VariableNode.vue create mode 100644 web-app-vue/src/components/VariableViewer.vue create mode 100644 web-app-vue/src/components/preset/GenericPresetForm.vue create mode 100644 web-app-vue/src/components/preset/OpenAIPresetForm.vue create mode 100644 web-app-vue/src/types/preset.d.ts create mode 100644 web-app-vue/src/views/preset/PresetEdit.vue create mode 100644 web-app-vue/src/views/preset/PresetList.vue create mode 100644 web-app-vue/src/views/script/ScriptManager.vue create mode 100644 web-app-vue/src/views/worldbook/WorldBookListDrawer.vue create mode 100644 word_info/word1.json diff --git a/.gitignore b/.gitignore index 62cac9c..b57880b 100644 --- a/.gitignore +++ b/.gitignore @@ -179,4 +179,5 @@ uploads/ # SillyTavern 核心脚本和扩展文件(从 web-app 一次性复制,不提交到 Git) server/data/st-core-scripts/ -server/data/extensions/ \ No newline at end of file +server/data/extensions/ +.vite \ No newline at end of file diff --git a/server/api/v1/app/ai_preset.go b/server/api/v1/app/ai_preset.go new file mode 100644 index 0000000..80f307a --- /dev/null +++ b/server/api/v1/app/ai_preset.go @@ -0,0 +1,256 @@ +package app + +import ( + "git.echol.cn/loser/st/server/global" + "git.echol.cn/loser/st/server/model/app/request" + "git.echol.cn/loser/st/server/model/common/response" + "git.echol.cn/loser/st/server/service" + "git.echol.cn/loser/st/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" + "strconv" +) + +type AIPresetApi struct{} + +var aiPresetService = service.ServiceGroupApp.AppServiceGroup.AIPresetService + +// CreateAIPreset 创建预设 +// @Tags AIPreset +// @Summary 创建AI预设 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.CreateAIPresetRequest true "预设信息" +// @Success 200 {object} response.Response{data=app.AIPreset,msg=string} "创建成功" +// @Router /app/preset/create [post] +func (a *AIPresetApi) CreateAIPreset(c *gin.Context) { + var req request.CreateAIPresetRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + + // 获取用户ID + userID := utils.GetUserID(c) + if userID == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + preset, err := aiPresetService.CreateAIPreset(userID, &req) + if err != nil { + global.GVA_LOG.Error("创建预设失败", zap.Error(err)) + response.FailWithMessage("创建预设失败: "+err.Error(), c) + return + } + + response.OkWithData(preset, c) +} + +// UpdateAIPreset 更新预设 +// @Tags AIPreset +// @Summary 更新AI预设 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param id path int true "预设ID" +// @Param data body request.UpdateAIPresetRequest true "预设信息" +// @Success 200 {object} response.Response{msg=string} "更新成功" +// @Router /app/preset/update/{id} [put] +func (a *AIPresetApi) UpdateAIPreset(c *gin.Context) { + presetID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + response.FailWithMessage("无效的预设ID", c) + return + } + + var req request.UpdateAIPresetRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + + // 获取用户ID + userID := utils.GetUserID(c) + if userID == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + if err := aiPresetService.UpdateAIPreset(userID, uint(presetID), &req); err != nil { + global.GVA_LOG.Error("更新预设失败", zap.Error(err)) + response.FailWithMessage("更新预设失败: "+err.Error(), c) + return + } + + response.OkWithMessage("更新成功", c) +} + +// DeleteAIPreset 删除预设 +// @Tags AIPreset +// @Summary 删除AI预设 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param id path int true "预设ID" +// @Success 200 {object} response.Response{msg=string} "删除成功" +// @Router /app/preset/delete/{id} [delete] +func (a *AIPresetApi) DeleteAIPreset(c *gin.Context) { + presetID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + response.FailWithMessage("无效的预设ID", c) + return + } + + // 获取用户ID + userID := utils.GetUserID(c) + if userID == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + if err := aiPresetService.DeleteAIPreset(userID, uint(presetID)); err != nil { + global.GVA_LOG.Error("删除预设失败", zap.Error(err)) + response.FailWithMessage("删除预设失败: "+err.Error(), c) + return + } + + response.OkWithMessage("删除成功", c) +} + +// GetAIPreset 获取预设详情 +// @Tags AIPreset +// @Summary 获取AI预设详情 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param id path int true "预设ID" +// @Success 200 {object} response.Response{data=response.AIPresetResponse,msg=string} "获取成功" +// @Router /app/preset/get/{id} [get] +func (a *AIPresetApi) GetAIPreset(c *gin.Context) { + presetID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + response.FailWithMessage("无效的预设ID", c) + return + } + + // 获取用户ID + userID := utils.GetUserID(c) + if userID == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + preset, err := aiPresetService.GetAIPreset(userID, uint(presetID)) + if err != nil { + global.GVA_LOG.Error("获取预设失败", zap.Error(err)) + response.FailWithMessage("获取预设失败: "+err.Error(), c) + return + } + + response.OkWithData(preset, c) +} + +// GetAIPresetList 获取预设列表 +// @Tags AIPreset +// @Summary 获取AI预设列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.AIPresetSearch true "搜索条件" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功" +// @Router /app/preset/list [post] +func (a *AIPresetApi) GetAIPresetList(c *gin.Context) { + var req request.AIPresetSearch + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + + // 获取用户ID + userID := utils.GetUserID(c) + if userID == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + list, total, err := aiPresetService.GetAIPresetList(userID, &req) + if err != nil { + global.GVA_LOG.Error("获取预设列表失败", zap.Error(err)) + response.FailWithMessage("获取预设列表失败: "+err.Error(), c) + return + } + + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: req.Page, + PageSize: req.PageSize, + }, "获取成功", c) +} + +// DuplicateAIPreset 复制预设 +// @Tags AIPreset +// @Summary 复制AI预设 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param id path int true "预设ID" +// @Success 200 {object} response.Response{data=app.AIPreset,msg=string} "复制成功" +// @Router /app/preset/duplicate/{id} [post] +func (a *AIPresetApi) DuplicateAIPreset(c *gin.Context) { + presetID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + response.FailWithMessage("无效的预设ID", c) + return + } + + // 获取用户ID + userID := utils.GetUserID(c) + if userID == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + preset, err := aiPresetService.DuplicateAIPreset(userID, uint(presetID)) + if err != nil { + global.GVA_LOG.Error("复制预设失败", zap.Error(err)) + response.FailWithMessage("复制预设失败: "+err.Error(), c) + return + } + + response.OkWithData(preset, c) +} + +// SetDefaultPreset 设置默认预设 +// @Tags AIPreset +// @Summary 设置默认AI预设 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param id path int true "预设ID" +// @Success 200 {object} response.Response{msg=string} "设置成功" +// @Router /app/preset/setDefault/{id} [post] +func (a *AIPresetApi) SetDefaultPreset(c *gin.Context) { + presetID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + response.FailWithMessage("无效的预设ID", c) + return + } + + // 获取用户ID + userID := utils.GetUserID(c) + if userID == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + if err := aiPresetService.SetDefaultPreset(userID, uint(presetID)); err != nil { + global.GVA_LOG.Error("设置默认预设失败", zap.Error(err)) + response.FailWithMessage("设置默认预设失败: "+err.Error(), c) + return + } + + response.OkWithMessage("设置成功", c) +} diff --git a/server/api/v1/app/enter.go b/server/api/v1/app/enter.go index e1d0a0f..073d94b 100644 --- a/server/api/v1/app/enter.go +++ b/server/api/v1/app/enter.go @@ -9,6 +9,7 @@ type ApiGroup struct { RegexScriptApi ProviderApi ChatApi + AIPresetApi } var ( diff --git a/server/initialize/gorm.go b/server/initialize/gorm.go index 644f900..243dd60 100644 --- a/server/initialize/gorm.go +++ b/server/initialize/gorm.go @@ -96,6 +96,7 @@ func RegisterTables() { app.AIUsageStat{}, app.AIRegexScript{}, app.AICharacterRegexScript{}, + app.AICharacterWorldInfo{}, ) if err != nil { global.GVA_LOG.Error("register table failed", zap.Error(err)) diff --git a/server/initialize/router.go b/server/initialize/router.go index 0672f0e..7e18d92 100644 --- a/server/initialize/router.go +++ b/server/initialize/router.go @@ -153,6 +153,7 @@ func Routers() *gin.Engine { appRouter.InitRegexScriptRouter(appGroup) // 正则脚本路由:/app/regex/* appRouter.InitProviderRouter(appGroup) // AI提供商路由:/app/provider/* appRouter.InitChatRouter(appGroup) // 对话路由:/app/chat/* + appRouter.InitAIPresetRouter(appGroup) // AI预设路由:/app/preset/* } //插件路由安装 diff --git a/server/model/app/request/ai_preset.go b/server/model/app/request/ai_preset.go new file mode 100644 index 0000000..5b45598 --- /dev/null +++ b/server/model/app/request/ai_preset.go @@ -0,0 +1,24 @@ +package request + +import "git.echol.cn/loser/st/server/model/common/request" + +// CreateAIPresetRequest 创建预设请求 +type CreateAIPresetRequest struct { + Name string `json:"name" binding:"required,max=200"` + Type string `json:"type" binding:"required,max=100"` + Content map[string]interface{} `json:"content" binding:"required"` +} + +// UpdateAIPresetRequest 更新预设请求 +type UpdateAIPresetRequest struct { + Name string `json:"name" binding:"required,max=200"` + Type string `json:"type" binding:"required,max=100"` + Content map[string]interface{} `json:"content" binding:"required"` +} + +// AIPresetSearch 预设搜索请求 +type AIPresetSearch struct { + request.PageInfo + Name string `json:"name" form:"name"` + Type string `json:"type" form:"type"` +} diff --git a/server/model/app/response/ai_preset.go b/server/model/app/response/ai_preset.go new file mode 100644 index 0000000..a8bb3ff --- /dev/null +++ b/server/model/app/response/ai_preset.go @@ -0,0 +1,42 @@ +package response + +import ( + "encoding/json" + "git.echol.cn/loser/st/server/model/app" + "time" +) + +// AIPresetResponse 预设响应 +type AIPresetResponse struct { + ID uint `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Content map[string]interface{} `json:"content"` + IsSystem bool `json:"isSystem"` + IsDefault bool `json:"isDefault"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +// ToAIPresetResponse 转换为响应格式 +func ToAIPresetResponse(preset *app.AIPreset) *AIPresetResponse { + content := make(map[string]interface{}) + if preset.Config != nil && len(preset.Config) > 0 { + // 将 datatypes.JSON 转换为 map + if err := json.Unmarshal(preset.Config, &content); err != nil { + // 如果解析失败,返回空 map + content = make(map[string]interface{}) + } + } + + return &AIPresetResponse{ + ID: preset.ID, + Name: preset.Name, + Type: preset.PresetType, + Content: content, + IsSystem: preset.IsSystem, + IsDefault: preset.IsDefault, + CreatedAt: preset.CreatedAt, + UpdatedAt: preset.UpdatedAt, + } +} diff --git a/server/router/app/ai_preset.go b/server/router/app/ai_preset.go new file mode 100644 index 0000000..66ea62a --- /dev/null +++ b/server/router/app/ai_preset.go @@ -0,0 +1,23 @@ +package app + +import ( + "git.echol.cn/loser/st/server/api/v1" + "git.echol.cn/loser/st/server/middleware" + "github.com/gin-gonic/gin" +) + +type AIPresetRouter struct{} + +func (r *AIPresetRouter) InitAIPresetRouter(Router *gin.RouterGroup) { + presetRouter := Router.Group("preset").Use(middleware.JWTAuth()) + presetApi := v1.ApiGroupApp.AppApiGroup.AIPresetApi + { + presetRouter.POST("create", presetApi.CreateAIPreset) // 创建预设 + presetRouter.PUT("update/:id", presetApi.UpdateAIPreset) // 更新预设 + presetRouter.DELETE("delete/:id", presetApi.DeleteAIPreset) // 删除预设 + presetRouter.GET("get/:id", presetApi.GetAIPreset) // 获取预设详情 + presetRouter.POST("list", presetApi.GetAIPresetList) // 获取预设列表 + presetRouter.POST("duplicate/:id", presetApi.DuplicateAIPreset) // 复制预设 + presetRouter.POST("setDefault/:id", presetApi.SetDefaultPreset) // 设置默认预设 + } +} diff --git a/server/router/app/enter.go b/server/router/app/enter.go index 007b1e7..ba22319 100644 --- a/server/router/app/enter.go +++ b/server/router/app/enter.go @@ -7,4 +7,5 @@ type RouterGroup struct { RegexScriptRouter ProviderRouter ChatRouter + AIPresetRouter } diff --git a/server/service/app/ai_preset.go b/server/service/app/ai_preset.go new file mode 100644 index 0000000..46958e1 --- /dev/null +++ b/server/service/app/ai_preset.go @@ -0,0 +1,198 @@ +package app + +import ( + "encoding/json" + "errors" + "git.echol.cn/loser/st/server/global" + "git.echol.cn/loser/st/server/model/app" + "git.echol.cn/loser/st/server/model/app/request" + "git.echol.cn/loser/st/server/model/app/response" + "gorm.io/datatypes" +) + +type AIPresetService struct{} + +// CreateAIPreset 创建预设 +func (s *AIPresetService) CreateAIPreset(userID uint, req *request.CreateAIPresetRequest) (*app.AIPreset, error) { + // 将 content 转换为 JSON + configBytes, err := json.Marshal(req.Content) + if err != nil { + return nil, errors.New("配置格式错误") + } + + preset := &app.AIPreset{ + Name: req.Name, + UserID: &userID, + PresetType: req.Type, + Config: datatypes.JSON(configBytes), + IsSystem: false, + IsDefault: false, + } + + if err := global.GVA_DB.Create(preset).Error; err != nil { + return nil, err + } + + return preset, nil +} + +// UpdateAIPreset 更新预设 +func (s *AIPresetService) UpdateAIPreset(userID uint, presetID uint, req *request.UpdateAIPresetRequest) error { + // 检查预设是否存在且属于当前用户 + var preset app.AIPreset + if err := global.GVA_DB.Where("id = ? AND user_id = ?", presetID, userID).First(&preset).Error; err != nil { + return errors.New("预设不存在或无权限") + } + + // 系统预设不允许修改 + if preset.IsSystem { + return errors.New("系统预设不允许修改") + } + + // 将 content 转换为 JSON + configBytes, err := json.Marshal(req.Content) + if err != nil { + return errors.New("配置格式错误") + } + + // 更新 + updates := map[string]interface{}{ + "name": req.Name, + "preset_type": req.Type, + "config": datatypes.JSON(configBytes), + } + + if err := global.GVA_DB.Model(&preset).Updates(updates).Error; err != nil { + return err + } + + return nil +} + +// DeleteAIPreset 删除预设 +func (s *AIPresetService) DeleteAIPreset(userID uint, presetID uint) error { + // 检查预设是否存在且属于当前用户 + var preset app.AIPreset + if err := global.GVA_DB.Where("id = ? AND user_id = ?", presetID, userID).First(&preset).Error; err != nil { + return errors.New("预设不存在或无权限") + } + + // 系统预设不允许删除 + if preset.IsSystem { + return errors.New("系统预设不允许删除") + } + + if err := global.GVA_DB.Delete(&preset).Error; err != nil { + return err + } + + return nil +} + +// GetAIPreset 获取预设详情 +func (s *AIPresetService) GetAIPreset(userID uint, presetID uint) (*response.AIPresetResponse, error) { + var preset app.AIPreset + // 可以查看自己的预设或系统预设 + if err := global.GVA_DB.Where("id = ? AND (user_id = ? OR is_system = true)", presetID, userID).First(&preset).Error; err != nil { + return nil, errors.New("预设不存在") + } + + return response.ToAIPresetResponse(&preset), nil +} + +// GetAIPresetList 获取预设列表 +func (s *AIPresetService) GetAIPresetList(userID uint, req *request.AIPresetSearch) ([]response.AIPresetResponse, int64, error) { + var presets []app.AIPreset + var total int64 + + db := global.GVA_DB.Model(&app.AIPreset{}) + + // 只查询自己的预设和系统预设 + db = db.Where("user_id = ? OR is_system = true", userID) + + // 搜索条件 + if req.Name != "" { + db = db.Where("name LIKE ?", "%"+req.Name+"%") + } + if req.Type != "" { + db = db.Where("preset_type = ?", req.Type) + } + + // 获取总数 + if err := db.Count(&total).Error; err != nil { + return nil, 0, err + } + + // 分页查询 + offset := (req.Page - 1) * req.PageSize + if err := db.Order("is_default DESC, updated_at DESC"). + Offset(offset). + Limit(req.PageSize). + Find(&presets).Error; err != nil { + return nil, 0, err + } + + // 转换为响应格式 + var result []response.AIPresetResponse + for _, preset := range presets { + result = append(result, *response.ToAIPresetResponse(&preset)) + } + + return result, total, nil +} + +// DuplicateAIPreset 复制预设 +func (s *AIPresetService) DuplicateAIPreset(userID uint, presetID uint) (*app.AIPreset, error) { + // 获取原预设 + var original app.AIPreset + if err := global.GVA_DB.Where("id = ? AND (user_id = ? OR is_system = true)", presetID, userID).First(&original).Error; err != nil { + return nil, errors.New("预设不存在") + } + + // 创建副本 + duplicate := &app.AIPreset{ + Name: original.Name + " (副本)", + UserID: &userID, + PresetType: original.PresetType, + Config: original.Config, + IsSystem: false, + IsDefault: false, + } + + if err := global.GVA_DB.Create(duplicate).Error; err != nil { + return nil, err + } + + return duplicate, nil +} + +// SetDefaultPreset 设置默认预设 +func (s *AIPresetService) SetDefaultPreset(userID uint, presetID uint) error { + // 检查预设是否存在且属于当前用户 + var preset app.AIPreset + if err := global.GVA_DB.Where("id = ? AND user_id = ?", presetID, userID).First(&preset).Error; err != nil { + return errors.New("预设不存在或无权限") + } + + // 开启事务 + tx := global.GVA_DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 取消当前用户的所有默认预设 + if err := tx.Model(&app.AIPreset{}).Where("user_id = ?", userID).Update("is_default", false).Error; err != nil { + tx.Rollback() + return err + } + + // 设置新的默认预设 + if err := tx.Model(&preset).Update("is_default", true).Error; err != nil { + tx.Rollback() + return err + } + + return tx.Commit().Error +} diff --git a/server/service/app/enter.go b/server/service/app/enter.go index 6ac734a..fdf0ff1 100644 --- a/server/service/app/enter.go +++ b/server/service/app/enter.go @@ -7,4 +7,5 @@ type AppServiceGroup struct { RegexScriptService ProviderService ChatService + AIPresetService } diff --git a/web-app-vue/package-lock.json b/web-app-vue/package-lock.json index 8ff0abf..4228b89 100644 --- a/web-app-vue/package-lock.json +++ b/web-app-vue/package-lock.json @@ -17,6 +17,7 @@ "element-plus": "^2.13.2", "jquery": "^4.0.0", "lodash": "^4.17.23", + "marked": "^17.0.3", "pinia": "^3.0.4", "uuid": "^13.0.0", "vue": "^3.5.25", @@ -1295,7 +1296,7 @@ }, "node_modules/@types/dompurify": { "version": "3.0.5", - "resolved": "https://registry.npmmirror.com/@types/dompurify/-/dompurify-3.0.5.tgz", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", "license": "MIT", "dependencies": { @@ -1770,7 +1771,7 @@ }, "node_modules/dompurify": { "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.1.tgz", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { @@ -2314,6 +2315,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.3.tgz", + "integrity": "sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/web-app-vue/package.json b/web-app-vue/package.json index f773df8..6a9e4b4 100644 --- a/web-app-vue/package.json +++ b/web-app-vue/package.json @@ -18,6 +18,7 @@ "element-plus": "^2.13.2", "jquery": "^4.0.0", "lodash": "^4.17.23", + "marked": "^17.0.3", "pinia": "^3.0.4", "uuid": "^13.0.0", "vue": "^3.5.25", diff --git a/web-app-vue/src/api/preset.ts b/web-app-vue/src/api/preset.ts new file mode 100644 index 0000000..f4ff140 --- /dev/null +++ b/web-app-vue/src/api/preset.ts @@ -0,0 +1,74 @@ +import request from '@/utils/request' + +/** + * 创建AI预设 + */ +export const createPreset = (data: any) => { + return request({ + url: '/app/preset/create', + method: 'post', + data + }) +} + +/** + * 更新AI预设 + */ +export const updatePreset = (id: number, data: any) => { + return request({ + url: `/app/preset/update/${id}`, + method: 'put', + data + }) +} + +/** + * 删除AI预设 + */ +export const deletePreset = (id: number) => { + return request({ + url: `/app/preset/delete/${id}`, + method: 'delete' + }) +} + +/** + * 获取AI预设详情 + */ +export const getPreset = (id: number) => { + return request({ + url: `/app/preset/get/${id}`, + method: 'get' + }) +} + +/** + * 获取AI预设列表 + */ +export const getPresetList = (data: any) => { + return request({ + url: '/app/preset/list', + method: 'post', + data + }) +} + +/** + * 复制AI预设 + */ +export const duplicatePreset = (id: number) => { + return request({ + url: `/app/preset/duplicate/${id}`, + method: 'post' + }) +} + +/** + * 设置默认预设 + */ +export const setDefaultPreset = (id: number) => { + return request({ + url: `/app/preset/setDefault/${id}`, + method: 'post' + }) +} diff --git a/web-app-vue/src/components.d.ts b/web-app-vue/src/components.d.ts index 2588bd1..e39ce84 100644 --- a/web-app-vue/src/components.d.ts +++ b/web-app-vue/src/components.d.ts @@ -11,9 +11,12 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + DrawerContentWrapper: typeof import('./components/DrawerContentWrapper.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] ElAvatar: typeof import('element-plus/es')['ElAvatar'] ElBadge: typeof import('element-plus/es')['ElBadge'] + ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] + ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] ElButton: typeof import('element-plus/es')['ElButton'] ElCard: typeof import('element-plus/es')['ElCard'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] @@ -59,9 +62,14 @@ declare module 'vue' { ElTag: typeof import('element-plus/es')['ElTag'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElUpload: typeof import('element-plus/es')['ElUpload'] + GenericPresetForm: typeof import('./components/preset/GenericPresetForm.vue')['default'] HelloWorld: typeof import('./components/HelloWorld.vue')['default'] + MessageRenderer: typeof import('./components/MessageRenderer.vue')['default'] + OpenAIPresetForm: typeof import('./components/preset/OpenAIPresetForm.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + VariableNode: typeof import('./components/VariableNode.vue')['default'] + VariableViewer: typeof import('./components/VariableViewer.vue')['default'] } export interface GlobalDirectives { vLoading: typeof import('element-plus/es')['ElLoadingDirective'] diff --git a/web-app-vue/src/components/DrawerContentWrapper.vue b/web-app-vue/src/components/DrawerContentWrapper.vue new file mode 100644 index 0000000..37ea89e --- /dev/null +++ b/web-app-vue/src/components/DrawerContentWrapper.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/web-app-vue/src/components/MessageRenderer.vue b/web-app-vue/src/components/MessageRenderer.vue new file mode 100644 index 0000000..960ff70 --- /dev/null +++ b/web-app-vue/src/components/MessageRenderer.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/web-app-vue/src/components/VariableNode.vue b/web-app-vue/src/components/VariableNode.vue new file mode 100644 index 0000000..33ea7b4 --- /dev/null +++ b/web-app-vue/src/components/VariableNode.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/web-app-vue/src/components/VariableViewer.vue b/web-app-vue/src/components/VariableViewer.vue new file mode 100644 index 0000000..55afc21 --- /dev/null +++ b/web-app-vue/src/components/VariableViewer.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/web-app-vue/src/components/preset/GenericPresetForm.vue b/web-app-vue/src/components/preset/GenericPresetForm.vue new file mode 100644 index 0000000..0905d0c --- /dev/null +++ b/web-app-vue/src/components/preset/GenericPresetForm.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/web-app-vue/src/components/preset/OpenAIPresetForm.vue b/web-app-vue/src/components/preset/OpenAIPresetForm.vue new file mode 100644 index 0000000..ca7378f --- /dev/null +++ b/web-app-vue/src/components/preset/OpenAIPresetForm.vue @@ -0,0 +1,326 @@ + + + + + diff --git a/web-app-vue/src/layouts/DefaultLayout.vue b/web-app-vue/src/layouts/DefaultLayout.vue index 027663c..81aea97 100644 --- a/web-app-vue/src/layouts/DefaultLayout.vue +++ b/web-app-vue/src/layouts/DefaultLayout.vue @@ -4,42 +4,85 @@

云酒馆

- - - - + + +
+ 角色广场 - - - - 我的角色卡 - - - - 世界书 - - - - 正则脚本 - - - - 对话 - - - - AI 配置 - - + + + +
- +
@@ -50,7 +93,7 @@ 注册
- +