feat(frontend): 헤더 단순화 + 등록/수정 모달 도입
헤더 깨짐 수정 (MainLayout): - logo 를 div 박스 대신 단순 img(/vite.svg) 로 (ProLayout 기본 슬롯 호환) - avatarProps.render 의 chevron 제거 → ProLayout 기본 dom 만 Dropdown 트리거 - token.header 의 잘못된 키(colorBgRightActionsItemHover) 제거, heightLayoutHeader 56 으로 통일 - actionsRender 의 정산월 영역을 Space + 라벨 조합으로 정리 등록/수정 모달 (DrawerForm / ModalForm): - AgentList: DrawerForm (680px) — 설계사명 / 사번 / 소속(Select) / 직급 / 전화 / 이메일 / 주민번호(암호화) / 은행 / 계좌(암호화) / 입사일, 수정 시 detail API 자동 fetch + initialValues 주입, 액션 컬럼: 수정/퇴사 (Modal.confirm) - ContractList: ModalForm (680px) — 증권번호 / 설계사(Select) / 상품(Select) / 계약자 / 피보험자 / 보험료 / 납입주기 / 계약일 / 효력발생일 toolBarRender 등록 버튼 추가 - ExceptionLedger: ModalForm (620px) — 설계사 / 정산월 / 예외코드 / 금액 / 증권번호 / 보험사 / 내용 toolBarRender 등록 버튼 추가, 예외코드 옵션은 ruleApi 에서 동적 로드 api/contract.ts: ContractSaveReq + create/update 추가 검증: tsc -b 통과 / dev 서버 HMR 정상 반영 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,19 +1,55 @@
|
||||
import { ProTable, type ProColumns } from '@ant-design/pro-components';
|
||||
import { useRef, useState } from 'react';
|
||||
import { message } from 'antd';
|
||||
import {
|
||||
ModalForm, ProForm, ProFormDatePicker, ProFormDigit, ProFormSelect, ProFormText,
|
||||
ProTable, type ActionType, type ProColumns,
|
||||
} from '@ant-design/pro-components';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import dayjs from 'dayjs';
|
||||
import PageContainer from '@/components/common/PageContainer';
|
||||
import CodeBadge from '@/components/common/CodeBadge';
|
||||
import MoneyText from '@/components/common/MoneyText';
|
||||
import PermissionButton from '@/components/common/PermissionButton';
|
||||
import { useCommonCodes } from '@/hooks/useCommonCodes';
|
||||
import { contractApi, ContractResp, ContractSearchParam } from '@/api/contract';
|
||||
import { contractApi, ContractResp, ContractSaveReq, ContractSearchParam } from '@/api/contract';
|
||||
import { productApi } from '@/api/product';
|
||||
import { agentApi } from '@/api/agent';
|
||||
|
||||
export default function ContractList() {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [open, setOpen] = useState(false);
|
||||
const { data: insuranceCodes = [] } = useCommonCodes('INSURANCE_TYPE');
|
||||
const { data: statusCodes = [] } = useCommonCodes('CONTRACT_STATUS');
|
||||
const { data: payCycleCodes = [] } = useCommonCodes('PAY_CYCLE');
|
||||
|
||||
// 등록 모달용 - 상품/설계사 옵션
|
||||
const { data: products } = useQuery({
|
||||
queryKey: ['products', 'all'],
|
||||
queryFn: () => productApi.list({ pageSize: 999 }),
|
||||
enabled: open,
|
||||
});
|
||||
const { data: agents } = useQuery({
|
||||
queryKey: ['agents', 'all'],
|
||||
queryFn: () => agentApi.list({ pageSize: 999 }),
|
||||
enabled: open,
|
||||
});
|
||||
|
||||
const onSubmit = async (v: any) => {
|
||||
const req: ContractSaveReq = {
|
||||
...v,
|
||||
contractDate: v.contractDate ? dayjs(v.contractDate).format('YYYY-MM-DD') : undefined,
|
||||
effectiveDate: v.effectiveDate ? dayjs(v.effectiveDate).format('YYYY-MM-DD') : undefined,
|
||||
};
|
||||
await contractApi.create(req);
|
||||
message.success('등록 완료');
|
||||
setOpen(false);
|
||||
actionRef.current?.reload();
|
||||
return true;
|
||||
};
|
||||
|
||||
const columns: ProColumns<ContractResp>[] = [
|
||||
{
|
||||
title: '검색어', dataIndex: 'searchKeyword', hideInTable: true,
|
||||
fieldProps: { placeholder: '계약자/증권번호' },
|
||||
},
|
||||
{ title: '검색어', dataIndex: 'searchKeyword', hideInTable: true,
|
||||
fieldProps: { placeholder: '계약자/증권번호' } },
|
||||
{
|
||||
title: '보험종류', dataIndex: 'insuranceType', width: 100, valueType: 'select',
|
||||
valueEnum: Object.fromEntries(insuranceCodes.map((c) => [c.code, { text: c.codeName }])),
|
||||
@@ -43,6 +79,7 @@ export default function ContractList() {
|
||||
return (
|
||||
<PageContainer title="계약 관리" description="보험사·상품·계약 통합 관리">
|
||||
<ProTable<ContractResp, ContractSearchParam>
|
||||
actionRef={actionRef}
|
||||
rowKey="contractId"
|
||||
columns={columns}
|
||||
scroll={{ x: 1300 }}
|
||||
@@ -52,15 +89,56 @@ export default function ContractList() {
|
||||
return { data: res.list, total: res.total, success: true };
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 20,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['20', '50', '100'],
|
||||
pageSize: 20, showSizeChanger: true, pageSizeOptions: ['20', '50', '100'],
|
||||
showTotal: (total) => `총 ${total.toLocaleString()}건`,
|
||||
}}
|
||||
search={{ labelWidth: 'auto', searchText: '검색', resetText: '초기화', collapsed: false, collapseRender: false }}
|
||||
options={{ density: false, fullScreen: true, reload: true, setting: true }}
|
||||
toolBarRender={() => [
|
||||
<PermissionButton key="create" menuCode="CONTRACT_LIST" permCode="CREATE" type="primary"
|
||||
onClick={() => setOpen(true)}>+ 계약 등록</PermissionButton>,
|
||||
]}
|
||||
dateFormatter="string"
|
||||
/>
|
||||
|
||||
<ModalForm
|
||||
title="계약 등록"
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
width={680}
|
||||
layout="vertical"
|
||||
onFinish={onSubmit}
|
||||
submitter={{ searchConfig: { submitText: '등록', resetText: '취소' } }}
|
||||
modalProps={{ destroyOnClose: true, maskClosable: false }}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormText name="policyNo" label="증권번호" rules={[{ required: true }]} width="md" />
|
||||
<ProFormSelect
|
||||
name="agentId" label="설계사" rules={[{ required: true }]} width="md" showSearch
|
||||
options={agents?.list.map((a) => ({ value: a.agentId, label: `${a.agentName} (${a.licenseNo ?? '-'})` })) ?? []}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProFormSelect
|
||||
name="productId" label="상품" rules={[{ required: true }]} showSearch
|
||||
options={products?.list.map((p) => ({ value: p.productId, label: `${p.productName} — ${p.companyName}` })) ?? []}
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProFormText name="contractorName" label="계약자" width="md" />
|
||||
<ProFormText name="insuredName" label="피보험자" width="md" />
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProFormDigit name="premium" label="보험료" width="md" min={0}
|
||||
fieldProps={{ formatter: (v) => `${v}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',') }} />
|
||||
<ProFormSelect
|
||||
name="payCycle" label="납입주기" width="md"
|
||||
options={payCycleCodes.map((c) => ({ value: c.code, label: c.codeName }))}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProFormDatePicker name="contractDate" label="계약일" width="md" />
|
||||
<ProFormDatePicker name="effectiveDate" label="효력발생일" width="md" />
|
||||
</ProForm.Group>
|
||||
</ModalForm>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user