From 2c74f6a08c9136b9994d4d5df240a5e304aae53e Mon Sep 17 00:00:00 2001 From: jacy-dukang Date: Tue, 7 Jul 2026 15:22:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E4=BC=99=E4=BA=BA=E7=AB=AF=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=8E=A5=E5=8F=A3=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/PartnerAccountsPage.tsx | 84 ++++++++++++++----- apps/admin-web/src/pages/PartnersPage.tsx | 70 +++++++++++----- .../src/modules/ops/admin-partners.service.ts | 26 ++++-- .../src/modules/ops/dto/admin-mutate.dto.ts | 4 + 4 files changed, 133 insertions(+), 51 deletions(-) diff --git a/apps/admin-web/src/pages/PartnerAccountsPage.tsx b/apps/admin-web/src/pages/PartnerAccountsPage.tsx index a090f5b..8fb3d31 100644 --- a/apps/admin-web/src/pages/PartnerAccountsPage.tsx +++ b/apps/admin-web/src/pages/PartnerAccountsPage.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { - Button, Descriptions, Drawer, Form, Input, Modal, Select, Space, Table, Tabs, Tag, Typography, message, + Button, Drawer, Form, Input, Modal, Select, Space, Table, Tabs, Tag, Typography, message, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { request, type Paginated } from '../lib/api'; @@ -28,6 +28,7 @@ type Detail = Row & { bills?: BillRow[]; orders?: OrderRow[] }; export default function PartnerAccountsPage() { const [form] = Form.useForm(); + const [editForm] = Form.useForm(); const [createForm] = Form.useForm(); const [filters, setFilters] = useState>({}); const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList( @@ -50,6 +51,22 @@ export default function PartnerAccountsPage() { setPartners(res.items); } + async function openAccount(id: string) { + const d = await request(`/admin/partner-accounts/${id}`); + setDetail(d); + editForm.setFieldsValue({ name: d.name, phone: d.phone, status: d.status }); + setDrawerOpen(true); + } + + async function saveAccount() { + if (!detail) return; + const v = await editForm.validateFields(); + await request(`/admin/partner-accounts/${detail.id}`, { method: 'PUT', body: JSON.stringify(v) }); + message.success('已保存'); + setDrawerOpen(false); + void reload(); + } + const columns: ColumnsType = [ { title: '姓名', dataIndex: 'name', width: 100 }, { title: '手机', dataIndex: 'phone', width: 120 }, @@ -58,12 +75,12 @@ export default function PartnerAccountsPage() { { title: '状态', dataIndex: 'status', width: 80, render: (s) => {ACCOUNT_STATUS_LABELS[s] || s} }, { title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime }, { - title: '操作', width: 80, + title: '操作', width: 120, render: (_, row) => ( - + + + + ), }, ]; @@ -100,27 +117,41 @@ export default function PartnerAccountsPage() { { setPage(p); setPageSize(ps); } }} /> - setDrawerOpen(false)} - extra={detail && ( - + + + + + + + ({ value: p.id, label: p.companyName }))} /> - + + + diff --git a/apps/admin-web/src/pages/PartnersPage.tsx b/apps/admin-web/src/pages/PartnersPage.tsx index 5470a11..42413a1 100644 --- a/apps/admin-web/src/pages/PartnersPage.tsx +++ b/apps/admin-web/src/pages/PartnersPage.tsx @@ -12,6 +12,24 @@ type Row = { storeCount: number; accountCount: number; cityCount: number; createdAt: string; }; +type PartnerDetail = Row & { + bankAccountName?: string | null; + bankAccountNo?: string | null; + bankBranch?: string | null; + cities?: Array<{ name: string; code: string; status: string }>; +}; + +function pickPartnerFormValues(d: PartnerDetail) { + return { + companyName: d.companyName, + contactPhone: d.contactPhone, + address: d.address, + bankAccountName: d.bankAccountName ?? '', + bankAccountNo: d.bankAccountNo ?? '', + bankBranch: d.bankBranch ?? '', + }; +} + export default function PartnersPage() { const [form] = Form.useForm(); const [editForm] = Form.useForm(); @@ -27,10 +45,26 @@ export default function PartnersPage() { }, [filters], ); - const [detail, setDetail] = useState | null>(null); + const [detail, setDetail] = useState(null); const [drawerOpen, setDrawerOpen] = useState(false); const [createOpen, setCreateOpen] = useState(false); + async function openPartner(id: string) { + const d = await request(`/admin/partners/${id}`); + setDetail(d); + editForm.setFieldsValue(pickPartnerFormValues(d)); + setDrawerOpen(true); + } + + async function savePartner() { + if (!detail) return; + const v = await editForm.validateFields(); + await request(`/admin/partners/${detail.id}`, { method: 'PUT', body: JSON.stringify(v) }); + message.success('已保存'); + setDrawerOpen(false); + void reload(); + } + const columns: ColumnsType = [ { title: '公司名', dataIndex: 'companyName' }, { title: '联系电话', dataIndex: 'contactPhone', width: 130 }, @@ -42,15 +76,8 @@ export default function PartnersPage() { title: '操作', width: 120, render: (_, row) => ( - - + + ), }, @@ -69,30 +96,29 @@ export default function PartnersPage() {
{ setPage(p); setPageSize(ps); } }} /> - setDrawerOpen(false)} - extra={detail && ( - - )}> + setDrawerOpen(false)} + extra={} + > {detail && ( <> - {Array.isArray(detail.cities) && ( + {Array.isArray(detail.cities) && detail.cities.length > 0 && ( - {(detail.cities as Array<{ name: string; code: string; status: string }>).map((c) => ( + {detail.cities.map((c) => ( {c.name} ({c.status}) ))} )} -
}> + + )} diff --git a/server/dukang-api/src/modules/ops/admin-partners.service.ts b/server/dukang-api/src/modules/ops/admin-partners.service.ts index 54e57f3..5c2a84b 100644 --- a/server/dukang-api/src/modules/ops/admin-partners.service.ts +++ b/server/dukang-api/src/modules/ops/admin-partners.service.ts @@ -141,13 +141,25 @@ export class AdminPartnersService { } async updatePartnerAccount(id: bigint, dto: UpdatePartnerAccountDto) { - const account = await this.prisma.partnerAccount.update({ - where: { id }, - data: { - ...(dto.name !== undefined ? { name: dto.name } : {}), - ...(dto.status !== undefined ? { status: dto.status as 'ACTIVE' | 'DISABLED' } : {}), - }, - }); + const existing = await this.prisma.partnerAccount.findUnique({ where: { id } }); + if (!existing) throw new NotFoundException('开城合伙人账号不存在'); + + const data: Prisma.PartnerAccountUpdateInput = {}; + if (dto.name !== undefined) data.name = dto.name; + if (dto.status !== undefined) data.status = dto.status as 'ACTIVE' | 'DISABLED'; + if (dto.phone !== undefined) { + const phone = dto.phone.trim(); + if (!/^1[3-9]\d{9}$/.test(phone)) { + throw new BadRequestException('请输入正确的手机号码'); + } + const phoneTaken = await this.prisma.partnerAccount.findUnique({ where: { phone } }); + if (phoneTaken && phoneTaken.id !== id) { + throw new BadRequestException('该手机号已被使用'); + } + data.phone = phone; + } + + const account = await this.prisma.partnerAccount.update({ where: { id }, data }); return serializeBigInt(account); } } diff --git a/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts b/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts index 27ff707..6c2b944 100644 --- a/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts +++ b/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts @@ -193,6 +193,10 @@ export class UpdatePartnerAccountDto { @IsString() name?: string; + @IsOptional() + @IsString() + phone?: string; + @IsOptional() @IsIn(['ACTIVE', 'DISABLED']) status?: string;