فهرست منبع

update:admin 租户点播配置

ct 1 ماه پیش
والد
کامیت
33127b2ac1
4فایلهای تغییر یافته به همراه298 افزوده شده و 35 حذف شده
  1. 10 1
      src/api/admin/sysCompany.js
  2. 13 3
      src/api/system/config.js
  3. 274 30
      src/views/admin/frontConfig/index.vue
  4. 1 1
      src/views/admin/menu.js

+ 10 - 1
src/api/admin/sysCompany.js

@@ -17,6 +17,15 @@ export function listAllCompanies(query) {
   })
 }
 
+// 租户下拉列表(分页:pageNum、pageSize;搜索:tenantName;筛选:status 等)
+export function listAdminTenantList(query) {
+  return request({
+    url: '/admin/company/tenantList',
+    method: 'get',
+    params: query
+  })
+}
+
 // 获取租户详情
 export function getCompanyInfo(companyId) {
   return request({
@@ -113,4 +122,4 @@ export function resetTenantPwd(companyId, password) {
     method: 'put',
     data: { password }
   })
-}
+}

+ 13 - 3
src/api/system/config.js

@@ -17,10 +17,20 @@ export function getConfig(configId) {
   })
 }
 
-export function getConfigByKey(configKey) {
+/**
+ * 按 configKey 查询配置
+ * @param {string} configKey 配置键
+ * @param {string|number} [tenantId] 租户ID;传入则查该租户库,不传则查主库
+ */
+export function getConfigByKey(configKey, tenantId) {
+  const params = {}
+  if (tenantId !== undefined && tenantId !== null && tenantId !== '') {
+    params.tenantId = tenantId
+  }
   return request({
     url: '/system/config/getConfigByKey/' + configKey,
-    method: 'get'
+    method: 'get',
+    params
   })
 }
 // 根据参数键名查询参数值
@@ -97,4 +107,4 @@ export function getGatewayList(query) {
     method: 'get',
     params: query
   })
-}
+}

+ 274 - 30
src/views/admin/frontConfig/index.vue

@@ -1,9 +1,63 @@
 <template>
   <div class="app-container">
     <el-card v-loading="loading" shadow="never">
-      <div slot="header">
-        <span>前端配置</span>
+      <div slot="header" class="page-header">
+        <span>点播线路配置</span>
+        <el-form :inline="true" size="small" class="tenant-form">
+          <el-form-item label="选择租户">
+            <el-select
+              ref="tenantSelect"
+              v-model="selectedTenantId"
+              placeholder="请输入租户名称搜索"
+              filterable
+              remote
+              clearable
+              :remote-method="handleTenantSearch"
+              :loading="tenantSelectLoading"
+              style="width: 280px"
+              @visible-change="handleTenantDropdownVisible"
+              @clear="handleTenantSelectClear"
+              @change="loadConfig"
+            >
+              <el-option
+                v-for="item in tenantList"
+                :key="item.id"
+                :label="formatTenantLabel(item)"
+                :value="item.id"
+              />
+              <el-option v-if="hasMoreTenants" key="tenant-load-more" disabled class="tenant-load-more-option">
+                <div class="load-more" @click.stop="loadMoreTenants">
+                  <span>加载更多</span>
+                  <i v-if="tenantLoadingMore" class="el-icon-loading" />
+                </div>
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-form>
       </div>
+
+      <el-alert
+        v-if="!selectedTenantId"
+        title="当前为系统默认配置(主库),保存后将更新全局配置;选择租户后可单独配置该租户参数"
+        type="info"
+        :closable="false"
+        show-icon
+        class="tenant-tip"
+      />
+
+      <el-alert
+        v-if="selectedTenantId && tenantConfigEmpty"
+        title="该租户尚未保存过此配置,可直接填写后保存;如需参考系统默认配置,可点击下方按钮加载(不会自动保存)。"
+        type="warning"
+        :closable="false"
+        show-icon
+        class="tenant-tip"
+      >
+        <el-button type="text" size="small" :loading="fallbackLoading" @click="loadGlobalConfigAsFallback">
+          加载系统默认配置
+        </el-button>
+      </el-alert>
+
       <el-form ref="form" :model="form" label-width="200px">
         <!-- 存储桶配置 -->
         <el-card shadow="never" class="section-card">
@@ -125,8 +179,8 @@
             </el-col>
           </el-row>
         </el-card>
+        </el-form>
 
-      </el-form>
       <div style="text-align: center; margin-top: 20px; padding-bottom: 20px;">
         <el-button type="primary" size="medium" @click="submitForm" :loading="submitLoading">保存配置</el-button>
       </div>
@@ -136,6 +190,23 @@
 
 <script>
 import { getConfigByKey, updateConfigByKey } from '@/api/system/config'
