Commit caab2d9a71bc59d83bb8db4e275222e55af4211a

Authored by shaofan
1 parent 6a1f8b67

优化接口响应时间,提升系统性能

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
... ... @@ -19,6 +19,7 @@ export const iconRegistry: Record&lt;string, string&gt; = {
19 19 TeamOutlined: 'team',
20 20 TrophyOutlined: 'trophy',
21 21 SettingOutlined: 'setting',
  22 + MessageOutlined: 'message',
22 23 }
23 24  
24 25 export const pinnedQuickLinks: QuickLinkItem[] = [
... ...
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&lt;string, any&gt; = {
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&lt;string, any&gt; = {
141 142 TeamOutlined,
142 143 TrophyOutlined,
143 144 SettingOutlined,
  145 + MessageOutlined,
144 146 }
145 147  
146 148 function resolveIcon(icon?: string) {
... ...