diff --git a/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.test.ts b/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.test.ts index 1e646df..be1578b 100644 --- a/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.test.ts +++ b/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.test.ts @@ -19,4 +19,16 @@ describe("LeadsPage initial loading", () => { expect(source.match(/class="lead-detail-item"/g)).toHaveLength(6); expect(source).toContain("v-permission=\"'admin:leads:update'\""); }); + + it("submits a selected status from the dialog footer", async () => { + const source = await readFile(fileURLToPath(new URL("./LeadsPage.vue", import.meta.url)), "utf8"); + + expect(source).toContain('const pendingStatus = ref("");'); + expect(source).toContain("pendingStatus.value = lead.status;"); + expect(source).toContain("async function submitStatus(lead: Lead)"); + expect(source).toContain('class="lead-detail-footer"'); + expect(source).toContain('@click="submitStatus(selectedLead)"'); + expect(source).toContain(">提交状态"); + expect(source).not.toContain('@update:model-value="changeStatus(selectedLead, $event)"'); + }); }); diff --git a/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.vue b/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.vue index 6696840..6fffd08 100644 --- a/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.vue +++ b/WonderQ-Admin-UI-Vue/src/pages/LeadsPage.vue @@ -8,6 +8,7 @@ import type { Lead, LeadStatus, LeadType } from "@/types"; const leads = ref([]); const detailOpen = ref(false); const selectedId = ref(""); +const pendingStatus = ref(""); const leadType = ref(""); const status = ref(""); const keyword = ref(""); @@ -46,21 +47,21 @@ function resetFilters() { function openDetail(lead: Lead) { selectedId.value = lead.id; + pendingStatus.value = lead.status; detailOpen.value = true; } -async function changeStatus(lead: Lead, nextStatus: LeadStatus) { - if (updatingId.value || lead.status === nextStatus) return; - const previous = lead.status; - lead.status = nextStatus; +async function submitStatus(lead: Lead) { + const nextStatus = pendingStatus.value; + if (updatingId.value || !nextStatus || lead.status === nextStatus) return; updatingId.value = lead.id; try { const saved = await updateLeadStatus(lead.id, nextStatus); const index = leads.value.findIndex((item) => item.id === saved.id); if (index >= 0) leads.value[index] = saved; + pendingStatus.value = saved.status; ElMessage.success(`线索已更新为${leadStatusLabel(nextStatus)}`); } catch (updateError) { - lead.status = previous; ElMessage.error(updateError instanceof Error ? updateError.message : "线索状态更新失败"); } finally { updatingId.value = ""; @@ -115,14 +116,8 @@ onMounted(load); - + + diff --git a/WonderQ-Admin-UI-Vue/src/styles.css b/WonderQ-Admin-UI-Vue/src/styles.css index f07dc44..860865a 100644 --- a/WonderQ-Admin-UI-Vue/src/styles.css +++ b/WonderQ-Admin-UI-Vue/src/styles.css @@ -61,11 +61,10 @@ button, input { font: inherit; } .lead-filter-card { margin-bottom: 16px; } .lead-filters { display: grid; grid-template-columns: 170px 170px minmax(180px, 1fr) auto; gap: 10px; } .lead-pagination { display: flex; justify-content: flex-end; align-items: center; gap: 14px; padding-top: 16px; color: var(--muted); font-size: 12px; } -.lead-detail-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; background: #f8fafc; } -.lead-detail-toolbar .el-tag { flex: 0 0 auto; } -.lead-detail-toolbar .el-select { min-width: 0; flex: 1 1 auto; max-width: 310px; } .lead-summary { display: grid; gap: 4px; min-width: 0; } .lead-summary span { overflow: hidden; color: var(--muted); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } +.lead-detail-dialog .el-dialog__body { padding-bottom: 16px; } +.lead-detail-dialog .el-dialog__footer { padding-top: 0; } .lead-detail-card { overflow: hidden; margin-top: 0; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); } .lead-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; margin: 0; background: var(--line); } .lead-detail-item { min-width: 0; padding: 15px 16px; background: var(--surface); } @@ -76,6 +75,10 @@ button, input { font: inherit; } .lead-detail-section-title::before { width: 3px; height: 14px; border-radius: 3px; background: var(--blue); content: ""; } .lead-detail-section p { margin: 7px 0 0; overflow-wrap: anywhere; color: #475569; line-height: 1.6; } .lead-detail-note { border-color: #e4eaf2; background: #f5f8fc; } +.lead-detail-footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-top: 14px; border-top: 1px solid var(--line); } +.lead-detail-footer-status, .lead-detail-footer-actions { display: flex; align-items: center; gap: 10px; } +.lead-detail-footer-status { color: var(--muted); font-size: 12px; } +.lead-detail-footer-actions .el-select { width: 190px; } .media-toolbar { justify-content: flex-end; } .media-toolbar .el-input { width: 150px; } .media-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; } @@ -198,7 +201,7 @@ button, input { font: inherit; } @media (max-width: 880px) { .wanfa-workspace { grid-template-columns: 1fr; } } @media (max-width: 980px) { .media-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } } @media (max-width: 760px) { .concierge-row { align-items: flex-start; flex-direction: column; } .concierge-actions { justify-content: flex-start; } .lead-filters { grid-template-columns: 1fr 1fr; } .lead-filters .el-input { grid-column: 1 / -1; } .operations-grid { grid-template-columns: 1fr; } .media-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } -@media (max-width: 640px) { .site-config-intro { align-items: flex-start; } .site-config-intro .el-button, .page-intro-actions { width: 100%; } .page-intro-actions { justify-content: stretch; } .page-intro-actions .el-button { flex: 1; } .editor-options { grid-template-columns: 1fr; gap: 0; } .site-config-panel .panel-heading { align-items: flex-start; flex-direction: column; } .site-config-panel .panel-heading .el-button { width: 100%; } .concierge-detail-row { grid-template-columns: 1fr; } .lead-filters { grid-template-columns: 1fr; } .lead-filters .el-input { grid-column: auto; } .lead-detail-grid { grid-template-columns: 1fr; } .lead-detail-toolbar { align-items: stretch; flex-direction: column; } .lead-detail-toolbar .el-select { width: 100%; max-width: none; } .media-toolbar { align-items: stretch; flex-direction: column; width: 100%; } .media-toolbar .el-input { width: 100%; } .media-grid { grid-template-columns: 1fr; } } +@media (max-width: 640px) { .site-config-intro { align-items: flex-start; } .site-config-intro .el-button, .page-intro-actions { width: 100%; } .page-intro-actions { justify-content: stretch; } .page-intro-actions .el-button { flex: 1; } .editor-options { grid-template-columns: 1fr; gap: 0; } .site-config-panel .panel-heading { align-items: flex-start; flex-direction: column; } .site-config-panel .panel-heading .el-button { width: 100%; } .concierge-detail-row { grid-template-columns: 1fr; } .lead-filters { grid-template-columns: 1fr; } .lead-filters .el-input { grid-column: auto; } .lead-detail-grid { grid-template-columns: 1fr; } .lead-detail-footer { align-items: flex-start; flex-direction: column; } .lead-detail-footer-actions { width: 100%; } .lead-detail-footer-actions .el-select { min-width: 0; flex: 1; width: auto; } .media-toolbar { align-items: stretch; flex-direction: column; width: 100%; } .media-toolbar .el-input { width: 100%; } .media-grid { grid-template-columns: 1fr; } } /* RuoYi-compatible shell: fixed sidebar, compact navbar, tags view and responsive content. */ .admin-layout {