包含前后端完整代码: - 前端:Vue3+Vite+ElementPlus - 后端:FastAPI+SQLAlchemy - 模块:驾驶舱/KPI/战略地图/预警/预算/成本/预测/改善行动 - 当前版本:v1.0.0
202 lines
7.8 KiB
Vue
202 lines
7.8 KiB
Vue
<template>
|
|
<div>
|
|
<div style="display:flex;justify-content:space-between;align-items:center;">
|
|
<h3>通知配置</h3>
|
|
<el-button type="primary" @click="showAddDialog = true">添加渠道</el-button>
|
|
</div>
|
|
|
|
<!-- 渠道列表 -->
|
|
<el-table :data="channels" v-loading="loading" style="width:100%;margin-top:16px;">
|
|
<el-table-column prop="name" label="名称" width="150" />
|
|
<el-table-column prop="channel_type" label="类型" width="120">
|
|
<template #default="{ row }">
|
|
<el-tag :type="row.channel_type === 'wecom' ? 'success' : row.channel_type === 'wecom_app' ? 'warning' : 'primary'">
|
|
{{ row.channel_type === 'wecom' ? '企业微信' : row.channel_type === 'wecom_app' ? '企微应用' : '邮件' }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="配置摘要" min-width="200">
|
|
<template #default="{ row }">
|
|
<span v-if="row.channel_type === 'wecom'" style="font-size:12px;color:#999;">
|
|
Webhook: {{ (row.config?.webhook_url || '').substring(0, 40) }}...
|
|
</span>
|
|
<span v-else-if="row.channel_type === 'mail'" style="font-size:12px;color:#999;">
|
|
收件人: {{ (row.config?.to || []).join(', ') }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="状态" width="80">
|
|
<template #default="{ row }">
|
|
<el-switch :model-value="row.enabled" @change="toggleChannel(row)" size="small" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="160">
|
|
<template #default="{ row }">
|
|
<el-button size="small" @click="testChannel(row)">测试</el-button>
|
|
<el-button size="small" type="danger" plain @click="deleteChannel(row)">删除</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<el-empty v-if="!loading && channels.length === 0" description="暂无通知渠道,请添加" />
|
|
|
|
<!-- 通知日志 -->
|
|
<h3 style="margin-top:30px;">发送记录</h3>
|
|
<el-table :data="logs" style="width:100%;margin-top:8px;" size="small">
|
|
<el-table-column prop="created_at" label="时间" width="160" />
|
|
<el-table-column prop="title" label="标题" min-width="200" />
|
|
<el-table-column prop="channel" label="渠道" width="80" />
|
|
<el-table-column prop="recipient" label="接收方" width="120" />
|
|
<el-table-column prop="status" label="状态" width="80">
|
|
<template #default="{ row }">
|
|
<el-tag :type="row.status === 'sent' ? 'success' : 'danger'" size="small">{{ row.status === 'sent' ? '成功' : '失败' }}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<!-- 添加渠道对话框 -->
|
|
<MyDialog v-model="showAddDialog" title="添加通知渠道" :width="500">
|
|
<el-form :model="form" label-width="100px">
|
|
<el-form-item label="名称"><el-input v-model="form.name" placeholder="如:管理群、财务部邮箱" /></el-form-item>
|
|
<el-form-item label="类型">
|
|
<el-select v-model="form.channel_type" style="width:100%;">
|
|
<el-option value="wecom" label="企业微信群机器人" />
|
|
<el-option value="wecom_app" label="企微应用消息" />
|
|
<el-option value="mail" label="邮件" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item v-if="form.channel_type === 'wecom'" label="Webhook地址">
|
|
<el-input v-model="form.webhook_url" type="textarea" rows="2" placeholder="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx" />
|
|
</el-form-item>
|
|
<template v-if="form.channel_type === 'wecom_app'">
|
|
<el-form-item label="CorpID"><el-input v-model="form.corp_id" placeholder="企业ID,如 wx..." /></el-form-item>
|
|
<el-form-item label="CorpSecret"><el-input v-model="form.corp_secret" type="password" show-password /></el-form-item>
|
|
<el-form-item label="AgentID"><el-input v-model="form.agent_id" placeholder="应用AgentID,如 1000020" /></el-form-item>
|
|
<el-form-item label="接收人UserID"><el-input v-model="form.touser" placeholder="企微成员UserID,多个用 | 分隔" /></el-form-item>
|
|
</template>
|
|
<template v-if="form.channel_type === 'mail'">
|
|
<el-form-item label="SMTP地址"><el-input v-model="form.smtp_host" placeholder="smtp.qq.com" /></el-form-item>
|
|
<el-form-item label="端口"><el-input v-model="form.smtp_port" placeholder="465" /></el-form-item>
|
|
<el-form-item label="账号"><el-input v-model="form.smtp_user" placeholder="xxx@qq.com" /></el-form-item>
|
|
<el-form-item label="密码"><el-input v-model="form.smtp_password" type="password" show-password /></el-form-item>
|
|
<el-form-item label="收件人"><el-input v-model="form.mail_to" placeholder="用逗号分隔多个邮箱" /></el-form-item>
|
|
</template>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="showAddDialog = false">取消</el-button>
|
|
<el-button type="primary" @click="saveChannel" :loading="saving">保存</el-button>
|
|
</template>
|
|
</MyDialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted, reactive } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { notificationApi } from '../api/index'
|
|
import MyDialog from '../components/MyDialog.vue'
|
|
|
|
const loading = ref(false)
|
|
const saving = ref(false)
|
|
const channels = ref<any[]>([])
|
|
const logs = ref<any[]>([])
|
|
const showAddDialog = ref(false)
|
|
const form = reactive({
|
|
name: '',
|
|
channel_type: 'wecom',
|
|
webhook_url: '',
|
|
corp_id: '',
|
|
corp_secret: '',
|
|
agent_id: '',
|
|
touser: '',
|
|
smtp_host: 'smtp.qq.com',
|
|
smtp_port: '465',
|
|
smtp_user: '',
|
|
smtp_password: '',
|
|
mail_to: '',
|
|
})
|
|
|
|
async function loadChannels() {
|
|
loading.value = true
|
|
try {
|
|
const r: any = await notificationApi.list()
|
|
channels.value = r.data || []
|
|
} catch (e: any) {
|
|
ElMessage.error('加载失败')
|
|
}
|
|
loading.value = false
|
|
}
|
|
|
|
async function loadLogs() {
|
|
try {
|
|
const r: any = await notificationApi.logs()
|
|
logs.value = r.data || []
|
|
} catch (_) {}
|
|
}
|
|
|
|
async function saveChannel() {
|
|
saving.value = true
|
|
try {
|
|
const config: any = {}
|
|
if (form.channel_type === 'wecom') {
|
|
config.webhook_url = form.webhook_url
|
|
} else if (form.channel_type === 'wecom_app') {
|
|
config.corp_id = form.corp_id
|
|
config.corp_secret = form.corp_secret
|
|
config.agent_id = form.agent_id
|
|
config.touser = form.touser
|
|
} else {
|
|
config.host = form.smtp_host
|
|
config.port = form.smtp_port
|
|
config.user = form.smtp_user
|
|
config.password = form.smtp_password
|
|
config.from_addr = form.smtp_user
|
|
config.to = form.mail_to.split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
}
|
|
await notificationApi.create({
|
|
name: form.name,
|
|
channel_type: form.channel_type,
|
|
config,
|
|
})
|
|
ElMessage.success('添加成功')
|
|
showAddDialog.value = false
|
|
loadChannels()
|
|
} catch (e: any) {
|
|
ElMessage.error(e?.response?.data?.detail || '保存失败')
|
|
}
|
|
saving.value = false
|
|
}
|
|
|
|
async function toggleChannel(row: any) {
|
|
try {
|
|
await notificationApi.update(row.id, { enabled: !row.enabled })
|
|
row.enabled = !row.enabled
|
|
} catch (_) {}
|
|
}
|
|
|
|
async function testChannel(row: any) {
|
|
try {
|
|
const r: any = await notificationApi.test(row.id)
|
|
const result = r.results?.[0]
|
|
if (result?.success) {
|
|
ElMessage.success(result.message || '测试推送成功')
|
|
} else {
|
|
ElMessage.warning(result?.message || '推送失败')
|
|
}
|
|
} catch (e: any) {
|
|
ElMessage.error('测试异常')
|
|
}
|
|
}
|
|
|
|
async function deleteChannel(row: any) {
|
|
try {
|
|
await ElMessageBox.confirm('确定删除此通知渠道?', '确认')
|
|
await notificationApi.delete(row.id)
|
|
ElMessage.success('已删除')
|
|
loadChannels()
|
|
} catch (_) {}
|
|
}
|
|
|
|
onMounted(() => { loadChannels(); loadLogs() })
|
|
</script>
|