Commit b37151b626a60e4d06c8403d211396fb3a3aada7
1 parent
054f8469
init
Showing
15 changed files
with
1095 additions
and
495 deletions
src/api/index.ts
| ... | ... | @@ -35,7 +35,7 @@ export const substationApi = { |
| 35 | 35 | ban: (id: number) => request.post('/api/platform/substation/ban', null, { params: { id } }), |
| 36 | 36 | cancelBan: (id: number) => request.post('/api/platform/substation/cancelBan', null, { params: { id } }), |
| 37 | 37 | del: (id: number) => request.delete('/api/platform/substation/del', { params: { id } }), |
| 38 | - changePassword: (data: { oldPassword: string; newPassword: string }) => | |
| 38 | + changePassword: (data: { id: number; oldPassword: string; newPassword: string }) => | |
| 39 | 39 | request.post('/api/platform/substation/changePassword', data), |
| 40 | 40 | } |
| 41 | 41 | ... | ... |
src/layouts/MainLayout.vue
| 1 | 1 | <template> |
| 2 | - <div class="layout-shell"> | |
| 2 | + <div class="layout-shell" :class="{ collapsed }"> | |
| 3 | 3 | <aside class="soft-sider" :class="{ collapsed }"> |
| 4 | 4 | <button class="sider-toggle" type="button" @click="collapsed = !collapsed"> |
| 5 | 5 | <menu-fold-outlined v-if="!collapsed" /> |
| ... | ... | @@ -18,6 +18,7 @@ |
| 18 | 18 | <a-menu |
| 19 | 19 | v-model:selectedKeys="selectedKeys" |
| 20 | 20 | mode="inline" |
| 21 | + :inline-collapsed="collapsed" | |
| 21 | 22 | @click="onMenuClick" |
| 22 | 23 | > |
| 23 | 24 | <a-menu-item key="/dashboard"> |
| ... | ... | @@ -163,6 +164,10 @@ function handleLogout() { |
| 163 | 164 | padding: 16px; |
| 164 | 165 | } |
| 165 | 166 | |
| 167 | +.layout-shell.collapsed { | |
| 168 | + grid-template-columns: 88px minmax(0, 1fr); | |
| 169 | +} | |
| 170 | + | |
| 166 | 171 | .soft-sider, |
| 167 | 172 | .soft-topbar { |
| 168 | 173 | border: 1px solid rgba(255, 255, 255, 0.58); |
| ... | ... | @@ -190,6 +195,10 @@ function handleLogout() { |
| 190 | 195 | padding-right: 4px; |
| 191 | 196 | } |
| 192 | 197 | |
| 198 | +.soft-sider.collapsed .menu-scroll { | |
| 199 | + padding-right: 0; | |
| 200 | +} | |
| 201 | + | |
| 193 | 202 | .menu-scroll::-webkit-scrollbar { |
| 194 | 203 | width: 8px; |
| 195 | 204 | } |
| ... | ... | @@ -201,6 +210,7 @@ function handleLogout() { |
| 201 | 210 | |
| 202 | 211 | .soft-sider.collapsed { |
| 203 | 212 | width: 88px; |
| 213 | + padding-inline: 8px; | |
| 204 | 214 | } |
| 205 | 215 | |
| 206 | 216 | .sider-toggle { |
| ... | ... | @@ -215,6 +225,10 @@ function handleLogout() { |
| 215 | 225 | margin-bottom: 12px; |
| 216 | 226 | } |
| 217 | 227 | |
| 228 | +.soft-sider.collapsed .sider-toggle { | |
| 229 | + align-self: center; | |
| 230 | +} | |
| 231 | + | |
| 218 | 232 | .brand-block { |
| 219 | 233 | display: flex; |
| 220 | 234 | align-items: center; |
| ... | ... | @@ -222,6 +236,11 @@ function handleLogout() { |
| 222 | 236 | padding: 6px 8px 14px; |
| 223 | 237 | } |
| 224 | 238 | |
| 239 | +.soft-sider.collapsed .brand-block { | |
| 240 | + justify-content: center; | |
| 241 | + padding-inline: 0; | |
| 242 | +} | |
| 243 | + | |
| 225 | 244 | .brand-mark { |
| 226 | 245 | width: 44px; |
| 227 | 246 | height: 44px; |
| ... | ... | @@ -274,6 +293,10 @@ h1 { |
| 274 | 293 | background: transparent; |
| 275 | 294 | } |
| 276 | 295 | |
| 296 | +.soft-sider.collapsed :deep(.ant-menu) { | |
| 297 | + width: 100%; | |
| 298 | +} | |
| 299 | + | |
| 277 | 300 | .sider-foot { |
| 278 | 301 | margin-top: 10px; |
| 279 | 302 | flex-shrink: 0; |
| ... | ... | @@ -384,6 +407,33 @@ h1 { |
| 384 | 407 | font-size: 13px; |
| 385 | 408 | } |
| 386 | 409 | |
| 410 | +.soft-sider.collapsed :deep(.ant-menu-item), | |
| 411 | +.soft-sider.collapsed :deep(.ant-menu-submenu-title) { | |
| 412 | + width: 100% !important; | |
| 413 | + margin-inline: 0 !important; | |
| 414 | + padding-inline: 0 !important; | |
| 415 | + display: flex !important; | |
| 416 | + align-items: center !important; | |
| 417 | + justify-content: center !important; | |
| 418 | + text-align: center; | |
| 419 | +} | |
| 420 | + | |
| 421 | +.soft-sider.collapsed :deep(.ant-menu-inline-collapsed .ant-menu-item .ant-menu-item-icon), | |
| 422 | +.soft-sider.collapsed :deep(.ant-menu-inline-collapsed .ant-menu-submenu-title .ant-menu-item-icon), | |
| 423 | +.soft-sider.collapsed :deep(.ant-menu-inline-collapsed .ant-menu-item .anticon), | |
| 424 | +.soft-sider.collapsed :deep(.ant-menu-inline-collapsed .ant-menu-submenu-title .anticon) { | |
| 425 | + margin-inline: 0; | |
| 426 | + font-size: 16px; | |
| 427 | +} | |
| 428 | + | |
| 429 | +.soft-sider.collapsed :deep(.ant-menu-title-content) { | |
| 430 | + display: none !important; | |
| 431 | +} | |
| 432 | + | |
| 433 | +.soft-sider.collapsed :deep(.ant-menu-submenu-arrow) { | |
| 434 | + display: none !important; | |
| 435 | +} | |
| 436 | + | |
| 387 | 437 | @media (max-width: 960px) { |
| 388 | 438 | .layout-shell { |
| 389 | 439 | grid-template-columns: 1fr; | ... | ... |
src/style.css
| ... | ... | @@ -226,6 +226,76 @@ a { |
| 226 | 226 | |
| 227 | 227 | .ant-modal .ant-modal-footer { |
| 228 | 228 | padding: 12px 20px 18px; |
| 229 | + border-top: 1px solid rgba(189, 180, 234, 0.14); | |
| 230 | +} | |
| 231 | + | |
| 232 | +.ant-modal .ant-modal-footer .ant-btn + .ant-btn { | |
| 233 | + margin-inline-start: 8px; | |
| 234 | +} | |
| 235 | + | |
| 236 | +.ant-modal .ant-form-vertical .ant-form-item-label { | |
| 237 | + padding-bottom: 6px; | |
| 238 | +} | |
| 239 | + | |
| 240 | +.ant-modal .ant-form-vertical .ant-form-item-label > label { | |
| 241 | + color: var(--text-dark); | |
| 242 | + font-size: var(--font-size-md); | |
| 243 | + font-weight: 600; | |
| 244 | +} | |
| 245 | + | |
| 246 | +.ant-modal .ant-row { | |
| 247 | + row-gap: 0; | |
| 248 | +} | |
| 249 | + | |
| 250 | +.ant-modal .ant-radio-group { | |
| 251 | + display: inline-flex; | |
| 252 | + gap: 8px; | |
| 253 | + flex-wrap: wrap; | |
| 254 | +} | |
| 255 | + | |
| 256 | +.ant-modal .ant-radio-wrapper, | |
| 257 | +.ant-modal .ant-radio-button-wrapper { | |
| 258 | + font-size: var(--font-size-md); | |
| 259 | +} | |
| 260 | + | |
| 261 | +.ant-modal textarea.ant-input { | |
| 262 | + min-height: 88px; | |
| 263 | + line-height: 1.6; | |
| 264 | + padding-top: 8px; | |
| 265 | + padding-bottom: 8px; | |
| 266 | +} | |
| 267 | + | |
| 268 | +.ant-modal .ant-switch { | |
| 269 | + min-width: 38px; | |
| 270 | +} | |
| 271 | + | |
| 272 | +.ant-descriptions { | |
| 273 | + border-radius: 14px; | |
| 274 | + overflow: hidden; | |
| 275 | +} | |
| 276 | + | |
| 277 | +.ant-descriptions .ant-descriptions-item-label { | |
| 278 | + width: 128px; | |
| 279 | + background: rgba(244, 240, 255, 0.72) !important; | |
| 280 | + color: var(--text-dark) !important; | |
| 281 | + font-size: var(--font-size-md); | |
| 282 | + font-weight: 600; | |
| 283 | +} | |
| 284 | + | |
| 285 | +.ant-descriptions .ant-descriptions-item-content { | |
| 286 | + background: rgba(255, 255, 255, 0.72) !important; | |
| 287 | + font-size: var(--font-size-md); | |
| 288 | +} | |
| 289 | + | |
| 290 | +.ant-modal .ant-alert { | |
| 291 | + border-radius: 12px; | |
| 292 | + font-size: var(--font-size-md); | |
| 293 | +} | |
| 294 | + | |
| 295 | +.ant-modal .ant-table-small .ant-table-thead > tr > th, | |
| 296 | +.ant-modal .ant-table-small .ant-table-tbody > tr > td { | |
| 297 | + padding-top: 9px; | |
| 298 | + padding-bottom: 9px; | |
| 229 | 299 | } |
| 230 | 300 | |
| 231 | 301 | .ant-tag { |
| ... | ... | @@ -268,6 +338,30 @@ a { |
| 268 | 338 | color: var(--brand-deep) !important; |
| 269 | 339 | } |
| 270 | 340 | |
| 341 | +.ant-menu-submenu-popup .ant-menu { | |
| 342 | + padding: 8px !important; | |
| 343 | + border-radius: 16px !important; | |
| 344 | + border: 1px solid rgba(228, 223, 247, 0.72) !important; | |
| 345 | + background: rgba(255, 255, 255, 0.96) !important; | |
| 346 | + backdrop-filter: blur(20px); | |
| 347 | + box-shadow: 0 18px 40px rgba(121, 104, 213, 0.14) !important; | |
| 348 | +} | |
| 349 | + | |
| 350 | +.ant-menu-submenu-popup .ant-menu-item, | |
| 351 | +.ant-menu-submenu-popup .ant-menu-submenu-title { | |
| 352 | + min-height: 36px !important; | |
| 353 | + line-height: 36px !important; | |
| 354 | + margin: 2px 0 !important; | |
| 355 | + width: 100% !important; | |
| 356 | + border-radius: 12px !important; | |
| 357 | + font-size: var(--font-size-md) !important; | |
| 358 | +} | |
| 359 | + | |
| 360 | +.ant-menu-submenu-popup .ant-menu-item:hover, | |
| 361 | +.ant-menu-submenu-popup .ant-menu-submenu-title:hover { | |
| 362 | + background: rgba(244, 240, 255, 0.92) !important; | |
| 363 | +} | |
| 364 | + | |
| 271 | 365 | .ant-menu-item:hover, |
| 272 | 366 | .ant-menu-submenu-title:hover { |
| 273 | 367 | color: var(--brand-deep) !important; |
| ... | ... | @@ -304,8 +398,165 @@ a { |
| 304 | 398 | pointer-events: none; |
| 305 | 399 | } |
| 306 | 400 | |
| 401 | +.soft-page-stack { | |
| 402 | + display: grid; | |
| 403 | + gap: 16px; | |
| 404 | +} | |
| 405 | + | |
| 406 | +.soft-section { | |
| 407 | + display: grid; | |
| 408 | + gap: 14px; | |
| 409 | + margin-bottom: 18px; | |
| 410 | +} | |
| 411 | + | |
| 412 | +.soft-section:last-child { | |
| 413 | + margin-bottom: 0; | |
| 414 | +} | |
| 415 | + | |
| 416 | +.soft-section-header { | |
| 417 | + display: flex; | |
| 418 | + align-items: flex-start; | |
| 419 | + justify-content: space-between; | |
| 420 | + gap: 12px; | |
| 421 | + flex-wrap: wrap; | |
| 422 | +} | |
| 423 | + | |
| 424 | +.soft-section-heading { | |
| 425 | + min-width: 0; | |
| 426 | +} | |
| 427 | + | |
| 428 | +.soft-section-title { | |
| 429 | + margin: 0; | |
| 430 | + color: var(--text-dark); | |
| 431 | + font-size: 15px; | |
| 432 | + font-weight: 700; | |
| 433 | + line-height: 1.35; | |
| 434 | +} | |
| 435 | + | |
| 436 | +.soft-section-subtitle { | |
| 437 | + margin: 4px 0 0; | |
| 438 | + color: var(--text-soft); | |
| 439 | + font-size: var(--font-size-sm); | |
| 440 | + line-height: 1.6; | |
| 441 | +} | |
| 442 | + | |
| 443 | +.soft-section-actions { | |
| 444 | + display: flex; | |
| 445 | + align-items: center; | |
| 446 | + gap: 8px; | |
| 447 | + flex-wrap: wrap; | |
| 448 | +} | |
| 449 | + | |
| 450 | +.soft-note-card { | |
| 451 | + border-radius: 16px; | |
| 452 | + padding: 14px 16px; | |
| 453 | + background: linear-gradient(135deg, rgba(244, 240, 255, 0.9), rgba(255, 250, 253, 0.9)); | |
| 454 | + border: 1px solid rgba(206, 196, 244, 0.36); | |
| 455 | +} | |
| 456 | + | |
| 457 | +.soft-note-card strong { | |
| 458 | + display: block; | |
| 459 | + margin-bottom: 4px; | |
| 460 | + color: var(--text-dark); | |
| 461 | + font-size: var(--font-size-md); | |
| 462 | +} | |
| 463 | + | |
| 464 | +.soft-note-card p { | |
| 465 | + margin: 0; | |
| 466 | + color: var(--text-soft); | |
| 467 | + font-size: var(--font-size-sm); | |
| 468 | + line-height: 1.6; | |
| 469 | +} | |
| 470 | + | |
| 471 | +.soft-inline-actions { | |
| 472 | + display: flex; | |
| 473 | + align-items: center; | |
| 474 | + gap: 10px; | |
| 475 | + flex-wrap: wrap; | |
| 476 | +} | |
| 477 | + | |
| 478 | +.soft-dashed-block { | |
| 479 | + border-radius: 18px; | |
| 480 | + border: 1px dashed rgba(180, 170, 231, 0.5); | |
| 481 | + background: rgba(255, 255, 255, 0.48); | |
| 482 | + padding: 14px; | |
| 483 | +} | |
| 484 | + | |
| 485 | +.soft-result-grid { | |
| 486 | + display: grid; | |
| 487 | + gap: 16px; | |
| 488 | +} | |
| 489 | + | |
| 490 | +.list-toolbar { | |
| 491 | + display: flex; | |
| 492 | + align-items: center; | |
| 493 | + justify-content: space-between; | |
| 494 | + gap: 12px; | |
| 495 | + margin-bottom: 14px; | |
| 496 | + flex-wrap: wrap; | |
| 497 | +} | |
| 498 | + | |
| 499 | +.list-toolbar-left, | |
| 500 | +.list-toolbar-right { | |
| 501 | + display: flex; | |
| 502 | + align-items: center; | |
| 503 | + gap: 10px; | |
| 504 | + flex-wrap: wrap; | |
| 505 | +} | |
| 506 | + | |
| 507 | +.list-toolbar-right { | |
| 508 | + justify-content: flex-end; | |
| 509 | +} | |
| 510 | + | |
| 511 | +.list-filter { | |
| 512 | + min-width: 120px; | |
| 513 | +} | |
| 514 | + | |
| 515 | +.list-search { | |
| 516 | + width: 220px; | |
| 517 | +} | |
| 518 | + | |
| 519 | +.list-table-card .ant-card-head { | |
| 520 | + min-height: 52px; | |
| 521 | +} | |
| 522 | + | |
| 523 | +.list-table-card .ant-card-head-title { | |
| 524 | + font-size: 15px; | |
| 525 | +} | |
| 526 | + | |
| 527 | +.list-table-card .ant-card-body { | |
| 528 | + padding-top: 16px; | |
| 529 | +} | |
| 530 | + | |
| 307 | 531 | @media (max-width: 1200px) { |
| 308 | 532 | .soft-page-shell { |
| 309 | 533 | padding: 18px; |
| 310 | 534 | } |
| 311 | 535 | } |
| 536 | + | |
| 537 | +@media (max-width: 960px) { | |
| 538 | + .soft-section-header { | |
| 539 | + align-items: stretch; | |
| 540 | + } | |
| 541 | + | |
| 542 | + .soft-section-actions { | |
| 543 | + width: 100%; | |
| 544 | + } | |
| 545 | + | |
| 546 | + .list-toolbar { | |
| 547 | + align-items: stretch; | |
| 548 | + } | |
| 549 | + | |
| 550 | + .list-toolbar-left, | |
| 551 | + .list-toolbar-right { | |
| 552 | + width: 100%; | |
| 553 | + } | |
| 554 | + | |
| 555 | + .list-toolbar-right { | |
| 556 | + justify-content: flex-start; | |
| 557 | + } | |
| 558 | + | |
| 559 | + .list-search { | |
| 560 | + width: 100%; | |
| 561 | + } | |
| 562 | +} | ... | ... |
src/views/city/CityList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="租户管理" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-button type="primary" @click="openAdd">新增</a-button> | |
| 6 | - </template> | |
| 3 | + <a-card title="租户管理" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-right"> | |
| 6 | + <a-button type="primary" @click="openAdd">新增</a-button> | |
| 7 | + </div> | |
| 8 | + </div> | |
| 7 | 9 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 8 | 10 | <template #bodyCell="{ column, record }"> |
| 9 | 11 | <template v-if="column.key === 'status'"> |
| ... | ... | @@ -78,6 +80,11 @@ |
| 78 | 80 | |
| 79 | 81 | <div class="plan-content"> |
| 80 | 82 | <template v-if="currentPlan && config"> |
| 83 | + <div class="soft-note-card plan-note-card"> | |
| 84 | + <strong>当前配置说明</strong> | |
| 85 | + <p>当前弹窗只维护外卖配送规则。你可以在同一租户下维护多套方案,并设置默认启用方案用于实时计价。</p> | |
| 86 | + </div> | |
| 87 | + | |
| 81 | 88 | <div class="plan-toolbar"> |
| 82 | 89 | <a-space wrap> |
| 83 | 90 | <a-button @click="copyPlan" :disabled="!selectedPlanId">复制当前方案</a-button> |
| ... | ... | @@ -92,32 +99,42 @@ |
| 92 | 99 | </a-space> |
| 93 | 100 | </div> |
| 94 | 101 | |
| 95 | - <a-row :gutter="16"> | |
| 96 | - <a-col :span="8"> | |
| 97 | - <a-form-item label="方案名称"> | |
| 98 | - <a-input v-model:value="currentPlan.name" placeholder="如:标准午高峰方案" /> | |
| 99 | - </a-form-item> | |
| 100 | - </a-col> | |
| 101 | - <a-col :span="8"> | |
| 102 | - <a-form-item label="状态"> | |
| 103 | - <a-select v-model:value="currentPlan.status"> | |
| 104 | - <a-select-option :value="1">启用</a-select-option> | |
| 105 | - <a-select-option :value="0">停用</a-select-option> | |
| 106 | - </a-select> | |
| 107 | - </a-form-item> | |
| 108 | - </a-col> | |
| 109 | - <a-col :span="8"> | |
| 110 | - <a-form-item label="排序"> | |
| 111 | - <a-input-number v-model:value="currentPlan.listOrder" :min="0" style="width:100%" /> | |
| 112 | - </a-form-item> | |
| 113 | - </a-col> | |
| 114 | - </a-row> | |
| 115 | - <a-form-item label="备注"> | |
| 116 | - <a-input v-model:value="currentPlan.remark" placeholder="可填写适用业务场景说明" /> | |
| 117 | - </a-form-item> | |
| 102 | + <div class="plan-section"> | |
| 103 | + <div class="soft-section-header"> | |
| 104 | + <div class="soft-section-heading"> | |
| 105 | + <h3 class="soft-section-title">方案基础信息</h3> | |
| 106 | + <p class="soft-section-subtitle">维护方案名称、状态和备注说明。</p> | |
| 107 | + </div> | |
| 108 | + </div> | |
| 109 | + | |
| 110 | + <a-row :gutter="16"> | |
| 111 | + <a-col :span="8"> | |
| 112 | + <a-form-item label="方案名称"> | |
| 113 | + <a-input v-model:value="currentPlan.name" placeholder="如:标准午高峰方案" /> | |
| 114 | + </a-form-item> | |
| 115 | + </a-col> | |
| 116 | + <a-col :span="8"> | |
| 117 | + <a-form-item label="状态"> | |
| 118 | + <a-select v-model:value="currentPlan.status"> | |
| 119 | + <a-select-option :value="1">启用</a-select-option> | |
| 120 | + <a-select-option :value="0">停用</a-select-option> | |
| 121 | + </a-select> | |
| 122 | + </a-form-item> | |
| 123 | + </a-col> | |
| 124 | + <a-col :span="8"> | |
| 125 | + <a-form-item label="排序"> | |
| 126 | + <a-input-number v-model:value="currentPlan.listOrder" :min="0" style="width:100%" /> | |
| 127 | + </a-form-item> | |
| 128 | + </a-col> | |
| 129 | + </a-row> | |
| 130 | + <a-form-item label="备注"> | |
| 131 | + <a-input v-model:value="currentPlan.remark" placeholder="可填写适用业务场景说明" /> | |
| 132 | + </a-form-item> | |
| 133 | + </div> | |
| 118 | 134 | |
| 119 | 135 | <a-card class="preview-card" :bordered="false"> |
| 120 | 136 | <template #title>草稿试算</template> |
| 137 | + <div class="soft-section-subtitle preview-subtitle">保存前可先按草稿参数试算配送费,确认距离、时段和附加项是否符合预期。</div> | |
| 121 | 138 | <a-row :gutter="12"> |
| 122 | 139 | <a-col :span="6"> |
| 123 | 140 | <a-form-item label="起点经度"> |
| ... | ... | @@ -172,42 +189,55 @@ |
| 172 | 189 | </a-card> |
| 173 | 190 | |
| 174 | 191 | <a-form :model="config" layout="vertical"> |
| 175 | - <a-divider orientation="left">费用总览</a-divider> | |
| 176 | - <a-row :gutter="16"> | |
| 177 | - <a-col :span="12"> | |
| 178 | - <a-form-item label="保底费用(元)"> | |
| 179 | - <a-input-number v-model:value="config.type6.minFee" :min="0" :step="0.1" style="width:100%" /> | |
| 180 | - </a-form-item> | |
| 181 | - </a-col> | |
| 182 | - <a-col :span="12"> | |
| 183 | - <a-form-item label="基础费(元/单)"> | |
| 184 | - <a-input-number v-model:value="config.type6.baseFee" :min="0" :step="0.1" style="width:100%" /> | |
| 185 | - </a-form-item> | |
| 186 | - </a-col> | |
| 187 | - </a-row> | |
| 188 | - | |
| 189 | - <a-divider orientation="left">里程阶梯</a-divider> | |
| 190 | - <a-row :gutter="16"> | |
| 191 | - <a-col :span="12"> | |
| 192 | - <a-form-item label="起步里程(km内)"> | |
| 193 | - <a-input-number v-model:value="config.type6.distanceBasic" :min="0" :step="0.1" style="width:100%" /> | |
| 194 | - </a-form-item> | |
| 195 | - </a-col> | |
| 196 | - <a-col :span="12"> | |
| 197 | - <a-form-item label="起步费用(元)"> | |
| 198 | - <a-input-number v-model:value="config.type6.distanceBasicMoney" :min="0" :step="0.1" style="width:100%" /> | |
| 199 | - </a-form-item> | |
| 200 | - </a-col> | |
| 201 | - </a-row> | |
| 202 | - <div style="margin-bottom:12px"> | |
| 203 | - <a-button type="dashed" block @click="addDistanceStep">新增阶梯段</a-button> | |
| 192 | + <div class="plan-section"> | |
| 193 | + <div class="soft-section-header"> | |
| 194 | + <div class="soft-section-heading"> | |
| 195 | + <h3 class="soft-section-title">费用总览</h3> | |
| 196 | + <p class="soft-section-subtitle">基础费与保底费会参与最终总价计算。</p> | |
| 197 | + </div> | |
| 198 | + </div> | |
| 199 | + <a-row :gutter="16"> | |
| 200 | + <a-col :span="12"> | |
| 201 | + <a-form-item label="保底费用(元)"> | |
| 202 | + <a-input-number v-model:value="config.type6.minFee" :min="0" :step="0.1" style="width:100%" /> | |
| 203 | + </a-form-item> | |
| 204 | + </a-col> | |
| 205 | + <a-col :span="12"> | |
| 206 | + <a-form-item label="基础费(元/单)"> | |
| 207 | + <a-input-number v-model:value="config.type6.baseFee" :min="0" :step="0.1" style="width:100%" /> | |
| 208 | + </a-form-item> | |
| 209 | + </a-col> | |
| 210 | + </a-row> | |
| 204 | 211 | </div> |
| 205 | - <div v-if="distanceSteps.length"> | |
| 212 | + | |
| 213 | + <div class="plan-section"> | |
| 214 | + <div class="soft-section-header"> | |
| 215 | + <div class="soft-section-heading"> | |
| 216 | + <h3 class="soft-section-title">里程阶梯</h3> | |
| 217 | + <p class="soft-section-subtitle">先配置起步距离,再追加超出后的阶梯规则。</p> | |
| 218 | + </div> | |
| 219 | + <div class="soft-section-actions"> | |
| 220 | + <a-button type="dashed" @click="addDistanceStep">新增阶梯段</a-button> | |
| 221 | + </div> | |
| 222 | + </div> | |
| 223 | + <a-row :gutter="16"> | |
| 224 | + <a-col :span="12"> | |
| 225 | + <a-form-item label="起步里程(km内)"> | |
| 226 | + <a-input-number v-model:value="config.type6.distanceBasic" :min="0" :step="0.1" style="width:100%" /> | |
| 227 | + </a-form-item> | |
| 228 | + </a-col> | |
| 229 | + <a-col :span="12"> | |
| 230 | + <a-form-item label="起步费用(元)"> | |
| 231 | + <a-input-number v-model:value="config.type6.distanceBasicMoney" :min="0" :step="0.1" style="width:100%" /> | |
| 232 | + </a-form-item> | |
| 233 | + </a-col> | |
| 234 | + </a-row> | |
| 235 | + <div v-if="distanceSteps.length" class="soft-dashed-block"> | |
| 206 | 236 | <a-row |
| 207 | 237 | v-for="(step, index) in distanceSteps" |
| 208 | 238 | :key="index" |
| 209 | 239 | :gutter="12" |
| 210 | - style="margin-bottom:12px;align-items:flex-start" | |
| 240 | + class="plan-dynamic-row" | |
| 211 | 241 | > |
| 212 | 242 | <a-col :span="7"> |
| 213 | 243 | <a-form-item :label="index === 0 ? '结束里程(km)' : ''"> |
| ... | ... | @@ -230,45 +260,59 @@ |
| 230 | 260 | </a-form-item> |
| 231 | 261 | </a-col> |
| 232 | 262 | </a-row> |
| 263 | + </div> | |
| 264 | + <a-empty v-else description="暂无里程阶梯配置" /> | |
| 233 | 265 | </div> |
| 234 | - <a-empty v-else description="暂无里程阶梯配置" /> | |
| 235 | 266 | |
| 236 | - <a-divider orientation="left">重量计费</a-divider> | |
| 237 | - <a-row :gutter="16"> | |
| 238 | - <a-col :span="12"> | |
| 239 | - <a-form-item label="首重(kg)"> | |
| 240 | - <a-input-number v-model:value="config.type6.weightFirst" :min="0" :step="0.1" style="width:100%" /> | |
| 241 | - </a-form-item> | |
| 242 | - </a-col> | |
| 243 | - <a-col :span="12"> | |
| 244 | - <a-form-item label="首重费用(元)"> | |
| 245 | - <a-input-number v-model:value="config.type6.weightFirstFee" :min="0" :step="0.1" style="width:100%" /> | |
| 246 | - </a-form-item> | |
| 247 | - </a-col> | |
| 248 | - </a-row> | |
| 249 | - <a-row :gutter="16"> | |
| 250 | - <a-col :span="12"> | |
| 251 | - <a-form-item label="续重单价(元/kg)"> | |
| 252 | - <a-input-number v-model:value="config.type6.weightUnitFee" :min="0" :step="0.1" style="width:100%" /> | |
| 253 | - </a-form-item> | |
| 254 | - </a-col> | |
| 255 | - <a-col :span="12"> | |
| 256 | - <a-form-item label="封顶费用(元)"> | |
| 257 | - <a-input-number v-model:value="config.type6.weightCapFee" :min="0" :step="0.1" style="width:100%" /> | |
| 258 | - </a-form-item> | |
| 259 | - </a-col> | |
| 260 | - </a-row> | |
| 261 | - | |
| 262 | - <a-divider orientation="left">件数计费</a-divider> | |
| 263 | - <div style="margin-bottom:12px"> | |
| 264 | - <a-button type="dashed" block @click="addPieceRule">新增件数区间</a-button> | |
| 267 | + <div class="plan-section"> | |
| 268 | + <div class="soft-section-header"> | |
| 269 | + <div class="soft-section-heading"> | |
| 270 | + <h3 class="soft-section-title">重量计费</h3> | |
| 271 | + <p class="soft-section-subtitle">重量按首重和续重单价计算,可设置费用封顶。</p> | |
| 272 | + </div> | |
| 273 | + </div> | |
| 274 | + <a-row :gutter="16"> | |
| 275 | + <a-col :span="12"> | |
| 276 | + <a-form-item label="首重(kg)"> | |
| 277 | + <a-input-number v-model:value="config.type6.weightFirst" :min="0" :step="0.1" style="width:100%" /> | |
| 278 | + </a-form-item> | |
| 279 | + </a-col> | |
| 280 | + <a-col :span="12"> | |
| 281 | + <a-form-item label="首重费用(元)"> | |
| 282 | + <a-input-number v-model:value="config.type6.weightFirstFee" :min="0" :step="0.1" style="width:100%" /> | |
| 283 | + </a-form-item> | |
| 284 | + </a-col> | |
| 285 | + </a-row> | |
| 286 | + <a-row :gutter="16"> | |
| 287 | + <a-col :span="12"> | |
| 288 | + <a-form-item label="续重单价(元/kg)"> | |
| 289 | + <a-input-number v-model:value="config.type6.weightUnitFee" :min="0" :step="0.1" style="width:100%" /> | |
| 290 | + </a-form-item> | |
| 291 | + </a-col> | |
| 292 | + <a-col :span="12"> | |
| 293 | + <a-form-item label="封顶费用(元)"> | |
| 294 | + <a-input-number v-model:value="config.type6.weightCapFee" :min="0" :step="0.1" style="width:100%" /> | |
| 295 | + </a-form-item> | |
| 296 | + </a-col> | |
| 297 | + </a-row> | |
| 265 | 298 | </div> |
| 266 | - <div v-if="pieceRules.length"> | |
| 299 | + | |
| 300 | + <div class="plan-section"> | |
| 301 | + <div class="soft-section-header"> | |
| 302 | + <div class="soft-section-heading"> | |
| 303 | + <h3 class="soft-section-title">件数计费</h3> | |
| 304 | + <p class="soft-section-subtitle">适合按商品件数额外加价的场景。</p> | |
| 305 | + </div> | |
| 306 | + <div class="soft-section-actions"> | |
| 307 | + <a-button type="dashed" @click="addPieceRule">新增件数区间</a-button> | |
| 308 | + </div> | |
| 309 | + </div> | |
| 310 | + <div v-if="pieceRules.length" class="soft-dashed-block"> | |
| 267 | 311 | <a-row |
| 268 | 312 | v-for="(rule, index) in pieceRules" |
| 269 | 313 | :key="index" |
| 270 | 314 | :gutter="12" |
| 271 | - style="margin-bottom:12px;align-items:flex-start" | |
| 315 | + class="plan-dynamic-row" | |
| 272 | 316 | > |
| 273 | 317 | <a-col :span="6"> |
| 274 | 318 | <a-form-item :label="index === 0 ? '起始件数' : ''"> |
| ... | ... | @@ -291,19 +335,26 @@ |
| 291 | 335 | </a-form-item> |
| 292 | 336 | </a-col> |
| 293 | 337 | </a-row> |
| 338 | + </div> | |
| 339 | + <a-empty v-else description="暂无件数区间配置" /> | |
| 294 | 340 | </div> |
| 295 | - <a-empty v-else description="暂无件数区间配置" /> | |
| 296 | 341 | |
| 297 | - <a-divider orientation="left">时段附加费</a-divider> | |
| 298 | - <div style="margin-bottom:12px"> | |
| 299 | - <a-button type="dashed" block @click="addTimePeriod">新增时段</a-button> | |
| 300 | - </div> | |
| 301 | - <div v-if="timePeriods.length"> | |
| 342 | + <div class="plan-section"> | |
| 343 | + <div class="soft-section-header"> | |
| 344 | + <div class="soft-section-heading"> | |
| 345 | + <h3 class="soft-section-title">时段附加费</h3> | |
| 346 | + <p class="soft-section-subtitle">用于午晚高峰或夜间等时段加价。</p> | |
| 347 | + </div> | |
| 348 | + <div class="soft-section-actions"> | |
| 349 | + <a-button type="dashed" @click="addTimePeriod">新增时段</a-button> | |
| 350 | + </div> | |
| 351 | + </div> | |
| 352 | + <div v-if="timePeriods.length" class="soft-dashed-block"> | |
| 302 | 353 | <a-row |
| 303 | 354 | v-for="(period, index) in timePeriods" |
| 304 | 355 | :key="index" |
| 305 | 356 | :gutter="12" |
| 306 | - style="margin-bottom:12px;align-items:flex-start" | |
| 357 | + class="plan-dynamic-row" | |
| 307 | 358 | > |
| 308 | 359 | <a-col :span="5"> |
| 309 | 360 | <a-form-item :label="index === 0 ? '开始时间' : ''"> |
| ... | ... | @@ -334,25 +385,33 @@ |
| 334 | 385 | </a-form-item> |
| 335 | 386 | </a-col> |
| 336 | 387 | </a-row> |
| 388 | + </div> | |
| 389 | + <a-empty v-else description="暂无时段附加费配置" /> | |
| 337 | 390 | </div> |
| 338 | - <a-empty v-else description="暂无时段附加费配置" /> | |
| 339 | 391 | |
| 340 | - <a-divider orientation="left">预计送达与展示</a-divider> | |
| 341 | - <a-row :gutter="16"> | |
| 342 | - <a-col :span="12"> | |
| 343 | - <a-form-item label="预计送达基础时间(分钟)"> | |
| 344 | - <a-input-number v-model:value="config.distanceBasicTime" :min="0" style="width:100%" /> | |
| 345 | - </a-form-item> | |
| 346 | - </a-col> | |
| 347 | - <a-col :span="12"> | |
| 348 | - <a-form-item label="超出每km增加时间(分钟)"> | |
| 349 | - <a-input-number v-model:value="config.distanceMoreTime" :min="0" style="width:100%" /> | |
| 350 | - </a-form-item> | |
| 351 | - </a-col> | |
| 352 | - </a-row> | |
| 353 | - <a-form-item label="附近骑手显示范围(km)"> | |
| 354 | - <a-input-number v-model:value="config.riderDistance" :min="0" :step="0.1" style="width:100%" /> | |
| 355 | - </a-form-item> | |
| 392 | + <div class="plan-section plan-section-last"> | |
| 393 | + <div class="soft-section-header"> | |
| 394 | + <div class="soft-section-heading"> | |
| 395 | + <h3 class="soft-section-title">预计送达与展示</h3> | |
| 396 | + <p class="soft-section-subtitle">控制预计送达时间和骑手可视距离。</p> | |
| 397 | + </div> | |
| 398 | + </div> | |
| 399 | + <a-row :gutter="16"> | |
| 400 | + <a-col :span="12"> | |
| 401 | + <a-form-item label="预计送达基础时间(分钟)"> | |
| 402 | + <a-input-number v-model:value="config.distanceBasicTime" :min="0" style="width:100%" /> | |
| 403 | + </a-form-item> | |
| 404 | + </a-col> | |
| 405 | + <a-col :span="12"> | |
| 406 | + <a-form-item label="超出每km增加时间(分钟)"> | |
| 407 | + <a-input-number v-model:value="config.distanceMoreTime" :min="0" style="width:100%" /> | |
| 408 | + </a-form-item> | |
| 409 | + </a-col> | |
| 410 | + </a-row> | |
| 411 | + <a-form-item label="附近骑手显示范围(km)"> | |
| 412 | + <a-input-number v-model:value="config.riderDistance" :min="0" :step="0.1" style="width:100%" /> | |
| 413 | + </a-form-item> | |
| 414 | + </div> | |
| 356 | 415 | </a-form> |
| 357 | 416 | </template> |
| 358 | 417 | <div v-else class="plan-empty-state"> |
| ... | ... | @@ -1132,6 +1191,10 @@ onMounted(loadList) |
| 1132 | 1191 | padding-right: 4px; |
| 1133 | 1192 | } |
| 1134 | 1193 | |
| 1194 | +.plan-note-card { | |
| 1195 | + margin-bottom: 16px; | |
| 1196 | +} | |
| 1197 | + | |
| 1135 | 1198 | .plan-toolbar { |
| 1136 | 1199 | display: flex; |
| 1137 | 1200 | align-items: center; |
| ... | ... | @@ -1140,12 +1203,33 @@ onMounted(loadList) |
| 1140 | 1203 | margin-bottom: 16px; |
| 1141 | 1204 | } |
| 1142 | 1205 | |
| 1206 | +.plan-section { | |
| 1207 | + margin-bottom: 22px; | |
| 1208 | + padding-bottom: 18px; | |
| 1209 | + border-bottom: 1px solid rgba(206, 196, 244, 0.2); | |
| 1210 | +} | |
| 1211 | + | |
| 1212 | +.plan-section-last { | |
| 1213 | + margin-bottom: 0; | |
| 1214 | + padding-bottom: 0; | |
| 1215 | + border-bottom: 0; | |
| 1216 | +} | |
| 1217 | + | |
| 1218 | +.plan-dynamic-row { | |
| 1219 | + margin-bottom: 12px; | |
| 1220 | + align-items: flex-start; | |
| 1221 | +} | |
| 1222 | + | |
| 1143 | 1223 | .preview-card { |
| 1144 | 1224 | margin-bottom: 20px; |
| 1145 | 1225 | border-radius: 16px; |
| 1146 | 1226 | background: #fafbff; |
| 1147 | 1227 | } |
| 1148 | 1228 | |
| 1229 | +.preview-subtitle { | |
| 1230 | + margin-bottom: 14px; | |
| 1231 | +} | |
| 1232 | + | |
| 1149 | 1233 | .preview-result { |
| 1150 | 1234 | display: flex; |
| 1151 | 1235 | flex-wrap: wrap; | ... | ... |
src/views/dashboard/DashboardHome.vue
| 1 | 1 | <template> |
| 2 | - <div class="dashboard-home"> | |
| 2 | + <div class="dashboard-home soft-page-stack"> | |
| 3 | 3 | <section class="hero-card"> |
| 4 | 4 | <div class="hero-copy"> |
| 5 | - <div class="hero-pill">Soft-Neo Dashboard</div> | |
| 5 | + <div class="hero-pill">运营总览</div> | |
| 6 | 6 | <h2>欢迎回到地利外卖运营工作台</h2> |
| 7 | - <p>把租户、骑手、订单和分站管理集中在一张更轻盈的首页里,业务页面本身只保留导航与内容,让操作区域更大、更专注。</p> | |
| 7 | + <p>首页保留总览和快捷入口,业务菜单页只保留导航与内容区域,减少顶部公共模块对操作空间的挤压。</p> | |
| 8 | 8 | <div class="hero-grid"> |
| 9 | 9 | <div class="hero-metric"> |
| 10 | - <span>Operation Focus</span> | |
| 11 | - <strong>租户 · 骑手 · 订单</strong> | |
| 10 | + <span>核心范围</span> | |
| 11 | + <strong>租户 / 骑手 / 订单</strong> | |
| 12 | 12 | </div> |
| 13 | 13 | <div class="hero-metric"> |
| 14 | - <span>Visual Mood</span> | |
| 15 | - <strong>Lavender / Warm Glow</strong> | |
| 14 | + <span>当前主题</span> | |
| 15 | + <strong>轻柔紫调 / 紧凑运营风格</strong> | |
| 16 | 16 | </div> |
| 17 | 17 | </div> |
| 18 | 18 | </div> |
| ... | ... | @@ -24,7 +24,13 @@ |
| 24 | 24 | </section> |
| 25 | 25 | |
| 26 | 26 | <section class="content-grid"> |
| 27 | - <a-card title="快捷入口" :bordered="false"> | |
| 27 | + <a-card :bordered="false" class="soft-section-card"> | |
| 28 | + <div class="soft-section-header"> | |
| 29 | + <div class="soft-section-heading"> | |
| 30 | + <h3 class="soft-section-title">快捷入口</h3> | |
| 31 | + <p class="soft-section-subtitle">常用运营菜单集中放在首页,减少跳转层级。</p> | |
| 32 | + </div> | |
| 33 | + </div> | |
| 28 | 34 | <div class="quick-links"> |
| 29 | 35 | <button v-for="item in quickLinks" :key="item.path" class="quick-link" type="button" @click="go(item.path)"> |
| 30 | 36 | <strong>{{ item.title }}</strong> |
| ... | ... | @@ -33,7 +39,13 @@ |
| 33 | 39 | </div> |
| 34 | 40 | </a-card> |
| 35 | 41 | |
| 36 | - <a-card title="界面说明" :bordered="false"> | |
| 42 | + <a-card :bordered="false" class="soft-section-card"> | |
| 43 | + <div class="soft-section-header"> | |
| 44 | + <div class="soft-section-heading"> | |
| 45 | + <h3 class="soft-section-title">界面说明</h3> | |
| 46 | + <p class="soft-section-subtitle">这一版样式调整重点放在密度、统一性和中文后台习惯。</p> | |
| 47 | + </div> | |
| 48 | + </div> | |
| 37 | 49 | <ul class="soft-notes"> |
| 38 | 50 | <li>首页保留大视觉和信息卡片,适合做总览和快捷入口。</li> |
| 39 | 51 | <li>其他菜单页只保留紧凑头部和内容区,避免公共模块挤压表格空间。</li> |
| ... | ... | @@ -95,12 +107,18 @@ function go(path: string) { |
| 95 | 107 | |
| 96 | 108 | .hero-copy h2 { |
| 97 | 109 | margin: 12px 0 8px; |
| 98 | - font-size: 28px; | |
| 110 | + font-size: 24px; | |
| 99 | 111 | line-height: 1.15; |
| 100 | 112 | font-family: 'Outfit', sans-serif; |
| 101 | 113 | color: #2f2946; |
| 102 | 114 | } |
| 103 | 115 | |
| 116 | +.hero-copy p { | |
| 117 | + margin: 0; | |
| 118 | + font-size: 13px; | |
| 119 | + line-height: 1.7; | |
| 120 | +} | |
| 121 | + | |
| 104 | 122 | .hero-copy p, |
| 105 | 123 | .hero-metric span, |
| 106 | 124 | .quick-link span, |
| ... | ... | @@ -119,7 +137,7 @@ function go(path: string) { |
| 119 | 137 | min-width: 190px; |
| 120 | 138 | border-radius: 18px; |
| 121 | 139 | background: rgba(255, 255, 255, 0.74); |
| 122 | - padding: 12px 14px; | |
| 140 | + padding: 11px 13px; | |
| 123 | 141 | } |
| 124 | 142 | |
| 125 | 143 | .hero-metric span { |
| ... | ... | @@ -193,9 +211,22 @@ function go(path: string) { |
| 193 | 211 | cursor: pointer; |
| 194 | 212 | } |
| 195 | 213 | |
| 214 | +.quick-link strong { | |
| 215 | + display: block; | |
| 216 | + margin-bottom: 4px; | |
| 217 | + font-size: 14px; | |
| 218 | +} | |
| 219 | + | |
| 220 | +.quick-link span { | |
| 221 | + font-size: 12px; | |
| 222 | + line-height: 1.6; | |
| 223 | +} | |
| 224 | + | |
| 196 | 225 | .soft-notes { |
| 197 | 226 | margin: 0; |
| 198 | 227 | padding-left: 18px; |
| 228 | + font-size: 13px; | |
| 229 | + line-height: 1.7; | |
| 199 | 230 | } |
| 200 | 231 | |
| 201 | 232 | .soft-notes li + li { | ... | ... |
src/views/delivery/DeliveryOrderList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="配送订单" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-select v-model:value="filterStatus" placeholder="状态" allowClear style="width:130px" @change="loadList"> | |
| 3 | + <a-card title="配送订单" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-select v-model:value="filterStatus" placeholder="状态" allowClear class="list-filter" @change="loadList"> | |
| 7 | 7 | <a-select-option :value="2">待接单</a-select-option> |
| 8 | 8 | <a-select-option :value="3">已接单</a-select-option> |
| 9 | 9 | <a-select-option :value="4">配送中</a-select-option> |
| 10 | 10 | <a-select-option :value="6">已完成</a-select-option> |
| 11 | 11 | <a-select-option :value="10">已取消</a-select-option> |
| 12 | 12 | </a-select> |
| 13 | - <a-input-search v-model:value="keyword" placeholder="外部订单号" @search="loadList" style="width:220px" /> | |
| 13 | + <a-input-search v-model:value="keyword" placeholder="外部订单号" @search="loadList" class="list-search" /> | |
| 14 | + </div> | |
| 15 | + <div class="list-toolbar-right"> | |
| 14 | 16 | <a-button type="primary" @click="queryByNo" :loading="querying">查询</a-button> |
| 15 | - </a-space> | |
| 16 | - </template> | |
| 17 | + </div> | |
| 18 | + </div> | |
| 17 | 19 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 18 | 20 | <template #bodyCell="{ column, record }"> |
| 19 | 21 | <template v-if="column.key === 'status'"> |
| ... | ... | @@ -29,16 +31,21 @@ |
| 29 | 31 | </a-table> |
| 30 | 32 | </a-card> |
| 31 | 33 | |
| 32 | - <!-- 查询结果弹窗 --> | |
| 33 | 34 | <a-modal v-model:open="queryVisible" title="配送订单详情" :footer="null"> |
| 34 | - <a-descriptions :column="1" bordered size="small" v-if="queryResult"> | |
| 35 | - <a-descriptions-item label="配送订单ID">{{ queryResult.deliveryOrderId }}</a-descriptions-item> | |
| 36 | - <a-descriptions-item label="外部订单号">{{ queryResult.outOrderNo }}</a-descriptions-item> | |
| 37 | - <a-descriptions-item label="状态">{{ statusMap[queryResult.status] }}</a-descriptions-item> | |
| 38 | - <a-descriptions-item label="配送费">¥{{ queryResult.totalFee }}</a-descriptions-item> | |
| 39 | - <a-descriptions-item label="距离">{{ queryResult.distance }} km</a-descriptions-item> | |
| 40 | - <a-descriptions-item label="预计时间">{{ queryResult.estimatedMinutes }} 分钟</a-descriptions-item> | |
| 41 | - </a-descriptions> | |
| 35 | + <div v-if="queryResult" class="soft-page-stack"> | |
| 36 | + <div class="soft-note-card"> | |
| 37 | + <strong>订单查询结果</strong> | |
| 38 | + <p>这里展示开放平台配送单的核心状态和计费结果,适合按外部订单号快速核对。</p> | |
| 39 | + </div> | |
| 40 | + <a-descriptions :column="1" bordered size="small"> | |
| 41 | + <a-descriptions-item label="配送订单ID">{{ queryResult.deliveryOrderId }}</a-descriptions-item> | |
| 42 | + <a-descriptions-item label="外部订单号">{{ queryResult.outOrderNo }}</a-descriptions-item> | |
| 43 | + <a-descriptions-item label="状态">{{ statusMap[queryResult.status] }}</a-descriptions-item> | |
| 44 | + <a-descriptions-item label="配送费">¥{{ queryResult.totalFee }}</a-descriptions-item> | |
| 45 | + <a-descriptions-item label="距离">{{ queryResult.distance }} km</a-descriptions-item> | |
| 46 | + <a-descriptions-item label="预计时间">{{ queryResult.estimatedMinutes }} 分钟</a-descriptions-item> | |
| 47 | + </a-descriptions> | |
| 48 | + </div> | |
| 42 | 49 | </a-modal> |
| 43 | 50 | </div> |
| 44 | 51 | </template> | ... | ... |
src/views/merchant/EnterList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="入驻申请" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-select v-model:value="filterStatus" placeholder="状态" allowClear style="width:120px" @change="loadList"> | |
| 3 | + <a-card title="入驻申请" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-select v-model:value="filterStatus" placeholder="状态" allowClear class="list-filter" @change="loadList"> | |
| 7 | 7 | <a-select-option :value="0">未处理</a-select-option> |
| 8 | 8 | <a-select-option :value="1">已通过</a-select-option> |
| 9 | 9 | <a-select-option :value="-1">已拒绝</a-select-option> |
| 10 | 10 | </a-select> |
| 11 | - <a-select v-model:value="filterType" placeholder="类型" allowClear style="width:120px" @change="loadList"> | |
| 11 | + <a-select v-model:value="filterType" placeholder="类型" allowClear class="list-filter" @change="loadList"> | |
| 12 | 12 | <a-select-option :value="1">商家入驻</a-select-option> |
| 13 | 13 | <a-select-option :value="2">骑手入驻</a-select-option> |
| 14 | 14 | <a-select-option :value="3">商务合作</a-select-option> |
| 15 | 15 | </a-select> |
| 16 | - </a-space> | |
| 17 | - </template> | |
| 16 | + </div> | |
| 17 | + </div> | |
| 18 | 18 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 19 | 19 | <template #bodyCell="{ column, record }"> |
| 20 | 20 | <template v-if="column.key === 'type'"> | ... | ... |
src/views/merchant/StoreList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="店铺管理" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-select v-model:value="filterCityId" placeholder="选择租户" allowClear style="width:150px" @change="loadList"> | |
| 3 | + <a-card title="店铺管理" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-select v-model:value="filterCityId" placeholder="选择租户" allowClear class="list-filter" @change="loadList"> | |
| 7 | 7 | <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> |
| 8 | 8 | </a-select> |
| 9 | - <a-input-search v-model:value="keyword" placeholder="搜索店铺名" @search="loadList" style="width:200px" /> | |
| 9 | + <a-input-search v-model:value="keyword" placeholder="搜索店铺名" @search="loadList" class="list-search" /> | |
| 10 | + </div> | |
| 11 | + <div class="list-toolbar-right"> | |
| 10 | 12 | <a-button type="primary" @click="openAdd">新增店铺</a-button> |
| 11 | - </a-space> | |
| 12 | - </template> | |
| 13 | + </div> | |
| 14 | + </div> | |
| 13 | 15 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 14 | 16 | <template #bodyCell="{ column, record }"> |
| 15 | 17 | <template v-if="column.key === 'operatingState'"> |
| ... | ... | @@ -17,6 +19,9 @@ |
| 17 | 19 | {{ record.operatingState === 1 ? '营业中' : '打烊' }} |
| 18 | 20 | </a-tag> |
| 19 | 21 | </template> |
| 22 | + <template v-if="column.key === 'cityId'"> | |
| 23 | + {{ getCityName(record.cityId) }} | |
| 24 | + </template> | |
| 20 | 25 | <template v-if="column.key === 'shippingType'"> |
| 21 | 26 | {{ record.shippingType === 1 ? '外卖配送' : '到店自提' }} |
| 22 | 27 | </template> |
| ... | ... | @@ -38,54 +43,63 @@ |
| 38 | 43 | </a-table> |
| 39 | 44 | </a-card> |
| 40 | 45 | |
| 41 | - <!-- 新增/编辑弹窗 --> | |
| 42 | - <a-modal v-model:open="modalVisible" :title="editingId ? '编辑店铺' : '新增店铺'" | |
| 43 | - @ok="handleSave" :confirmLoading="saving" width="600px"> | |
| 44 | - <a-form :model="form" layout="vertical"> | |
| 45 | - <a-form-item label="店铺名称"><a-input v-model:value="form.name" /></a-form-item> | |
| 46 | - <a-form-item label="所属租户"> | |
| 47 | - <a-select v-model:value="form.cityId" placeholder="选择租户"> | |
| 48 | - <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 49 | - </a-select> | |
| 50 | - </a-form-item> | |
| 51 | - <a-form-item label="外部门店编号(选填,接入方对账用)"> | |
| 52 | - <a-input v-model:value="form.outStoreId" placeholder="接入方自己系统的门店ID" /> | |
| 53 | - </a-form-item> | |
| 54 | - <a-form-item label="店铺地址"><a-input v-model:value="form.address" /></a-form-item> | |
| 55 | - <a-row :gutter="16"> | |
| 56 | - <a-col :span="12"> | |
| 57 | - <a-form-item label="经度"><a-input v-model:value="form.lng" /></a-form-item> | |
| 58 | - </a-col> | |
| 59 | - <a-col :span="12"> | |
| 60 | - <a-form-item label="纬度"><a-input v-model:value="form.lat" /></a-form-item> | |
| 61 | - </a-col> | |
| 62 | - </a-row> | |
| 63 | - <a-form-item label="配送类型"> | |
| 64 | - <a-radio-group v-model:value="form.shippingType"> | |
| 65 | - <a-radio :value="1">外卖配送</a-radio> | |
| 66 | - <a-radio :value="2">到店自提</a-radio> | |
| 67 | - </a-radio-group> | |
| 68 | - </a-form-item> | |
| 69 | - <a-form-item label="自动接单"> | |
| 70 | - <a-switch v-model:checked="form.automaticOrder" :checked-value="1" :un-checked-value="0" /> | |
| 71 | - </a-form-item> | |
| 72 | - <a-form-item label="商家账号手机号(新增时创建登录账号)" v-if="!editingId"> | |
| 73 | - <a-input v-model:value="form.accountMobile" placeholder="选填" /> | |
| 74 | - </a-form-item> | |
| 75 | - <a-form-item label="店铺简介"><a-textarea v-model:value="form.about" :rows="3" /></a-form-item> | |
| 76 | - </a-form> | |
| 46 | + <a-modal v-model:open="modalVisible" :title="editingId ? '编辑店铺' : '新增店铺'" @ok="handleSave" :confirmLoading="saving" width="600px"> | |
| 47 | + <div class="soft-page-stack"> | |
| 48 | + <div class="soft-note-card"> | |
| 49 | + <strong>店铺资料说明</strong> | |
| 50 | + <p>当前页面只维护外卖相关店铺信息。新增时可选填商家账号手机号,系统会同步创建登录账号。</p> | |
| 51 | + </div> | |
| 52 | + <a-form :model="form" layout="vertical"> | |
| 53 | + <a-form-item label="店铺名称"><a-input v-model:value="form.name" /></a-form-item> | |
| 54 | + <a-form-item label="所属租户"> | |
| 55 | + <a-select v-model:value="form.cityId" placeholder="选择租户"> | |
| 56 | + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 57 | + </a-select> | |
| 58 | + </a-form-item> | |
| 59 | + <a-form-item label="外部门店编号(选填,接入方对账用)"> | |
| 60 | + <a-input v-model:value="form.outStoreId" placeholder="接入方自己系统的门店ID" /> | |
| 61 | + </a-form-item> | |
| 62 | + <a-form-item label="店铺地址"><a-input v-model:value="form.address" /></a-form-item> | |
| 63 | + <a-row :gutter="16"> | |
| 64 | + <a-col :span="12"> | |
| 65 | + <a-form-item label="经度"><a-input v-model:value="form.lng" /></a-form-item> | |
| 66 | + </a-col> | |
| 67 | + <a-col :span="12"> | |
| 68 | + <a-form-item label="纬度"><a-input v-model:value="form.lat" /></a-form-item> | |
| 69 | + </a-col> | |
| 70 | + </a-row> | |
| 71 | + <a-form-item label="配送类型"> | |
| 72 | + <a-radio-group v-model:value="form.shippingType"> | |
| 73 | + <a-radio :value="1">外卖配送</a-radio> | |
| 74 | + <a-radio :value="2">到店自提</a-radio> | |
| 75 | + </a-radio-group> | |
| 76 | + </a-form-item> | |
| 77 | + <a-form-item label="自动接单"> | |
| 78 | + <a-switch v-model:checked="form.automaticOrder" :checked-value="1" :un-checked-value="0" /> | |
| 79 | + </a-form-item> | |
| 80 | + <a-form-item label="商家账号手机号(新增时创建登录账号)" v-if="!editingId"> | |
| 81 | + <a-input v-model:value="form.accountMobile" placeholder="选填" /> | |
| 82 | + </a-form-item> | |
| 83 | + <a-form-item label="店铺简介"><a-textarea v-model:value="form.about" :rows="3" /></a-form-item> | |
| 84 | + </a-form> | |
| 85 | + </div> | |
| 77 | 86 | </a-modal> |
| 78 | 87 | |
| 79 | - <!-- 费用配置弹窗 --> | |
| 80 | 88 | <a-modal v-model:open="feeVisible" title="费用配置" @ok="handleFeeSave" :confirmLoading="saving"> |
| 81 | - <a-form layout="vertical"> | |
| 82 | - <a-form-item label="免运费门槛(元,0=不免运费)"> | |
| 83 | - <a-input-number v-model:value="feeForm.freeShipping" :min="0" style="width:100%" /> | |
| 84 | - </a-form-item> | |
| 85 | - <a-form-item label="起送金额(元,0=不限)"> | |
| 86 | - <a-input-number v-model:value="feeForm.upToSend" :min="0" style="width:100%" /> | |
| 87 | - </a-form-item> | |
| 88 | - </a-form> | |
| 89 | + <div class="soft-page-stack"> | |
| 90 | + <div class="soft-note-card"> | |
| 91 | + <strong>店铺费用配置</strong> | |
| 92 | + <p>这里只配置店铺侧起送和免运费门槛,不替代租户计价方案;最终配送费仍按租户默认计价方案计算。</p> | |
| 93 | + </div> | |
| 94 | + <a-form layout="vertical"> | |
| 95 | + <a-form-item label="免运费门槛(元,0=不免运费)"> | |
| 96 | + <a-input-number v-model:value="feeForm.freeShipping" :min="0" style="width:100%" /> | |
| 97 | + </a-form-item> | |
| 98 | + <a-form-item label="起送金额(元,0=不限)"> | |
| 99 | + <a-input-number v-model:value="feeForm.upToSend" :min="0" style="width:100%" /> | |
| 100 | + </a-form-item> | |
| 101 | + </a-form> | |
| 102 | + </div> | |
| 89 | 103 | </a-modal> |
| 90 | 104 | </div> |
| 91 | 105 | </template> |
| ... | ... | @@ -111,7 +125,7 @@ const feeForm = reactive({ freeShipping: 0, upToSend: 0 }) |
| 111 | 125 | const columns = [ |
| 112 | 126 | { title: 'ID', dataIndex: 'id', width: 80 }, |
| 113 | 127 | { title: '店铺名', dataIndex: 'name' }, |
| 114 | - { title: '租户', dataIndex: 'cityId' }, | |
| 128 | + { title: '租户', key: 'cityId' }, | |
| 115 | 129 | { title: '外部编号', dataIndex: 'outStoreId', ellipsis: true }, |
| 116 | 130 | { title: '接入方', dataIndex: 'appKey', ellipsis: true }, |
| 117 | 131 | { title: '地址', dataIndex: 'address', ellipsis: true }, |
| ... | ... | @@ -133,6 +147,11 @@ async function loadCities() { |
| 133 | 147 | cityList.value = res.data |
| 134 | 148 | } |
| 135 | 149 | |
| 150 | +function getCityName(cityId?: number) { | |
| 151 | + const city = cityList.value.find(item => item.id === cityId) | |
| 152 | + return city?.name || (cityId ? `租户#${cityId}` : '-') | |
| 153 | +} | |
| 154 | + | |
| 136 | 155 | function openAdd() { |
| 137 | 156 | editingId.value = null |
| 138 | 157 | Object.assign(form, { name: '', cityId: undefined, address: '', lng: '', lat: '', shippingType: 1, automaticOrder: 0, accountMobile: '', about: '', outStoreId: '' }) | ... | ... |
src/views/open/OpenAppList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="开放平台应用管理" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-button type="primary" @click="openAdd">创建应用</a-button> | |
| 6 | - </template> | |
| 3 | + <a-card title="开放平台应用管理" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-right"> | |
| 6 | + <a-button type="primary" @click="openAdd">创建应用</a-button> | |
| 7 | + </div> | |
| 8 | + </div> | |
| 7 | 9 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 8 | 10 | <template #bodyCell="{ column, record }"> |
| 9 | 11 | <template v-if="column.key === 'status'"> |
| ... | ... | @@ -14,6 +16,9 @@ |
| 14 | 16 | <template v-if="column.key === 'appKey'"> |
| 15 | 17 | <a-typography-text copyable>{{ record.appKey }}</a-typography-text> |
| 16 | 18 | </template> |
| 19 | + <template v-if="column.key === 'cityId'"> | |
| 20 | + {{ getCityName(record.cityId) }} | |
| 21 | + </template> | |
| 17 | 22 | <template v-if="column.key === 'action'"> |
| 18 | 23 | <a-space> |
| 19 | 24 | <a @click="handleResetSecret(record)">重置密钥</a> |
| ... | ... | @@ -32,47 +37,65 @@ |
| 32 | 37 | </a-table> |
| 33 | 38 | </a-card> |
| 34 | 39 | |
| 35 | - <!-- 创建应用 --> | |
| 36 | 40 | <a-modal v-model:open="addVisible" title="创建应用" @ok="handleCreate" :confirmLoading="saving"> |
| 37 | - <a-form :model="addForm" layout="vertical"> | |
| 38 | - <a-form-item label="应用名称"><a-input v-model:value="addForm.appName" /></a-form-item> | |
| 39 | - <a-form-item label="关联租户(必填)"> | |
| 40 | - <a-select v-model:value="addForm.cityId" placeholder="选择租户" style="width:100%"> | |
| 41 | - <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 42 | - </a-select> | |
| 43 | - </a-form-item> | |
| 44 | - <a-form-item label="备注"><a-input v-model:value="addForm.remark" /></a-form-item> | |
| 45 | - </a-form> | |
| 41 | + <div class="soft-page-stack"> | |
| 42 | + <div class="soft-note-card"> | |
| 43 | + <strong>应用创建说明</strong> | |
| 44 | + <p>开放应用会绑定到单一租户,后续模拟推单和对外计价都会基于该租户的配置执行。</p> | |
| 45 | + </div> | |
| 46 | + <a-form :model="addForm" layout="vertical"> | |
| 47 | + <a-form-item label="应用名称"><a-input v-model:value="addForm.appName" /></a-form-item> | |
| 48 | + <a-form-item label="关联租户(必填)"> | |
| 49 | + <a-select v-model:value="addForm.cityId" placeholder="选择租户" style="width:100%"> | |
| 50 | + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 51 | + </a-select> | |
| 52 | + </a-form-item> | |
| 53 | + <a-form-item label="备注"><a-input v-model:value="addForm.remark" /></a-form-item> | |
| 54 | + </a-form> | |
| 55 | + </div> | |
| 46 | 56 | </a-modal> |
| 47 | 57 | |
| 48 | - <!-- Webhook配置 --> | |
| 49 | 58 | <a-modal v-model:open="webhookVisible" title="Webhook配置" @ok="handleWebhookSave" :confirmLoading="saving"> |
| 50 | - <a-form layout="vertical"> | |
| 51 | - <a-form-item label="回调地址"> | |
| 52 | - <a-input v-model:value="webhookForm.webhookUrl" placeholder="https://your-server.com/webhook" /> | |
| 53 | - </a-form-item> | |
| 54 | - <a-form-item label="订阅事件(JSON数组)"> | |
| 55 | - <a-textarea v-model:value="webhookForm.webhookEvents" | |
| 56 | - placeholder='["order.paid","order.completed","order.cancelled"]' :rows="4" /> | |
| 57 | - </a-form-item> | |
| 58 | - <a-alert message="支持事件:order.paid / order.accepted / order.completed / order.cancelled / order.refund" type="info" show-icon /> | |
| 59 | - </a-form> | |
| 59 | + <div class="soft-page-stack"> | |
| 60 | + <div class="soft-note-card"> | |
| 61 | + <strong>Webhook说明</strong> | |
| 62 | + <p>推送日志页可查看回调结果,建议只订阅当前业务实际需要的事件,避免无效重试。</p> | |
| 63 | + </div> | |
| 64 | + <a-form layout="vertical"> | |
| 65 | + <a-form-item label="回调地址"> | |
| 66 | + <a-input v-model:value="webhookForm.webhookUrl" placeholder="https://your-server.com/webhook" /> | |
| 67 | + </a-form-item> | |
| 68 | + <a-form-item label="订阅事件(JSON数组)"> | |
| 69 | + <a-textarea | |
| 70 | + v-model:value="webhookForm.webhookEvents" | |
| 71 | + placeholder='["order.paid","order.completed","order.cancelled"]' | |
| 72 | + :rows="4" | |
| 73 | + /> | |
| 74 | + </a-form-item> | |
| 75 | + <a-alert message="支持事件:order.paid / order.accepted / order.completed / order.cancelled / order.refund" type="info" show-icon /> | |
| 76 | + </a-form> | |
| 77 | + </div> | |
| 60 | 78 | </a-modal> |
| 61 | 79 | |
| 62 | - <!-- 推送日志 --> | |
| 63 | 80 | <a-modal v-model:open="logsVisible" title="Webhook推送日志" :footer="null" width="800px"> |
| 64 | - <a-table :dataSource="logs" :columns="logColumns" rowKey="id" size="small" :pagination="false"> | |
| 65 | - <template #bodyCell="{ column, record }"> | |
| 66 | - <template v-if="column.key === 'status'"> | |
| 67 | - <a-tag :color="record.status === 1 ? 'green' : 'red'"> | |
| 68 | - {{ record.status === 1 ? '成功' : '失败' }} | |
| 69 | - </a-tag> | |
| 81 | + <div class="soft-page-stack"> | |
| 82 | + <div class="soft-note-card"> | |
| 83 | + <strong>推送日志</strong> | |
| 84 | + <p>失败记录支持手动重试,便于排查接入方回调地址或签名处理异常。</p> | |
| 85 | + </div> | |
| 86 | + <a-table :dataSource="logs" :columns="logColumns" rowKey="id" size="small" :pagination="false"> | |
| 87 | + <template #bodyCell="{ column, record }"> | |
| 88 | + <template v-if="column.key === 'status'"> | |
| 89 | + <a-tag :color="record.status === 1 ? 'green' : 'red'"> | |
| 90 | + {{ record.status === 1 ? '成功' : '失败' }} | |
| 91 | + </a-tag> | |
| 92 | + </template> | |
| 93 | + <template v-if="column.key === 'action'"> | |
| 94 | + <a v-if="record.status === 0" @click="retryLog(record.id)">重试</a> | |
| 95 | + </template> | |
| 70 | 96 | </template> |
| 71 | - <template v-if="column.key === 'action'"> | |
| 72 | - <a v-if="record.status === 0" @click="retryLog(record.id)">重试</a> | |
| 73 | - </template> | |
| 74 | - </template> | |
| 75 | - </a-table> | |
| 97 | + </a-table> | |
| 98 | + </div> | |
| 76 | 99 | </a-modal> |
| 77 | 100 | </div> |
| 78 | 101 | </template> |
| ... | ... | @@ -98,7 +121,7 @@ const columns = [ |
| 98 | 121 | { title: 'ID', dataIndex: 'id', width: 80 }, |
| 99 | 122 | { title: '应用名称', dataIndex: 'appName' }, |
| 100 | 123 | { title: 'AppKey', key: 'appKey' }, |
| 101 | - { title: '租户', dataIndex: 'cityId' }, | |
| 124 | + { title: '租户', key: 'cityId' }, | |
| 102 | 125 | { title: '状态', key: 'status' }, |
| 103 | 126 | { title: '回调地址', dataIndex: 'webhookUrl', ellipsis: true }, |
| 104 | 127 | { title: '操作', key: 'action' }, |
| ... | ... | @@ -127,6 +150,11 @@ async function loadCities() { |
| 127 | 150 | cityList.value = res.data |
| 128 | 151 | } |
| 129 | 152 | |
| 153 | +function getCityName(cityId?: number) { | |
| 154 | + const city = cityList.value.find(item => item.id === cityId) | |
| 155 | + return city?.name || (cityId ? `租户#${cityId}` : '-') | |
| 156 | +} | |
| 157 | + | |
| 130 | 158 | function openAdd() { |
| 131 | 159 | Object.assign(addForm, { appName: '', cityId: undefined, remark: '' }) |
| 132 | 160 | addVisible.value = true | ... | ... |
src/views/open/OpenMockDelivery.vue
| 1 | 1 | <template> |
| 2 | - <div class="mock-delivery-page"> | |
| 3 | - <a-card title="模拟推送配送单" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 2 | + <div class="mock-delivery-page soft-page-stack"> | |
| 3 | + <a-card :bordered="false" class="soft-section-card"> | |
| 4 | + <div class="soft-section-header"> | |
| 5 | + <div class="soft-section-heading"> | |
| 6 | + <h3 class="soft-section-title">模拟推送配送单</h3> | |
| 7 | + <p class="soft-section-subtitle">按开放应用模拟创建外卖配送单,并在发送前直接试算当前租户的配送费。</p> | |
| 8 | + </div> | |
| 9 | + <div class="soft-section-actions"> | |
| 6 | 10 | <a-button @click="fillDemo">填充示例</a-button> |
| 7 | 11 | <a-button @click="resetForm">重置</a-button> |
| 8 | 12 | <a-button :loading="calcLoading" @click="handleCalcFee">按应用租户试算配送费</a-button> |
| 9 | 13 | <a-button type="primary" :loading="saving" @click="handleSubmit">发送推单</a-button> |
| 10 | - </a-space> | |
| 11 | - </template> | |
| 12 | - | |
| 14 | + </div> | |
| 15 | + </div> | |
| 16 | + | |
| 17 | + <div class="soft-note-card"> | |
| 18 | + <strong>使用说明</strong> | |
| 19 | + <p>系统会根据当前开放应用自动识别所属租户。推单页只覆盖外卖场景,门店可选填接入方门店编号,未填写时再手动补全门店信息。</p> | |
| 20 | + </div> | |
| 21 | + | |
| 22 | + <div class="mock-form-section"> | |
| 23 | + <div class="soft-section-header"> | |
| 24 | + <div class="soft-section-heading"> | |
| 25 | + <h3 class="soft-section-title">应用与订单基础信息</h3> | |
| 26 | + <p class="soft-section-subtitle">先确定开放应用、租户和外部订单标识。</p> | |
| 27 | + </div> | |
| 28 | + </div> | |
| 13 | 29 | <a-form layout="vertical"> |
| 14 | 30 | <a-row :gutter="16"> |
| 15 | 31 | <a-col :xs="24" :lg="12"> |
| ... | ... | @@ -47,100 +63,121 @@ |
| 47 | 63 | </a-col> |
| 48 | 64 | </a-row> |
| 49 | 65 | |
| 50 | - <a-divider orientation="left">发货方信息</a-divider> | |
| 51 | - <a-alert | |
| 52 | - type="info" | |
| 53 | - show-icon | |
| 54 | - message="如果已同步过门店,优先填写接入方门店编号;未填写时请手动补全发货门店名称、地址和经纬度。" | |
| 55 | - style="margin-bottom: 16px" | |
| 56 | - /> | |
| 57 | - <a-row :gutter="16"> | |
| 58 | - <a-col :xs="24" :lg="12"> | |
| 59 | - <a-form-item label="接入方门店编号"> | |
| 60 | - <a-input v-model:value="form.outStoreId" placeholder="可选,填写后可自动补门店信息" /> | |
| 61 | - </a-form-item> | |
| 62 | - </a-col> | |
| 63 | - <a-col :xs="24" :lg="12"> | |
| 64 | - <a-form-item label="发货门店名称"> | |
| 65 | - <a-input v-model:value="form.storeName" placeholder="未填写门店编号时必填" /> | |
| 66 | - </a-form-item> | |
| 67 | - </a-col> | |
| 68 | - </a-row> | |
| 69 | - <a-form-item label="发货门店地址"> | |
| 70 | - <a-input v-model:value="form.storeAddr" placeholder="请输入发货门店地址" /> | |
| 71 | - </a-form-item> | |
| 72 | - <a-row :gutter="16"> | |
| 73 | - <a-col :xs="24" :lg="12"> | |
| 74 | - <a-form-item label="发货经度"> | |
| 75 | - <a-input v-model:value="form.storeLng" placeholder="如:113.264385" /> | |
| 76 | - </a-form-item> | |
| 77 | - </a-col> | |
| 78 | - <a-col :xs="24" :lg="12"> | |
| 79 | - <a-form-item label="发货纬度"> | |
| 80 | - <a-input v-model:value="form.storeLat" placeholder="如:23.129112" /> | |
| 81 | - </a-form-item> | |
| 82 | - </a-col> | |
| 83 | - </a-row> | |
| 66 | + <div class="mock-form-section"> | |
| 67 | + <div class="soft-section-header"> | |
| 68 | + <div class="soft-section-heading"> | |
| 69 | + <h3 class="soft-section-title">发货方信息</h3> | |
| 70 | + <p class="soft-section-subtitle">如果已同步门店,优先填写接入方门店编号;否则补全门店名称、地址和经纬度。</p> | |
| 71 | + </div> | |
| 72 | + </div> | |
| 73 | + <a-row :gutter="16"> | |
| 74 | + <a-col :xs="24" :lg="12"> | |
| 75 | + <a-form-item label="接入方门店编号"> | |
| 76 | + <a-input v-model:value="form.outStoreId" placeholder="可选,填写后可自动补门店信息" /> | |
| 77 | + </a-form-item> | |
| 78 | + </a-col> | |
| 79 | + <a-col :xs="24" :lg="12"> | |
| 80 | + <a-form-item label="发货门店名称"> | |
| 81 | + <a-input v-model:value="form.storeName" placeholder="未填写门店编号时必填" /> | |
| 82 | + </a-form-item> | |
| 83 | + </a-col> | |
| 84 | + </a-row> | |
| 85 | + <a-form-item label="发货门店地址"> | |
| 86 | + <a-input v-model:value="form.storeAddr" placeholder="请输入发货门店地址" /> | |
| 87 | + </a-form-item> | |
| 88 | + <a-row :gutter="16"> | |
| 89 | + <a-col :xs="24" :lg="12"> | |
| 90 | + <a-form-item label="发货经度"> | |
| 91 | + <a-input v-model:value="form.storeLng" placeholder="如:113.264385" /> | |
| 92 | + </a-form-item> | |
| 93 | + </a-col> | |
| 94 | + <a-col :xs="24" :lg="12"> | |
| 95 | + <a-form-item label="发货纬度"> | |
| 96 | + <a-input v-model:value="form.storeLat" placeholder="如:23.129112" /> | |
| 97 | + </a-form-item> | |
| 98 | + </a-col> | |
| 99 | + </a-row> | |
| 100 | + </div> | |
| 84 | 101 | |
| 85 | - <a-divider orientation="left">收件人信息</a-divider> | |
| 86 | - <a-row :gutter="16"> | |
| 87 | - <a-col :xs="24" :lg="12"> | |
| 88 | - <a-form-item label="收件人姓名"> | |
| 89 | - <a-input v-model:value="form.recipName" placeholder="请输入收件人姓名" /> | |
| 90 | - </a-form-item> | |
| 91 | - </a-col> | |
| 92 | - <a-col :xs="24" :lg="12"> | |
| 93 | - <a-form-item label="收件人电话"> | |
| 94 | - <a-input v-model:value="form.recipPhone" placeholder="请输入收件人手机号" /> | |
| 95 | - </a-form-item> | |
| 96 | - </a-col> | |
| 97 | - </a-row> | |
| 98 | - <a-form-item label="收件人地址"> | |
| 99 | - <a-input v-model:value="form.recipAddr" placeholder="请输入收件地址" /> | |
| 100 | - </a-form-item> | |
| 101 | - <a-row :gutter="16"> | |
| 102 | - <a-col :xs="24" :lg="12"> | |
| 103 | - <a-form-item label="收件经度"> | |
| 104 | - <a-input v-model:value="form.recipLng" placeholder="如:113.270000" /> | |
| 105 | - </a-form-item> | |
| 106 | - </a-col> | |
| 107 | - <a-col :xs="24" :lg="12"> | |
| 108 | - <a-form-item label="收件纬度"> | |
| 109 | - <a-input v-model:value="form.recipLat" placeholder="如:23.135000" /> | |
| 110 | - </a-form-item> | |
| 111 | - </a-col> | |
| 112 | - </a-row> | |
| 102 | + <div class="mock-form-section"> | |
| 103 | + <div class="soft-section-header"> | |
| 104 | + <div class="soft-section-heading"> | |
| 105 | + <h3 class="soft-section-title">收件人信息</h3> | |
| 106 | + <p class="soft-section-subtitle">用于试算距离和创建配送单,收件人地址与经纬度建议同时填写。</p> | |
| 107 | + </div> | |
| 108 | + </div> | |
| 109 | + <a-row :gutter="16"> | |
| 110 | + <a-col :xs="24" :lg="12"> | |
| 111 | + <a-form-item label="收件人姓名"> | |
| 112 | + <a-input v-model:value="form.recipName" placeholder="请输入收件人姓名" /> | |
| 113 | + </a-form-item> | |
| 114 | + </a-col> | |
| 115 | + <a-col :xs="24" :lg="12"> | |
| 116 | + <a-form-item label="收件人电话"> | |
| 117 | + <a-input v-model:value="form.recipPhone" placeholder="请输入收件人手机号" /> | |
| 118 | + </a-form-item> | |
| 119 | + </a-col> | |
| 120 | + </a-row> | |
| 121 | + <a-form-item label="收件人地址"> | |
| 122 | + <a-input v-model:value="form.recipAddr" placeholder="请输入收件地址" /> | |
| 123 | + </a-form-item> | |
| 124 | + <a-row :gutter="16"> | |
| 125 | + <a-col :xs="24" :lg="12"> | |
| 126 | + <a-form-item label="收件经度"> | |
| 127 | + <a-input v-model:value="form.recipLng" placeholder="如:113.270000" /> | |
| 128 | + </a-form-item> | |
| 129 | + </a-col> | |
| 130 | + <a-col :xs="24" :lg="12"> | |
| 131 | + <a-form-item label="收件纬度"> | |
| 132 | + <a-input v-model:value="form.recipLat" placeholder="如:23.135000" /> | |
| 133 | + </a-form-item> | |
| 134 | + </a-col> | |
| 135 | + </a-row> | |
| 136 | + </div> | |
| 113 | 137 | |
| 114 | - <a-divider orientation="left">订单信息</a-divider> | |
| 115 | - <a-row :gutter="16"> | |
| 116 | - <a-col :xs="24" :lg="12"> | |
| 117 | - <a-form-item label="货物重量(kg)"> | |
| 118 | - <a-input-number v-model:value="form.weight" :min="0" :step="0.1" style="width:100%" /> | |
| 119 | - </a-form-item> | |
| 120 | - </a-col> | |
| 121 | - <a-col :xs="24" :lg="12"> | |
| 122 | - <a-form-item label="订单级回调地址"> | |
| 123 | - <a-input v-model:value="form.callbackUrl" placeholder="可选,留空则使用应用级 Webhook" /> | |
| 124 | - </a-form-item> | |
| 125 | - </a-col> | |
| 126 | - </a-row> | |
| 127 | - <a-form-item label="整单备注"> | |
| 128 | - <a-textarea v-model:value="form.remark" :rows="3" placeholder="如:请尽快送达" /> | |
| 129 | - </a-form-item> | |
| 130 | - <a-form-item label="货物备注"> | |
| 131 | - <a-textarea v-model:value="form.itemRemark" :rows="2" placeholder="如:饮品分开装、轻拿轻放" /> | |
| 132 | - </a-form-item> | |
| 133 | - | |
| 134 | - <a-divider orientation="left">货物清单</a-divider> | |
| 135 | - <div style="margin-bottom:12px"> | |
| 136 | - <a-button type="dashed" block @click="addItem">新增货物</a-button> | |
| 138 | + <div class="mock-form-section"> | |
| 139 | + <div class="soft-section-header"> | |
| 140 | + <div class="soft-section-heading"> | |
| 141 | + <h3 class="soft-section-title">订单信息</h3> | |
| 142 | + <p class="soft-section-subtitle">当前只处理外卖场景,支持重量、整单备注和应用级回调。</p> | |
| 143 | + </div> | |
| 144 | + </div> | |
| 145 | + <a-row :gutter="16"> | |
| 146 | + <a-col :xs="24" :lg="12"> | |
| 147 | + <a-form-item label="货物重量(kg)"> | |
| 148 | + <a-input-number v-model:value="form.weight" :min="0" :step="0.1" style="width:100%" /> | |
| 149 | + </a-form-item> | |
| 150 | + </a-col> | |
| 151 | + <a-col :xs="24" :lg="12"> | |
| 152 | + <a-form-item label="订单级回调地址"> | |
| 153 | + <a-input v-model:value="form.callbackUrl" placeholder="可选,留空则使用应用级 Webhook" /> | |
| 154 | + </a-form-item> | |
| 155 | + </a-col> | |
| 156 | + </a-row> | |
| 157 | + <a-form-item label="整单备注"> | |
| 158 | + <a-textarea v-model:value="form.remark" :rows="3" placeholder="如:请尽快送达" /> | |
| 159 | + </a-form-item> | |
| 160 | + <a-form-item label="货物备注"> | |
| 161 | + <a-textarea v-model:value="form.itemRemark" :rows="2" placeholder="如:饮品分开装、轻拿轻放" /> | |
| 162 | + </a-form-item> | |
| 137 | 163 | </div> |
| 138 | - <div v-if="form.items.length"> | |
| 164 | + | |
| 165 | + <div class="mock-form-section"> | |
| 166 | + <div class="soft-section-header"> | |
| 167 | + <div class="soft-section-heading"> | |
| 168 | + <h3 class="soft-section-title">货物清单</h3> | |
| 169 | + <p class="soft-section-subtitle">件数会参与配送费试算,名称为空的货物不会提交到后端。</p> | |
| 170 | + </div> | |
| 171 | + <div class="soft-section-actions"> | |
| 172 | + <a-button type="dashed" @click="addItem">新增货物</a-button> | |
| 173 | + </div> | |
| 174 | + </div> | |
| 175 | + <div v-if="form.items.length" class="soft-dashed-block"> | |
| 139 | 176 | <a-row |
| 140 | 177 | v-for="(item, index) in form.items" |
| 141 | 178 | :key="index" |
| 142 | 179 | :gutter="12" |
| 143 | - style="margin-bottom:12px;align-items:flex-start" | |
| 180 | + class="mock-item-row" | |
| 144 | 181 | > |
| 145 | 182 | <a-col :xs="24" :lg="6"> |
| 146 | 183 | <a-form-item :label="index === 0 ? '名称' : ''"> |
| ... | ... | @@ -168,18 +205,21 @@ |
| 168 | 205 | </a-form-item> |
| 169 | 206 | </a-col> |
| 170 | 207 | </a-row> |
| 208 | + </div> | |
| 209 | + <a-empty v-else description="暂无货物清单" /> | |
| 171 | 210 | </div> |
| 172 | - <a-empty v-else description="暂无货物清单" /> | |
| 173 | 211 | </a-form> |
| 212 | + </div> | |
| 174 | 213 | </a-card> |
| 175 | 214 | |
| 176 | - <a-card v-if="feeResult" title="按应用租户试算结果" :bordered="false"> | |
| 177 | - <a-alert | |
| 178 | - type="info" | |
| 179 | - show-icon | |
| 180 | - message="试算时不会手填租户,系统会自动使用当前开放应用绑定的租户;实际推单金额以后端创建订单时实时计算为准。" | |
| 181 | - style="margin-bottom: 16px" | |
| 182 | - /> | |
| 215 | + <div v-if="feeResult || result" class="soft-result-grid"> | |
| 216 | + <a-card v-if="feeResult" :bordered="false" class="soft-section-card"> | |
| 217 | + <div class="soft-section-header"> | |
| 218 | + <div class="soft-section-heading"> | |
| 219 | + <h3 class="soft-section-title">按应用租户试算结果</h3> | |
| 220 | + <p class="soft-section-subtitle">试算时不会手填租户,系统会自动使用当前开放应用绑定的租户;实际推单金额以后端创建订单时实时计算为准。</p> | |
| 221 | + </div> | |
| 222 | + </div> | |
| 183 | 223 | <a-descriptions bordered :column="2"> |
| 184 | 224 | <a-descriptions-item label="配送费">¥{{ feeResult.totalFee }}</a-descriptions-item> |
| 185 | 225 | <a-descriptions-item label="配送距离">{{ feeResult.distance }} km</a-descriptions-item> |
| ... | ... | @@ -201,9 +241,15 @@ |
| 201 | 241 | <a-descriptions-item label="计费重量">{{ feeResult.weight }} kg</a-descriptions-item> |
| 202 | 242 | <a-descriptions-item label="计费件数">{{ feeResult.pieces || 0 }} 件</a-descriptions-item> |
| 203 | 243 | </a-descriptions> |
| 204 | - </a-card> | |
| 205 | - | |
| 206 | - <a-card v-if="result" title="推单结果" :bordered="false"> | |
| 244 | + </a-card> | |
| 245 | + | |
| 246 | + <a-card v-if="result" :bordered="false" class="soft-section-card"> | |
| 247 | + <div class="soft-section-header"> | |
| 248 | + <div class="soft-section-heading"> | |
| 249 | + <h3 class="soft-section-title">推单结果</h3> | |
| 250 | + <p class="soft-section-subtitle">展示后台创建后的配送单编号、状态和费用结果。</p> | |
| 251 | + </div> | |
| 252 | + </div> | |
| 207 | 253 | <a-descriptions bordered :column="2"> |
| 208 | 254 | <a-descriptions-item label="配送单ID">{{ result.deliveryOrderId }}</a-descriptions-item> |
| 209 | 255 | <a-descriptions-item label="平台订单号">{{ result.orderNo }}</a-descriptions-item> |
| ... | ... | @@ -216,7 +262,8 @@ |
| 216 | 262 | <a-descriptions-item label="时段附加费">¥{{ result.moneyTime }}</a-descriptions-item> |
| 217 | 263 | <a-descriptions-item label="预计送达">{{ result.estimatedMinutes }} 分钟</a-descriptions-item> |
| 218 | 264 | </a-descriptions> |
| 219 | - </a-card> | |
| 265 | + </a-card> | |
| 266 | + </div> | |
| 220 | 267 | </div> |
| 221 | 268 | </template> |
| 222 | 269 | |
| ... | ... | @@ -470,7 +517,17 @@ watch( |
| 470 | 517 | |
| 471 | 518 | <style scoped> |
| 472 | 519 | .mock-delivery-page { |
| 473 | - display: grid; | |
| 474 | - gap: 18px; | |
| 520 | + gap: 16px; | |
| 521 | +} | |
| 522 | + | |
| 523 | +.mock-form-section { | |
| 524 | + margin-top: 8px; | |
| 525 | + padding-top: 18px; | |
| 526 | + border-top: 1px solid rgba(206, 196, 244, 0.22); | |
| 527 | +} | |
| 528 | + | |
| 529 | +.mock-item-row { | |
| 530 | + margin-bottom: 12px; | |
| 531 | + align-items: flex-start; | |
| 475 | 532 | } |
| 476 | 533 | </style> | ... | ... |
src/views/order/OrderList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="订单管理" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-select v-model:value="filterStatus" placeholder="订单状态" allowClear style="width:130px" @change="loadList"> | |
| 3 | + <a-card title="订单管理" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-select v-model:value="filterStatus" placeholder="订单状态" allowClear class="list-filter" @change="loadList"> | |
| 7 | 7 | <a-select-option :value="2">已支付</a-select-option> |
| 8 | 8 | <a-select-option :value="3">已接单</a-select-option> |
| 9 | 9 | <a-select-option :value="4">服务中</a-select-option> |
| ... | ... | @@ -11,14 +11,14 @@ |
| 11 | 11 | <a-select-option :value="7">退款申请</a-select-option> |
| 12 | 12 | <a-select-option :value="10">已取消</a-select-option> |
| 13 | 13 | </a-select> |
| 14 | - <a-select v-model:value="filterTrans" placeholder="转单状态" allowClear style="width:130px" @change="loadList"> | |
| 14 | + <a-select v-model:value="filterTrans" placeholder="转单状态" allowClear class="list-filter" @change="loadList"> | |
| 15 | 15 | <a-select-option :value="2">转单申请中</a-select-option> |
| 16 | 16 | <a-select-option :value="1">已转单</a-select-option> |
| 17 | 17 | <a-select-option :value="3">转单拒绝</a-select-option> |
| 18 | 18 | </a-select> |
| 19 | - <a-input-search v-model:value="keyword" placeholder="订单号" @search="loadList" style="width:200px" /> | |
| 20 | - </a-space> | |
| 21 | - </template> | |
| 19 | + <a-input-search v-model:value="keyword" placeholder="订单号" @search="loadList" class="list-search" /> | |
| 20 | + </div> | |
| 21 | + </div> | |
| 22 | 22 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 23 | 23 | <template #bodyCell="{ column, record }"> |
| 24 | 24 | <template v-if="column.key === 'status'"> |
| ... | ... | @@ -48,57 +48,72 @@ |
| 48 | 48 | </a-table> |
| 49 | 49 | </a-card> |
| 50 | 50 | |
| 51 | - <!-- 指派骑手弹窗 --> | |
| 52 | 51 | <a-modal v-model:open="designateVisible" title="指派骑手" @ok="handleDesignate" :confirmLoading="saving"> |
| 53 | - <a-form layout="vertical"> | |
| 54 | - <a-form-item label="选择骑手"> | |
| 55 | - <a-select | |
| 56 | - v-model:value="designateRiderId" | |
| 57 | - style="width:100%" | |
| 58 | - placeholder="请选择可指派骑手" | |
| 59 | - :loading="candidateLoading" | |
| 60 | - show-search | |
| 61 | - :filter-option="filterCandidateOption" | |
| 62 | - option-label-prop="label" | |
| 63 | - > | |
| 64 | - <a-select-option | |
| 65 | - v-for="item in designateCandidates" | |
| 66 | - :key="item.id" | |
| 67 | - :value="item.id" | |
| 68 | - :label="`${item.userNickname || '未命名'}(ID:${item.id})`" | |
| 52 | + <div class="soft-page-stack"> | |
| 53 | + <div class="soft-note-card"> | |
| 54 | + <strong>指派说明</strong> | |
| 55 | + <p>这里只展示当前订单可指派的骑手候选,列表中会带出手机号和在线/休息状态,避免只能靠 ID 操作。</p> | |
| 56 | + </div> | |
| 57 | + <a-form layout="vertical"> | |
| 58 | + <a-form-item label="选择骑手"> | |
| 59 | + <a-select | |
| 60 | + v-model:value="designateRiderId" | |
| 61 | + style="width:100%" | |
| 62 | + placeholder="请选择可指派骑手" | |
| 63 | + :loading="candidateLoading" | |
| 64 | + show-search | |
| 65 | + :filter-option="filterCandidateOption" | |
| 66 | + option-label-prop="label" | |
| 69 | 67 | > |
| 70 | - {{ item.userNickname || '未命名' }}(ID:{{ item.id }} / {{ item.mobile || '无手机号' }} / {{ item.isRest === 1 ? '休息' : '在线' }}) | |
| 71 | - </a-select-option> | |
| 72 | - </a-select> | |
| 73 | - </a-form-item> | |
| 74 | - </a-form> | |
| 68 | + <a-select-option | |
| 69 | + v-for="item in designateCandidates" | |
| 70 | + :key="item.id" | |
| 71 | + :value="item.id" | |
| 72 | + :label="`${item.userNickname || '未命名'}(ID:${item.id})`" | |
| 73 | + > | |
| 74 | + {{ item.userNickname || '未命名' }}(ID:{{ item.id }} / {{ item.mobile || '无手机号' }} / {{ item.isRest === 1 ? '休息' : '在线' }}) | |
| 75 | + </a-select-option> | |
| 76 | + </a-select> | |
| 77 | + </a-form-item> | |
| 78 | + </a-form> | |
| 79 | + </div> | |
| 75 | 80 | </a-modal> |
| 76 | 81 | |
| 77 | - <!-- 退款记录弹窗 --> | |
| 78 | 82 | <a-modal v-model:open="refundVisible" title="退款记录" :footer="null"> |
| 79 | - <a-descriptions :column="1" bordered size="small" v-if="refundRecord"> | |
| 80 | - <a-descriptions-item label="订单号">{{ refundRecord.orderNo }}</a-descriptions-item> | |
| 81 | - <a-descriptions-item label="申请角色">{{ refundRecord.role === 1 ? '用户' : '骑手' }}</a-descriptions-item> | |
| 82 | - <a-descriptions-item label="退款原因">{{ refundRecord.reason }}</a-descriptions-item> | |
| 83 | - <a-descriptions-item label="退款金额">¥{{ refundRecord.money }}</a-descriptions-item> | |
| 84 | - <a-descriptions-item label="状态"> | |
| 85 | - <a-tag :color="refundRecord.status === 1 ? 'green' : refundRecord.status === 2 ? 'red' : 'orange'"> | |
| 86 | - {{ ({'0': '待处理', '1': '已通过', '2': '已拒绝'} as Record<string, string>)[String(refundRecord.status)] }} | |
| 87 | - </a-tag> | |
| 88 | - </a-descriptions-item> | |
| 89 | - <a-descriptions-item label="处理备注">{{ refundRecord.remark || '-' }}</a-descriptions-item> | |
| 90 | - </a-descriptions> | |
| 91 | - <a-space style="margin-top:16px" v-if="refundRecord && refundRecord.status === 0"> | |
| 92 | - <a-popconfirm title="确认通过退款?" @confirm="handleRefund(1)"> | |
| 93 | - <a-button type="primary">通过退款</a-button> | |
| 94 | - </a-popconfirm> | |
| 95 | - <a-button danger @click="openReject">拒绝退款</a-button> | |
| 96 | - </a-space> | |
| 83 | + <div v-if="refundRecord" class="soft-page-stack"> | |
| 84 | + <div class="soft-note-card"> | |
| 85 | + <strong>退款处理说明</strong> | |
| 86 | + <p>通过或拒绝退款后会更新订单退款状态,处理前建议先核对申请角色、原因和金额。</p> | |
| 87 | + </div> | |
| 88 | + <a-descriptions :column="1" bordered size="small"> | |
| 89 | + <a-descriptions-item label="订单号">{{ refundRecord.orderNo }}</a-descriptions-item> | |
| 90 | + <a-descriptions-item label="申请角色">{{ refundRecord.role === 1 ? '用户' : '骑手' }}</a-descriptions-item> | |
| 91 | + <a-descriptions-item label="退款原因">{{ refundRecord.reason }}</a-descriptions-item> | |
| 92 | + <a-descriptions-item label="退款金额">¥{{ refundRecord.money }}</a-descriptions-item> | |
| 93 | + <a-descriptions-item label="状态"> | |
| 94 | + <a-tag :color="refundRecord.status === 1 ? 'green' : refundRecord.status === 2 ? 'red' : 'orange'"> | |
| 95 | + {{ ({'0': '待处理', '1': '已通过', '2': '已拒绝'} as Record<string, string>)[String(refundRecord.status)] }} | |
| 96 | + </a-tag> | |
| 97 | + </a-descriptions-item> | |
| 98 | + <a-descriptions-item label="处理备注">{{ refundRecord.remark || '-' }}</a-descriptions-item> | |
| 99 | + </a-descriptions> | |
| 100 | + <div v-if="refundRecord.status === 0" class="soft-inline-actions"> | |
| 101 | + <a-popconfirm title="确认通过退款?" @confirm="handleRefund(1)"> | |
| 102 | + <a-button type="primary">通过退款</a-button> | |
| 103 | + </a-popconfirm> | |
| 104 | + <a-button danger @click="openReject">拒绝退款</a-button> | |
| 105 | + </div> | |
| 106 | + </div> | |
| 97 | 107 | </a-modal> |
| 98 | 108 | |
| 99 | - <!-- 拒绝退款弹窗 --> | |
| 100 | 109 | <a-modal v-model:open="rejectVisible" title="拒绝退款" @ok="handleRefund(2)" :confirmLoading="saving"> |
| 101 | - <a-textarea v-model:value="rejectRemark" :rows="3" placeholder="填写拒绝原因" /> | |
| 110 | + <div class="soft-page-stack"> | |
| 111 | + <div class="soft-note-card"> | |
| 112 | + <strong>填写拒绝原因</strong> | |
| 113 | + <p>拒绝原因会进入退款处理记录,建议填写明确、可追溯的说明。</p> | |
| 114 | + </div> | |
| 115 | + <a-textarea v-model:value="rejectRemark" :rows="3" placeholder="填写拒绝原因" /> | |
| 116 | + </div> | |
| 102 | 117 | </a-modal> |
| 103 | 118 | </div> |
| 104 | 119 | </template> | ... | ... |
src/views/order/RefundList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="退款管理" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-select v-model:value="filterStatus" placeholder="状态" allowClear style="width:120px" @change="loadList"> | |
| 3 | + <a-card title="退款管理" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-select v-model:value="filterStatus" placeholder="状态" allowClear class="list-filter" @change="loadList"> | |
| 7 | 7 | <a-select-option :value="0">待处理</a-select-option> |
| 8 | 8 | <a-select-option :value="1">已通过</a-select-option> |
| 9 | 9 | <a-select-option :value="2">已拒绝</a-select-option> |
| 10 | 10 | </a-select> |
| 11 | - <a-input-search v-model:value="keyword" placeholder="订单号" @search="loadList" style="width:200px" /> | |
| 12 | - </a-space> | |
| 13 | - </template> | |
| 11 | + <a-input-search v-model:value="keyword" placeholder="订单号" @search="loadList" class="list-search" /> | |
| 12 | + </div> | |
| 13 | + </div> | |
| 14 | 14 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 15 | 15 | <template #bodyCell="{ column, record }"> |
| 16 | 16 | <template v-if="column.key === 'role'"> | ... | ... |
src/views/rider/RiderEvaluateList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="骑手评价" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-input-number v-model:value="filterRiderId" placeholder="骑手ID" style="width:130px" /> | |
| 7 | - <a-select v-model:value="filterType" placeholder="评价类型" allowClear style="width:120px"> | |
| 3 | + <a-card title="骑手评价" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-input-number v-model:value="filterRiderId" placeholder="骑手ID" class="list-filter" /> | |
| 7 | + <a-select v-model:value="filterType" placeholder="评价类型" allowClear class="list-filter"> | |
| 8 | 8 | <a-select-option :value="0">全部</a-select-option> |
| 9 | 9 | <a-select-option :value="1">好评(4-5星)</a-select-option> |
| 10 | 10 | <a-select-option :value="2">中评(3星)</a-select-option> |
| 11 | 11 | <a-select-option :value="3">差评(1-2星)</a-select-option> |
| 12 | 12 | </a-select> |
| 13 | + </div> | |
| 14 | + <div class="list-toolbar-right"> | |
| 13 | 15 | <a-button type="primary" @click="loadList">查询</a-button> |
| 14 | - </a-space> | |
| 15 | - </template> | |
| 16 | + </div> | |
| 17 | + </div> | |
| 16 | 18 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 17 | 19 | <template #bodyCell="{ column, record }"> |
| 20 | + <template v-if="column.key === 'cityId'"> | |
| 21 | + {{ getCityName(record.cityId) }} | |
| 22 | + </template> | |
| 18 | 23 | <template v-if="column.key === 'star'"> |
| 19 | 24 | <a-rate :value="record.star" disabled /> |
| 20 | 25 | </template> |
| ... | ... | @@ -25,11 +30,12 @@ |
| 25 | 30 | </template> |
| 26 | 31 | |
| 27 | 32 | <script setup lang="ts"> |
| 28 | -import { ref } from 'vue' | |
| 29 | -import { riderApi } from '@/api' | |
| 33 | +import { ref, onMounted } from 'vue' | |
| 34 | +import { cityApi, riderApi } from '@/api' | |
| 30 | 35 | |
| 31 | 36 | const loading = ref(false) |
| 32 | 37 | const list = ref<any[]>([]) |
| 38 | +const cityList = ref<any[]>([]) | |
| 33 | 39 | const filterRiderId = ref<number | undefined>() |
| 34 | 40 | const filterType = ref(0) |
| 35 | 41 | |
| ... | ... | @@ -38,9 +44,19 @@ const columns = [ |
| 38 | 44 | { title: '骑手ID', dataIndex: 'rid' }, |
| 39 | 45 | { title: '评分', key: 'star' }, |
| 40 | 46 | { title: '内容', dataIndex: 'content', ellipsis: true }, |
| 41 | - { title: '租户', dataIndex: 'cityId' }, | |
| 47 | + { title: '租户', key: 'cityId' }, | |
| 42 | 48 | ] |
| 43 | 49 | |
| 50 | +async function loadCities() { | |
| 51 | + const res: any = await cityApi.openList() | |
| 52 | + cityList.value = Array.isArray(res?.data) ? res.data : [] | |
| 53 | +} | |
| 54 | + | |
| 55 | +function getCityName(cityId?: number) { | |
| 56 | + const city = cityList.value.find(item => item.id === cityId) | |
| 57 | + return city?.name || (cityId ? `租户#${cityId}` : '-') | |
| 58 | +} | |
| 59 | + | |
| 44 | 60 | async function loadList() { |
| 45 | 61 | if (!filterRiderId.value) return |
| 46 | 62 | loading.value = true |
| ... | ... | @@ -49,4 +65,6 @@ async function loadList() { |
| 49 | 65 | list.value = res.data || [] |
| 50 | 66 | } finally { loading.value = false } |
| 51 | 67 | } |
| 68 | + | |
| 69 | +onMounted(loadCities) | |
| 52 | 70 | </script> | ... | ... |
src/views/rider/RiderList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="骑手管理" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-select v-model:value="filterStatus" placeholder="审核状态" allowClear style="width:120px" @change="loadList"> | |
| 3 | + <a-card title="骑手管理" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-select v-model:value="filterStatus" placeholder="审核状态" allowClear class="list-filter" @change="loadList"> | |
| 7 | 7 | <a-select-option :value="2">待审核</a-select-option> |
| 8 | 8 | <a-select-option :value="1">已通过</a-select-option> |
| 9 | 9 | <a-select-option :value="0">已拒绝</a-select-option> |
| 10 | 10 | </a-select> |
| 11 | - <a-input-search v-model:value="keyword" placeholder="搜索姓名/手机" @search="loadList" style="width:200px" /> | |
| 11 | + <a-input-search v-model:value="keyword" placeholder="搜索姓名/手机" @search="loadList" class="list-search" /> | |
| 12 | + </div> | |
| 13 | + <div class="list-toolbar-right"> | |
| 12 | 14 | <a-button type="primary" @click="openAdd">新增骑手</a-button> |
| 13 | - </a-space> | |
| 14 | - </template> | |
| 15 | + </div> | |
| 16 | + </div> | |
| 15 | 17 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 16 | 18 | <template #bodyCell="{ column, record }"> |
| 17 | 19 | <template v-if="column.key === 'userStatus'"> |
| ... | ... | @@ -19,6 +21,9 @@ |
| 19 | 21 | {{ statusMap[record.userStatus] }} |
| 20 | 22 | </a-tag> |
| 21 | 23 | </template> |
| 24 | + <template v-if="column.key === 'cityId'"> | |
| 25 | + {{ getCityName(record.cityId) }} | |
| 26 | + </template> | |
| 22 | 27 | <template v-if="column.key === 'levelName'"> |
| 23 | 28 | {{ record.levelName || '默认等级' }} |
| 24 | 29 | </template> |
| ... | ... | @@ -67,38 +72,50 @@ |
| 67 | 72 | </a-card> |
| 68 | 73 | |
| 69 | 74 | <a-modal v-model:open="modalVisible" title="新增骑手" @ok="handleAdd" :confirmLoading="saving"> |
| 70 | - <a-form :model="form" layout="vertical"> | |
| 71 | - <a-form-item v-if="isAdmin" label="租户"> | |
| 72 | - <a-select v-model:value="form.cityId" placeholder="选择租户"> | |
| 73 | - <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 74 | - </a-select> | |
| 75 | - </a-form-item> | |
| 76 | - <a-form-item label="昵称"> | |
| 77 | - <a-input v-model:value="form.userNickname" placeholder="请输入骑手昵称" /> | |
| 78 | - </a-form-item> | |
| 79 | - <a-form-item label="手机号"> | |
| 80 | - <a-input v-model:value="form.mobile" placeholder="请输入手机号" /> | |
| 81 | - </a-form-item> | |
| 82 | - <a-form-item label="密码"> | |
| 83 | - <a-input-password v-model:value="form.password" placeholder="请输入登录密码" /> | |
| 84 | - </a-form-item> | |
| 85 | - </a-form> | |
| 75 | + <div class="soft-page-stack"> | |
| 76 | + <div class="soft-note-card"> | |
| 77 | + <strong>骑手创建说明</strong> | |
| 78 | + <p>当前入口用于后台手动新增骑手账号。管理员需要先选择租户,新增后可继续设置等级、启用状态和全职/兼职类型。</p> | |
| 79 | + </div> | |
| 80 | + <a-form :model="form" layout="vertical"> | |
| 81 | + <a-form-item v-if="isAdmin" label="租户"> | |
| 82 | + <a-select v-model:value="form.cityId" placeholder="选择租户"> | |
| 83 | + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 84 | + </a-select> | |
| 85 | + </a-form-item> | |
| 86 | + <a-form-item label="昵称"> | |
| 87 | + <a-input v-model:value="form.userNickname" placeholder="请输入骑手昵称" /> | |
| 88 | + </a-form-item> | |
| 89 | + <a-form-item label="手机号"> | |
| 90 | + <a-input v-model:value="form.mobile" placeholder="请输入手机号" /> | |
| 91 | + </a-form-item> | |
| 92 | + <a-form-item label="密码"> | |
| 93 | + <a-input-password v-model:value="form.password" placeholder="请输入登录密码" /> | |
| 94 | + </a-form-item> | |
| 95 | + </a-form> | |
| 96 | + </div> | |
| 86 | 97 | </a-modal> |
| 87 | 98 | |
| 88 | 99 | <a-modal v-model:open="levelVisible" title="设置骑手等级" @ok="handleSetLevel" :confirmLoading="levelSaving"> |
| 89 | - <a-form layout="vertical"> | |
| 90 | - <a-form-item label="骑手"> | |
| 91 | - <a-input :value="levelTargetName" disabled /> | |
| 92 | - </a-form-item> | |
| 93 | - <a-form-item label="等级"> | |
| 94 | - <a-select v-model:value="selectedLevelId" placeholder="请选择等级"> | |
| 95 | - <a-select-option :value="0">使用默认等级</a-select-option> | |
| 96 | - <a-select-option v-for="item in levelOptions" :key="item.id" :value="item.id"> | |
| 97 | - {{ item.name }}{{ item.isDefault === 1 ? '(默认)' : '' }} | |
| 98 | - </a-select-option> | |
| 99 | - </a-select> | |
| 100 | - </a-form-item> | |
| 101 | - </a-form> | |
| 100 | + <div class="soft-page-stack"> | |
| 101 | + <div class="soft-note-card"> | |
| 102 | + <strong>等级设置说明</strong> | |
| 103 | + <p>骑手等级会影响骑手收入规则。未单独指定时,将自动使用当前租户的默认等级。</p> | |
| 104 | + </div> | |
| 105 | + <a-form layout="vertical"> | |
| 106 | + <a-form-item label="骑手"> | |
| 107 | + <a-input :value="levelTargetName" disabled /> | |
| 108 | + </a-form-item> | |
| 109 | + <a-form-item label="等级"> | |
| 110 | + <a-select v-model:value="selectedLevelId" placeholder="请选择等级"> | |
| 111 | + <a-select-option :value="0">使用默认等级</a-select-option> | |
| 112 | + <a-select-option v-for="item in levelOptions" :key="item.id" :value="item.id"> | |
| 113 | + {{ item.name }}{{ item.isDefault === 1 ? '(默认)' : '' }} | |
| 114 | + </a-select-option> | |
| 115 | + </a-select> | |
| 116 | + </a-form-item> | |
| 117 | + </a-form> | |
| 118 | + </div> | |
| 102 | 119 | </a-modal> |
| 103 | 120 | </div> |
| 104 | 121 | </template> |
| ... | ... | @@ -137,7 +154,7 @@ const columns = [ |
| 137 | 154 | { title: 'ID', dataIndex: 'id', width: 80 }, |
| 138 | 155 | { title: '昵称', dataIndex: 'userNickname' }, |
| 139 | 156 | { title: '手机', dataIndex: 'mobile' }, |
| 140 | - { title: '租户ID', dataIndex: 'cityId' }, | |
| 157 | + { title: '租户', key: 'cityId' }, | |
| 141 | 158 | { title: '等级', key: 'levelName' }, |
| 142 | 159 | { title: '类型', key: 'type' }, |
| 143 | 160 | { title: '审核状态', key: 'userStatus' }, |
| ... | ... | @@ -155,6 +172,11 @@ function getWorkStatus(record: any) { |
| 155 | 172 | return record.isRest === 1 ? 1 : 0 |
| 156 | 173 | } |
| 157 | 174 | |
| 175 | +function getCityName(cityId?: number) { | |
| 176 | + const city = cityList.value.find(item => item.id === cityId) | |
| 177 | + return city?.name || (cityId ? `租户#${cityId}` : '-') | |
| 178 | +} | |
| 179 | + | |
| 158 | 180 | async function loadList() { |
| 159 | 181 | loading.value = true |
| 160 | 182 | try { |
| ... | ... | @@ -243,8 +265,6 @@ async function setType(riderId: number, type: number) { |
| 243 | 265 | |
| 244 | 266 | onMounted(() => { |
| 245 | 267 | loadList() |
| 246 | - if (isAdmin.value) { | |
| 247 | - loadCities() | |
| 248 | - } | |
| 268 | + loadCities() | |
| 249 | 269 | }) |
| 250 | 270 | </script> | ... | ... |
src/views/substation/SubstationList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <a-card title="分站管理" :bordered="false"> | |
| 4 | - <template #extra> | |
| 5 | - <a-space> | |
| 6 | - <a-input-search v-model:value="keyword" placeholder="搜索账号/昵称/手机" @search="loadList" style="width:220px" /> | |
| 3 | + <a-card title="分站管理" :bordered="false" class="list-table-card"> | |
| 4 | + <div class="list-toolbar"> | |
| 5 | + <div class="list-toolbar-left"> | |
| 6 | + <a-input-search v-model:value="keyword" placeholder="搜索账号/昵称/手机" @search="loadList" class="list-search" /> | |
| 7 | + </div> | |
| 8 | + <div class="list-toolbar-right"> | |
| 7 | 9 | <a-button type="primary" @click="openAdd">新增分站</a-button> |
| 8 | - </a-space> | |
| 9 | - </template> | |
| 10 | + </div> | |
| 11 | + </div> | |
| 10 | 12 | <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false"> |
| 11 | 13 | <template #bodyCell="{ column, record }"> |
| 14 | + <template v-if="column.key === 'cityId'"> | |
| 15 | + {{ getCityName(record.cityId) }} | |
| 16 | + </template> | |
| 12 | 17 | <template v-if="column.key === 'status'"> |
| 13 | 18 | <a-tag :color="record.userStatus === 1 ? 'green' : 'red'"> |
| 14 | 19 | {{ record.userStatus === 1 ? '正常' : '禁用' }} |
| ... | ... | @@ -34,39 +39,49 @@ |
| 34 | 39 | </a-table> |
| 35 | 40 | </a-card> |
| 36 | 41 | |
| 37 | - <a-modal v-model:open="modalVisible" :title="editingId ? '编辑分站' : '新增分站'" | |
| 38 | - @ok="handleSave" :confirmLoading="saving"> | |
| 39 | - <a-form :model="form" layout="vertical"> | |
| 40 | - <a-form-item label="所属租户"> | |
| 41 | - <a-select v-model:value="form.cityId" placeholder="选择租户"> | |
| 42 | - <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 43 | - </a-select> | |
| 44 | - </a-form-item> | |
| 45 | - <a-form-item label="登录账号"> | |
| 46 | - <a-input v-model:value="form.userLogin" :disabled="!!editingId" /> | |
| 47 | - </a-form-item> | |
| 48 | - <a-form-item label="昵称"> | |
| 49 | - <a-input v-model:value="form.userNickname" /> | |
| 50 | - </a-form-item> | |
| 51 | - <a-form-item label="手机号"> | |
| 52 | - <a-input v-model:value="form.mobile" /> | |
| 53 | - </a-form-item> | |
| 54 | - <a-form-item :label="editingId ? '新密码(不填不修改)' : '密码'"> | |
| 55 | - <a-input-password v-model:value="form.userPass" /> | |
| 56 | - </a-form-item> | |
| 57 | - </a-form> | |
| 42 | + <a-modal v-model:open="modalVisible" :title="editingId ? '编辑分站' : '新增分站'" @ok="handleSave" :confirmLoading="saving"> | |
| 43 | + <div class="soft-page-stack"> | |
| 44 | + <div class="soft-note-card"> | |
| 45 | + <strong>分站账号说明</strong> | |
| 46 | + <p>分站账号绑定单一租户,用于日常站点运营。编辑时登录账号保持不变,密码留空则不修改。</p> | |
| 47 | + </div> | |
| 48 | + <a-form :model="form" layout="vertical"> | |
| 49 | + <a-form-item label="所属租户"> | |
| 50 | + <a-select v-model:value="form.cityId" placeholder="选择租户"> | |
| 51 | + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option> | |
| 52 | + </a-select> | |
| 53 | + </a-form-item> | |
| 54 | + <a-form-item label="登录账号"> | |
| 55 | + <a-input v-model:value="form.userLogin" :disabled="!!editingId" /> | |
| 56 | + </a-form-item> | |
| 57 | + <a-form-item label="昵称"> | |
| 58 | + <a-input v-model:value="form.userNickname" /> | |
| 59 | + </a-form-item> | |
| 60 | + <a-form-item label="手机号"> | |
| 61 | + <a-input v-model:value="form.mobile" /> | |
| 62 | + </a-form-item> | |
| 63 | + <a-form-item :label="editingId ? '新密码(不填不修改)' : '密码'"> | |
| 64 | + <a-input-password v-model:value="form.userPass" /> | |
| 65 | + </a-form-item> | |
| 66 | + </a-form> | |
| 67 | + </div> | |
| 58 | 68 | </a-modal> |
| 59 | 69 | |
| 60 | - <!-- 改密码弹窗 --> | |
| 61 | 70 | <a-modal v-model:open="pwdVisible" title="修改密码" @ok="handleChangePwd" :confirmLoading="pwdSaving"> |
| 62 | - <a-form layout="vertical"> | |
| 63 | - <a-form-item label="原密码"> | |
| 64 | - <a-input-password v-model:value="pwdForm.oldPassword" /> | |
| 65 | - </a-form-item> | |
| 66 | - <a-form-item label="新密码"> | |
| 67 | - <a-input-password v-model:value="pwdForm.newPassword" /> | |
| 68 | - </a-form-item> | |
| 69 | - </a-form> | |
| 71 | + <div class="soft-page-stack"> | |
| 72 | + <div class="soft-note-card"> | |
| 73 | + <strong>密码修改说明</strong> | |
| 74 | + <p>这里修改的是当前选中分站账号的登录密码,提交时会按目标分站账号执行,不影响其他账号。</p> | |
| 75 | + </div> | |
| 76 | + <a-form layout="vertical"> | |
| 77 | + <a-form-item label="原密码"> | |
| 78 | + <a-input-password v-model:value="pwdForm.oldPassword" /> | |
| 79 | + </a-form-item> | |
| 80 | + <a-form-item label="新密码"> | |
| 81 | + <a-input-password v-model:value="pwdForm.newPassword" /> | |
| 82 | + </a-form-item> | |
| 83 | + </a-form> | |
| 84 | + </div> | |
| 70 | 85 | </a-modal> |
| 71 | 86 | </div> |
| 72 | 87 | </template> |
| ... | ... | @@ -90,11 +105,16 @@ const columns = [ |
| 90 | 105 | { title: '账号', dataIndex: 'userLogin' }, |
| 91 | 106 | { title: '昵称', dataIndex: 'userNickname' }, |
| 92 | 107 | { title: '手机', dataIndex: 'mobile' }, |
| 93 | - { title: '租户ID', dataIndex: 'cityId' }, | |
| 108 | + { title: '租户', key: 'cityId' }, | |
| 94 | 109 | { title: '状态', key: 'status' }, |
| 95 | 110 | { title: '操作', key: 'action' }, |
| 96 | 111 | ] |
| 97 | 112 | |
| 113 | +function getCityName(cityId?: number) { | |
| 114 | + const city = cityList.value.find(item => item.id === cityId) | |
| 115 | + return city?.name || (cityId ? `租户#${cityId}` : '-') | |
| 116 | +} | |
| 117 | + | |
| 98 | 118 | async function loadList() { |
| 99 | 119 | loading.value = true |
| 100 | 120 | try { |
| ... | ... | @@ -169,7 +189,7 @@ async function handleChangePwd() { |
| 169 | 189 | } |
| 170 | 190 | pwdSaving.value = true |
| 171 | 191 | try { |
| 172 | - await substationApi.changePassword(pwdForm) | |
| 192 | + await substationApi.changePassword({ id: pwdTargetId.value, ...pwdForm }) | |
| 173 | 193 | message.success('密码修改成功') |
| 174 | 194 | pwdVisible.value = false |
| 175 | 195 | } finally { pwdSaving.value = false } | ... | ... |