index.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <template>
  2. <div class="app-container">
  3. <!-- 统计卡片 -->
  4. <el-row :gutter="16" class="mb16">
  5. <el-col :span="6">
  6. <el-card shadow="never" class="stat-card">
  7. <div class="stat-label">租户总数</div>
  8. <div class="stat-value">{{ smsCount.totalCount || 0 }}</div>
  9. </el-card>
  10. </el-col>
  11. <el-col :span="6">
  12. <el-card shadow="never" class="stat-card">
  13. <div class="stat-label">短信剩余总量</div>
  14. <div class="stat-value" style="color:#1890ff">{{ smsCount.totalRemain || 0 }}</div>
  15. </el-card>
  16. </el-col>
  17. <el-col :span="6">
  18. <el-card shadow="never" class="stat-card">
  19. <div class="stat-label">正常租户数</div>
  20. <div class="stat-value" style="color:#52c41a">{{ smsCount.activeCount || 0 }}</div>
  21. </el-card>
  22. </el-col>
  23. <el-col :span="6">
  24. <el-card shadow="never" class="stat-card">
  25. <div class="stat-label">禁用租户数</div>
  26. <div class="stat-value" style="color:#ff4d4f">{{ smsCount.disabledCount || 0 }}</div>
  27. </el-card>
  28. </el-col>
  29. </el-row>
  30. <!-- 搜索栏 -->
  31. <el-card shadow="never" class="mb16 filter-card">
  32. <el-form :model="queryParams" ref="queryForm" :inline="true" size="small">
  33. <el-form-item label="租户名称" prop="tenantId">
  34. <inline-tenant-selector @change="handleTenantChange" />
  35. </el-form-item>
  36. <el-form-item>
  37. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
  38. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  39. </el-form-item>
  40. </el-form>
  41. </el-card>
  42. <!-- 操作栏 -->
  43. <el-row :gutter="10" class="mb8">
  44. <el-col :span="1.5">
  45. <el-button type="warning" plain icon="el-icon-download" size="mini" :loading="exportLoading" @click="handleExport">导出</el-button>
  46. </el-col>
  47. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
  48. </el-row>
  49. <!-- 表格 -->
  50. <el-table v-loading="loading" :data="smsList" border size="small" style="width:100%">
  51. <el-table-column label="ID" align="center" prop="smsId" min-width="60" />
  52. <el-table-column label="所属租户" align="center" prop="companyName" min-width="120" />
  53. <el-table-column label="短信剩余条数" align="center" prop="smsRemain" min-width="110">
  54. <template slot-scope="scope">
  55. <span style="color:#1890ff;font-weight:bold">{{ scope.row.smsRemain || 0 }}</span>
  56. </template>
  57. </el-table-column>
  58. <el-table-column label="状态" align="center" min-width="80">
  59. <template slot-scope="scope">
  60. <el-tag v-if="scope.row.status === '0' || scope.row.status === 0" type="success" size="mini">正常</el-tag>
  61. <el-tag v-else type="danger" size="mini">禁用</el-tag>
  62. </template>
  63. </el-table-column>
  64. <el-table-column label="创建时间" align="center" prop="createTime" min-width="150" />
  65. <el-table-column label="操作" align="center" width="100" fixed="right">
  66. <template slot-scope="scope">
  67. <el-button size="mini" type="text" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
  68. </template>
  69. </el-table-column>
  70. </el-table>
  71. <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
  72. <!-- 编辑弹窗 -->
  73. <el-dialog title="编辑短信配置" :visible.sync="editVisible" width="500px" append-to-body>
  74. <el-form ref="editForm" :model="editForm" :rules="editRules" label-width="110px">
  75. <el-form-item label="所属租户">
  76. <span>{{ editForm.companyName }}</span>
  77. </el-form-item>
  78. <el-form-item label="短信剩余条数">
  79. <span style="color:#1890ff;font-weight:bold">{{ editForm.smsRemain || 0 }}</span>
  80. </el-form-item>
  81. <el-form-item label="调整条数" prop="adjustCount">
  82. <el-input-number v-model="editForm.adjustCount" :min="1" :precision="0" style="width:200px" />
  83. <el-radio-group v-model="editForm.adjustType" style="margin-left:10px">
  84. <el-radio label="add">增加</el-radio>
  85. <el-radio label="reduce">减少</el-radio>
  86. </el-radio-group>
  87. </el-form-item>
  88. <el-form-item label="状态" prop="status">
  89. <el-select v-model="editForm.status" placeholder="请选择状态" style="width:200px">
  90. <el-option label="正常" value="0" />
  91. <el-option label="禁用" value="1" />
  92. </el-select>
  93. </el-form-item>
  94. <el-form-item label="备注" prop="remark">
  95. <el-input v-model="editForm.remark" type="textarea" :rows="2" placeholder="请输入备注" />
  96. </el-form-item>
  97. </el-form>
  98. <div slot="footer">
  99. <el-button @click="editVisible = false">取 消</el-button>
  100. <el-button type="primary" :loading="submitLoading" @click="submitEdit">确 定</el-button>
  101. </div>
  102. </el-dialog>
  103. </div>
  104. </template>
  105. <script>
  106. import request from '@/utils/request'
  107. import InlineTenantSelector from '@/components/InlineTenantSelector'
  108. export default {
  109. name: 'AdminSms',
  110. components: { InlineTenantSelector },
  111. data() {
  112. return {
  113. loading: false,
  114. exportLoading: false,
  115. submitLoading: false,
  116. showSearch: true,
  117. total: 0,
  118. smsList: [],
  119. smsCount: {},
  120. queryParams: {
  121. pageNum: 1,
  122. pageSize: 10,
  123. companyName: null,
  124. tenantId: null
  125. },
  126. editVisible: false,
  127. editForm: {
  128. smsId: null,
  129. companyName: '',
  130. smsRemain: 0,
  131. adjustCount: 1,
  132. adjustType: 'add',
  133. status: '0',
  134. remark: ''
  135. },
  136. editRules: {
  137. adjustCount: [{ required: true, message: '请输入调整条数', trigger: 'blur' }],
  138. status: [{ required: true, message: '请选择状态', trigger: 'change' }]
  139. }
  140. }
  141. },
  142. created() {
  143. this.getList()
  144. this.getCount()
  145. },
  146. methods: {
  147. getList() {
  148. if (!this.queryParams.tenantId) {
  149. this.smsList = []
  150. this.total = 0
  151. this.loading = false
  152. return
  153. }
  154. this.loading = true
  155. request({
  156. url: '/admin/sms-admin/list',
  157. method: 'get',
  158. params: this.queryParams
  159. }).then(res => {
  160. this.smsList = res.rows || []
  161. this.total = res.total || 0
  162. this.loading = false
  163. }).catch(() => { this.loading = false })
  164. },
  165. getCount() {
  166. if (!this.queryParams.tenantId) {
  167. this.smsCount = {}
  168. return
  169. }
  170. request({
  171. url: '/admin/sms-admin/count',
  172. method: 'get'
  173. }).then(res => {
  174. this.smsCount = res.data || {}
  175. })
  176. },
  177. handleQuery() {
  178. if (!this.queryParams.tenantId) {
  179. this.$message.warning('请先选择租户')
  180. return
  181. }
  182. this.queryParams.pageNum = 1
  183. this.getList()
  184. },
  185. handleTenantChange(val) {
  186. this.queryParams.tenantId = val || null
  187. this.handleQuery()
  188. },
  189. resetQuery() {
  190. this.resetForm('queryForm')
  191. this.queryParams.tenantId = null
  192. this.handleQuery()
  193. },
  194. handleEdit(row) {
  195. request({
  196. url: '/admin/sms-admin/' + row.smsId,
  197. method: 'get'
  198. }).then(res => {
  199. const data = res.data || {}
  200. this.editForm = {
  201. smsId: data.smsId,
  202. companyName: data.companyName,
  203. smsRemain: data.smsRemain,
  204. adjustCount: 1,
  205. adjustType: 'add',
  206. status: String(data.status || '0'),
  207. remark: ''
  208. }
  209. this.editVisible = true
  210. })
  211. },
  212. submitEdit() {
  213. this.$refs['editForm'].validate(valid => {
  214. if (!valid) return
  215. this.submitLoading = true
  216. const data = {
  217. smsId: this.editForm.smsId,
  218. status: this.editForm.status,
  219. remark: this.editForm.remark
  220. }
  221. // 计算调整后的短信条数
  222. if (this.editForm.adjustType === 'add') {
  223. data.smsRemain = (this.editForm.smsRemain || 0) + (this.editForm.adjustCount || 0)
  224. } else {
  225. data.smsRemain = Math.max(0, (this.editForm.smsRemain || 0) - (this.editForm.adjustCount || 0))
  226. }
  227. request({
  228. url: '/admin/sms-admin',
  229. method: 'put',
  230. data: data
  231. }).then(() => {
  232. this.$message.success('修改成功')
  233. this.editVisible = false
  234. this.getList()
  235. this.getCount()
  236. }).finally(() => { this.submitLoading = false })
  237. })
  238. },
  239. handleExport() {
  240. this.exportLoading = true
  241. request({
  242. url: '/admin/sms-admin/export',
  243. method: 'get',
  244. params: this.queryParams,
  245. responseType: 'blob'
  246. }).then(res => {
  247. const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
  248. const link = document.createElement('a')
  249. link.href = URL.createObjectURL(blob)
  250. link.download = '短信管理.xlsx'
  251. link.click()
  252. URL.revokeObjectURL(link.href)
  253. }).catch(() => {
  254. this.$message.error('导出失败')
  255. }).finally(() => { this.exportLoading = false })
  256. }
  257. }
  258. }
  259. </script>
  260. <style scoped>
  261. .mb8 { margin-bottom: 8px; }
  262. .mb16 { margin-bottom: 16px; }
  263. .filter-card { padding-bottom: 0; }
  264. .stat-card { text-align: center; }
  265. .stat-label { font-size: 13px; color: #909399; margin-bottom: 8px; }
  266. .stat-value { font-size: 24px; font-weight: bold; color: #303133; }
  267. </style>