feat: add Feishu notification configuration UI

This commit is contained in:
jxxghp
2026-05-12 21:40:56 +08:00
parent 9a480dd803
commit b3c8faab70
10 changed files with 284 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -47,6 +47,7 @@ const notificationInfo = ref<NotificationConf>({
// 各通知类型的名称字典
const notificationTypeNames: { [key: string]: string } = {
wechat: t('notification.wechat.name'),
feishu: t('notification.feishu.name'),
wechatclawbot: t('notification.wechatclawbot.name'),
telegram: t('notification.telegram.name'),
qqbot: t('notification.qqbot.name'),
@@ -417,6 +418,8 @@ const getIcon = computed(() => {
return getLogoUrl('wechat')
case 'wechatclawbot':
return getLogoUrl('wechatclawbot')
case 'feishu':
return getLogoUrl('feishu')
case 'telegram':
return getLogoUrl('telegram')
case 'qqbot':
@@ -777,6 +780,84 @@ watch(notificationInfoDialog, value => {
</VCard>
</VCol>
</VRow>
<VRow v-else-if="notificationInfo.type == 'feishu'">
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.name"
:label="t('notification.name')"
:placeholder="t('notification.name')"
:hint="t('notification.nameHint')"
persistent-hint
prepend-inner-icon="mdi-label"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.config.FEISHU_APP_ID"
:label="t('notification.feishu.appId')"
:hint="t('notification.feishu.appIdHint')"
persistent-hint
prepend-inner-icon="mdi-application"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.config.FEISHU_APP_SECRET"
:label="t('notification.feishu.appSecret')"
:hint="t('notification.feishu.appSecretHint')"
persistent-hint
prepend-inner-icon="mdi-key"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.config.FEISHU_OPEN_ID"
:label="t('notification.feishu.openId')"
:placeholder="t('notification.feishu.openIdPlaceholder')"
:hint="t('notification.feishu.openIdHint')"
persistent-hint
prepend-inner-icon="mdi-account"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.config.FEISHU_CHAT_ID"
:label="t('notification.feishu.chatId')"
:placeholder="t('notification.feishu.chatIdPlaceholder')"
:hint="t('notification.feishu.chatIdHint')"
persistent-hint
prepend-inner-icon="mdi-chat-processing"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.config.FEISHU_ADMINS"
:label="t('notification.feishu.admins')"
:placeholder="t('notification.feishu.adminsPlaceholder')"
:hint="t('notification.feishu.adminsHint')"
persistent-hint
prepend-inner-icon="mdi-account-supervisor"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.config.FEISHU_VERIFICATION_TOKEN"
:label="t('notification.feishu.verificationToken')"
:hint="t('notification.feishu.verificationTokenHint')"
persistent-hint
prepend-inner-icon="mdi-shield-key"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="notificationInfo.config.FEISHU_ENCRYPT_KEY"
:label="t('notification.feishu.encryptKey')"
:hint="t('notification.feishu.encryptKeyHint')"
persistent-hint
prepend-inner-icon="mdi-lock"
/>
</VCol>
</VRow>
<VRow v-else-if="notificationInfo.type == 'telegram'">
<VCol cols="12" md="6">
<VTextField

View File

@@ -328,6 +328,7 @@ export function useSetupWizard() {
},
// 通知映射
notification: {
'feishu': 'FeishuModule',
'telegram': 'TelegramModule',
'wechat': 'WechatModule',
'wechatclawbot': 'WechatClawBotModule',
@@ -427,6 +428,7 @@ export function useSetupWizard() {
if (!wizardData.value.notification.name || wizardData.value.notification.name.includes('通知')) {
const displayNameMap: Record<string, string> = {
wechat: '企业微信',
feishu: '飞书',
wechatclawbot: '微信 ClawBot',
telegram: 'Telegram',
slack: 'Slack',
@@ -679,6 +681,16 @@ export function useSetupWizard() {
break
case 'wechatclawbot':
break
case 'feishu':
if (!config.FEISHU_APP_ID?.trim()) {
errors.push(t('notification.feishu.appIdRequired'))
validationErrors.value.notification.FEISHU_APP_ID = true
}
if (!config.FEISHU_APP_SECRET?.trim()) {
errors.push(t('notification.feishu.appSecretRequired'))
validationErrors.value.notification.FEISHU_APP_SECRET = true
}
break
case 'telegram':
if (!config.TELEGRAM_TOKEN?.trim()) {
errors.push(t('notification.telegram.tokenRequired'))

View File

@@ -505,6 +505,28 @@ export default {
logoutSuccess: 'WeChat ClawBot logged out',
logoutFailed: 'Failed to logout WeChat ClawBot',
},
feishu: {
name: 'Feishu',
appId: 'App ID',
appIdHint: 'App ID of the Feishu Open Platform application',
appIdRequired: 'App ID cannot be empty',
appSecret: 'App Secret',
appSecretHint: 'App Secret of the Feishu Open Platform application',
appSecretRequired: 'App Secret cannot be empty',
openId: 'Default User Open ID',
openIdHint: 'Default recipient user Open ID; leave empty to prefer recent interacted users',
openIdPlaceholder: 'ou_xxx',
chatId: 'Default Group Chat ID',
chatIdHint: 'Default recipient group chat ID; either this or Open ID is enough',
chatIdPlaceholder: 'oc_xxx',
admins: 'Admin Whitelist',
adminsHint: 'Open IDs allowed to run commands and admin actions, separated by commas',
adminsPlaceholder: 'Open ID list, separated by commas',
verificationToken: 'Verification Token',
verificationTokenHint: 'Verification Token for Feishu event subscription, required when validation is enabled',
encryptKey: 'Encrypt Key',
encryptKeyHint: 'Encrypt Key for Feishu event subscription, required when encryption is enabled',
},
telegram: {
name: 'Telegram',
token: 'Bot Token',
@@ -1769,6 +1791,7 @@ export default {
channel: 'Notification',
wechat: 'WeChat Work',
wechatClawBot: 'WeChat ClawBot',
feishu: 'Feishu',
resourceDownload: 'Resource Download',
mediaImport: 'Media Import',
subscription: 'Subscription',
@@ -2851,6 +2874,7 @@ export default {
accountBinding: 'Account Binding',
wechatUser: 'WeChat Work User',
wechatClawBotUser: 'WeChat ClawBot User',
feishuUser: 'Feishu User',
telegramUser: 'Telegram User',
slackUser: 'Slack User',
discordUser: 'Discord User',
@@ -3441,6 +3465,7 @@ export default {
typeHint: 'Select the type of notification channel to use',
name: 'Notification Name',
nameHint: 'Set a name for the notification channel',
feishuConfig: 'Feishu Configuration',
telegramConfig: 'Telegram Configuration',
emailConfig: 'Email Configuration',
botToken: 'Bot Token',

View File

@@ -500,6 +500,28 @@ export default {
logoutSuccess: '微信 ClawBot 已退出登录',
logoutFailed: '微信 ClawBot 退出登录失败',
},
feishu: {
name: '飞书',
appId: 'App ID',
appIdHint: '飞书开放平台应用的 App ID',
appIdRequired: 'App ID 不能为空',
appSecret: 'App Secret',
appSecretHint: '飞书开放平台应用的 App Secret',
appSecretRequired: 'App Secret 不能为空',
openId: '默认用户 Open ID',
openIdHint: '默认通知接收用户的 Open ID留空则优先使用互动用户',
openIdPlaceholder: 'ou_xxx',
chatId: '默认群聊 Chat ID',
chatIdHint: '默认通知接收群聊的 Chat ID和 Open ID 二选一即可',
chatIdPlaceholder: 'oc_xxx',
admins: '管理员白名单',
adminsHint: '允许执行命令和管理操作的 Open ID 列表,多个使用 , 分隔',
adminsPlaceholder: 'Open ID 列表,多个使用 , 分隔',
verificationToken: 'Verification Token',
verificationTokenHint: '飞书事件订阅的 Verification Token启用事件校验时填写',
encryptKey: 'Encrypt Key',
encryptKeyHint: '飞书事件订阅的 Encrypt Key启用消息加密时填写',
},
telegram: {
name: 'Telegram',
token: 'Bot Token',
@@ -1739,6 +1761,7 @@ export default {
channel: '通知',
wechat: '企业微信',
wechatClawBot: '微信 ClawBot',
feishu: '飞书',
resourceDownload: '资源下载',
mediaImport: '整理入库',
subscription: '订阅',
@@ -2803,6 +2826,7 @@ export default {
accountBinding: '账号绑定',
wechatUser: '企业微信用户',
wechatClawBotUser: '微信 ClawBot 用户',
feishuUser: '飞书用户',
telegramUser: 'Telegram用户',
slackUser: 'Slack用户',
discordUser: 'Discord用户',
@@ -3386,6 +3410,7 @@ export default {
typeHint: '选择要使用的通知渠道类型',
name: '通知名称',
nameHint: '为通知渠道设置一个名称',
feishuConfig: '飞书配置',
telegramConfig: 'Telegram 配置',
emailConfig: '邮件配置',
botToken: '机器人令牌',

View File

@@ -501,6 +501,28 @@ export default {
logoutSuccess: '微信 ClawBot 已退出登入',
logoutFailed: '微信 ClawBot 退出登入失敗',
},
feishu: {
name: '飛書',
appId: 'App ID',
appIdHint: '飛書開放平台應用的 App ID',
appIdRequired: 'App ID 不能為空',
appSecret: 'App Secret',
appSecretHint: '飛書開放平台應用的 App Secret',
appSecretRequired: 'App Secret 不能為空',
openId: '預設用戶 Open ID',
openIdHint: '預設通知接收用戶的 Open ID留空則優先使用互動用戶',
openIdPlaceholder: 'ou_xxx',
chatId: '預設群聊 Chat ID',
chatIdHint: '預設通知接收群聊的 Chat ID和 Open ID 二選一即可',
chatIdPlaceholder: 'oc_xxx',
admins: '管理員白名單',
adminsHint: '允許執行命令與管理操作的 Open ID 列表,多個使用 , 分隔',
adminsPlaceholder: 'Open ID 列表,多個使用 , 分隔',
verificationToken: 'Verification Token',
verificationTokenHint: '飛書事件訂閱的 Verification Token啟用事件校驗時填寫',
encryptKey: 'Encrypt Key',
encryptKeyHint: '飛書事件訂閱的 Encrypt Key啟用消息加密時填寫',
},
telegram: {
name: 'Telegram',
token: 'Bot Token',
@@ -1741,6 +1763,7 @@ export default {
channel: '通知',
wechat: '企業微信',
wechatClawBot: '微信 ClawBot',
feishu: '飛書',
resourceDownload: '資源下載',
mediaImport: '整理入庫',
subscription: '訂閱',
@@ -2805,6 +2828,7 @@ export default {
accountBinding: '賬號綁定',
wechatUser: '企業微信用戶',
wechatClawBotUser: '微信 ClawBot 用戶',
feishuUser: '飛書用戶',
telegramUser: 'Telegram用戶',
slackUser: 'Slack用戶',
discordUser: 'Discord用戶',
@@ -3388,6 +3412,7 @@ export default {
typeHint: '選擇要使用的通知管道類型',
name: '通知名稱',
nameHint: '為通知管道設定一個名稱',
feishuConfig: '飛書設定',
telegramConfig: 'Telegram 設定',
emailConfig: '郵件設定',
botToken: '機器人權杖',

View File

@@ -14,6 +14,7 @@ import plexLogo from '@/assets/images/logos/plex.png'
import trimemediaLogo from '@/assets/images/logos/trimemedia.png'
import ugreenLogo from '@/assets/images/logos/ugreen.png'
import wechatLogo from '@/assets/images/logos/wechat.png'
import feishuLogo from '@/assets/images/logos/feishu.png'
import clawbotLogo from '@/assets/images/logos/clawbot.png'
import telegramLogo from '@/assets/images/logos/telegram.webp'
import slackLogo from '@/assets/images/logos/slack.webp'
@@ -47,6 +48,7 @@ const logoMap: Record<string, string> = {
trimemedia: trimemediaLogo,
ugreen: ugreenLogo,
wechat: wechatLogo,
feishu: feishuLogo,
wechatclawbot: clawbotLogo,
telegram: telegramLogo,
slack: slackLogo,

View File

@@ -357,9 +357,12 @@ onMounted(() => {
<VListItem @click="addNotification('wechatclawbot')">
<VListItemTitle>{{ t('setting.notification.wechatClawBot') }}</VListItemTitle>
</VListItem>
<VListItem @click="addNotification('telegram')">
<VListItemTitle>{{ t('setting.notification.telegram') }}</VListItemTitle>
<VListItem @click="addNotification('feishu')">
<VListItemTitle>{{ t('setting.notification.feishu') }}</VListItemTitle>
</VListItem>
<VListItem @click="addNotification('telegram')">
<VListItemTitle>{{ t('setting.notification.telegram') }}</VListItemTitle>
</VListItem>
<VListItem @click="addNotification('slack')">
<VListItemTitle>{{ t('setting.notification.slack') }}</VListItemTitle>
</VListItem>

View File

@@ -66,6 +66,19 @@ const notificationTypes = [
</VCardText>
</VCard>
</VCol>
<VCol cols="12" md="3">
<VCard
:color="wizardData.notification.type === 'feishu' ? 'primary' : 'default'"
:variant="wizardData.notification.type === 'feishu' ? 'tonal' : 'outlined'"
class="cursor-pointer"
@click="selectNotification('feishu')"
>
<VCardText class="text-center">
<VImg :src="getLogoUrl('feishu')" height="48" width="48" class="mx-auto mb-2" />
<div class="text-h6">飞书</div>
</VCardText>
</VCard>
</VCol>
<VCol cols="12" md="3">
<VCard
:color="wizardData.notification.type === 'telegram' ? 'primary' : 'default'"
@@ -308,6 +321,93 @@ const notificationTypes = [
/>
</VCol>
</VRow>
<VRow v-else-if="wizardData.notification.type === 'feishu'">
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.name"
:label="t('notification.name')"
:placeholder="t('notification.name')"
:hint="t('notification.nameHint')"
:error="validationErrors.notification.name"
:error-messages="validationErrors.notification.name ? [t('notification.nameRequired')] : []"
persistent-hint
prepend-inner-icon="mdi-label"
required
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.config.FEISHU_APP_ID"
:label="t('notification.feishu.appId')"
:hint="t('notification.feishu.appIdHint')"
:error="validationErrors.notification.FEISHU_APP_ID"
:error-messages="validationErrors.notification.FEISHU_APP_ID ? [t('notification.feishu.appIdRequired')] : []"
persistent-hint
prepend-inner-icon="mdi-application"
required
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.config.FEISHU_APP_SECRET"
:label="t('notification.feishu.appSecret')"
:hint="t('notification.feishu.appSecretHint')"
:error="validationErrors.notification.FEISHU_APP_SECRET"
:error-messages="validationErrors.notification.FEISHU_APP_SECRET ? [t('notification.feishu.appSecretRequired')] : []"
persistent-hint
prepend-inner-icon="mdi-key"
required
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.config.FEISHU_OPEN_ID"
:label="t('notification.feishu.openId')"
:placeholder="t('notification.feishu.openIdPlaceholder')"
:hint="t('notification.feishu.openIdHint')"
persistent-hint
prepend-inner-icon="mdi-account"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.config.FEISHU_CHAT_ID"
:label="t('notification.feishu.chatId')"
:placeholder="t('notification.feishu.chatIdPlaceholder')"
:hint="t('notification.feishu.chatIdHint')"
persistent-hint
prepend-inner-icon="mdi-chat-processing"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.config.FEISHU_ADMINS"
:label="t('notification.feishu.admins')"
:placeholder="t('notification.feishu.adminsPlaceholder')"
:hint="t('notification.feishu.adminsHint')"
persistent-hint
prepend-inner-icon="mdi-account-supervisor"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.config.FEISHU_VERIFICATION_TOKEN"
:label="t('notification.feishu.verificationToken')"
:hint="t('notification.feishu.verificationTokenHint')"
persistent-hint
prepend-inner-icon="mdi-shield-key"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.notification.config.FEISHU_ENCRYPT_KEY"
:label="t('notification.feishu.encryptKey')"
:hint="t('notification.feishu.encryptKeyHint')"
persistent-hint
prepend-inner-icon="mdi-lock"
/>
</VCol>
</VRow>
<VRow v-else-if="wizardData.notification.type === 'telegram'">
<VCol cols="12" md="6">
<VTextField

View File

@@ -445,6 +445,15 @@ watch(
prepend-inner-icon="mdi-robot-happy-outline"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="accountInfo.settings.feishu_openid"
density="comfortable"
clearable
:label="t('profile.feishuUser')"
prepend-inner-icon="mdi-message-badge-outline"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="accountInfo.settings.telegram_userid"