|
@@ -1,9 +1,63 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="app-container">
|
|
<div class="app-container">
|
|
|
<el-card v-loading="loading" shadow="never">
|
|
<el-card v-loading="loading" shadow="never">
|
|
|
- <div slot="header">
|
|
|
|
|
|
|
+ <div slot="header" class="page-header">
|
|
|
<span>OSS云存储配置</span>
|
|
<span>OSS云存储配置</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>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-alert
|
|
|
|
|
+ v-if="!selectedTenantId"
|
|
|
|
|
+ title="当前为系统默认配置(主库),保存后将更新全局配置;选择租户后可单独配置该租户 OSS 参数"
|
|
|
|
|
+ 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-form ref="form" :model="form" label-width="200px">
|
|
|
<el-form-item label="类型" prop="type">
|
|
<el-form-item label="类型" prop="type">
|
|
|
<el-radio-group v-model="form.type">
|
|
<el-radio-group v-model="form.type">
|
|
@@ -107,8 +161,8 @@
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
</template>
|
|
</template>
|
|
|
-
|
|
|
|
|
</el-form>
|
|
</el-form>
|
|
|
|
|
+
|
|
|
<div style="text-align: center; margin-top: 20px; padding-bottom: 20px;">
|
|
<div style="text-align: center; margin-top: 20px; padding-bottom: 20px;">
|
|
|
<el-button type="primary" size="medium" @click="submitForm" :loading="submitLoading">保存配置</el-button>
|
|
<el-button type="primary" size="medium" @click="submitForm" :loading="submitLoading">保存配置</el-button>
|
|
|
</div>
|
|
</div>
|
|
@@ -118,6 +172,39 @@
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
import { getConfigByKey, updateConfigByKey } from '@/api/system/config'
|
|
import { getConfigByKey, updateConfigByKey } from '@/api/system/config'
|
|
|
|
|
+import { listAdminTenantList } from '@/api/admin/sysCompany'
|
|
|
|
|
+
|
|
|
|
|
+function defaultOssForm() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ type: 1,
|
|
|
|
|
+ qiniuDomain: '',
|
|
|
|
|
+ qiniuPrefix: '',
|
|
|
|
|
+ qiniuAccessKey: '',
|
|
|
|
|
+ qiniuSecretKey: '',
|
|
|
|
|
+ qiniuBucketName: '',
|
|
|
|
|
+ aliyunDomain: '',
|
|
|
|
|
+ aliyunPrefix: '',
|
|
|
|
|
+ aliyunEndPoint: '',
|
|
|
|
|
+ aliyunAccessKeyId: '',
|
|
|
|
|
+ aliyunAccessKeySecret: '',
|
|
|
|
|
+ aliyunBucketName: '',
|
|
|
|
|
+ qcloudDomain: '',
|
|
|
|
|
+ qcloudPrefix: '',
|
|
|
|
|
+ qcloudSecretId: '',
|
|
|
|
|
+ qcloudSecretKey: '',
|
|
|
|
|
+ qcloudBucketName: '',
|
|
|
|
|
+ qcloudRegion: '',
|
|
|
|
|
+ huaweiDomain: '',
|
|
|
|
|
+ huaweiEndpoint: '',
|
|
|
|
|
+ huaweiAK: '',
|
|
|
|
|
+ huaweiSK: '',
|
|
|
|
|
+ huaweiBucketName: ''
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function mergeOssForm(parsed) {
|
|
|
|
|
+ return { ...defaultOssForm(), ...(parsed || {}) }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
name: 'AdminOssConfig',
|
|
name: 'AdminOssConfig',
|
|
@@ -127,81 +214,192 @@ export default {
|
|
|
submitLoading: false,
|
|
submitLoading: false,
|
|
|
configId: null,
|
|
configId: null,
|
|
|
configKey: 'sys.oss.cloudStorage',
|
|
configKey: 'sys.oss.cloudStorage',
|
|
|
- form: {
|
|
|
|
|
- type: 1,
|
|
|
|
|
- // 七牛云
|
|
|
|
|
- qiniuDomain: '',
|
|
|
|
|
- qiniuPrefix: '',
|
|
|
|
|
- qiniuAccessKey: '',
|
|
|
|
|
- qiniuSecretKey: '',
|
|
|
|
|
- qiniuBucketName: '',
|
|
|
|
|
- // 阿里云
|
|
|
|
|
- aliyunDomain: '',
|
|
|
|
|
- aliyunPrefix: '',
|
|
|
|
|
- aliyunEndPoint: '',
|
|
|
|
|
- aliyunAccessKeyId: '',
|
|
|
|
|
- aliyunAccessKeySecret: '',
|
|
|
|
|
- aliyunBucketName: '',
|
|
|
|
|
- // 腾讯云
|
|
|
|
|
- qcloudDomain: '',
|
|
|
|
|
- qcloudPrefix: '',
|
|
|
|
|
- qcloudSecretId: '',
|
|
|
|
|
- qcloudSecretKey: '',
|
|
|
|
|
- qcloudBucketName: '',
|
|
|
|
|
- qcloudRegion: '',
|
|
|
|
|
- // 华为云
|
|
|
|
|
- huaweiDomain: '',
|
|
|
|
|
- huaweiEndpoint: '',
|
|
|
|
|
- huaweiAK: '',
|
|
|
|
|
- huaweiSK: '',
|
|
|
|
|
- huaweiBucketName: ''
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ selectedTenantId: null,
|
|
|
|
|
+ tenantConfigEmpty: false,
|
|
|
|
|
+ fallbackLoading: false,
|
|
|
|
|
+ tenantList: [],
|
|
|
|
|
+ tenantTotal: 0,
|
|
|
|
|
+ hasMoreTenants: false,
|
|
|
|
|
+ tenantSelectLoading: false,
|
|
|
|
|
+ tenantLoadingMore: false,
|
|
|
|
|
+ tenantQueryParams: {
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 20,
|
|
|
|
|
+ tenantName: '',
|
|
|
|
|
+ status: 1
|
|
|
|
|
+ },
|
|
|
|
|
+ form: defaultOssForm(),
|
|
|
|
|
+ tenantSearchTimer: null,
|
|
|
|
|
+ tenantSelectInputEl: null,
|
|
|
|
|
+ tenantSelectInputHandler: null
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- created() {
|
|
|
|
|
|
|
+ mounted() {
|
|
|
this.loadConfig()
|
|
this.loadConfig()
|
|
|
},
|
|
},
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+ this.unbindTenantSelectInputListener()
|
|
|
|
|
+ },
|
|
|
methods: {
|
|
methods: {
|
|
|
- loadConfig() {
|
|
|
|
|
- this.loading = true
|
|
|
|
|
- getConfigByKey(this.configKey).then(response => {
|
|
|
|
|
- const configValue = response && response.data ? response.data.configValue : null
|
|
|
|
|
- if (response.data) {
|
|
|
|
|
- this.configId = response.data.configId
|
|
|
|
|
|
|
+ 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
|
|
|
|
|
+ },
|
|
|
|
|
+ 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 {
|
|
} else {
|
|
|
- this.configId = null
|
|
|
|
|
|
|
+ this.tenantList = rows
|
|
|
}
|
|
}
|
|
|
- // 安全解析
|
|
|
|
|
- if (configValue !== null && configValue !== undefined && configValue !== '' && configValue !== 'null') {
|
|
|
|
|
- try {
|
|
|
|
|
- const parsed = JSON.parse(configValue)
|
|
|
|
|
- if (parsed !== null && parsed !== undefined) {
|
|
|
|
|
- this.form = { ...this.form, ...parsed }
|
|
|
|
|
|
|
+ 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)
|
|
|
|
|
+ },
|
|
|
|
|
+ loadConfig() {
|
|
|
|
|
+ this.loading = true
|
|
|
|
|
+ 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
|
|
|
|
|
+ const configValue = data.configValue
|
|
|
|
|
+ if (configValue !== null && configValue !== undefined && configValue !== '' && configValue !== 'null') {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.form = mergeOssForm(JSON.parse(configValue))
|
|
|
|
|
+ this.tenantConfigEmpty = false
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this.form = defaultOssForm()
|
|
|
|
|
+ this.tenantConfigEmpty = !!this.selectedTenantId
|
|
|
}
|
|
}
|
|
|
- } catch (e) {
|
|
|
|
|
- // 使用默认值
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.form = defaultOssForm()
|
|
|
|
|
+ this.tenantConfigEmpty = !!this.selectedTenantId
|
|
|
}
|
|
}
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.configId = null
|
|
|
|
|
+ this.form = defaultOssForm()
|
|
|
|
|
+ this.tenantConfigEmpty = !!this.selectedTenantId
|
|
|
}
|
|
}
|
|
|
}).finally(() => {
|
|
}).finally(() => {
|
|
|
this.loading = false
|
|
this.loading = false
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
+ loadGlobalConfigAsFallback() {
|
|
|
|
|
+ this.fallbackLoading = true
|
|
|
|
|
+ getConfigByKey(this.configKey).then(response => {
|
|
|
|
|
+ if (!response.data || !response.data.configValue) {
|
|
|
|
|
+ this.msgWarning('系统默认配置为空')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.form = mergeOssForm(JSON.parse(response.data.configValue))
|
|
|
|
|
+ this.msgSuccess('已加载系统默认配置,保存时将写入当前租户')
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this.msgWarning('系统默认配置解析失败')
|
|
|
|
|
+ }
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+ this.fallbackLoading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
submitForm() {
|
|
submitForm() {
|
|
|
this.$refs['form'].validate(valid => {
|
|
this.$refs['form'].validate(valid => {
|
|
|
- if (valid) {
|
|
|
|
|
- this.submitLoading = true
|
|
|
|
|
- const param = {
|
|
|
|
|
- configId: this.configId,
|
|
|
|
|
- configValue: JSON.stringify(this.form)
|
|
|
|
|
- }
|
|
|
|
|
- updateConfigByKey(param).then(response => {
|
|
|
|
|
- if (response.code === 200) {
|
|
|
|
|
- this.msgSuccess('修改成功')
|
|
|
|
|
- }
|
|
|
|
|
- }).finally(() => {
|
|
|
|
|
- this.submitLoading = false
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ if (!valid) return
|
|
|
|
|
+ this.submitLoading = true
|
|
|
|
|
+ const param = {
|
|
|
|
|
+ configId: (this.configId != null && this.configId !== '') ? Number(this.configId) : null,
|
|
|
|
|
+ configName: 'OSS云存储配置',
|
|
|
|
|
+ configKey: this.configKey,
|
|
|
|
|
+ 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
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -213,4 +411,40 @@ export default {
|
|
|
margin-bottom: 20px;
|
|
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>
|
|
</style>
|