feat: 租户详情页添加瑞美云配置 Tab
All checks were successful
continuous-integration/drone/push Build is passing

- 提供友好的表单界面配置瑞美云连接信息
- 支持保存配置和测试连接
- 私钥加密存储

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-03 11:43:22 +08:00
parent afcf30b519
commit facb854e3d

View File

@@ -230,8 +230,137 @@ watch(activeTab, (newVal) => {
fetchToolConfigs()
fetchConfigSchema()
}
if (newVal === 'ruimeiyun' && !ruimeiyunLoaded.value) {
fetchRuimeiyunConfig()
}
})
// ========================================
// 瑞美云配置
// ========================================
const ruimeiyunLoading = ref(false)
const ruimeiyunLoaded = ref(false)
const ruimeiyunTesting = ref(false)
const ruimeiyunFormRef = ref(null)
const ruimeiyunForm = reactive({
base_url: '',
account: '',
private_key: '',
allowed_apis: ''
})
const ruimeiyunRules = {
base_url: [{ required: true, message: '请输入睿美云 API 地址', trigger: 'blur' }],
account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
private_key: [{ required: true, message: '请输入私钥', trigger: 'blur' }]
}
async function fetchRuimeiyunConfig() {
ruimeiyunLoading.value = true
try {
const res = await api.get('/api/tool-configs', {
params: { tenant_id: tenantId, tool_code: 'ruimeiyun', size: 100 }
})
const items = res.data.items || []
// 映射配置到表单
items.forEach(item => {
if (item.config_key === 'ruimeiyun_base_url') {
ruimeiyunForm.base_url = item.config_value || ''
} else if (item.config_key === 'ruimeiyun_account') {
ruimeiyunForm.account = item.config_value || ''
} else if (item.config_key === 'ruimeiyun_private_key') {
// 加密字段显示占位符
ruimeiyunForm.private_key = item.is_encrypted ? '********' : (item.config_value || '')
} else if (item.config_key === 'ruimeiyun_allowed_apis') {
ruimeiyunForm.allowed_apis = item.config_value || ''
}
})
ruimeiyunLoaded.value = true
} catch (e) {
console.error('获取瑞美云配置失败:', e)
} finally {
ruimeiyunLoading.value = false
}
}
async function saveRuimeiyunConfig() {
await ruimeiyunFormRef.value.validate()
ruimeiyunLoading.value = true
try {
// 构建配置列表
const configs = [
{
config_type: 'external_api',
config_key: 'ruimeiyun_base_url',
config_value: ruimeiyunForm.base_url,
is_encrypted: 0,
description: '睿美云 API 地址'
},
{
config_type: 'external_api',
config_key: 'ruimeiyun_account',
config_value: ruimeiyunForm.account,
is_encrypted: 0,
description: '睿美云账号'
}
]
// 如果私钥不是占位符,则更新
if (ruimeiyunForm.private_key && ruimeiyunForm.private_key !== '********') {
configs.push({
config_type: 'external_api',
config_key: 'ruimeiyun_private_key',
config_value: ruimeiyunForm.private_key,
is_encrypted: 1,
description: '睿美云私钥'
})
}
// 如果有接口限制
if (ruimeiyunForm.allowed_apis) {
configs.push({
config_type: 'external_api',
config_key: 'ruimeiyun_allowed_apis',
config_value: ruimeiyunForm.allowed_apis,
is_encrypted: 0,
description: '允许调用的接口列表'
})
}
await api.post('/api/tool-configs/batch', {
tenant_id: tenantId,
tool_code: 'ruimeiyun',
configs
})
ElMessage.success('保存成功')
// 重新加载
ruimeiyunLoaded.value = false
fetchRuimeiyunConfig()
} catch (e) {
console.error('保存失败:', e)
} finally {
ruimeiyunLoading.value = false
}
}
async function testRuimeiyunConnection() {
ruimeiyunTesting.value = true
try {
const res = await api.get(`/api/ruimeiyun/health/${tenantId}`)
if (res.data.status === 'connected') {
ElMessage.success(`连接成功!账号: ${res.data.account}`)
} else {
ElMessage.error(`连接失败: ${res.data.message}`)
}
} catch (e) {
ElMessage.error(`测试失败: ${e.response?.data?.detail || e.message}`)
} finally {
ruimeiyunTesting.value = false
}
}
onMounted(() => {
fetchDetail()
})
@@ -304,6 +433,73 @@ onMounted(() => {
</div>
</el-tab-pane>
<!-- 瑞美云配置 Tab -->
<el-tab-pane label="瑞美云配置" name="ruimeiyun">
<div class="ruimeiyun-container" v-loading="ruimeiyunLoading">
<el-alert
type="info"
:closable="false"
style="margin-bottom: 20px"
>
<template #title>
配置租户的睿美云 TPOS 接口连接信息配置后可通过 Platform 代理调用睿美云接口
</template>
</el-alert>
<el-form
ref="ruimeiyunFormRef"
:model="ruimeiyunForm"
:rules="ruimeiyunRules"
label-width="120px"
style="max-width: 600px"
>
<el-form-item label="API 地址" prop="base_url">
<el-input
v-model="ruimeiyunForm.base_url"
placeholder="例如: https://xxx.ruimeiyun.com"
/>
<div class="form-hint">睿美云 TPOS 接口的基础地址</div>
</el-form-item>
<el-form-item label="账号" prop="account">
<el-input
v-model="ruimeiyunForm.account"
placeholder="TPOS 接口账号"
/>
</el-form-item>
<el-form-item label="私钥" prop="private_key">
<el-input
v-model="ruimeiyunForm.private_key"
type="textarea"
:rows="4"
placeholder="RSA 私钥PEM 格式)"
/>
<div class="form-hint">用于 TPOS 接口签名的 RSA 私钥将加密存储</div>
</el-form-item>
<el-form-item label="接口限制">
<el-input
v-model="ruimeiyunForm.allowed_apis"
type="textarea"
:rows="2"
placeholder='可选JSON 数组格式,例如: ["customer.search", "order.list"]'
/>
<div class="form-hint">限制租户可调用的接口留空表示允许所有接口</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveRuimeiyunConfig" :loading="ruimeiyunLoading">
保存配置
</el-button>
<el-button @click="testRuimeiyunConnection" :loading="ruimeiyunTesting">
测试连接
</el-button>
</el-form-item>
</el-form>
</div>
</el-tab-pane>
<!-- 工具配置 Tab -->
<el-tab-pane label="工具配置" name="config">
<div class="config-container" v-loading="configLoading">
@@ -506,4 +702,15 @@ onMounted(() => {
color: #909399;
font-family: monospace;
}
.ruimeiyun-container {
padding: 0 4px;
}
.form-hint {
font-size: 12px;
color: #909399;
line-height: 1.5;
margin-top: 4px;
}
</style>