diff --git a/src/components/CommonSelect/DictRegionSelect.tsx b/src/components/CommonSelect/DictRegionSelect.tsx index 5439e82..1d26d81 100644 --- a/src/components/CommonSelect/DictRegionSelect.tsx +++ b/src/components/CommonSelect/DictRegionSelect.tsx @@ -3,18 +3,64 @@ import { Cascader, Spin } from 'antd'; import type { DefaultOptionType } from 'antd/es/cascader'; import { getChild } from './services'; -// 只用函数式组件的 props,不声明泛型 const DictRegionSelect: React.FC>> = (props) => { const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); + // 处理回显的所有级联节点 useEffect(() => { - setLoading(true); - fetchRegionOptions('0').then(data => { - setOptions(data); - setLoading(false); - }); - }, []); + let destroyed = false; + // 只在有初始值时触发自动懒加载 + async function loadAllLevels() { + setLoading(true); + // props.value 是 [省, 市, 区] 的 id 数组 + const path = (props.value as string[]) || []; + let currentPid = '0'; + let currentOptions: DefaultOptionType[] = []; + let optionLevel = []; + let parentOptions = currentOptions; + for (let i = 0; i < path.length; i++) { + // 拉取本级所有 children + // eslint-disable-next-line no-await-in-loop + const children = await fetchRegionOptions(currentPid); + // 新建 option 节点 + const optionNodes = children.map(child => ({ + ...child, + children: undefined, // 只有下一步才补 + })); + // 当前级别节点加入到 options 树 + if (i === 0) { + // 顶级直接 set + currentOptions = optionNodes; + } else { + // 找到父节点,挂到父节点 children + let parent = parentOptions.find(opt => opt.value === path[i - 1]); + if (parent) parent.children = optionNodes; + } + // 记录本级 option,下一轮用于找 parent + parentOptions = optionNodes; + currentPid = path[i]; + } + if (!destroyed) { + setOptions(currentOptions); + setLoading(false); + } + } + + // 如果有 value,并且是懒加载(options 为空或只有顶级) + if (Array.isArray(props.value) && props.value.length > 0) { + loadAllLevels(); + } else { + // 首次只拉顶级 + setLoading(true); + fetchRegionOptions('0').then(data => { + setOptions(data); + setLoading(false); + }); + } + return () => { destroyed = true; }; + // eslint-disable-next-line + }, [props.value]); const loadData = async (selectedOptions: DefaultOptionType[]) => { const targetOption = selectedOptions[selectedOptions.length - 1]; @@ -26,8 +72,6 @@ const DictRegionSelect: React.FC => { - console.log(pId,'pId'); - const res = await getChild({ pId }); if (res && res.code === 200 && Array.isArray(res.data)) { return res.data.map((item: any) => ({ @@ -39,11 +83,10 @@ const DictRegionSelect: React.FC void, // 关键类型断言! + loadData: loadData as (selectedOptions: DefaultOptionType[]) => void, placeholder: "请选择地区", ...props, }; diff --git a/src/components/CompanyInfo/component/BankFormModal.tsx b/src/components/CompanyInfo/component/BankFormModal.tsx index f392531..ff06d84 100644 --- a/src/components/CompanyInfo/component/BankFormModal.tsx +++ b/src/components/CompanyInfo/component/BankFormModal.tsx @@ -1,10 +1,10 @@ import React, { useEffect, useState } from 'react'; import { Modal, Form, Input, message, Row, Col, Descriptions, Select } from 'antd'; import { getDictList } from '@/servers/api/dicts'; -import { bankView, bankAdd, bankEdit } from '../services'; +import { bankView, bankAdd, bankEdit } from '../services'; import type { DictItem } from '@/servers/api/dicts'; import DictRegionSelect from '@/components/CommonSelect/DictRegionSelect' - +import { dictRegion } from '@/servers/api/user' interface props { visible: boolean; onOk: () => void; @@ -49,6 +49,11 @@ const InvoiceFormModal: React.FC = ({ //提交防抖 const [submitting, setSubmitting] = useState(false); const [currency, setCurrency] = useState([]); + //币种 + const [currencyMap, setCurrencyMap] = useState<{ [code: string]: string }>({}); + const [nationName, setNationName] = useState(''); + const [provinceName, setProvinceName] = useState(''); + const [cityName, setCityName] = useState(''); useEffect(() => { if (visible) { @@ -60,23 +65,36 @@ const InvoiceFormModal: React.FC = ({ ...data, id: data.id ? data.id : null, address: [ - Number(data.nation), - Number(data.province), - Number(data.city), - ] + data.nation ? String(data.nation) : undefined, + data.province ? String(data.province) : undefined, + data.city ? String(data.city) : undefined, + ] }; console.log(fields); form.setFieldsValue(fields); setViewData(fields); + + if (data.nation) dictRegion(data.nation).then(r => setNationName(r?.data?.name || '')); + if (data.province) dictRegion(data.province).then(r => setProvinceName(r?.data?.name || '')); + if (data.city) dictRegion(data.city).then(r => setCityName(r?.data?.name || '')); } }); } else { - form.resetFields(); + form.resetFields(); + setViewData({}); + setNationName(''); + setProvinceName(''); + setCityName(''); } getDictList('currency').then((res) => { if (res.code === 200) { + const map: { [code: string]: string } = {}; + res.data.forEach((item: { code: string, dicName: string }) => { + map[item.code] = item.dicName; + }); + setCurrencyMap(map); setCurrency(res.data); } }); @@ -121,7 +139,13 @@ const InvoiceFormModal: React.FC = ({ setSubmitting(false); // 无论成功失败都解锁 } }; - + const fetchRegionNames = async (codes: string) => { + console.log(codes,'codes'); + + const { data } = await dictRegion(codes); + console.log(data); + + }; return ( = ({ {viewData.accountName} {viewData.bank} {viewData.interbankNumber} - {viewData.nationName} - {viewData.provinceName} - {viewData.cityName} - {viewData.currency} + {nationName || viewData.nation} + {provinceName || viewData.province} + {cityName || viewData.city} + {currencyMap[viewData.currency as string] || viewData.currency} ) : (
- + diff --git a/src/components/CompanyInfo/component/BankInfoTab.tsx b/src/components/CompanyInfo/component/BankInfoTab.tsx index 62f8c67..3cc4c37 100644 --- a/src/components/CompanyInfo/component/BankInfoTab.tsx +++ b/src/components/CompanyInfo/component/BankInfoTab.tsx @@ -1,9 +1,11 @@ import React, { useEffect, useState } from 'react'; -import { Table, Button, message, Switch } from 'antd'; +import { Table, Button, message, Switch, Spin } from 'antd'; import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; import { bankGetPage, bankEdit } from '../services'; import { useIntl } from 'umi'; import BankFormModal from './BankFormModal'; +import { dictRegion } from '@/servers/api/user' +import { getDictList } from '@/servers/api/dicts'; interface BankInfo { id: string; @@ -23,6 +25,24 @@ interface Props { viewType?: boolean; record?: string; } + +const codeNameCache = new Map(); +const fetchRegionNames = async (codes: string[]) => { + const waitCodes = codes.filter(code => code && !codeNameCache.has(code)); + if (waitCodes.length === 0) return; + // 批量接口推荐你后端支持,单个请求也兼容如下 + await Promise.all(waitCodes.map(async (code) => { + try { + const { code: status, data } = await dictRegion(code); + if (status === 200 && data && data.name) { + codeNameCache.set(code, data.name); + } + } catch { /* ignore */ } + })); +}; + + + const BankInfoTab: React.FC = (props) => { const userId = sessionStorage.getItem('userId') || ''; const { viewType = false, record = userId } = props; @@ -34,6 +54,8 @@ const BankInfoTab: React.FC = (props) => { const [loading, setLoading] = useState(false); //列表分页 const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //币种 + const [currencyMap, setCurrencyMap] = useState<{ [code: string]: string }>({}); //列表方法 const getList = async (pageNo: number = 1, pageSize: number = 10) => { setLoading(true); @@ -74,10 +96,11 @@ const BankInfoTab: React.FC = (props) => { setIsViewMode(true); setFormVisible(true); }; + //是否作废 - const handleObsoleteChange = async (checked: boolean, id:string) => { + const handleObsoleteChange = async (checked: boolean, id: string) => { // 调用你的作废接口 - const res = await bankEdit( { id, delFlag: checked? 'normal':'deleted' } ); + const res = await bankEdit({ id, delFlag: checked ? 'normal' : 'deleted' }); if (res.code === 200) { message.success('操作成功'); getList(pagination.current, pagination.pageSize); // 刷新列表 @@ -87,10 +110,48 @@ const BankInfoTab: React.FC = (props) => { } //初始化 useEffect(() => { - if(record) { + if (record) { + getDictList('currency').then((res) => { + if (res.code === 200) { + const map: { [code: string]: string } = {}; + res.data.forEach((item: { code:string, dicName: string }) => { + map[item.code] = item.dicName; + }); + setCurrencyMap(map); + } + }); getList(); } }, [record]); + + const [regionLoading, setRegionLoading] = useState(false); + const [, forceUpdate] = useState({}); // 用于触发重新渲染 + + // 新增一个 effect,当 data 变化时批量请求 + useEffect(() => { + // 收集所有 nation、province、city 的 code + const codes: string[] = []; + data.forEach(item => { + if (item.nation) codes.push(item.nation); + if (item.province) codes.push(item.province); + if (item.city) codes.push(item.city); + }); + // 查缓存,没有再查接口 + setRegionLoading(true); + fetchRegionNames(codes).then(() => { + setRegionLoading(false); + forceUpdate({}); // 强制刷新 + }); + }, [data]); + // 通用渲染 + const renderRegionName = (code: string) => { + if (!code) return ''; + if (codeNameCache.has(code)) { + return codeNameCache.get(code); + } + return ; + }; + // 表格头部 const columns: ColumnsType = [ { @@ -123,23 +184,27 @@ const BankInfoTab: React.FC = (props) => { }, { title: 'page.workbench.bank.currency', - dataIndex: 'currencyName', - key: 'currency', ellipsis: true + dataIndex: 'currency', + key: 'currency', ellipsis: true, + render: (code: string) => currencyMap[code] || code }, { title: 'page.workbench.bank.nation', - dataIndex: 'nationName', - key: 'nation', ellipsis: true + dataIndex: 'nation', + key: 'nation', ellipsis: true, + render: renderRegionName, }, { title: 'page.workbench.bank.province', - dataIndex: 'provinceName', - key: 'province', ellipsis: true + dataIndex: 'province', + key: 'province', ellipsis: true, + render: renderRegionName, }, { title: 'page.workbench.bank.city', - dataIndex: 'cityName', - key: 'city', ellipsis: true + dataIndex: 'city', + key: 'city', ellipsis: true, + render: renderRegionName, }, { title: 'page.workbench.bank.updateTime', @@ -172,8 +237,8 @@ const BankInfoTab: React.FC = (props) => { <> handleView(record)}>查看 {record.delFlag === 'normal' && ( - handleEdit(record)}>修改 - )} + handleEdit(record)}>修改 + )} ), }, diff --git a/src/components/CompanyInfo/component/InvoiceTab.tsx b/src/components/CompanyInfo/component/InvoiceTab.tsx index 39ac318..3cf3d2b 100644 --- a/src/components/CompanyInfo/component/InvoiceTab.tsx +++ b/src/components/CompanyInfo/component/InvoiceTab.tsx @@ -4,7 +4,7 @@ import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; import { invoiceGetPage, invoiceEdit } from '../services'; import { useIntl } from 'umi'; import InvoiceFormModal from './InvoiceFormModal'; - +import { getDictList } from '@/servers/api/dicts'; interface InvoiceInfo { id: string; taxpayerType: string; @@ -76,10 +76,10 @@ const InvoiceTab: React.FC = (props) => { setIsViewMode(true); setFormVisible(true); }; - //是否作废 - const handleObsoleteChange = async (checked: boolean, id:string) => { + //是否作废 + const handleObsoleteChange = async (checked: boolean, id: string) => { // 调用你的作废接口 - const res = await invoiceEdit( { id, delFlag: checked? 'normal':'deleted' } ); + const res = await invoiceEdit({ id, delFlag: checked ? 'normal' : 'deleted' }); if (res.code === 200) { message.success('操作成功'); getList(pagination.current, pagination.pageSize); // 刷新列表 @@ -87,16 +87,27 @@ const InvoiceTab: React.FC = (props) => { message.error('操作失败'); } } + const [taxpayerTypeMap, setTaxpayerTypeMap] = useState<{ [code: string]: string }>({}); //初始化 useEffect(() => { - if(record) { + if (record) { + getDictList('taxpayer_type').then((res) => { + if (res.code === 200) { + const map: { [code: string]: string } = {}; + res.data.forEach((item: { code: string, dicName: string }) => { + map[item.code] = item.dicName; + }); + setTaxpayerTypeMap(map); + } + }); getList(); } }, [record]); + const columns: ColumnsType = [ { title: 'page.workbench.invoice.index', dataIndex: 'index', width: 80, key: 'index', render: (_: any, __: any, index: number) => index + 1 }, - { title: 'page.workbench.invoice.taxpayerType', dataIndex: 'taxpayerTypeCn', ellipsis: true }, + { title: 'page.workbench.invoice.taxpayerType', dataIndex: 'taxpayerType', ellipsis: true, render: (code: string) => taxpayerTypeMap[code] || code }, { title: 'page.workbench.invoice.taxpayerCode', dataIndex: 'taxpayerCode', ellipsis: true }, { title: 'page.workbench.invoice.head', dataIndex: 'head', ellipsis: true }, { title: 'page.workbench.invoice.address', dataIndex: 'address', ellipsis: true }, @@ -136,8 +147,8 @@ const InvoiceTab: React.FC = (props) => { <> handleView(record)}>查看 {record.delFlag === 'normal' && ( - handleEdit(record)}>修改 - )} + handleEdit(record)}>修改 + )} ), }, diff --git a/src/servers/api/user.ts b/src/servers/api/user.ts index 84fdda2..a043900 100644 --- a/src/servers/api/user.ts +++ b/src/servers/api/user.ts @@ -11,3 +11,23 @@ export async function getUserList(params: API.UserListRequest) { params, }); } +/** + * 获取全国列表 + * @param params 查询参数 + * @returns 全国列表响应 + */ +export async function dictRegion(code:string) { + return request(`/v1/dictRegion/${code}`, { + method: 'GET', + }); +} +/** + * 获取全球列表 + * @param params 查询参数 + * @returns 全球列表响应 + */ +export async function dictRegionInternational(code:string) { + return request(`/v1/dictRegionInternational/${code}`, { + method: 'GET', + }); +}