+import { listAdminTenantList } from '@/api/admin/sysCompany'
+
+const defaultForm = () => ({
+  obsAccessKeyId: '',
+  obsSecretAccessKey: '',
+  obsServer: '',
+  obsBucket: '',
+  cosBucket: '',
+  cosRegion: '',
+  videoLinePrimary: '',
+  videoLineSecondary: '',
+  livePath: '',
+  volcanoVideoDomain: '',
+  volcanoVodSpace: '',
+  liveWebSocketUrl: '',
+  courseDefaultType: '1'
+})
 
 export default {
   name: 'AdminFrontConfig',
@@ -145,56 +216,193 @@ export default {
       submitLoading: false,
       configId: null,
       configKey: 'his.adminUi.config',
-      form: {
-        obsAccessKeyId: '',
-        obsSecretAccessKey: '',
-        obsServer: '',
-        obsBucket: '',
-        cosBucket: '',
-        cosRegion: '',
-        videoLinePrimary: '',
-        videoLineSecondary: '',
-        livePath: '',
-        volcanoVideoDomain: '',
-        volcanoVodSpace: '',
-        liveWebSocketUrl: '',
-        courseDefaultType: '1'
-      }
+      selectedTenantId: null,
+      tenantConfigEmpty: false,
+      fallbackLoading: false,
+      tenantList: [],
+      tenantTotal: 0,
+      hasMoreTenants: false,
+      tenantSelectLoading: false,
+      tenantLoadingMore: false,
+      tenantQueryParams: {
+        pageNum: 1,
+        pageSize: 20,
+        tenantName: '',
+        status: 1
+      },
+      form: defaultForm(),
+      tenantSearchTimer: null,
+      tenantSelectInputEl: null,
+      tenantSelectInputHandler: null
     }
   },
