write-channel-manage.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. const fs = require('fs')
  2. const path = 'd:/ylrz_saas_new/adminui/src/views/saas/tenant/ChannelManage.vue'
  3. const content = `<template>
  4. <div v-loading="loading">
  5. <el-alert type="info" :closable="false" show-icon style="margin-bottom: 12px">
  6. <template slot="title">{{ labels.alertTitle }}</template>
  7. </el-alert>
  8. <el-table :data="plugins" border size="small" max-height="420">
  9. <el-table-column :label="labels.colGrant" width="70" align="center">
  10. <template slot-scope="{ row }">
  11. <el-checkbox v-model="row.granted" />
  12. </template>
  13. </el-table-column>
  14. <el-table-column prop="channelType" :label="labels.colCode" width="110" />
  15. <el-table-column :label="labels.colName" min-width="130">
  16. <template slot-scope="{ row }">
  17. <i :class="row.icon" style="margin-right: 4px" />{{ row.displayName }}
  18. </template>
  19. </el-table-column>
  20. <el-table-column label="SDK" width="88" align="center">
  21. <template slot-scope="{ row }">
  22. <el-tag :type="row.hasMessageChannel ? 'success' : 'warning'" size="mini">
  23. {{ row.hasMessageChannel ? labels.sdkReady : labels.sdkPending }}
  24. </el-tag>
  25. </template>
  26. </el-table-column>
  27. <el-table-column :label="labels.colEnable" width="72" align="center">
  28. <template slot-scope="{ row }">
  29. <el-switch
  30. v-model="row._enabled"
  31. :disabled="!row.granted || !row.hasMessageChannel"
  32. />
  33. </template>
  34. </el-table-column>
  35. <el-table-column :label="labels.colCredential" min-width="260">
  36. <template slot-scope="{ row }">
  37. <template v-if="!row.granted">
  38. <span class="text-muted">{{ labels.notGranted }}</span>
  39. </template>
  40. <template v-else-if="isBuiltin(row.channelType)">
  41. <el-tag size="mini" type="info">{{ labels.builtinNoConfig }}</el-tag>
  42. </template>
  43. <template v-else-if="row.channelType === 'WHATSAPP'">
  44. <div class="config-row">
  45. <el-input v-model="row._waPhone" placeholder="Phone Number ID" size="mini" class="cfg-md" />
  46. <el-input v-model="row._waToken" placeholder="Token" size="mini" class="cfg-md" type="password" show-password />
  47. </div>
  48. </template>
  49. <template v-else>
  50. <el-input v-model="row._apiKey" placeholder="API Key / Webhook URL" size="mini" class="cfg-lg" />
  51. </template>
  52. </template>
  53. </el-table-column>
  54. <el-table-column :label="labels.colAction" width="88" fixed="right" align="center">
  55. <template slot-scope="{ row }">
  56. <el-button
  57. type="text"
  58. size="mini"
  59. :disabled="!row.granted || !row._enabled"
  60. @click="testChannel(row)"
  61. >{{ labels.test }}</el-button>
  62. </template>
  63. </el-table-column>
  64. </el-table>
  65. </div>
  66. </template>
  67. <script>
  68. import { getTenantLobsterChannels, saveTenantLobsterChannels, testTenantLobsterChannel } from '@/api/admin/lobsterChannel'
  69. const LABELS = {
  70. alertTitle: '\\u6e20\\u9053\\u7531\\u603b\\u540e\\u53f0\\u7edf\\u4e00\\u5f00\\u901a\\u4e0e\\u914d\\u7f6e\\uff08\\u6d89\\u53ca\\u79df\\u6237\\u8ba1\\u8d39\\uff09\\u3002\\u52fe\\u9009\\u300c\\u5f00\\u901a\\u300d\\u6388\\u6743\\u6e20\\u9053\\uff1b\\u5bf9\\u5df2\\u5f00\\u901a\\u6e20\\u9053\\u53ef\\u542f\\u7528\\u5e76\\u586b\\u5199\\u7b2c\\u4e09\\u65b9\\u51ed\\u8bc1\\u3002',
  71. colGrant: '\\u5f00\\u901a',
  72. colCode: '\\u7f16\\u7801',
  73. colName: '\\u6e20\\u9053',
  74. colEnable: '\\u542f\\u7528',
  75. colCredential: '\\u51ed\\u8bc1',
  76. colAction: '\\u64cd\\u4f5c',
  77. sdkReady: '\\u5df2\\u63a5\\u5165',
  78. sdkPending: '\\u5f85\\u63a5\\u5165',
  79. notGranted: '\\u672a\\u5f00\\u901a',
  80. builtinNoConfig: '\\u5185\\u7f6e\\u514d\\u914d\\u7f6e',
  81. test: '\\u6d4b\\u8bd5',
  82. needOneChannel: '\\u8bf7\\u81f3\\u5c11\\u5f00\\u901a\\u4e00\\u4e2a\\u6e20\\u9053',
  83. saveOk: '\\u6e20\\u9053\\u914d\\u7f6e\\u5df2\\u4fdd\\u5b58',
  84. testOk: '\\u8fde\\u63a5\\u6b63\\u5e38',
  85. testFail: '\\u8fde\\u63a5\\u5931\\u8d25'
  86. }
  87. export default {
  88. name: 'TenantChannelManage',
  89. props: {
  90. tenantId: { type: [Number, String], required: true }
  91. },
  92. data() {
  93. return {
  94. loading: false,
  95. plugins: [],
  96. labels: LABELS
  97. }
  98. },
  99. watch: {
  100. tenantId: {
  101. immediate: true,
  102. handler(v) {
  103. if (v) this.load()
  104. }
  105. }
  106. },
  107. methods: {
  108. isBuiltin(type) {
  109. return type === 'QW' || type === 'WX' || type === 'IM'
  110. },
  111. load() {
  112. this.loading = true
  113. getTenantLobsterChannels(this.tenantId).then(res => {
  114. const list = res.data || []
  115. this.plugins = list.map(row => this.decorateRow(row))
  116. }).finally(() => { this.loading = false })
  117. },
  118. decorateRow(row) {
  119. const cfg = this.parseConfig(row.configJson)
  120. return {
  121. ...row,
  122. granted: row.granted === true || row.granted === 1,
  123. _enabled: row.enabled === 1 || row.enabled === true,
  124. _waPhone: cfg.phoneNumberId || '',
  125. _waToken: cfg.token || '',
  126. _apiKey: cfg.apiKey || ''
  127. }
  128. },
  129. parseConfig(json) {
  130. if (!json) return {}
  131. try {
  132. return typeof json === 'string' ? JSON.parse(json) : json
  133. } catch (e) {
  134. return {}
  135. }
  136. },
  137. buildPayload() {
  138. const channelTypes = this.plugins.filter(p => p.granted).map(p => p.channelType)
  139. if (!channelTypes.length) {
  140. this.$message.warning(LABELS.needOneChannel)
  141. return null
  142. }
  143. const plugins = this.plugins.filter(p => p.granted).map(p => {
  144. let config = {}
  145. if (!this.isBuiltin(p.channelType)) {
  146. if (p.channelType === 'WHATSAPP') {
  147. config = { phoneNumberId: p._waPhone, token: p._waToken }
  148. } else {
  149. config = { apiKey: p._apiKey }
  150. }
  151. }
  152. return {
  153. channelType: p.channelType,
  154. enabled: p._enabled,
  155. config
  156. }
  157. })
  158. return { channelTypes, plugins }
  159. },
  160. save() {
  161. const payload = this.buildPayload()
  162. if (!payload) return Promise.reject()
  163. this.loading = true
  164. return saveTenantLobsterChannels(this.tenantId, payload).then(() => {
  165. this.$message.success(LABELS.saveOk)
  166. this.load()
  167. this.$emit('success')
  168. }).finally(() => { this.loading = false })
  169. },
  170. testChannel(row) {
  171. testTenantLobsterChannel(this.tenantId, row.channelType).then(res => {
  172. const r = res.data || {}
  173. this.$message({
  174. type: r.ok ? 'success' : 'error',
  175. message: r.reason || (r.ok ? LABELS.testOk : LABELS.testFail)
  176. })
  177. })
  178. }
  179. }
  180. }
  181. </script>
  182. <style scoped>
  183. .config-row { display: flex; gap: 8px; flex-wrap: wrap; }
  184. .cfg-md { width: 140px; }
  185. .cfg-lg { width: 200px; }
  186. .text-muted { color: #909399; font-size: 12px; }
  187. </style>
  188. `
  189. fs.writeFileSync(path, content, 'utf8')
  190. console.log('written', path)