Przeglądaj źródła

feat(web-admin): add mp CRM quote card query helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong 3 tygodni temu
rodzic
commit
007eb2233d

+ 132 - 0
web-admin/src/utils/mp-crm-quote-card.test.ts

@@ -0,0 +1,132 @@
+// web-admin/src/utils/mp-crm-quote-card.test.ts
+import assert from 'node:assert/strict'
+import { describe, it } from 'node:test'
+import {
+  MP_QUOTE_MANAGE_TABS,
+  MP_QUOTE_APPROVE_TABS,
+  buildQuoteListQuery,
+  buildQuoteCard,
+  formatQuoteMoney,
+  quoteTypeLabel,
+  quoteTypeTagType
+} from './mp-crm-quote-card'
+
+describe('tabs', () => {
+  it('manage has all/submitted/accepted; approve has submitted/accepted', () => {
+    assert.deepEqual(
+      MP_QUOTE_MANAGE_TABS.map((x) => x.value),
+      ['all', 'submitted', 'accepted']
+    )
+    assert.deepEqual(
+      MP_QUOTE_APPROVE_TABS.map((x) => x.value),
+      ['submitted', 'accepted']
+    )
+  })
+})
+
+describe('buildQuoteListQuery', () => {
+  it('manage all excludes invalid and trims keyword', () => {
+    assert.deepEqual(
+      buildQuoteListQuery({
+        mode: 'manage',
+        view: 'all',
+        pageNum: 1,
+        pageSize: 20,
+        keyword: ' 酸 ',
+        currentUserId: 'u1'
+      }),
+      {
+        pageNum: 1,
+        pageSize: 20,
+        keyword: '酸',
+        excludeStatus: 'invalid'
+      }
+    )
+  })
+
+  it('manage submitted sets status only', () => {
+    const q = buildQuoteListQuery({
+      mode: 'manage',
+      view: 'submitted',
+      pageNum: 2,
+      pageSize: 10,
+      keyword: '',
+      currentUserId: 'u1'
+    })
+    assert.equal(q.status, 'submitted')
+    assert.equal(q.excludeStatus, undefined)
+    assert.equal('pendingApprovalUserId' in q, false)
+    assert.equal('keyword' in q, false)
+  })
+
+  it('manage accepted sets status accepted', () => {
+    const q = buildQuoteListQuery({
+      mode: 'manage',
+      view: 'accepted',
+      pageNum: 1,
+      pageSize: 20,
+      keyword: '',
+      currentUserId: 'u1'
+    })
+    assert.equal(q.status, 'accepted')
+    assert.equal('approvedByUserId' in q, false)
+  })
+
+  it('approve submitted uses pendingApprovalUserId', () => {
+    const q = buildQuoteListQuery({
+      mode: 'approve',
+      view: 'submitted',
+      pageNum: 1,
+      pageSize: 20,
+      keyword: '',
+      currentUserId: 'u9'
+    })
+    assert.equal(q.status, 'submitted')
+    assert.equal(q.pendingApprovalUserId, 'u9')
+  })
+
+  it('approve accepted uses approvedByUserId', () => {
+    const q = buildQuoteListQuery({
+      mode: 'approve',
+      view: 'accepted',
+      pageNum: 1,
+      pageSize: 20,
+      keyword: 'x',
+      currentUserId: 'u9'
+    })
+    assert.equal(q.approvedByUserId, 'u9')
+    assert.equal(q.keyword, 'x')
+    assert.equal('status' in q && q.status !== undefined, false)
+  })
+})
+
+describe('buildQuoteCard', () => {
+  it('maps fields and money', () => {
+    const card = buildQuoteCard({
+      id: 'q1',
+      quoteNo: 'BJ001',
+      quoteTitle: '硫酸报价',
+      customerName: '甲公司',
+      quoteType: 'sales',
+      finalAmount: 1234.5,
+      status: 'draft',
+      quoteDate: '2026-10-01'
+    })
+    assert.equal(card.id, 'q1')
+    assert.equal(card.quoteNo, 'BJ001')
+    assert.equal(card.quoteTypeLabel, '销售')
+    assert.equal(card.quoteTypeTagType, 'success')
+    assert.equal(card.amountText, '¥1,234.50')
+    assert.equal(card.status, 'draft')
+  })
+})
+
+describe('quoteType helpers', () => {
+  it('maps purchase/sales', () => {
+    assert.equal(quoteTypeLabel('purchase'), '采购')
+    assert.equal(quoteTypeTagType('purchase'), 'warning')
+    assert.equal(quoteTypeLabel('sales'), '销售')
+    assert.equal(formatQuoteMoney(1000), '¥1,000.00')
+    assert.equal(formatQuoteMoney(undefined), '—')
+  })
+})

