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,6 +25,7 @@ import { | ||
| 25 | ControlOutlined, | 25 | ControlOutlined, |
| 26 | GlobalOutlined, | 26 | GlobalOutlined, |
| 27 | HomeOutlined, | 27 | HomeOutlined, |
| 28 | + MessageOutlined, | ||
| 28 | SettingOutlined, | 29 | SettingOutlined, |
| 29 | ShopOutlined, | 30 | ShopOutlined, |
| 30 | StarOutlined, | 31 | StarOutlined, |
| @@ -51,6 +52,7 @@ const iconMap: Record<string, Component> = { | @@ -51,6 +52,7 @@ const iconMap: Record<string, Component> = { | ||
| 51 | TeamOutlined, | 52 | TeamOutlined, |
| 52 | TrophyOutlined, | 53 | TrophyOutlined, |
| 53 | SettingOutlined, | 54 | SettingOutlined, |
| 55 | + MessageOutlined, | ||
| 54 | } | 56 | } |
| 55 | 57 | ||
| 56 | const codeIconMap: Record<string, Component> = { | 58 | const codeIconMap: Record<string, Component> = { |
src/config/menu.ts
| @@ -19,6 +19,7 @@ export const iconRegistry: Record<string, string> = { | @@ -19,6 +19,7 @@ export const iconRegistry: Record<string, string> = { | ||
| 19 | TeamOutlined: 'team', | 19 | TeamOutlined: 'team', |
| 20 | TrophyOutlined: 'trophy', | 20 | TrophyOutlined: 'trophy', |
| 21 | SettingOutlined: 'setting', | 21 | SettingOutlined: 'setting', |
| 22 | + MessageOutlined: 'message', | ||
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | export const pinnedQuickLinks: QuickLinkItem[] = [ | 25 | export const pinnedQuickLinks: QuickLinkItem[] = [ |
src/views/message/MessageList.vue
| @@ -3,6 +3,18 @@ | @@ -3,6 +3,18 @@ | ||
| 3 | <a-card title="消息管理" :bordered="false"> | 3 | <a-card title="消息管理" :bordered="false"> |
| 4 | <div class="list-toolbar"> | 4 | <div class="list-toolbar"> |
| 5 | <div class="list-toolbar-left"> | 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 | <a-input | 18 | <a-input |
| 7 | v-model:value="searchForm.riderId" | 19 | v-model:value="searchForm.riderId" |
| 8 | placeholder="骑手ID" | 20 | placeholder="骑手ID" |
| @@ -18,14 +30,22 @@ | @@ -18,14 +30,22 @@ | ||
| 18 | <a-select-option :value="1">订单消息</a-select-option> | 30 | <a-select-option :value="1">订单消息</a-select-option> |
| 19 | <a-select-option :value="2">系统通知</a-select-option> | 31 | <a-select-option :value="2">系统通知</a-select-option> |
| 20 | </a-select> | 32 | </a-select> |
| 21 | - <a-button type="primary" @click="loadList">查询</a-button> | 33 | + <a-button type="primary" :disabled="!canQuery" @click="handleSearch">查询</a-button> |
| 22 | </div> | 34 | </div> |
| 23 | <div class="list-toolbar-right"> | 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 | </div> | 38 | </div> |
| 27 | </div> | 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 | <a-table | 49 | <a-table |
| 30 | :dataSource="list" | 50 | :dataSource="list" |
| 31 | :columns="columns" | 51 | :columns="columns" |
| @@ -59,6 +79,12 @@ | @@ -59,6 +79,12 @@ | ||
| 59 | @ok="handleSend" | 79 | @ok="handleSend" |
| 60 | :confirmLoading="sending" | 80 | :confirmLoading="sending" |
| 61 | > | 81 | > |
| 82 | + <a-alert | ||
| 83 | + :message="`将发送给当前租户(${currentCityName})下的指定骑手`" | ||
| 84 | + type="info" | ||
| 85 | + show-icon | ||
| 86 | + style="margin-bottom: 16px" | ||
| 87 | + /> | ||
| 62 | <a-form :model="sendForm" layout="vertical"> | 88 | <a-form :model="sendForm" layout="vertical"> |
| 63 | <a-form-item label="骑手ID" required> | 89 | <a-form-item label="骑手ID" required> |
| 64 | <a-input-number v-model:value="sendForm.riderId" style="width: 100%" placeholder="请输入骑手ID" /> | 90 | <a-input-number v-model:value="sendForm.riderId" style="width: 100%" placeholder="请输入骑手ID" /> |
| @@ -86,7 +112,7 @@ | @@ -86,7 +112,7 @@ | ||
| 86 | :confirmLoading="broadcasting" | 112 | :confirmLoading="broadcasting" |
| 87 | > | 113 | > |
| 88 | <a-alert | 114 | <a-alert |
| 89 | - message="将发送给当前城市所有正常状态的骑手" | 115 | + :message="`将发送给当前租户(${currentCityName})所有正常状态的骑手`" |
| 90 | type="warning" | 116 | type="warning" |
| 91 | show-icon | 117 | show-icon |
| 92 | style="margin-bottom: 16px" | 118 | style="margin-bottom: 16px" |
| @@ -113,14 +139,18 @@ | @@ -113,14 +139,18 @@ | ||
| 113 | import { onMounted, reactive, ref, computed } from 'vue' | 139 | import { onMounted, reactive, ref, computed } from 'vue' |
| 114 | import { message } from 'ant-design-vue' | 140 | import { message } from 'ant-design-vue' |
| 115 | import { messageApi } from '@/api' | 141 | import { messageApi } from '@/api' |
| 142 | +import { useRoleCityList } from '@/composables/useRoleCityList' | ||
| 116 | import type { TablePaginationConfig } from 'ant-design-vue' | 143 | import type { TablePaginationConfig } from 'ant-design-vue' |
| 117 | 144 | ||
| 145 | +const { isAdmin, managedCityId, managedCityName, cityList, loadCities, getCityName } = useRoleCityList() | ||
| 146 | + | ||
| 118 | const loading = ref(false) | 147 | const loading = ref(false) |
| 119 | const sending = ref(false) | 148 | const sending = ref(false) |
| 120 | const broadcasting = ref(false) | 149 | const broadcasting = ref(false) |
| 121 | const list = ref<any[]>([]) | 150 | const list = ref<any[]>([]) |
| 122 | const sendVisible = ref(false) | 151 | const sendVisible = ref(false) |
| 123 | const broadcastVisible = ref(false) | 152 | const broadcastVisible = ref(false) |
| 153 | +const selectedCityId = ref<number | undefined>() | ||
| 124 | 154 | ||
| 125 | const searchForm = reactive({ | 155 | const searchForm = reactive({ |
| 126 | riderId: undefined as number | undefined, | 156 | riderId: undefined as number | undefined, |
| @@ -143,6 +173,8 @@ const broadcastForm = reactive({ | @@ -143,6 +173,8 @@ const broadcastForm = reactive({ | ||
| 143 | const currentPage = ref(1) | 173 | const currentPage = ref(1) |
| 144 | const pageSize = ref(20) | 174 | const pageSize = ref(20) |
| 145 | const total = ref(0) | 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 | const pagination = computed<TablePaginationConfig>(() => ({ | 179 | const pagination = computed<TablePaginationConfig>(() => ({ |
| 148 | current: currentPage.value, | 180 | current: currentPage.value, |
| @@ -163,9 +195,15 @@ const columns = [ | @@ -163,9 +195,15 @@ const columns = [ | ||
| 163 | ] | 195 | ] |
| 164 | 196 | ||
| 165 | async function loadList() { | 197 | async function loadList() { |
| 198 | + if (!selectedCityId.value) { | ||
| 199 | + list.value = [] | ||
| 200 | + total.value = 0 | ||
| 201 | + return | ||
| 202 | + } | ||
| 166 | loading.value = true | 203 | loading.value = true |
| 167 | try { | 204 | try { |
| 168 | const res: any = await messageApi.list({ | 205 | const res: any = await messageApi.list({ |
| 206 | + cityId: selectedCityId.value, | ||
| 169 | riderId: searchForm.riderId, | 207 | riderId: searchForm.riderId, |
| 170 | type: searchForm.type, | 208 | type: searchForm.type, |
| 171 | page: currentPage.value, | 209 | page: currentPage.value, |
| @@ -180,6 +218,16 @@ async function loadList() { | @@ -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 | function handleTableChange(pag: TablePaginationConfig) { | 231 | function handleTableChange(pag: TablePaginationConfig) { |
| 184 | const nextPage = pag.current ?? 1 | 232 | const nextPage = pag.current ?? 1 |
| 185 | if (nextPage === currentPage.value) return | 233 | if (nextPage === currentPage.value) return |
| @@ -187,24 +235,35 @@ function handleTableChange(pag: TablePaginationConfig) { | @@ -187,24 +235,35 @@ function handleTableChange(pag: TablePaginationConfig) { | ||
| 187 | loadList() | 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 | function openSend() { | 246 | function openSend() { |
| 247 | + if (!ensureCitySelected()) return | ||
| 191 | Object.assign(sendForm, { riderId: undefined, type: 2, title: '', content: '' }) | 248 | Object.assign(sendForm, { riderId: undefined, type: 2, title: '', content: '' }) |
| 192 | sendVisible.value = true | 249 | sendVisible.value = true |
| 193 | } | 250 | } |
| 194 | 251 | ||
| 195 | function openBroadcast() { | 252 | function openBroadcast() { |
| 253 | + if (!ensureCitySelected()) return | ||
| 196 | Object.assign(broadcastForm, { type: 2, title: '', content: '' }) | 254 | Object.assign(broadcastForm, { type: 2, title: '', content: '' }) |
| 197 | broadcastVisible.value = true | 255 | broadcastVisible.value = true |
| 198 | } | 256 | } |
| 199 | 257 | ||
| 200 | async function handleSend() { | 258 | async function handleSend() { |
| 259 | + if (!ensureCitySelected()) return | ||
| 201 | if (!sendForm.riderId || !sendForm.title || !sendForm.content) { | 260 | if (!sendForm.riderId || !sendForm.title || !sendForm.content) { |
| 202 | message.error('请填写完整信息') | 261 | message.error('请填写完整信息') |
| 203 | return | 262 | return |
| 204 | } | 263 | } |
| 205 | sending.value = true | 264 | sending.value = true |
| 206 | try { | 265 | try { |
| 207 | - await messageApi.send(sendForm) | 266 | + await messageApi.send({ ...sendForm, cityId: selectedCityId.value }) |
| 208 | message.success('发送成功') | 267 | message.success('发送成功') |
| 209 | sendVisible.value = false | 268 | sendVisible.value = false |
| 210 | loadList() | 269 | loadList() |
| @@ -214,13 +273,14 @@ async function handleSend() { | @@ -214,13 +273,14 @@ async function handleSend() { | ||
| 214 | } | 273 | } |
| 215 | 274 | ||
| 216 | async function handleBroadcast() { | 275 | async function handleBroadcast() { |
| 276 | + if (!ensureCitySelected()) return | ||
| 217 | if (!broadcastForm.title || !broadcastForm.content) { | 277 | if (!broadcastForm.title || !broadcastForm.content) { |
| 218 | message.error('请填写完整信息') | 278 | message.error('请填写完整信息') |
| 219 | return | 279 | return |
| 220 | } | 280 | } |
| 221 | broadcasting.value = true | 281 | broadcasting.value = true |
| 222 | try { | 282 | try { |
| 223 | - await messageApi.broadcast(broadcastForm) | 283 | + await messageApi.broadcast({ ...broadcastForm, cityId: selectedCityId.value }) |
| 224 | message.success('群发成功') | 284 | message.success('群发成功') |
| 225 | broadcastVisible.value = false | 285 | broadcastVisible.value = false |
| 226 | loadList() | 286 | loadList() |
| @@ -242,18 +302,26 @@ function formatTime(timestamp: number) { | @@ -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 | </script> | 312 | </script> |
| 247 | 313 | ||
| 248 | <style scoped> | 314 | <style scoped> |
| 249 | .list-toolbar { | 315 | .list-toolbar { |
| 250 | display: flex; | 316 | display: flex; |
| 251 | justify-content: space-between; | 317 | justify-content: space-between; |
| 318 | + gap: 12px; | ||
| 252 | margin-bottom: 16px; | 319 | margin-bottom: 16px; |
| 253 | } | 320 | } |
| 254 | 321 | ||
| 255 | .list-toolbar-left { | 322 | .list-toolbar-left { |
| 256 | display: flex; | 323 | display: flex; |
| 324 | + flex-wrap: wrap; | ||
| 257 | gap: 8px; | 325 | gap: 8px; |
| 258 | } | 326 | } |
| 259 | 327 | ||
| @@ -261,4 +329,16 @@ onMounted(loadList) | @@ -261,4 +329,16 @@ onMounted(loadList) | ||
| 261 | display: flex; | 329 | display: flex; |
| 262 | gap: 8px; | 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 | </style> | 344 | </style> |
src/views/system/MenuManage.vue
| @@ -213,6 +213,7 @@ import { | @@ -213,6 +213,7 @@ import { | ||
| 213 | ControlOutlined, | 213 | ControlOutlined, |
| 214 | GlobalOutlined, | 214 | GlobalOutlined, |
| 215 | HomeOutlined, | 215 | HomeOutlined, |
| 216 | + MessageOutlined, | ||
| 216 | SettingOutlined, | 217 | SettingOutlined, |
| 217 | ShopOutlined, | 218 | ShopOutlined, |
| 218 | StarOutlined, | 219 | StarOutlined, |
| @@ -254,6 +255,7 @@ const iconOptions = [ | @@ -254,6 +255,7 @@ const iconOptions = [ | ||
| 254 | 'TeamOutlined', | 255 | 'TeamOutlined', |
| 255 | 'TrophyOutlined', | 256 | 'TrophyOutlined', |
| 256 | 'SettingOutlined', | 257 | 'SettingOutlined', |
| 258 | + 'MessageOutlined', | ||
| 257 | ] | 259 | ] |
| 258 | 260 | ||
| 259 | const iconMap: Record<string, any> = { | 261 | const iconMap: Record<string, any> = { |
| @@ -269,6 +271,7 @@ const iconMap: Record<string, any> = { | @@ -269,6 +271,7 @@ const iconMap: Record<string, any> = { | ||
| 269 | TeamOutlined, | 271 | TeamOutlined, |
| 270 | TrophyOutlined, | 272 | TrophyOutlined, |
| 271 | SettingOutlined, | 273 | SettingOutlined, |
| 274 | + MessageOutlined, | ||
| 272 | } | 275 | } |
| 273 | 276 | ||
| 274 | function resolveIcon(icon?: string) { | 277 | function resolveIcon(icon?: string) { |
src/views/system/RoleMenuAssign.vue
| @@ -101,6 +101,7 @@ import { | @@ -101,6 +101,7 @@ import { | ||
| 101 | ControlOutlined, | 101 | ControlOutlined, |
| 102 | GlobalOutlined, | 102 | GlobalOutlined, |
| 103 | HomeOutlined, | 103 | HomeOutlined, |
| 104 | + MessageOutlined, | ||
| 104 | SettingOutlined, | 105 | SettingOutlined, |
| 105 | ShopOutlined, | 106 | ShopOutlined, |
| 106 | StarOutlined, | 107 | StarOutlined, |
| @@ -141,6 +142,7 @@ const iconMap: Record<string, any> = { | @@ -141,6 +142,7 @@ const iconMap: Record<string, any> = { | ||
| 141 | TeamOutlined, | 142 | TeamOutlined, |
| 142 | TrophyOutlined, | 143 | TrophyOutlined, |
| 143 | SettingOutlined, | 144 | SettingOutlined, |
| 145 | + MessageOutlined, | ||
| 144 | } | 146 | } |
| 145 | 147 | ||
| 146 | function resolveIcon(icon?: string) { | 148 | function resolveIcon(icon?: string) { |