|
|
@@ -3,7 +3,7 @@
|
|
|
<el-form :model="queryParams" ref="queryForm" :inline="true" size="small">
|
|
|
<el-form-item label="租户" prop="tenantId">
|
|
|
<el-select v-model="queryParams.tenantId" placeholder="选择租户" clearable filterable>
|
|
|
- <el-option v-for="c in companyList" :key="c.companyId" :label="c.companyName" :value="c.companyId" />
|
|
|
+ <el-option v-for="c in companyList" :key="c.tenantId" :label="c.tenantName" :value="c.tenantId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="短信类型" prop="smsType">
|
|
|
@@ -21,11 +21,24 @@
|
|
|
<el-col :span="1.5">
|
|
|
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增绑定</el-button>
|
|
|
</el-col>
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button type="success" plain icon="el-icon-edit-outline" size="mini" :disabled="!selectedRows.length" @click="handleBatchPricing">批量定价</el-button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button type="warning" plain icon="el-icon-open" size="mini" :disabled="!selectedRows.length" @click="handleBatchStatus(1)">批量启用</el-button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button type="info" plain icon="el-icon-turn-off" size="mini" :disabled="!selectedRows.length" @click="handleBatchStatus(0)">批量禁用</el-button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="1.5" v-if="selectedRows.length">
|
|
|
+ <span class="selected-tip">已选 {{ selectedRows.length }} 条</span>
|
|
|
+ </el-col>
|
|
|
</el-row>
|
|
|
|
|
|
- <el-table v-loading="loading" :data="bindList" border size="small">
|
|
|
+ <el-table v-loading="loading" :data="bindList" border size="small" @selection-change="handleSelectionChange">
|
|
|
+ <el-table-column type="selection" width="45" align="center" />
|
|
|
<el-table-column label="ID" align="center" prop="id" width="60" />
|
|
|
- <el-table-column label="租户名称" align="center" prop="companyName" min-width="140" show-overflow-tooltip />
|
|
|
+ <el-table-column label="租户名称" align="center" prop="tenantName" min-width="140" show-overflow-tooltip />
|
|
|
<el-table-column label="接口名称" align="center" prop="apiName" min-width="140" show-overflow-tooltip />
|
|
|
<el-table-column label="短信类型" align="center" prop="smsType" width="140">
|
|
|
<template slot-scope="scope">
|
|
|
@@ -75,19 +88,44 @@
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
|
|
|
+ <pagination
|
|
|
+ v-show="total > 0"
|
|
|
+ :total="total"
|
|
|
+ :page.sync="queryParams.pageNum"
|
|
|
+ :limit.sync="queryParams.pageSize"
|
|
|
+ @pagination="getList"
|
|
|
+ />
|
|
|
+
|
|
|
<!-- 新增绑定弹窗 -->
|
|
|
- <el-dialog title="新增短信接口-租户绑定" :visible.sync="addDialogVisible" width="500px" append-to-body>
|
|
|
+ <el-dialog title="新增短信接口-租户绑定" :visible.sync="addDialogVisible" width="560px" append-to-body>
|
|
|
<el-form ref="addForm" :model="addForm" :rules="addRules" label-width="100px" size="small">
|
|
|
- <el-form-item label="选择接口" prop="apiId">
|
|
|
- <el-select v-model="addForm.apiId" placeholder="请选择短信接口" style="width:100%" @change="onApiChange">
|
|
|
+ <el-form-item label="选择接口" prop="apiIds">
|
|
|
+ <el-select
|
|
|
+ v-model="addForm.apiIds"
|
|
|
+ multiple
|
|
|
+ collapse-tags
|
|
|
+ placeholder="请选择短信接口(可多选)"
|
|
|
+ style="width:100%"
|
|
|
+ @change="onApiChange"
|
|
|
+ >
|
|
|
<el-option v-for="api in apiOptions" :key="api.apiId" :label="api.apiName + ' (' + smsTypeFormat(api.smsType) + ')'" :value="api.apiId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="选择租户" prop="tenantId">
|
|
|
- <el-select v-model="addForm.tenantId" placeholder="请选择租户" filterable style="width:100%">
|
|
|
- <el-option v-for="c in companyList" :key="c.tenantId" :label="c.companyName" :value="c.tenantId" />
|
|
|
+ <el-form-item label="选择租户" prop="tenantIds">
|
|
|
+ <el-select
|
|
|
+ v-model="addForm.tenantIds"
|
|
|
+ multiple
|
|
|
+ collapse-tags
|
|
|
+ filterable
|
|
|
+ placeholder="请选择租户(可多选)"
|
|
|
+ style="width:100%"
|
|
|
+ >
|
|
|
+ <el-option v-for="c in companyList" :key="c.tenantId" :label="c.tenantName" :value="c.tenantId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
+ <el-form-item v-if="addBindPreviewCount > 0" label="">
|
|
|
+ <span class="add-bind-preview">将创建 {{ addBindPreviewCount }} 条绑定关系</span>
|
|
|
+ </el-form-item>
|
|
|
<el-form-item label="租户售价" prop="price">
|
|
|
<el-input-number v-model="addForm.price" :precision="4" :step="0.001" :min="0" style="width:100%" />
|
|
|
<span style="color:#909399;font-size:12px;margin-left:8px">元/条</span>
|
|
|
@@ -100,13 +138,42 @@
|
|
|
<el-switch v-model="addForm.allowManual" :active-value="1" :inactive-value="0" active-text="允许" inactive-text="否" />
|
|
|
<span style="color:#909399;font-size:12px;margin-left:8px">是否允许销售手动选择此接口发送</span>
|
|
|
</el-form-item>
|
|
|
- <el-form-item v-if="selectedApiCost" label="">
|
|
|
- <span style="color:#909399;font-size:12px">该接口成本价: {{ selectedApiCost }} 元/条,建议售价 >= 成本价</span>
|
|
|
+ <el-form-item v-if="selectedApiCostText" label="">
|
|
|
+ <span style="color:#909399;font-size:12px">所选接口成本价: {{ selectedApiCostText }} 元/条,建议售价 >= 成本价</span>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<div slot="footer">
|
|
|
- <el-button type="primary" @click="submitAdd">确 定</el-button>
|
|
|
- <el-button @click="addDialogVisible = false">取 消</el-button>
|
|
|
+ <el-button type="primary" :loading="addSubmitting" :disabled="addSubmitting" @click="submitAdd">确 定</el-button>
|
|
|
+ <el-button :disabled="addSubmitting" @click="addDialogVisible = false">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 批量定价弹窗 -->
|
|
|
+ <el-dialog title="批量定价" :visible.sync="batchPricingOpen" width="520px" append-to-body>
|
|
|
+ <div class="batch-tip">将对已选 {{ selectedRows.length }} 条记录批量更新,仅填写需要修改的项,留空则保持原值不变。</div>
|
|
|
+ <el-form ref="batchPricingForm" :model="batchPricingForm" label-width="140px" size="small">
|
|
|
+ <el-form-item label="租户售价(元/条)">
|
|
|
+ <el-input-number v-model="batchPricingForm.price" :precision="4" :min="0" :step="0.001" style="width:100%" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="优先级">
|
|
|
+ <el-input-number v-model="batchPricingForm.priority" :min="1" :max="99" :step="1" style="width:100%" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="允许手动选择">
|
|
|
+ <el-checkbox v-model="batchPricingForm.applyAllowManual">更新手动选择</el-checkbox>
|
|
|
+ <el-switch
|
|
|
+ v-model="batchPricingForm.allowManual"
|
|
|
+ :disabled="!batchPricingForm.applyAllowManual"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="允许"
|
|
|
+ inactive-text="否"
|
|
|
+ style="margin-left:12px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" :loading="batchPricingSubmitting" @click="submitBatchPricing">确 定</el-button>
|
|
|
+ <el-button :disabled="batchPricingSubmitting" @click="batchPricingOpen = false">取 消</el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
|
|
|
@@ -114,7 +181,7 @@
|
|
|
<el-dialog title="调整租户售价" :visible.sync="editDialogVisible" width="450px" append-to-body>
|
|
|
<el-form ref="editForm" :model="editForm" label-width="100px" size="small">
|
|
|
<el-form-item label="租户">
|
|
|
- <span>{{ editForm.companyName }}</span>
|
|
|
+ <span>{{ editForm.tenantName }}</span>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="接口">
|
|
|
<span>{{ editForm.apiName }}</span>
|
|
|
@@ -145,7 +212,10 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { listSmsApiTenant, addSmsApiTenant, updateSmsApiTenant, delSmsApiTenant, listSmsApi } from '@/api/system/smsApi'
|
|
|
+import {
|
|
|
+ listSmsApiTenant, batchAddSmsApiTenant, updateSmsApiTenant, delSmsApiTenant, listSmsApi,
|
|
|
+ batchUpdateSmsApiTenantPricing, batchUpdateSmsApiTenantStatus
|
|
|
+} from '@/api/system/smsApi'
|
|
|
import { listAllCompanies } from '@/api/admin/sysCompany'
|
|
|
|
|
|
export default {
|
|
|
@@ -153,7 +223,9 @@ export default {
|
|
|
data() {
|
|
|
return {
|
|
|
loading: false,
|
|
|
+ total: 0,
|
|
|
bindList: [],
|
|
|
+ selectedRows: [],
|
|
|
companyList: [],
|
|
|
apiOptions: [],
|
|
|
smsTypeOptions: [
|
|
|
@@ -161,22 +233,43 @@ export default {
|
|
|
{ value: 2, label: '营销短信' },
|
|
|
{ value: 3, label: '5G消息' }
|
|
|
],
|
|
|
- queryParams: { tenantId: undefined, smsType: undefined },
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ tenantId: undefined,
|
|
|
+ smsType: undefined
|
|
|
+ },
|
|
|
addDialogVisible: false,
|
|
|
+ addSubmitting: false,
|
|
|
editDialogVisible: false,
|
|
|
- addForm: { apiId: undefined, tenantId: undefined, price: 0, priority: 1, allowManual: 0 },
|
|
|
- editForm: { id: undefined, price: 0, priority: 1, allowManual: 0, status: 1, companyName: '', apiName: '', costPrice: 0 },
|
|
|
+ batchPricingOpen: false,
|
|
|
+ batchPricingSubmitting: false,
|
|
|
+ batchPricingForm: {
|
|
|
+ price: undefined,
|
|
|
+ priority: undefined,
|
|
|
+ applyAllowManual: false,
|
|
|
+ allowManual: 0
|
|
|
+ },
|
|
|
+ addForm: { apiIds: [], tenantIds: [], price: 0, priority: 1, allowManual: 0 },
|
|
|
+ editForm: { id: undefined, price: 0, priority: 1, allowManual: 0, status: 1, tenantName: '', apiName: '', costPrice: 0 },
|
|
|
addRules: {
|
|
|
- apiId: [{ required: true, message: '请选择接口', trigger: 'change' }],
|
|
|
- tenantId: [{ required: true, message: '请选择租户', trigger: 'change' }],
|
|
|
+ apiIds: [{ type: 'array', required: true, min: 1, message: '请选择至少一个接口', trigger: 'change' }],
|
|
|
+ tenantIds: [{ type: 'array', required: true, min: 1, message: '请选择至少一个租户', trigger: 'change' }],
|
|
|
price: [{ required: true, message: '请填写售价', trigger: 'blur' }]
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
- selectedApiCost() {
|
|
|
- const api = this.apiOptions.find(a => a.apiId === this.addForm.apiId)
|
|
|
- return api && api.costPrice ? api.costPrice : ''
|
|
|
+ addBindPreviewCount() {
|
|
|
+ const apiCount = (this.addForm.apiIds || []).length
|
|
|
+ const tenantCount = (this.addForm.tenantIds || []).length
|
|
|
+ return apiCount * tenantCount
|
|
|
+ },
|
|
|
+ selectedApiCostText() {
|
|
|
+ const apiIds = this.addForm.apiIds || []
|
|
|
+ if (apiIds.length !== 1) return ''
|
|
|
+ const api = this.apiOptions.find(a => a.apiId === apiIds[0])
|
|
|
+ return api && api.costPrice != null ? api.costPrice : ''
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
@@ -202,12 +295,16 @@ export default {
|
|
|
getList() {
|
|
|
this.loading = true
|
|
|
listSmsApiTenant(this.queryParams).then(res => {
|
|
|
- this.bindList = res.data || []
|
|
|
+ this.bindList = res.rows || res.data || []
|
|
|
+ this.total = res.total || 0
|
|
|
}).finally(() => { this.loading = false })
|
|
|
},
|
|
|
loadCompanyList() {
|
|
|
listAllCompanies({ pageNum: 1, pageSize: 1000 }).then(res => {
|
|
|
- this.companyList = res.rows || res.data || []
|
|
|
+ this.companyList = (res.rows || res.data || []).map(c => ({
|
|
|
+ tenantId: c.id || c.companyId || c.tenantId,
|
|
|
+ tenantName: c.tenantName || c.companyName
|
|
|
+ }))
|
|
|
})
|
|
|
},
|
|
|
loadApiOptions() {
|
|
|
@@ -215,29 +312,72 @@ export default {
|
|
|
this.apiOptions = res.data || []
|
|
|
})
|
|
|
},
|
|
|
- onApiChange(apiId) {
|
|
|
- // 当选择接口时,自动填充默认售价
|
|
|
- const api = this.apiOptions.find(a => a.apiId === apiId)
|
|
|
- if (api && api.costPrice) {
|
|
|
- this.addForm.price = api.costPrice
|
|
|
+ onApiChange(apiIds) {
|
|
|
+ const ids = apiIds || this.addForm.apiIds || []
|
|
|
+ if (ids.length === 1) {
|
|
|
+ const api = this.apiOptions.find(a => a.apiId === ids[0])
|
|
|
+ if (api && api.costPrice != null) {
|
|
|
+ this.addForm.price = api.costPrice
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
- handleQuery() { this.getList() },
|
|
|
+ handleSelectionChange(rows) {
|
|
|
+ this.selectedRows = rows || []
|
|
|
+ },
|
|
|
+ handleQuery() {
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
+ this.getList()
|
|
|
+ },
|
|
|
resetQuery() {
|
|
|
- this.queryParams = { tenantId: undefined, smsType: undefined }
|
|
|
+ this.resetForm('queryForm')
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
+ this.queryParams.pageSize = 10
|
|
|
this.handleQuery()
|
|
|
},
|
|
|
handleAdd() {
|
|
|
- this.addForm = { apiId: undefined, tenantId: undefined, price: 0, priority: 1, allowManual: 0 }
|
|
|
+ this.addForm = { apiIds: [], tenantIds: [], price: 0, priority: 1, allowManual: 0 }
|
|
|
+ this.addSubmitting = false
|
|
|
this.addDialogVisible = true
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$refs.addForm) this.$refs.addForm.clearValidate()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ showBatchAddResult(res) {
|
|
|
+ const data = res.data || {}
|
|
|
+ const successCount = data.successCount || 0
|
|
|
+ const failCount = data.failCount || 0
|
|
|
+ const failMessages = data.failMessages || []
|
|
|
+ if (failCount > 0 && failMessages.length) {
|
|
|
+ const detail = failMessages.map((msg, index) => `${index + 1}. ${msg}`).join('<br/>')
|
|
|
+ this.$alert(detail, `失败 ${failCount} 条`, {
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
+ customClass: 'batch-add-fail-alert'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if (successCount > 0) {
|
|
|
+ this.$message.success(res.msg || `成功绑定 ${successCount} 条`)
|
|
|
+ this.addDialogVisible = false
|
|
|
+ this.getList()
|
|
|
+ } else if (failCount > 0) {
|
|
|
+ this.$message.warning(res.msg || `绑定失败 ${failCount} 条`)
|
|
|
+ }
|
|
|
},
|
|
|
submitAdd() {
|
|
|
+ if (this.addSubmitting) return
|
|
|
this.$refs.addForm.validate(valid => {
|
|
|
if (!valid) return
|
|
|
- addSmsApiTenant({ ...this.addForm, status: 1 }).then(() => {
|
|
|
- this.$message.success('绑定成功')
|
|
|
- this.addDialogVisible = false
|
|
|
- this.getList()
|
|
|
+ this.addSubmitting = true
|
|
|
+ batchAddSmsApiTenant({
|
|
|
+ apiIds: this.addForm.apiIds,
|
|
|
+ tenantIds: this.addForm.tenantIds,
|
|
|
+ price: this.addForm.price,
|
|
|
+ priority: this.addForm.priority,
|
|
|
+ allowManual: this.addForm.allowManual,
|
|
|
+ status: 1
|
|
|
+ }).then(res => {
|
|
|
+ this.showBatchAddResult(res)
|
|
|
+ }).finally(() => {
|
|
|
+ this.addSubmitting = false
|
|
|
})
|
|
|
})
|
|
|
},
|
|
|
@@ -248,7 +388,7 @@ export default {
|
|
|
priority: row.priority || 1,
|
|
|
allowManual: row.allowManual || 0,
|
|
|
status: row.status,
|
|
|
- companyName: row.companyName,
|
|
|
+ tenantName: row.tenantName,
|
|
|
apiName: row.apiName,
|
|
|
costPrice: row.costPrice
|
|
|
}
|
|
|
@@ -261,8 +401,64 @@ export default {
|
|
|
this.getList()
|
|
|
})
|
|
|
},
|
|
|
+ handleBatchPricing() {
|
|
|
+ if (!this.selectedRows.length) {
|
|
|
+ this.$message.warning('请先选择要批量定价的记录')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.batchPricingForm = {
|
|
|
+ price: undefined,
|
|
|
+ priority: undefined,
|
|
|
+ applyAllowManual: false,
|
|
|
+ allowManual: 0
|
|
|
+ }
|
|
|
+ this.batchPricingOpen = true
|
|
|
+ },
|
|
|
+ submitBatchPricing() {
|
|
|
+ const data = { ids: this.selectedRows.map(row => row.id) }
|
|
|
+ if (this.batchPricingForm.price != null && this.batchPricingForm.price !== '') {
|
|
|
+ data.price = this.batchPricingForm.price
|
|
|
+ }
|
|
|
+ if (this.batchPricingForm.priority != null && this.batchPricingForm.priority !== '') {
|
|
|
+ data.priority = this.batchPricingForm.priority
|
|
|
+ }
|
|
|
+ if (this.batchPricingForm.applyAllowManual) {
|
|
|
+ data.allowManual = this.batchPricingForm.allowManual
|
|
|
+ }
|
|
|
+ if (data.price == null && data.priority == null && data.allowManual == null) {
|
|
|
+ this.$message.warning('请至少填写或勾选一项要批量更新的配置')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (this.batchPricingSubmitting) return
|
|
|
+ this.batchPricingSubmitting = true
|
|
|
+ batchUpdateSmsApiTenantPricing(data).then(() => {
|
|
|
+ this.$message.success('批量定价成功')
|
|
|
+ this.batchPricingOpen = false
|
|
|
+ this.getList()
|
|
|
+ }).finally(() => {
|
|
|
+ this.batchPricingSubmitting = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handleBatchStatus(status) {
|
|
|
+ if (!this.selectedRows.length) {
|
|
|
+ this.$message.warning('请先选择要操作的记录')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const actionText = status === 1 ? '启用' : '禁用'
|
|
|
+ this.$confirm('确认批量' + actionText + '已选 ' + this.selectedRows.length + ' 条记录?', '提示', {
|
|
|
+ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ return batchUpdateSmsApiTenantStatus({
|
|
|
+ ids: this.selectedRows.map(row => row.id),
|
|
|
+ status
|
|
|
+ })
|
|
|
+ }).then(() => {
|
|
|
+ this.$message.success('批量' + actionText + '成功')
|
|
|
+ this.getList()
|
|
|
+ }).catch(() => {})
|
|
|
+ },
|
|
|
handleDelete(row) {
|
|
|
- this.$confirm('确认解除租户"' + row.companyName + '"与接口"' + row.apiName + '"的绑定?', '提示', {
|
|
|
+ this.$confirm('确认解除租户"' + row.tenantName + '"与接口"' + row.apiName + '"的绑定?', '提示', {
|
|
|
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
|
|
|
}).then(() => {
|
|
|
delSmsApiTenant(row.id).then(() => {
|
|
|
@@ -276,4 +472,31 @@ export default {
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
+.mb8 { margin-bottom: 8px; }
|
|
|
+.selected-tip {
|
|
|
+ color: #909399;
|
|
|
+ font-size: 13px;
|
|
|
+ line-height: 28px;
|
|
|
+}
|
|
|
+.batch-tip {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding: 10px 12px;
|
|
|
+ background: #f4f4f5;
|
|
|
+ border-radius: 4px;
|
|
|
+ color: #606266;
|
|
|
+ font-size: 13px;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+.add-bind-preview {
|
|
|
+ color: #409eff;
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|
|
|
+<style>
|
|
|
+.batch-add-fail-alert .el-message-box__message {
|
|
|
+ max-height: 320px;
|
|
|
+ overflow-y: auto;
|
|
|
+ word-break: break-all;
|
|
|
+}
|
|
|
</style>
|