+ 96 - 0
web-admin/src/utils/mp-crm-quote-card.ts

@@ -0,0 +1,96 @@
+export type MpQuoteMode = 'manage' | 'approve'
+export type MpQuoteView = 'all' | 'submitted' | 'accepted'
+
+export const MP_QUOTE_MANAGE_TABS: Array<{ value: MpQuoteView; label: string }> = [
+  { value: 'all', label: '全部' },
+  { value: 'submitted', label: '待审批' },
+  { value: 'accepted', label: '已审批' }
+]
+
+export const MP_QUOTE_APPROVE_TABS: Array<{ value: Exclude<MpQuoteView, 'all'>; label: string }> = [
+  { value: 'submitted', label: '待审批' },
+  { value: 'accepted', label: '已审批' }
+]
+
+export function quoteTypeLabel(type?: string): string {
+  if ((type || '').trim() === 'purchase') return '采购'
+  return '销售'
+}
+
+export function quoteTypeTagType(type?: string): 'success' | 'warning' {
+  if ((type || '').trim() === 'purchase') return 'warning'
+  return 'success'
+}
+
+export function formatQuoteMoney(amount?: number | null): string {
+  if (amount === undefined || amount === null || Number.isNaN(Number(amount))) return '—'
+  return `¥${Number(amount).toLocaleString('zh-CN', {
+    minimumFractionDigits: 2,
+    maximumFractionDigits: 2
+  })}`
+}
+
+export function buildQuoteListQuery(input: {
+  mode: MpQuoteMode
+  view: MpQuoteView
+  pageNum: number
+  pageSize: number
+  keyword: string
+  currentUserId: string
+}): Record<string, unknown> {
+  const params: Record<string, unknown> = {
+    pageNum: input.pageNum,
+    pageSize: input.pageSize
+  }
+  const kw = String(input.keyword || '').trim()
+  if (kw) params.keyword = kw
+
+  if (input.mode === 'manage') {
+    if (input.view === 'all') {
+      params.excludeStatus = 'invalid'
+    } else if (input.view === 'submitted') {
+      params.status = 'submitted'
+    } else {
+      params.status = 'accepted'
+    }
+    return params
+  }
+
+  // approve
+  if (input.view === 'submitted') {
+    params.status = 'submitted'
+    params.pendingApprovalUserId = input.currentUserId
+  } else {
+    params.approvedByUserId = input.currentUserId
+  }
+  return params
+}
+
+export function buildQuoteCard(row: {
+  id: string | number
+  quoteNo?: string
+  quoteTitle?: string
+  customerName?: string
+  quoteType?: string
+  finalAmount?: number
+  totalAmount?: number
+  status?: string
+  quoteDate?: string
+}) {
+  const amount =
+    row.finalAmount !== undefined && row.finalAmount !== null
+      ? row.finalAmount
+      : row.totalAmount
+  return {
+    id: String(row.id),
+    quoteNo: row.quoteNo || '—',
+    quoteTitle: row.quoteTitle || '—',
+    customerName: row.customerName || '—',
+    quoteType: row.quoteType || 'sales',
+    quoteTypeLabel: quoteTypeLabel(row.quoteType),
+    quoteTypeTagType: quoteTypeTagType(row.quoteType),
+    amountText: formatQuoteMoney(amount),
+    status: row.status || '',
+    quoteDate: row.quoteDate || '—'
+  }
+}