-  created() {
+  mounted() {
     this.loadConfig()
   },
+  beforeDestroy() {
+    this.unbindTenantSelectInputListener()
+  },
   methods: {
+    formatTenantLabel(item) {
+      if (item.tenantCode) {
+        return `${item.tenantName}(${item.tenantCode})`
+      }
+      return item.tenantName
+    },
+    handleTenantSearch(query) {
+      if (query === undefined) return
+      this.tenantQueryParams.tenantName = (query || '').trim()
+      this.tenantQueryParams.pageNum = 1
+      this.fetchTenantList()
+    },
+    /** 清空已选租户时,同时重置搜索条件并刷新列表 */
+    handleTenantSelectClear() {
+      this.handleTenantSearch('')
+    },
+    getTenantSelectInputEl() {
+      const root = this.$refs.tenantSelect && this.$refs.tenantSelect.$el
+      return root ? root.querySelector('input.el-input__inner') : null
+    },
+    /** Element UI remote 清空搜索框时不一定触发 remote-method,改监听原生 input */
+    bindTenantSelectInputListener() {
+      this.unbindTenantSelectInputListener()
+      const input = this.getTenantSelectInputEl()
+      if (!input) return
+      this.tenantSelectInputEl = input
+      this.tenantSelectInputHandler = () => {
+        const val = (input.value || '').trim()
+        if (!val && this.tenantQueryParams.tenantName) {
+          if (this.tenantSearchTimer) clearTimeout(this.tenantSearchTimer)
+          this.tenantSearchTimer = setTimeout(() => {
+            this.handleTenantSearch('')
+          }, 150)
+        }
+      }
+      input.addEventListener('input', this.tenantSelectInputHandler)
+    },
+    unbindTenantSelectInputListener() {
+      if (this.tenantSearchTimer) {
+        clearTimeout(this.tenantSearchTimer)
+        this.tenantSearchTimer = null
+      }
+      if (this.tenantSelectInputEl && this.tenantSelectInputHandler) {
+        this.tenantSelectInputEl.removeEventListener('input', this.tenantSelectInputHandler)
+      }
+      this.tenantSelectInputEl = null
+      this.tenantSelectInputHandler = null
+    },
+    syncTenantSearchFromInput() {
+      const input = this.getTenantSelectInputEl()
+      const inputVal = input ? (input.value || '').trim() : ''
+      if (!inputVal && this.tenantQueryParams.tenantName) {
+        this.handleTenantSearch('')
+      }
+    },
+    handleTenantDropdownVisible(visible) {
+      if (visible) {
+        if (this.tenantList.length === 0) {
+          this.handleTenantSearch('')
+        }
+        this.$nextTick(() => {
+          this.syncTenantSearchFromInput()
+          this.bindTenantSelectInputListener()
+        })
+      } else {
+        this.unbindTenantSelectInputListener()
+      }
+    },
+    fetchTenantList(isLoadMore = false) {
+      if (!isLoadMore) {
+        this.tenantSelectLoading = true
+      } else {
+        this.tenantLoadingMore = true
+      }
+      listAdminTenantList(this.tenantQueryParams).then(response => {
+        const rows = response.rows || []
+        if (isLoadMore) {
+          const existIds = new Set(this.tenantList.map(t => t.id))
+          const append = rows.filter(r => !existIds.has(r.id))
+          this.tenantList = this.tenantList.concat(append)
+        } else {
+          this.tenantList = rows
+        }
+        this.tenantTotal = response.total || 0
+        this.hasMoreTenants = this.tenantList.length < this.tenantTotal
+      }).finally(() => {
+        this.tenantSelectLoading = false
+        this.tenantLoadingMore = false
+      })
+    },
+    loadMoreTenants() {
+      if (this.tenantLoadingMore || !this.hasMoreTenants) return
+      this.tenantQueryParams.pageNum += 1
+      this.fetchTenantList(true)
+    },
+    resetForm() {
+      this.form = defaultForm()
+      this.configId = null
+      this.tenantConfigEmpty = false
+    },
     loadConfig() {
       this.loading = true
-      getConfigByKey(this.configKey).then(response => {
-        if (response.data) {
-          this.configId = response.data.configId
-          try {
-            const parsed = JSON.parse(response.data.configValue)
-            this.form = { ...this.form, ...parsed }
-          } catch (e) {
-            // 使用默认值
+      this.tenantConfigEmpty = false
+      const tenantId = this.selectedTenantId || undefined
+      getConfigByKey(this.configKey, tenantId).then(response => {
+        const data = response.data
+        if (data) {
+          this.configId = data.configId != null ? data.configId : null
+          if (data.configValue) {
+            this.tenantConfigEmpty = false
+            try {
+              this.form = { ...defaultForm(), ...JSON.parse(data.configValue) }
+            } catch (e) {
+              this.form = defaultForm()
+              this.tenantConfigEmpty = !!this.selectedTenantId
+            }
+          } else {
+            this.form = defaultForm()
+            this.tenantConfigEmpty = !!this.selectedTenantId
           }
         } else {
           this.configId = null
+          this.form = defaultForm()
+          this.tenantConfigEmpty = !!this.selectedTenantId
         }
       }).finally(() => {
         this.loading = false
       })
     },
+    /** 从主库加载默认配置填充表单,保留当前租户 configId 供保存使用 */
+    loadGlobalConfigAsFallback() {
+      this.fallbackLoading = true
+      getConfigByKey(this.configKey).then(response => {
+        if (!response.data || !response.data.configValue) {
+          this.msgWarning('系统默认配置为空')
+          return
+        }
+        try {
+          this.form = { ...defaultForm(), ...JSON.parse(response.data.configValue) }
+          this.msgSuccess('已加载系统默认配置,保存时将写入当前租户')
+        } catch (e) {
+          this.msgWarning('系统默认配置解析失败')
+        }
+      }).finally(() => {
+        this.fallbackLoading = false
+      })
+    },
     submitForm() {
       this.submitLoading = true
       const param = {
-        configId: this.configId,
-        configName: '前端配置',
+        configId: (this.configId != null && this.configId !== '') ? Number(this.configId) : null,
+        configName: '点播线路配置',
         configKey: this.configKey,
-        configValue: JSON.stringify(this.form)
+        configValue: JSON.stringify(this.form),
+        tenantId: this.selectedTenantId ? String(this.selectedTenantId) : null
       }
       updateConfigByKey(param).then(response => {
         if (response.code === 200) {
           this.msgSuccess('修改成功')
+          this.loadConfig()
         }
       }).finally(() => {
         this.submitLoading = false
@@ -209,4 +417,40 @@ export default {
   margin-bottom: 20px;
 }
 
+.page-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.tenant-form {
+  margin-bottom: 0;
+}
+
+.tenant-form >>> .el-form-item {
+  margin-bottom: 0;
+}
+
+.tenant-tip {
+  margin-bottom: 16px;
+}
+
+.tenant-load-more-option {
+  text-align: center;
+  padding: 8px 0;
+}
+
+.load-more {
+  color: #409eff;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 5px;
+}
+
+.load-more:hover {
+  color: #66b1ff;
+}
 </style>

+ 1 - 1
src/views/admin/menu.js

@@ -38,7 +38,7 @@ const adminRoutes = {
     { path: 'cidConfig', component: () => import('@/views/admin/cidConfig/index'), name: 'AdminCidConfig', meta: { title: 'CID配置' } },
     { path: 'wxConfig', component: () => import('@/views/admin/wxConfig/index'), name: 'AdminWxConfig', meta: { title: '个微配置' } },
     { path: 'ossConfig', component: () => import('@/views/admin/ossConfig/index'), name: 'AdminOssConfig', meta: { title: 'OSS配置' } },
-    { path: 'frontConfig', component: () => import('@/views/admin/frontConfig/index'), name: 'AdminFrontConfig', meta: { title: '前端配置' } },
+    { path: 'frontConfig', component: () => import('@/views/admin/frontConfig/index'), name: 'AdminFrontConfig', meta: { title: '点播线路配置' } },
     { path: 'dbConfig', component: () => import('@/views/admin/dbConfig/index'), name: 'AdminDbConfig', meta: { title: 'DB配置' } },
 
     // 7. 外呼管理