- 后端: TenantApp 模型添加 custom_configs 字段 (LONGTEXT) - 后端: tenant_apps API 支持自定义配置的增删改查 - 前端: 应用订阅编辑对话框增加自定义配置编辑区域 - 支持 key-value-备注 三字段结构 - value 使用 textarea 支持超长文本(如提示词)
This commit is contained in:
@@ -23,6 +23,10 @@ class TenantApp(Base):
|
||||
# 功能权限
|
||||
allowed_tools = Column(Text) # JSON 数组
|
||||
|
||||
# 自定义配置(JSON 数组)
|
||||
# [{"key": "industry", "value": "medical_beauty", "remark": "医美行业"}, ...]
|
||||
custom_configs = Column(Text)
|
||||
|
||||
status = Column(SmallInteger, default=1)
|
||||
created_at = Column(TIMESTAMP, default=datetime.now)
|
||||
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
@@ -17,6 +17,13 @@ router = APIRouter(prefix="/tenant-apps", tags=["应用配置"])
|
||||
|
||||
# Schemas
|
||||
|
||||
class CustomConfigItem(BaseModel):
|
||||
"""自定义配置项"""
|
||||
key: str # 配置键
|
||||
value: str # 配置值
|
||||
remark: Optional[str] = None # 备注说明
|
||||
|
||||
|
||||
class TenantAppCreate(BaseModel):
|
||||
tenant_id: str
|
||||
app_code: str = "tools"
|
||||
@@ -25,6 +32,7 @@ class TenantAppCreate(BaseModel):
|
||||
access_token: Optional[str] = None # 如果不传则自动生成
|
||||
allowed_origins: Optional[List[str]] = None
|
||||
allowed_tools: Optional[List[str]] = None
|
||||
custom_configs: Optional[List[CustomConfigItem]] = None # 自定义配置
|
||||
|
||||
|
||||
class TenantAppUpdate(BaseModel):
|
||||
@@ -33,6 +41,7 @@ class TenantAppUpdate(BaseModel):
|
||||
access_token: Optional[str] = None
|
||||
allowed_origins: Optional[List[str]] = None
|
||||
allowed_tools: Optional[List[str]] = None
|
||||
custom_configs: Optional[List[CustomConfigItem]] = None # 自定义配置
|
||||
status: Optional[int] = None
|
||||
|
||||
|
||||
@@ -111,6 +120,7 @@ async def create_tenant_app(
|
||||
access_token=access_token,
|
||||
allowed_origins=json.dumps(data.allowed_origins) if data.allowed_origins else None,
|
||||
allowed_tools=json.dumps(data.allowed_tools) if data.allowed_tools else None,
|
||||
custom_configs=json.dumps([c.model_dump() for c in data.custom_configs], ensure_ascii=False) if data.custom_configs else None,
|
||||
status=1
|
||||
)
|
||||
db.add(app)
|
||||
@@ -139,6 +149,14 @@ async def update_tenant_app(
|
||||
update_data['allowed_origins'] = json.dumps(update_data['allowed_origins']) if update_data['allowed_origins'] else None
|
||||
if 'allowed_tools' in update_data:
|
||||
update_data['allowed_tools'] = json.dumps(update_data['allowed_tools']) if update_data['allowed_tools'] else None
|
||||
if 'custom_configs' in update_data:
|
||||
if update_data['custom_configs']:
|
||||
update_data['custom_configs'] = json.dumps(
|
||||
[c.model_dump() if hasattr(c, 'model_dump') else c for c in update_data['custom_configs']],
|
||||
ensure_ascii=False
|
||||
)
|
||||
else:
|
||||
update_data['custom_configs'] = None
|
||||
|
||||
for key, value in update_data.items():
|
||||
setattr(app, key, value)
|
||||
@@ -228,6 +246,7 @@ def format_tenant_app(app: TenantApp, mask_secret: bool = True, db: Session = No
|
||||
"access_token": "******" if mask_secret and app.access_token else app.access_token,
|
||||
"allowed_origins": json.loads(app.allowed_origins) if app.allowed_origins else [],
|
||||
"allowed_tools": json.loads(app.allowed_tools) if app.allowed_tools else [],
|
||||
"custom_configs": json.loads(app.custom_configs) if app.custom_configs else [],
|
||||
"status": app.status,
|
||||
"created_at": app.created_at,
|
||||
"updated_at": app.updated_at
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Delete, Plus } from '@element-plus/icons-vue'
|
||||
import api from '@/api'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
@@ -36,7 +37,8 @@ const form = reactive({
|
||||
tenant_id: '',
|
||||
app_code: '',
|
||||
app_name: '',
|
||||
wechat_app_id: null
|
||||
wechat_app_id: null,
|
||||
custom_configs: [] // 自定义配置 [{key, value, remark}]
|
||||
})
|
||||
|
||||
// 当前选择的应用是否需要 JS-SDK
|
||||
@@ -142,7 +144,8 @@ function handleCreate() {
|
||||
tenant_id: '',
|
||||
app_code: '',
|
||||
app_name: '',
|
||||
wechat_app_id: null
|
||||
wechat_app_id: null,
|
||||
custom_configs: []
|
||||
})
|
||||
wechatAppList.value = []
|
||||
dialogVisible.value = true
|
||||
@@ -155,12 +158,22 @@ async function handleEdit(row) {
|
||||
tenant_id: row.tenant_id,
|
||||
app_code: row.app_code,
|
||||
app_name: row.app_name || '',
|
||||
wechat_app_id: row.wechat_app_id || null
|
||||
wechat_app_id: row.wechat_app_id || null,
|
||||
custom_configs: row.custom_configs ? [...row.custom_configs] : []
|
||||
})
|
||||
await fetchWechatApps(row.tenant_id)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 自定义配置管理
|
||||
function addCustomConfig() {
|
||||
form.custom_configs.push({ key: '', value: '', remark: '' })
|
||||
}
|
||||
|
||||
function removeCustomConfig(index) {
|
||||
form.custom_configs.splice(index, 1)
|
||||
}
|
||||
|
||||
|
||||
async function handleSubmit() {
|
||||
await formRef.value.validate()
|
||||
@@ -301,6 +314,14 @@ onMounted(() => {
|
||||
<el-tag v-else type="danger" size="small">未生成</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="自定义配置" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.custom_configs && row.custom_configs.length > 0" type="primary" size="small">
|
||||
{{ row.custom_configs.length }} 项
|
||||
</el-tag>
|
||||
<span v-else style="color: #909399; font-size: 12px">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
||||
@@ -330,7 +351,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- 编辑对话框 -->
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="550px">
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="750px">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="租户" prop="tenant_id">
|
||||
<el-select
|
||||
@@ -379,6 +400,51 @@ onMounted(() => {
|
||||
</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 自定义配置 -->
|
||||
<el-divider content-position="left">自定义配置</el-divider>
|
||||
|
||||
<div class="custom-configs-section">
|
||||
<div v-for="(config, index) in form.custom_configs" :key="index" class="config-item">
|
||||
<div class="config-row">
|
||||
<el-input
|
||||
v-model="config.key"
|
||||
placeholder="配置键 (如: industry)"
|
||||
style="width: 150px"
|
||||
/>
|
||||
<el-input
|
||||
v-model="config.remark"
|
||||
placeholder="备注说明"
|
||||
style="width: 180px; margin-left: 8px"
|
||||
/>
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
circle
|
||||
size="small"
|
||||
style="margin-left: 8px"
|
||||
@click="removeCustomConfig(index)"
|
||||
/>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="config.value"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:autosize="{ minRows: 2, maxRows: 10 }"
|
||||
placeholder="配置值(支持超长文本,如提示词等)"
|
||||
style="margin-top: 8px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-button type="primary" plain @click="addCustomConfig" style="margin-top: 8px">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加配置项
|
||||
</el-button>
|
||||
|
||||
<div v-if="form.custom_configs.length === 0" class="config-empty-tip">
|
||||
暂无自定义配置,点击上方按钮添加
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
@@ -449,4 +515,28 @@ onMounted(() => {
|
||||
margin-bottom: 8px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
/* 自定义配置样式 */
|
||||
.custom-configs-section {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
background: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.config-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.config-empty-tip {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user