Commit caab2d9a71bc59d83bb8db4e275222e55af4211a
1 parent
6a1f8b67
优化接口响应时间,提升系统性能
Showing
5 changed files
with
95 additions
and
7 deletions
src/components/AppMenuTree.vue
| ... | ... | @@ -25,6 +25,7 @@ import { |
| 25 | 25 | ControlOutlined, |
| 26 | 26 | GlobalOutlined, |
| 27 | 27 | HomeOutlined, |
| 28 | + MessageOutlined, | |
| 28 | 29 | SettingOutlined, |
| 29 | 30 | ShopOutlined, |
| 30 | 31 | StarOutlined, |
| ... | ... | @@ -51,6 +52,7 @@ const iconMap: Record<string, Component> = { |
| 51 | 52 | TeamOutlined, |
| 52 | 53 | TrophyOutlined, |
| 53 | 54 | SettingOutlined, |
| 55 | + MessageOutlined, | |
| 54 | 56 | } |
| 55 | 57 | |
| 56 | 58 | const codeIconMap: Record<string, Component> = { | ... | ... |
src/config/menu.ts
src/views/message/MessageList.vue
| ... | ... | @@ -3,6 +3,18 @@ |
| 3 | 3 | <a-card title="消息管理" :bordered="false"> |
| 4 | 4 | <div class="list-toolbar"> |
| 5 | 5 | <div class="list-toolbar-left"> |
| 6 | + <a-select | |
| 7 | + v-if="isAdmin" | |
| 8 | + v-model:value="selectedCityId" | |
| 9 | + placeholder="请选择租户" | |
| 10 | + style="width: 180px" | |
| 11 | + @change="handleCityChange" | |
| 12 | + > | |
| 13 | + <a-select-option v-for="item in cityList" :key="item.id" :value="item.id"> | |
| 14 | + {{ item.name }} | |
| 15 | + </a-select-option> | |
| 16 | + </a-select> | |
| 17 | + <div v-else class="managed-city-pill">当前租户:{{ currentCityName }}</div> | |
| 6 | 18 | <a-input |
| 7 | 19 | v-model:value="searchForm.riderId" |
| 8 | 20 | placeholder="骑手ID" |
| ... | ... | @@ -18,14 +30,22 @@ |
| 18 | 30 | <a-select-option :value="1">订单消息</a-select-option> |
| 19 | 31 | <a-select-option :value="2">系统通知</a-select-option> |
| 20 | 32 | </a-select> |
| 21 | - <a-button type="primary" @click="loadList">查询</a-button> | |
| 33 | + <a-button type="primary" :disabled="!canQuery" @click="handleSearch">查询</a-button> | |
| 22 | 34 | </div> |
| 23 | 35 | <div class="list-toolbar-right"> |
| 24 | - <a-button type="primary" @click="openSend">发送消息</a-button> | |
| 25 | - <a-button @click="openBroadcast">群发消息</a-button> | |
| 36 | + <a-button type="primary" :disabled="!canQuery" @click="openSend">发送消息</a-button> | |
| 37 | + <a-button :disabled="!canQuery" @click="openBroadcast">群发消息</a-button> | |
| 26 | 38 | </div> |
| 27 | 39 | </div> |
| 28 | 40 | |
| 41 | + <a-alert | |
| 42 | + v-if="isAdmin" | |
| 43 | + type="info" | |
| 44 | + show-icon | |
| 45 | + message="平台管理员需要先选择租户,消息列表、单发和群发都会限定在所选租户内。" | |
| 46 | + style="margin-bottom: 16px" | |
| 47 | + /> | |
| 48 | + | |
| 29 | 49 | <a-table |
| 30 | 50 | :dataSource="list" |
| 31 | 51 | :columns="columns" |
| ... | ... | @@ -59,6 +79,12 @@ |
| 59 | 79 | @ok="handleSend" |
| 60 | 80 | :confirmLoading="sending" |
| 61 | 81 | > |
| 82 | + <a-alert | |
| 83 | + :message="`将发送给当前租户(${currentCityName})下的指定骑手`" | |
| 84 | + type="info" | |
| 85 | + show-icon | |
| 86 | + style="margin-bottom: 16px" | |
| 87 | + /> | |
| 62 | 88 | <a-form :model="sendForm" layout="vertical"> |
| 63 | 89 | <a-form-item label="骑手ID" required> |
| 64 | 90 | <a-input-number v-model:value="sendForm.riderId" style="width: 100%" placeholder="请输入骑手ID" /> |
| ... | ... | @@ -86,7 +112,7 @@ |
| 86 | 112 | :confirmLoading="broadcasting" |
| 87 | 113 | > |
| 88 | 114 | <a-alert |
| 89 | - message="将发送给当前城市所有正常状态的骑手" | |
| 115 | + :message="`将发送给当前租户(${currentCityName})所有正常状态的骑手`" | |
| 90 | 116 | type="warning" |
| 91 | 117 | show-icon |
| 92 | 118 | style="margin-bottom: 16px" |
| ... | ... | @@ -113,14 +139,18 @@ |
| 113 | 139 | import { onMounted, reactive, ref, computed } from 'vue' |
| 114 | 140 | import { message } from 'ant-design-vue' |
| 115 | 141 | import { messageApi } from '@/api' |
| 142 | +import { useRoleCityList } from '@/composables/useRoleCityList' | |
| 116 | 143 | import type { TablePaginationConfig } from 'ant-design-vue' |
| 117 | 144 | |
| 145 | +const { isAdmin, managedCityId, managedCityName, cityList, loadCities, getCityName } = useRoleCityList() | |
| 146 | + | |
| 118 | 147 | const loading = ref(false) |
| 119 | 148 | const sending = ref(false) |
| 120 | 149 | const broadcasting = ref(false) |
| 121 | 150 | const list = ref<any[]>([]) |
| 122 | 151 | const sendVisible = ref(false) |
| 123 | 152 | const broadcastVisible = ref(false) |
| 153 | +const selectedCityId = ref<number | undefined>() | |
| 124 | 154 | |
| 125 | 155 | const searchForm = reactive({ |
| 126 | 156 | riderId: undefined as number | undefined, |
| ... | ... | @@ -143,6 +173,8 @@ const broadcastForm = reactive({ |
| 143 | 173 | const currentPage = ref(1) |
| 144 | 174 | const pageSize = ref(20) |
| 145 | 175 | const total = ref(0) |
| 176 | +const canQuery = computed(() => !!selectedCityId.value) | |
| 177 | +const currentCityName = computed(() => selectedCityId.value ? getCityName(selectedCityId.value) : managedCityName.value || '-') | |
| 146 | 178 | |
| 147 | 179 | const pagination = computed<TablePaginationConfig>(() => ({ |
| 148 | 180 | current: currentPage.value, |
| ... | ... | @@ -163,9 +195,15 @@ const columns = [ |
| 163 | 195 | ] |
| 164 | 196 | |
| 165 | 197 | async function loadList() { |
| 198 | + if (!selectedCityId.value) { | |
| 199 | + list.value = [] | |
| 200 | + total.value = 0 | |
| 201 | + return | |
| 202 | + } | |
| 166 | 203 | loading.value = true |
| 167 | 204 | try { |
| 168 | 205 | const res: any = await messageApi.list({ |
| 206 | + cityId: selectedCityId.value, | |
| 169 | 207 | riderId: searchForm.riderId, |
| 170 | 208 | type: searchForm.type, |
| 171 | 209 | page: currentPage.value, |
| ... | ... | @@ -180,6 +218,16 @@ async function loadList() { |
| 180 | 218 | } |
| 181 | 219 | } |
| 182 | 220 | |
| 221 | +function handleSearch() { | |
| 222 | + currentPage.value = 1 | |
| 223 | + loadList() | |
| 224 | +} | |
| 225 | + | |
| 226 | +function handleCityChange() { | |
| 227 | + currentPage.value = 1 | |
| 228 | + loadList() | |
| 229 | +} | |
| 230 | + | |
| 183 | 231 | function handleTableChange(pag: TablePaginationConfig) { |
| 184 | 232 | const nextPage = pag.current ?? 1 |
| 185 | 233 | if (nextPage === currentPage.value) return |
| ... | ... | @@ -187,24 +235,35 @@ function handleTableChange(pag: TablePaginationConfig) { |
| 187 | 235 | loadList() |
| 188 | 236 | } |
| 189 | 237 | |
| 238 | +function ensureCitySelected() { | |
| 239 | + if (!selectedCityId.value) { | |
| 240 | + message.error('请先选择租户') | |
| 241 | + return false | |
| 242 | + } | |
| 243 | + return true | |
| 244 | +} | |
| 245 | + | |
| 190 | 246 | function openSend() { |
| 247 | + if (!ensureCitySelected()) return | |
| 191 | 248 | Object.assign(sendForm, { riderId: undefined, type: 2, title: '', content: '' }) |
| 192 | 249 | sendVisible.value = true |
| 193 | 250 | } |
| 194 | 251 | |
| 195 | 252 | function openBroadcast() { |
| 253 | + if (!ensureCitySelected()) return | |
| 196 | 254 | Object.assign(broadcastForm, { type: 2, title: '', content: '' }) |
| 197 | 255 | broadcastVisible.value = true |
| 198 | 256 | } |
| 199 | 257 | |
| 200 | 258 | async function handleSend() { |
| 259 | + if (!ensureCitySelected()) return | |
| 201 | 260 | if (!sendForm.riderId || !sendForm.title || !sendForm.content) { |
| 202 | 261 | message.error('请填写完整信息') |
| 203 | 262 | return |
| 204 | 263 | } |
| 205 | 264 | sending.value = true |
| 206 | 265 | try { |
| 207 | - await messageApi.send(sendForm) | |
| 266 | + await messageApi.send({ ...sendForm, cityId: selectedCityId.value }) | |
| 208 | 267 | message.success('发送成功') |
| 209 | 268 | sendVisible.value = false |
| 210 | 269 | loadList() |
| ... | ... | @@ -214,13 +273,14 @@ async function handleSend() { |
| 214 | 273 | } |
| 215 | 274 | |
| 216 | 275 | async function handleBroadcast() { |
| 276 | + if (!ensureCitySelected()) return | |
| 217 | 277 | if (!broadcastForm.title || !broadcastForm.content) { |
| 218 | 278 | message.error('请填写完整信息') |
| 219 | 279 | return |
| 220 | 280 | } |
| 221 | 281 | broadcasting.value = true |
| 222 | 282 | try { |
| 223 | - await messageApi.broadcast(broadcastForm) | |
| 283 | + await messageApi.broadcast({ ...broadcastForm, cityId: selectedCityId.value }) | |
| 224 | 284 | message.success('群发成功') |
| 225 | 285 | broadcastVisible.value = false |
| 226 | 286 | loadList() |
| ... | ... | @@ -242,18 +302,26 @@ function formatTime(timestamp: number) { |
| 242 | 302 | }) |
| 243 | 303 | } |
| 244 | 304 | |
| 245 | -onMounted(loadList) | |
| 305 | +onMounted(async () => { | |
| 306 | + await loadCities() | |
| 307 | + if (!isAdmin.value) { | |
| 308 | + selectedCityId.value = managedCityId.value | |
| 309 | + await loadList() | |
| 310 | + } | |
| 311 | +}) | |
| 246 | 312 | </script> |
| 247 | 313 | |
| 248 | 314 | <style scoped> |
| 249 | 315 | .list-toolbar { |
| 250 | 316 | display: flex; |
| 251 | 317 | justify-content: space-between; |
| 318 | + gap: 12px; | |
| 252 | 319 | margin-bottom: 16px; |
| 253 | 320 | } |
| 254 | 321 | |
| 255 | 322 | .list-toolbar-left { |
| 256 | 323 | display: flex; |
| 324 | + flex-wrap: wrap; | |
| 257 | 325 | gap: 8px; |
| 258 | 326 | } |
| 259 | 327 | |
| ... | ... | @@ -261,4 +329,16 @@ onMounted(loadList) |
| 261 | 329 | display: flex; |
| 262 | 330 | gap: 8px; |
| 263 | 331 | } |
| 332 | + | |
| 333 | +.managed-city-pill { | |
| 334 | + display: inline-flex; | |
| 335 | + align-items: center; | |
| 336 | + min-height: 32px; | |
| 337 | + padding: 0 12px; | |
| 338 | + border: 1px solid var(--line); | |
| 339 | + border-radius: 10px; | |
| 340 | + background: var(--panel-strong); | |
| 341 | + color: var(--text-main); | |
| 342 | + font-size: 13px; | |
| 343 | +} | |
| 264 | 344 | </style> | ... | ... |
src/views/system/MenuManage.vue
| ... | ... | @@ -213,6 +213,7 @@ import { |
| 213 | 213 | ControlOutlined, |
| 214 | 214 | GlobalOutlined, |
| 215 | 215 | HomeOutlined, |
| 216 | + MessageOutlined, | |
| 216 | 217 | SettingOutlined, |
| 217 | 218 | ShopOutlined, |
| 218 | 219 | StarOutlined, |
| ... | ... | @@ -254,6 +255,7 @@ const iconOptions = [ |
| 254 | 255 | 'TeamOutlined', |
| 255 | 256 | 'TrophyOutlined', |
| 256 | 257 | 'SettingOutlined', |
| 258 | + 'MessageOutlined', | |
| 257 | 259 | ] |
| 258 | 260 | |
| 259 | 261 | const iconMap: Record<string, any> = { |
| ... | ... | @@ -269,6 +271,7 @@ const iconMap: Record<string, any> = { |
| 269 | 271 | TeamOutlined, |
| 270 | 272 | TrophyOutlined, |
| 271 | 273 | SettingOutlined, |
| 274 | + MessageOutlined, | |
| 272 | 275 | } |
| 273 | 276 | |
| 274 | 277 | function resolveIcon(icon?: string) { | ... | ... |
src/views/system/RoleMenuAssign.vue
| ... | ... | @@ -101,6 +101,7 @@ import { |
| 101 | 101 | ControlOutlined, |
| 102 | 102 | GlobalOutlined, |
| 103 | 103 | HomeOutlined, |
| 104 | + MessageOutlined, | |
| 104 | 105 | SettingOutlined, |
| 105 | 106 | ShopOutlined, |
| 106 | 107 | StarOutlined, |
| ... | ... | @@ -141,6 +142,7 @@ const iconMap: Record<string, any> = { |
| 141 | 142 | TeamOutlined, |
| 142 | 143 | TrophyOutlined, |
| 143 | 144 | SettingOutlined, |
| 145 | + MessageOutlined, | |
| 144 | 146 | } |
| 145 | 147 | |
| 146 | 148 | function resolveIcon(icon?: string) { | ... | ... |