🎨 优化预设正则解析

This commit is contained in:
2026-03-03 20:33:46 +08:00
parent f8306be916
commit f4ff763b78
12 changed files with 678 additions and 29 deletions

View File

@@ -170,7 +170,7 @@ func (a *AiPresetApi) ImportAiPreset(c *gin.Context) {
return
}
preset, err := aiPresetService.ParseImportedPreset(rawData)
preset, err := aiPresetService.ParseImportedPreset(rawData, "")
if err != nil {
response.FailWithMessage("解析预设失败:"+err.Error(), c)
return
@@ -222,7 +222,13 @@ func (a *AiPresetApi) ImportAiPresetFile(c *gin.Context) {
return
}
preset, err := aiPresetService.ParseImportedPreset(rawData)
// 从文件名提取预设名称(去掉 .json 后缀)
fileName := file.Filename
if strings.HasSuffix(fileName, ".json") {
fileName = fileName[:len(fileName)-5]
}
preset, err := aiPresetService.ParseImportedPreset(rawData, fileName)
if err != nil {
response.FailWithMessage("解析预设失败:"+err.Error(), c)
return

View File

@@ -72,11 +72,11 @@ email:
# system configuration
system:
env: local # 修改为public可以关闭路由日志输出
addr: 8888
db-type: mysql
env: public # 修改为public可以关闭路由日志输出
addr: 8989
db-type: pgsql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-redis: true # 使用redis
use-mongo: false # 使用mongo
use-multipoint: false
# IP限制次数 一个小时15000次
@@ -84,7 +84,7 @@ system:
# IP限制一个小时
iplimit-time: 3600
# 路由全局前缀
router-prefix: ""
router-prefix: /api
# 严格角色模式 打开后权限将会存在上下级关系
use-strict-auth: false

View File

@@ -238,7 +238,7 @@ sqlite:
system:
db-type: pgsql
oss-type: aliyun-oss
router-prefix: ""
router-prefix: "/api"
addr: 8989
iplimit-count: 15000
iplimit-time: 3600

View File

@@ -8,6 +8,8 @@ type ChatCompletionResponse struct {
Model string `json:"model"`
Choices []ChatCompletionChoice `json:"choices"`
Usage ChatCompletionUsage `json:"usage"`
// 扩展字段:正则脚本执行日志
RegexLogs *RegexExecutionLogs `json:"regex_logs,omitempty"`
}
type ChatCompletionChoice struct {
@@ -46,3 +48,19 @@ type ChatMessageDelta struct {
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
}
// RegexExecutionLogs 正则脚本执行日志
type RegexExecutionLogs struct {
InputScripts []RegexScriptLog `json:"input_scripts,omitempty"` // 输入前执行的脚本
OutputScripts []RegexScriptLog `json:"output_scripts,omitempty"` // 输出后执行的脚本
TotalMatches int `json:"total_matches"` // 总匹配次数
}
// RegexScriptLog 单个正则脚本的执行日志
type RegexScriptLog struct {
ScriptName string `json:"script_name"` // 脚本名称
ScriptID string `json:"script_id"` // 脚本ID
Executed bool `json:"executed"` // 是否执行
MatchCount int `json:"match_count"` // 匹配次数
ErrorMessage string `json:"error_message,omitempty"` // 错误信息
}

View File

@@ -26,7 +26,8 @@ func (s *AiModelService) DeleteAiModel(id uint, userID uint) error {
// UpdateAiModel 更新模型
func (s *AiModelService) UpdateAiModel(model *app.AiModel, userID uint) error {
return global.GVA_DB.Where("user_id = ?", userID).Updates(model).Error
// 使用 Select("*") 来更新所有字段,包括零值字段(如 enabled=false
return global.GVA_DB.Model(&app.AiModel{}).Where("id = ? AND user_id = ?", model.ID, userID).Select("*").Updates(model).Error
}
// GetAiModel 查询模型

View File

@@ -46,7 +46,8 @@ func (s *AiPresetService) GetAiPresetList(info request.PageInfo, userID uint) (l
}
// ParseImportedPreset 解析导入的预设,支持 SillyTavern 格式
func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*app.AiPreset, error) {
// defaultName: 当 JSON 中没有名称时使用的默认名称(通常是文件名)
func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}, defaultName string) (*app.AiPreset, error) {
preset := &app.AiPreset{
Enabled: true,
}
@@ -58,6 +59,9 @@ func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*
preset.Name = name
} else if name, ok := rawData["presetName"].(string); ok && name != "" {
preset.Name = name
} else if defaultName != "" {
// 使用默认名称(文件名)
preset.Name = defaultName
} else {
return nil, fmt.Errorf("预设名称不能为空")
}
@@ -110,7 +114,8 @@ func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*
json.Unmarshal(orderData, &preset.PromptOrder)
}
// 处理正则脚本
// 处理正则脚本 - 支持两种格式
// 格式1: 顶层 regex_scripts
if regexScripts, ok := rawData["regex_scripts"].([]interface{}); ok {
scriptsData, _ := json.Marshal(regexScripts)
json.Unmarshal(scriptsData, &preset.RegexScripts)
@@ -118,6 +123,20 @@ func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*
// 处理扩展配置
if extensions, ok := rawData["extensions"].(map[string]interface{}); ok {
// 格式2: extensions.regex_scripts (SillyTavern 格式)
if regexScripts, ok := extensions["regex_scripts"].([]interface{}); ok {
scriptsData, _ := json.Marshal(regexScripts)
// 同时填充到 RegexScripts 和 Extensions.RegexBinding.Regexes
json.Unmarshal(scriptsData, &preset.RegexScripts)
// 确保 Extensions.RegexBinding 被初始化
if preset.Extensions.RegexBinding == nil {
preset.Extensions.RegexBinding = &app.RegexBindingConfig{}
}
json.Unmarshal(scriptsData, &preset.Extensions.RegexBinding.Regexes)
}
// 解析其他扩展配置
extData, _ := json.Marshal(extensions)
json.Unmarshal(extData, &preset.Extensions)
}

View File

@@ -8,16 +8,29 @@ import (
"git.echol.cn/loser/ai_proxy/server/model/app"
"git.echol.cn/loser/ai_proxy/server/model/app/request"
"git.echol.cn/loser/ai_proxy/server/model/app/response"
)
// PresetInjector 预设注入器
type PresetInjector struct {
preset *app.AiPreset
preset *app.AiPreset
regexLogs *response.RegexExecutionLogs
}
// NewPresetInjector 创建预设注入器
func NewPresetInjector(preset *app.AiPreset) *PresetInjector {
return &PresetInjector{preset: preset}
return &PresetInjector{
preset: preset,
regexLogs: &response.RegexExecutionLogs{
InputScripts: []response.RegexScriptLog{},
OutputScripts: []response.RegexScriptLog{},
},
}
}
// GetRegexLogs 获取正则脚本执行日志
func (p *PresetInjector) GetRegexLogs() *response.RegexExecutionLogs {
return p.regexLogs
}
// InjectMessages 注入预设到消息列表
@@ -196,8 +209,8 @@ func (p *PresetInjector) applyRegexScripts(messages []request.ChatMessage, place
// 检查 placement
hasPlacement := false
for _, p := range script.Placement {
if p == placement {
for _, pl := range script.Placement {
if pl == placement {
hasPlacement = true
break
}
@@ -206,15 +219,34 @@ func (p *PresetInjector) applyRegexScripts(messages []request.ChatMessage, place
continue
}
// 应用正则替换
messages = p.applyRegexScript(messages, script)
// 应用正则替换并记录日志
var matchCount int
var err error
messages, matchCount, err = p.applyRegexScriptWithLog(messages, script)
// 记录执行日志
log := response.RegexScriptLog{
ScriptName: script.ScriptName,
ScriptID: script.ID,
Executed: true,
MatchCount: matchCount,
}
if err != nil {
log.ErrorMessage = err.Error()
}
// 根据 placement 添加到对应的日志列表
if placement == 1 {
p.regexLogs.InputScripts = append(p.regexLogs.InputScripts, log)
}
p.regexLogs.TotalMatches += matchCount
}
return messages
}
// applyRegexScript 应用单个正则脚本
func (p *PresetInjector) applyRegexScript(messages []request.ChatMessage, script app.RegexScript) []request.ChatMessage {
// applyRegexScriptWithLog 应用单个正则脚本并返回匹配次数
func (p *PresetInjector) applyRegexScriptWithLog(messages []request.ChatMessage, script app.RegexScript) ([]request.ChatMessage, int, error) {
// 解析正则表达式
pattern := script.FindRegex
// 移除正则标志(如 /pattern/g)
@@ -229,19 +261,25 @@ func (p *PresetInjector) applyRegexScript(messages []request.ChatMessage, script
re, err := regexp.Compile(pattern)
if err != nil {
return messages
return messages, 0, fmt.Errorf("正则编译失败: %v", err)
}
matchCount := 0
// 对每条消息应用替换
for i := range messages {
if script.PromptOnly && messages[i].Role != "user" {
continue
}
// 统计匹配次数
matches := re.FindAllString(messages[i].Content, -1)
matchCount += len(matches)
// 执行替换
messages[i].Content = re.ReplaceAllString(messages[i].Content, script.ReplaceString)
}
return messages
return messages, matchCount, nil
}
// ProcessResponse 处理AI响应(应用输出后的正则)
@@ -280,10 +318,31 @@ func (p *PresetInjector) ProcessResponse(content string) string {
re, err := regexp.Compile(pattern)
if err != nil {
// 记录错误日志
p.regexLogs.OutputScripts = append(p.regexLogs.OutputScripts, response.RegexScriptLog{
ScriptName: script.ScriptName,
ScriptID: script.ID,
Executed: false,
ErrorMessage: fmt.Sprintf("正则编译失败: %v", err),
})
continue
}
// 统计匹配次数
matches := re.FindAllString(content, -1)
matchCount := len(matches)
// 执行替换
content = re.ReplaceAllString(content, script.ReplaceString)
// 记录执行日志
p.regexLogs.OutputScripts = append(p.regexLogs.OutputScripts, response.RegexScriptLog{
ScriptName: script.ScriptName,
ScriptID: script.ID,
Executed: true,
MatchCount: matchCount,
})
p.regexLogs.TotalMatches += matchCount
}
return content

View File

@@ -34,8 +34,9 @@ func (s *AiProxyService) ProcessChatCompletion(ctx context.Context, req *request
}
// 2. 注入预设
var injector *PresetInjector
if preset != nil {
injector := NewPresetInjector(preset)
injector = NewPresetInjector(preset)
req.Messages = injector.InjectMessages(req.Messages)
injector.ApplyPresetParameters(req)
}
@@ -46,10 +47,15 @@ func (s *AiProxyService) ProcessChatCompletion(ctx context.Context, req *request
return nil, err
}
// 4. 处理响应
if preset != nil && len(resp.Choices) > 0 {
injector := NewPresetInjector(preset)
// 4. 处理响应并收集正则日志
if preset != nil && injector != nil && len(resp.Choices) > 0 {
resp.Choices[0].Message.Content = injector.ProcessResponse(resp.Choices[0].Message.Content)
// 添加正则执行日志到响应
regexLogs := injector.GetRegexLogs()
if regexLogs.TotalMatches > 0 || len(regexLogs.InputScripts) > 0 || len(regexLogs.OutputScripts) > 0 {
resp.RegexLogs = regexLogs
}
}
return resp, nil

View File

@@ -4,6 +4,7 @@
"/src/view/ai/binding/index.vue": "Index",
"/src/view/ai/model/index.vue": "Index",
"/src/view/ai/preset/components/PromptEditor.vue": "PromptEditor",
"/src/view/ai/preset/components/RegexEditor.vue": "RegexEditor",
"/src/view/ai/preset/index.vue": "Index",
"/src/view/ai/provider/index.vue": "Index",
"/src/view/dashboard/components/banner.vue": "Banner",

View File

@@ -0,0 +1,526 @@
<template>
<el-dialog
v-model="visible"
title="正则脚本编辑器"
width="1200px"
:close-on-click-modal="false"
@close="handleClose"
>
<div class="regex-editor">
<div class="toolbar">
<el-button type="primary" size="small" @click="addScript">
<el-icon><Plus /></el-icon>
添加脚本
</el-button>
<el-button type="success" size="small" @click="importScripts">
<el-icon><Upload /></el-icon>
导入脚本
</el-button>
<el-button type="warning" size="small" @click="testRegex" :disabled="!currentScript">
<el-icon><View /></el-icon>
测试当前脚本
</el-button>
</div>
<div class="editor-content">
<div class="script-list">
<div class="list-header">
<span>脚本列表 ({{ scripts.length }})</span>
</div>
<el-scrollbar height="500px">
<div
v-for="(script, index) in scripts"
:key="script.id || index"
:class="['script-item', { active: currentIndex === index, disabled: script.disabled }]"
@click="selectScript(index)"
>
<div class="script-info">
<div class="script-name">
<el-icon v-if="script.disabled"><CircleClose /></el-icon>
<el-icon v-else><CircleCheck /></el-icon>
{{ script.scriptName || '未命名脚本' }}
</div>
<div class="script-placement">
<el-tag v-if="script.placement?.includes(1)" size="small" type="info">输入前</el-tag>
<el-tag v-if="script.placement?.includes(2)" size="small" type="success">输出后</el-tag>
</div>
</div>
<div class="script-actions">
<el-button
type="primary"
link
size="small"
@click.stop="duplicateScript(index)"
>
<el-icon><CopyDocument /></el-icon>
</el-button>
<el-button
type="danger"
link
size="small"
@click.stop="deleteScript(index)"
>
<el-icon><Delete /></el-icon>
</el-button>
</div>
</div>
</el-scrollbar>
</div>
<div class="script-editor" v-if="currentScript">
<el-form :model="currentScript" label-width="120px" label-position="left">
<el-form-item label="脚本名称">
<el-input v-model="currentScript.scriptName" placeholder="请输入脚本名称" />
</el-form-item>
<el-form-item label="正则表达式">
<el-input
v-model="currentScript.findRegex"
type="textarea"
:rows="3"
placeholder="请输入正则表达式,如: /<pattern>/flags"
/>
<div class="hint">支持 JavaScript 正则表达式格式: /pattern/gi</div>
</el-form-item>
<el-form-item label="替换字符串">
<el-input
v-model="currentScript.replaceString"
type="textarea"
:rows="2"
placeholder="替换内容,支持 $1, $2 等捕获组引用"
/>
</el-form-item>
<el-form-item label="执行位置">
<el-checkbox-group v-model="currentScript.placement">
<el-checkbox :value="1">用户输入前处理</el-checkbox>
<el-checkbox :value="2">AI输出后处理</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="应用深度">
<el-row :gutter="10">
<el-col :span="12">
<el-input-number
v-model="currentScript.minDepth"
placeholder="最小深度"
:min="0"
style="width: 100%"
/>
<div class="hint">最小深度 (null=不限制)</div>
</el-col>
<el-col :span="12">
<el-input-number
v-model="currentScript.maxDepth"
placeholder="最大深度"
:min="0"
style="width: 100%"
/>
<div class="hint">最大深度 (null=不限制)</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="选项">
<el-checkbox v-model="currentScript.disabled">禁用此脚本</el-checkbox>
<el-checkbox v-model="currentScript.markdownOnly">仅处理 Markdown</el-checkbox>
<el-checkbox v-model="currentScript.promptOnly">仅处理提示词</el-checkbox>
<el-checkbox v-model="currentScript.runOnEdit">编辑时运行</el-checkbox>
</el-form-item>
<el-form-item label="修剪字符串">
<el-select
v-model="currentScript.trimStrings"
multiple
filterable
allow-create
placeholder="输入要修剪的字符串"
style="width: 100%"
>
</el-select>
<div class="hint">在应用正则前要删除的字符串列表</div>
</el-form-item>
</el-form>
</div>
<div v-else class="empty-state">
<el-empty description="请选择或创建一个脚本" />
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
</template>
<!-- 测试对话框 -->
<el-dialog v-model="testDialogVisible" title="测试正则脚本" width="800px" append-to-body>
<el-form label-width="100px">
<el-form-item label="测试文本">
<el-input
v-model="testInput"
type="textarea"
:rows="6"
placeholder="输入要测试的文本"
/>
</el-form-item>
<el-form-item label="匹配结果">
<el-input
v-model="testOutput"
type="textarea"
:rows="6"
readonly
placeholder="替换后的结果将显示在这里"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="testDialogVisible = false">关闭</el-button>
<el-button type="primary" @click="runTest">执行测试</el-button>
</template>
</el-dialog>
</el-dialog>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
Upload,
View,
Delete,
CopyDocument,
CircleCheck,
CircleClose
} from '@element-plus/icons-vue'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
regexScripts: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:modelValue', 'save'])
const visible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
const scripts = ref([])
const currentIndex = ref(-1)
const testDialogVisible = ref(false)
const testInput = ref('')
const testOutput = ref('')
const currentScript = computed(() => {
if (currentIndex.value >= 0 && currentIndex.value < scripts.value.length) {
return scripts.value[currentIndex.value]
}
return null
})
watch(
() => props.modelValue,
(val) => {
if (val) {
scripts.value = JSON.parse(JSON.stringify(props.regexScripts || []))
if (scripts.value.length > 0) {
currentIndex.value = 0
} else {
currentIndex.value = -1
}
}
}
)
const generateId = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0
const v = c === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}
const addScript = () => {
const newScript = {
id: generateId(),
scriptName: '新建脚本',
findRegex: '',
replaceString: '',
trimStrings: [],
placement: [2],
disabled: false,
markdownOnly: false,
promptOnly: false,
runOnEdit: false,
substituteRegex: 0,
minDepth: null,
maxDepth: null
}
scripts.value.push(newScript)
currentIndex.value = scripts.value.length - 1
ElMessage.success('已添加新脚本')
}
const selectScript = (index) => {
currentIndex.value = index
}
const duplicateScript = (index) => {
const original = scripts.value[index]
const duplicate = {
...JSON.parse(JSON.stringify(original)),
id: generateId(),
scriptName: original.scriptName + ' (副本)'
}
scripts.value.splice(index + 1, 0, duplicate)
currentIndex.value = index + 1
ElMessage.success('已复制脚本')
}
const deleteScript = (index) => {
ElMessageBox.confirm('确定要删除这个脚本吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
scripts.value.splice(index, 1)
if (currentIndex.value >= scripts.value.length) {
currentIndex.value = scripts.value.length - 1
}
ElMessage.success('已删除脚本')
}).catch(() => {})
}
const importScripts = () => {
ElMessageBox.prompt('请粘贴正则脚本 JSON 数组', '导入脚本', {
confirmButtonText: '导入',
cancelButtonText: '取消',
inputType: 'textarea'
}).then(({ value }) => {
try {
const imported = JSON.parse(value)
if (Array.isArray(imported)) {
imported.forEach(script => {
if (!script.id) {
script.id = generateId()
}
})
scripts.value.push(...imported)
ElMessage.success(`成功导入 ${imported.length} 个脚本`)
} else {
ElMessage.error('导入的数据必须是数组格式')
}
} catch (error) {
ElMessage.error('JSON 解析失败: ' + error.message)
}
}).catch(() => {})
}
const testRegex = () => {
if (!currentScript.value) {
ElMessage.warning('请先选择一个脚本')
return
}
testInput.value = ''
testOutput.value = ''
testDialogVisible.value = true
}
const runTest = () => {
if (!testInput.value) {
ElMessage.warning('请输入测试文本')
return
}
try {
let text = testInput.value
// 应用 trimStrings
if (currentScript.value.trimStrings && currentScript.value.trimStrings.length > 0) {
currentScript.value.trimStrings.forEach(trim => {
text = text.split(trim).join('')
})
}
// 解析正则表达式
let regex
const regexStr = currentScript.value.findRegex
// 尝试解析 /pattern/flags 格式
const match = regexStr.match(/^\/(.+)\/([gimsuvy]*)$/)
if (match) {
regex = new RegExp(match[1], match[2])
} else {
// 直接作为正则表达式
regex = new RegExp(regexStr, 'g')
}
// 执行替换
testOutput.value = text.replace(regex, currentScript.value.replaceString || '')
ElMessage.success('测试完成')
} catch (error) {
ElMessage.error('正则表达式错误: ' + error.message)
testOutput.value = '错误: ' + error.message
}
}
const handleClose = () => {
visible.value = false
}
const handleSave = () => {
// 验证脚本
for (let i = 0; i < scripts.value.length; i++) {
const script = scripts.value[i]
if (!script.scriptName) {
ElMessage.warning(`${i + 1} 个脚本缺少名称`)
currentIndex.value = i
return
}
if (!script.findRegex) {
ElMessage.warning(`脚本 "${script.scriptName}" 缺少正则表达式`)
currentIndex.value = i
return
}
}
emit('save', scripts.value)
visible.value = false
}
</script>
<style scoped>
.regex-editor {
display: flex;
flex-direction: column;
gap: 15px;
}
.toolbar {
display: flex;
gap: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.editor-content {
display: flex;
gap: 15px;
height: 550px;
}
.script-list {
width: 280px;
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.list-header {
padding: 10px 15px;
background: #f5f7fa;
border-bottom: 1px solid #ebeef5;
font-weight: 500;
font-size: 14px;
}
.script-item {
padding: 12px 15px;
border-bottom: 1px solid #ebeef5;
cursor: pointer;
transition: all 0.2s;
display: flex;
justify-content: space-between;
align-items: center;
}
.script-item:hover {
background: #f5f7fa;
}
.script-item.active {
background: #ecf5ff;
border-left: 3px solid #409eff;
}
.script-item.disabled {
opacity: 0.6;
}
.script-info {
flex: 1;
min-width: 0;
}
.script-name {
font-size: 14px;
font-weight: 500;
margin-bottom: 5px;
display: flex;
align-items: center;
gap: 5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.script-placement {
display: flex;
gap: 5px;
}
.script-actions {
display: flex;
gap: 5px;
opacity: 0;
transition: opacity 0.2s;
}
.script-item:hover .script-actions {
opacity: 1;
}
.script-editor {
flex: 1;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 20px;
overflow-y: auto;
}
.empty-state {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed #dcdfe6;
border-radius: 4px;
}
.hint {
font-size: 12px;
color: #909399;
margin-top: 5px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style>

View File

@@ -185,6 +185,13 @@
@save="handlePromptSave"
/>
<!-- 正则脚本编辑器 -->
<RegexEditor
v-model="regexEditorVisible"
:regex-scripts="formData.regex_scripts"
@save="handleRegexSave"
/>
<!-- 查看预设对话框 -->
<el-dialog v-model="viewDialogVisible" title="预设详情" width="900px">
<el-descriptions :column="2" border>
@@ -253,6 +260,7 @@ import {
importAiPresetFile
} from '@/api/aiPreset'
import PromptEditor from './components/PromptEditor.vue'
import RegexEditor from './components/RegexEditor.vue'
const page = ref(1)
const pageSize = ref(10)
@@ -271,6 +279,7 @@ const importTabActive = ref('file')
const uploadRef = ref(null)
const uploadFile = ref(null)
const promptEditorVisible = ref(false)
const regexEditorVisible = ref(false)
const formData = ref({
name: '',
@@ -468,7 +477,12 @@ const handlePromptSave = (prompts) => {
}
const openRegexEditor = () => {
ElMessage.info('正则脚本编辑器功能开发中...')
regexEditorVisible.value = true
}
const handleRegexSave = (regexScripts) => {
formData.value.regex_scripts = regexScripts
ElMessage.success('正则脚本已更新')
}
getTableData()

View File

@@ -76,9 +76,8 @@ export default ({ mode }) => {
[process.env.VITE_BASE_API]: {
// 需要代理的路径 例如 '/api'
target: `${process.env.VITE_BASE_PATH}:${process.env.VITE_SERVER_PORT}/`, // 代理到 目标路径
changeOrigin: true,
rewrite: (path) =>
path.replace(new RegExp('^' + process.env.VITE_BASE_API), '')
changeOrigin: true
// 不需要 rewrite保留 /api 前缀
},
"/plugin": {
// 需要代理的路径 例如 '/api'