feat: 租户详情页添加瑞美云配置 Tab
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- 提供友好的表单界面配置瑞美云连接信息 - 支持保存配置和测试连接 - 私钥加密存储 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -230,8 +230,137 @@ watch(activeTab, (newVal) => {
|
|||||||
fetchToolConfigs()
|
fetchToolConfigs()
|
||||||
fetchConfigSchema()
|
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(() => {
|
onMounted(() => {
|
||||||
fetchDetail()
|
fetchDetail()
|
||||||
})
|
})
|
||||||
@@ -304,6 +433,73 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</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 -->
|
<!-- 工具配置 Tab -->
|
||||||
<el-tab-pane label="工具配置" name="config">
|
<el-tab-pane label="工具配置" name="config">
|
||||||
<div class="config-container" v-loading="configLoading">
|
<div class="config-container" v-loading="configLoading">
|
||||||
@@ -506,4 +702,15 @@ onMounted(() => {
|
|||||||
color: #909399;
|
color: #909399;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ruimeiyun-container {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user