diff --git a/package.json b/package.json index 589cacb..657bc71 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "fw-h5app-template", + "name": "h5app-cas", "version": "1.0.0", "scripts": { "reset": "npx rimraf ./**/node_modules", @@ -27,11 +27,12 @@ "dependencies": { "@ant-design/cssinjs": "^1.21.1", "@ant-design/icons": "^5.4.0", - "@feewee/h5app-common": "^0.1.138", + "@feewee/h5app-common": "^0.1.168", "@modern-js/runtime": "~2.57.0", "ahooks": "^3.8.0", "antd": "^5.20.0", "clsx": "^2.1.1", + "currency.js": "^2.0.4", "dayjs": "^1.11.12", "lodash": "^4.17.21", "query-string": "^9.1.0", @@ -44,8 +45,8 @@ "@modern-js-app/eslint-config": "~2.57.0", "@modern-js/app-tools": "~2.57.0", "@modern-js/eslint-config": "~2.57.0", - "@modern-js/plugin-tailwindcss": "~2.57.0", "@modern-js/plugin-polyfill": "~2.57.0", + "@modern-js/plugin-tailwindcss": "~2.57.0", "@modern-js/tsconfig": "~2.57.0", "@rsdoctor/rspack-plugin": "^0.3.11", "@types/jest": "^29.5.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 832c634..f2389fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^5.4.0 version: 5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@feewee/h5app-common': - specifier: ^0.1.138 - version: 0.1.141(antd@5.20.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^0.1.168 + version: 0.1.168(antd@5.20.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@modern-js/runtime': specifier: ~2.57.0 version: 2.57.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -29,6 +29,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + currency.js: + specifier: ^2.0.4 + version: 2.0.4 dayjs: specifier: ^1.11.12 version: 1.11.13 @@ -151,6 +154,13 @@ packages: react: '>=16.0.0' react-dom: '>=16.0.0' + '@ant-design/icons@5.5.1': + resolution: {integrity: sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + '@ant-design/react-slick@1.1.2': resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==} peerDependencies: @@ -1576,8 +1586,8 @@ packages: engines: {node: '>=14', npm: '>=6'} hasBin: true - '@feewee/h5app-common@0.1.141': - resolution: {integrity: sha512-21YZ5GqrRh4xzuQcfUzikvOsNVz4PxGMsL9V8H9ISGclkAe+VFjy1SWRDoVTSqSP4HCxjfGrBJJ0sysIMujZNQ==} + '@feewee/h5app-common@0.1.168': + resolution: {integrity: sha512-08x8IC2jaJGmM7UaQQcRmkBcJcb13prgOFVbSMkmeBq7+fj/rQxGOQTe2HwjU4NNOHCJOrv2Gjmnufy49GvI5g==} engines: {node: '>=18', npm: '>=6.9.0'} peerDependencies: antd: '>=5' @@ -1892,8 +1902,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nutui/icons-react@1.0.4': - resolution: {integrity: sha512-nLFYpJvMBkKzT+PpfMaYoO6vJXMTM0yxAragTkEHt9HAFNjdTdvByCSRfUazHHdLdr1AJxS9h62VB1+jmBEtWA==} + '@nutui/icons-react@1.0.5': + resolution: {integrity: sha512-0TYl3Fk+sVz95DKqn/7isYAvaK5YGnaBwMMib4rqYLoqi9GGFwgU9rp2hYXu/X5IYdWgshj0xiuGMK75/vUYbQ==} '@nutui/nutui-react@2.6.15': resolution: {integrity: sha512-/xX4pB6HSXSWwqnumoJR+omirA9b8HznDvwFHFr/iu+KJn4D658VAVR47T4Z0ROEq9lmErmlcDxjqnxmJOzl6w==} @@ -2338,14 +2348,14 @@ packages: '@swc/plugin-styled-components@2.0.9': resolution: {integrity: sha512-0aPv7lNed27qs8JBklLkVSlLhpPRU3YKRnKplObaAyhNWbpbOkCbVSTay5ArFT2Gz1rz844Np7l4DMozEtZRBg==} - '@tanstack/react-virtual@3.10.6': - resolution: {integrity: sha512-xaSy6uUxB92O8mngHZ6CvbhGuqxQ5lIZWCBy+FjhrbHmOwc6BnOnKkYm2FsB1/BpKw/+FVctlMbEtI+F6I1aJg==} + '@tanstack/react-virtual@3.10.9': + resolution: {integrity: sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@tanstack/virtual-core@3.10.6': - resolution: {integrity: sha512-1giLc4dzgEKLMx5pgKjL6HlG5fjZMgCjzlKAlpr7yoUtetVPELgER1NtephAI910nMwfPTHNyWKSFmJdHkz2Cw==} + '@tanstack/virtual-core@3.10.9': + resolution: {integrity: sha512-kBknKOKzmeR7lN+vSadaKWXaLS0SZZG+oqpQ/k80Q6g9REn6zRHS/ZYdrIzHnpHgy/eWs00SujveUN/GJT2qTw==} '@trysound/sax@0.2.0': resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} @@ -3403,6 +3413,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + currency.js@2.0.4: + resolution: {integrity: sha512-6/OplJYgJ0RUlli74d93HJ/OsKVBi8lB1+Z6eJYS1YZzBuIp4qKKHpJ7ad+GvTlWmLR/hLJOWTykN5Nm8NJ7+w==} + engines: {node: '>=4'} + current-script-polyfill@1.0.0: resolution: {integrity: sha512-qv8s+G47V6Hq+g2kRE5th+ASzzrL7b6l+tap1DHKK25ZQJv3yIFhH96XaQ7NGL+zRW3t/RDbweJf/dJDe5Z5KA==} @@ -5812,6 +5826,10 @@ packages: resolution: {integrity: sha512-t6dqMECpCkqfyv2FfwVS1xcB6lgXW/0XZSaKdsCNGYkqMO76AFiJEg4vINzoDKcZa6MS7JX+OHIjwh06K5vczw==} engines: {node: '>=18'} + query-string@9.1.1: + resolution: {integrity: sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg==} + engines: {node: '>=18'} + querystring-es3@0.2.1: resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} engines: {node: '>=0.4.x'} @@ -6091,6 +6109,12 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-photo-view@1.2.6: + resolution: {integrity: sha512-Fq17yxkMIv0oFp7HOJr39HgCZRP6A9K5T5rixJ4flSUYT2OO3V8vNxEExjhIKgIrfmTu+mDnHYEsI9RRWi1JHw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -7360,6 +7384,16 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@ant-design/icons@5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/colors': 7.1.0 + '@ant-design/icons-svg': 4.4.2 + '@babel/runtime': 7.25.6 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@ant-design/react-slick@1.1.2(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -9243,13 +9277,13 @@ snapshots: - jest - supports-color - '@feewee/h5app-common@0.1.141(antd@5.20.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@feewee/h5app-common@0.1.168(antd@5.20.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@ant-design/icons': 5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/icons': 5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@feewee/helper-wasm': 0.2.7 - '@nutui/icons-react': 1.0.4 + '@nutui/icons-react': 1.0.5 '@nutui/nutui-react': 2.6.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/react-virtual': 3.10.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-virtual': 3.10.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ahooks: 3.8.1(react@18.3.1) antd: 5.20.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) antd-mobile: 5.37.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -9260,9 +9294,10 @@ snapshots: js-cookie: 3.0.5 localforage: 1.10.0 lodash: 4.17.21 - query-string: 9.1.0 + query-string: 9.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-photo-view: 1.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - debug @@ -9964,12 +9999,12 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@nutui/icons-react@1.0.4': {} + '@nutui/icons-react@1.0.5': {} '@nutui/nutui-react@2.6.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@nutui/icons-react': 1.0.4 + '@nutui/icons-react': 1.0.5 '@nutui/touch-emulator': 1.0.0 '@react-spring/web': 9.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@use-gesture/react': 10.2.20(react@18.3.1) @@ -10600,13 +10635,13 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@tanstack/react-virtual@3.10.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-virtual@3.10.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/virtual-core': 3.10.6 + '@tanstack/virtual-core': 3.10.9 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@tanstack/virtual-core@3.10.6': {} + '@tanstack/virtual-core@3.10.9': {} '@trysound/sax@0.2.0': {} @@ -11971,6 +12006,8 @@ snapshots: csstype@3.1.3: {} + currency.js@2.0.4: {} + current-script-polyfill@1.0.0: {} data-uri-to-buffer@3.0.1: {} @@ -14490,6 +14527,12 @@ snapshots: filter-obj: 5.1.0 split-on-first: 3.0.0 + query-string@9.1.1: + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 + querystring-es3@0.2.1: {} queue-microtask@1.2.3: {} @@ -14827,7 +14870,7 @@ snapshots: rc-util@5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.24.5 + '@babel/runtime': 7.25.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-is: 18.3.1 @@ -14861,6 +14904,11 @@ snapshots: react-is@18.3.1: {} + react-photo-view@1.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-refresh@0.14.2: {} react-router-dom@6.22.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): diff --git a/src/components/CardView/index.tsx b/src/components/CardView/index.tsx new file mode 100644 index 0000000..c62c1e3 --- /dev/null +++ b/src/components/CardView/index.tsx @@ -0,0 +1,15 @@ +import type React from 'react'; +import './style.less'; + +interface Props { + style?: React.CSSProperties; + children: React.ReactNode; +} + +export default function CardView({ style, children }: Props) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/CardView/style.less b/src/components/CardView/style.less new file mode 100644 index 0000000..e595fb1 --- /dev/null +++ b/src/components/CardView/style.less @@ -0,0 +1,5 @@ +.card-view { + padding: 15px; + border-bottom: 1px solid #eee; + background-color: #fff; +} diff --git a/src/components/RowText/index.tsx b/src/components/RowText/index.tsx new file mode 100644 index 0000000..209a39d --- /dev/null +++ b/src/components/RowText/index.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import type { CSSProperties } from 'react'; +import { Row } from '@feewee/h5app-common'; + +import './style.less'; + +interface ComponentProps { + title: string; + content: string | number; + rowStyle?: CSSProperties; + titleStyle?: CSSProperties; + contentStyle?: CSSProperties; +} + +export default function RowText({ title, content, rowStyle, titleStyle, contentStyle }: ComponentProps) { + return ( + + + {title}: + + + {content} + + + ); +} diff --git a/src/components/RowText/style.less b/src/components/RowText/style.less new file mode 100644 index 0000000..216d95e --- /dev/null +++ b/src/components/RowText/style.less @@ -0,0 +1,29 @@ +.row-wrapper { + margin-bottom: 10px; +} + +.label { + color: #666; + font-size: 14px; + line-height: 14px; + font-weight: 500; +} + +.content { + color: #666; + font-size: 14px; + line-height: 14px; + font-weight: 500; + flex: 1; +} + +.labelBold { + color: #333; + font-size: 15px; + font-weight: bold; +} + +.contentBold { + .labelBold; + flex: 1; +} diff --git a/src/routes/ClaimReviewApprove/DetailList/page.tsx b/src/routes/ClaimReviewApprove/DetailList/page.tsx new file mode 100644 index 0000000..b2f1297 --- /dev/null +++ b/src/routes/ClaimReviewApprove/DetailList/page.tsx @@ -0,0 +1,373 @@ +/* eslint-disable max-lines */ +import { useEffect, useState, useMemo } from 'react'; +import { helper, PageProvider, PoolTable, usePagination, useUrlParams, FilePreview } from '@feewee/h5app-common'; +import { useNavigate } from '@modern-js/runtime/router'; +import { cloneDeep } from 'lodash'; +import dayjs from 'dayjs'; + +import type { DimensionEnum, QueryTypeEnum } from '../entity'; +import { QueryItemTypeEnum, QueryItemTypeNameMap } from '../entity'; +import { reviewListApi, type ReviewListResult } from '../service'; +import rmb from '@/utils/rmb'; + +interface PageParams { + applyId: number; + queryType: QueryTypeEnum; + queryItemType: QueryItemTypeEnum; + type: DimensionEnum | QueryTypeEnum.USER_PUNISH; + typeId: number; +} + +/** + * 单数明细 + */ +const DetailList = () => { + const params = useUrlParams(); + const navigate = useNavigate(); + + const { queryType, queryItemType } = params; + + // 添加了动态数据后的 list 和 columns + const [assembledList, setAssembledList] = useState([]); + + const [delay, setDelay] = useState(true); + const { list, param, setParams, loading, errMsg, resetParam, pagination } = usePagination(reviewListApi, params, delay); + const { current = 1, pageSize = 15 } = param; + + useEffect(() => { + if (params.applyId) { + setDelay(false); + setParams({ ...params, pageSize: 50 }, true); + } + }, []); + + function renderSecondaryAmount(val: number) { + return ( + + {rmb.p(val)}元 + + ); + } + + const columns = useMemo(() => { + let baseColumns = [ + { + title: '索赔单号', + dataIndex: 'claimNo', + width: 120, + }, + { + title: '附件', + dataIndex: 'fid', + width: 150, + render: val =>
{/* */}
, + }, + { + title: '索赔类型', + dataIndex: 'claimTypeName', + render: text => text || '--', + }, + ]; + + // 根据 queryItemType 展示不同列 + if ([QueryItemTypeEnum.SYSTEM_TOTAL, QueryItemTypeEnum.SYSTEM_ONLY].includes(queryItemType)) { + baseColumns = baseColumns.concat([ + { + title: '车牌号', + dataIndex: 'plateNo', + render: text => text || '--', + }, + { + title: '车架号', + dataIndex: 'vin', + render: text => text || '--', + }, + { + title: '车辆名称', + dataIndex: 'carName', + render: text => text || '--', + }, + { + title: '系统生成金额', + dataIndex: 'totalFee', + align: 'right', + render: renderSecondaryAmount, + }, + { + title: '差异原因', + dataIndex: 'differenceReason', + render: text => text || '--', + }, + ]); + } else if (queryItemType === QueryItemTypeEnum.IMPORT_ONLY) { + baseColumns = baseColumns.concat([ + { + title: '厂家兑现金额', + dataIndex: 'amount', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + ]); + } else if (queryItemType === QueryItemTypeEnum.DIFFERENCE_COUNT) { + baseColumns = baseColumns.concat([ + { + title: '系统生成金额', + dataIndex: 'totalFee', + align: 'center', + children: [ + { + title: '小计', + dataIndex: 'totalFee', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + { + title: '工时费', + dataIndex: 'systemManHoursFee', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + { + title: '材料费', + dataIndex: 'systemPartFee', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + ], + }, + { + title: '提报金额', + dataIndex: 'submitAmount', + align: 'center', + children: [ + { + title: '小计', + dataIndex: 'submitAmount', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + { + title: '工时费', + dataIndex: 'submitManHoursFee', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + { + title: '材料费', + dataIndex: 'submitPartFee', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + ], + }, + { + title: '厂家兑现金额', + dataIndex: 'factoryAmount', // @note 前端定义,仅作数据查找 + align: 'center', + children: [ + { + title: '小计', + dataIndex: 'amount', + width: 100, + align: 'right', + render: renderSecondaryAmount, + }, + // @slot 存放 importData 中的数据 + ], + }, + { + title: '差异金额', + dataIndex: 'claimNo', + align: 'center', + children: [ + { + title: '系统生成与厂家兑现差异', + dataIndex: 'differenceAmount', + width: 100, + align: 'right', + render: val => ( + + {rmb.p(val)}元 + + ), + }, + { + title: '索赔提报与厂家兑现差异', + dataIndex: 'differenceSubmitAmount', + width: 100, + align: 'right', + render: val => ( + + {rmb.p(val)}元 + + ), + }, + ], + }, + { + title: '车牌号', + dataIndex: 'plateNo', + render: text => text || '--', + }, + { + title: '车架号', + dataIndex: 'vin', + render: text => text || '--', + }, + { + title: '车辆名称', + dataIndex: 'carName', + render: text => text || '--', + }, + ]); + } else { + baseColumns = baseColumns.concat([ + { + title: '车牌号', + dataIndex: 'plateNo', + render: text => text || '--', + }, + { + title: '车架号', + dataIndex: 'vin', + render: text => text || '--', + }, + { + title: '车辆名称', + dataIndex: 'carName', + render: text => text || '--', + }, + { + title: '厂家兑现金额', + dataIndex: 'amount', + align: 'right', + render: renderSecondaryAmount, + }, + { + title: '系统生成金额', + dataIndex: 'totalFee', + align: 'right', + render: renderSecondaryAmount, + }, + { + title: '差异金额', + dataIndex: 'differenceAmount', + align: 'right', + render: val => ( + + {rmb.p(val)}元 + + ), + }, + ]); + } + + // 动态列:工时费、配件费、救援费等,差异时放到厂家兑现列中,其余情况追加在列后面 + const extraColumns = []; + // 添加额外动态列数据 + const newList = cloneDeep(list); + if (newList.length > 0) { + // 构建表头 + if (newList[0].importData && ![QueryItemTypeEnum.SYSTEM_TOTAL, QueryItemTypeEnum.SYSTEM_ONLY].includes(queryItemType)) { + const { importData } = newList[0]; + if (importData) { + try { + // { "工时费": 10, "配件费": 20, "救援费": 30, ... } + const data = JSON.parse(importData); + for (const key in data) { + extraColumns.push({ + title: key, + dataIndex: key, + align: 'right', + width: 100, + render: val => {val}, + }); + } + } catch (e) { + console.error('parse importData fail:', e); + } + } + } + + // 构建表格数据 + // 2. 解析数据,若数据列有 importData 则解析为对象形式 + for (let i = 0; i < newList.length; i++) { + if (newList[i].importData) { + try { + const data = JSON.parse(newList[i].importData); + Object.assign(newList[i], data); + } catch (e) { + console.error('parse importData fail:', e); + } + } + } + + // 处理分页数据 + if (current === 1) { + setAssembledList(newList); + } else { + // 替换当前页数据 + const start = (current - 1) * pageSize; + const end = start + pageSize; + const newPageList = [...assembledList]; + newPageList.splice(start, end - start, ...newList); + setAssembledList(newPageList); + } + } + + // 跟进差异类型,添加额外列 + if (queryItemType === QueryItemTypeEnum.DIFFERENCE_COUNT) { + for (const column of baseColumns) { + // @ts-ignore + if (column.dataIndex === 'factoryAmount') { + // @ts-ignore + column.children = column.children.concat(extraColumns); + break; + } + } + } else { + baseColumns = baseColumns.concat(extraColumns); + } + + // 添加提报信息 + baseColumns = baseColumns.concat([ + { + title: '提报人', + dataIndex: 'claimer', + width: 80, + render: val => val || '--', + }, + { + title: '提报商家', + dataIndex: 'submitDealerName', + render: val => val || '--', + }, + { + title: '提报时间', + dataIndex: 'commitTime', + render: val => dayjs(val).format('YYYY-MM-DD HH:mm:ss'), + }, + ]); + return baseColumns; + }, [queryType, queryItemType, list]); + + return ( + helper.checkBack(() => navigate(-1))} + > + + + ); +}; + +export default DetailList; diff --git a/src/routes/ClaimReviewApprove/entity.ts b/src/routes/ClaimReviewApprove/entity.ts new file mode 100644 index 0000000..0507d44 --- /dev/null +++ b/src/routes/ClaimReviewApprove/entity.ts @@ -0,0 +1,109 @@ +export enum MatchStatusEnum { + /** 匹配一致 */ + MATCH = 1, + /** 无工单记录 */ + NO_ORDER = 2, + /** 匹配不一致 */ + NOT_MATCH = 3, +} + +export enum MatchStatusName { + /** 匹配一致 */ + 匹配一致 = 1, + /** 无工单记录 */ + 无工单 = 2, + /** 匹配不一致 */ + 匹配差异 = 3, +} + +export enum DimensionEnum { + /** 索赔类型 */ + CLAIM_TYPE = 1, + /** 索赔人员 */ + CLAIM_USER = 2, + /** 施工门店 */ + CLAIM_SHOP = 3, + /** 差异原因 */ + DIFFERENCE_REASON = 4, +} + +export const DimensionData = [ + { + value: DimensionEnum.CLAIM_TYPE, + title: '索赔类型', + }, + { + value: DimensionEnum.CLAIM_USER, + title: '索赔人员', + }, + { + value: DimensionEnum.CLAIM_SHOP, + title: '施工门店', + }, + { + value: DimensionEnum.DIFFERENCE_REASON, + title: '差异原因', + }, +]; + +export const DimensionNamesMap: Record = { + [DimensionEnum.CLAIM_TYPE]: '索赔类型', + [DimensionEnum.CLAIM_USER]: '索赔人员', + [DimensionEnum.CLAIM_SHOP]: '施工门店', + [DimensionEnum.DIFFERENCE_REASON]: '差异原因', +}; + +export enum TypeEnum { + /** 奖励 */ + REWARD = 1, + /** 处罚 */ + PUNISH = 0, +} + +export enum SubTypeEnum { + /** 门店分摊 */ + APPORTION = 1, + /** 门店 */ + SHOP = 2, + /** 人员 */ + USER = 3, +} + +export const SubTypeNameMap = { + [SubTypeEnum.APPORTION]: '门店分摊', + [SubTypeEnum.SHOP]: '门店', + [SubTypeEnum.USER]: '人员', +}; + +export enum QueryTypeEnum { + /** 索赔 */ + REVIEW_BEFORE = 1, + /** 差异 */ + REVIEW_AFTER = 2, + /** 人员处罚 */ + USER_PUNISH = 3, +} + +export enum QueryItemTypeEnum { + /** 霏车车系统 */ + SYSTEM_TOTAL = 1, + /** 系统有,导入无 */ + SYSTEM_ONLY = 2, + /** 导入合计 */ + TOTAL_COUNT = 3, + /** 一致 */ + MATCH_COUNT = 4, + /** 差异 */ + DIFFERENCE_COUNT = 5, + /** 系统无,导入有 */ + IMPORT_ONLY = 6, +} + +export const QueryItemTypeNameMap = { + [QueryItemTypeEnum.SYSTEM_TOTAL]: '霏车车系统单数', + [QueryItemTypeEnum.SYSTEM_ONLY]: '系统有,导入无单数', + [QueryItemTypeEnum.TOTAL_COUNT]: '提报单数', + [QueryItemTypeEnum.MATCH_COUNT]: '匹配一致单数', + [QueryItemTypeEnum.DIFFERENCE_COUNT]: '匹配差异单数', + [QueryItemTypeEnum.IMPORT_ONLY]: '系统无,导入有单数', +}; diff --git a/src/routes/ClaimReviewApprove/page.tsx b/src/routes/ClaimReviewApprove/page.tsx new file mode 100644 index 0000000..109888a --- /dev/null +++ b/src/routes/ClaimReviewApprove/page.tsx @@ -0,0 +1,32 @@ +import { Button, helper, Toast, PageProvider } from '@feewee/h5app-common'; +import { Helmet } from '@modern-js/runtime/head'; +import { useNavigate } from '@modern-js/runtime/router'; + +// 索赔复核审批 todo 待迁移 +function Index() { + const navigate = useNavigate(); + + return ( + helper.checkBack(() => navigate(-1))}> +
+
+ +
+
+
+ ); +} + +export default Index; diff --git a/src/routes/ClaimReviewApprove/service.ts b/src/routes/ClaimReviewApprove/service.ts new file mode 100644 index 0000000..fbe38cc --- /dev/null +++ b/src/routes/ClaimReviewApprove/service.ts @@ -0,0 +1,345 @@ +import type { PromisePageResp, PageBaseParams } from '@feewee/h5app-common'; +import { host, http } from '@feewee/h5app-common'; + +import type { DimensionEnum, MatchStatusEnum, QueryItemTypeEnum, QueryTypeEnum, SubTypeEnum, TypeEnum } from './entity'; + +export interface DetailResult { + // 申请id + applyId: number; + // 文件id + fid: string; + // 文件名称 + fName: string; + // 文件大小,单位b + fSize: number; + // 品牌名称 + brandName: string; + // 往来单位名称 + contactUnits: string; + // 申报商家 id + applyDealerId: number; + // 申报商家名称 + applyDealerName: string; + // 匹配结果的最早提报时间 + beginTime: number; + // 匹配结果的最晚提报时间 + endTime: number; + // 未复核单数 + notReviewedCount: number; + // 未复核金额 + notReviewedAmount: number; + // 超60天未复核单数 + overtimeNotReviewedCount: number; + // 厂家审核金额 + amount: number; +} + +export interface ClaimBeforeData { + // 类型 + type: DimensionEnum; + // 类型id + typeId: number; + // 类型名称 + typeName: string; + // 系统单数 + systemCount: number; + // 系统金额 + systemAmount: number; + // 系统有,导入无单数 + notExistsCount: number; + // 系统有,导入无金额 + notExistsAmount: number; + // 本次导入 合计单数 + total: number; + // 本次导入 合计金额 + totalAmount: number; + // 本次导入 匹配一致单数 + consistentCount: number; + // 本次导入 匹配一致金额 + consistentAmount: number; + // 本次导入 匹配不一致单数 + nonConsistentCount: number; + // 本次导入 匹配不一致金额 + nonConsistentAmount: number; + // 本次导入,系统无,导入有单数 + importNonSystemCount: number; + // 本次导入,系统无,导入有金额 + importNonSystemAmount: number; +} + +export interface ClaimAfterData { + // 类型id + typeId: number; + // 类型名称 + typeName: string; + // 差异单数 + nonConsistentCount: number; + // 差异金额 + nonConsistentAmount: number; + // 系统金额 + systemAmount: number; + // 厂家金额 + factoryAmount: number; +} + +export interface PunishData { + // 人员id + userId: number; + // 人员名称 + userName: number; + // 提报单数 + submitCount: number; + // 差异单数 + nonConsistentCount: number; + // 差异金额 + nonConsistentAmount: number; + // 差异金额占比 + nonConsistentRate: number; + // 处罚金额 + punishAmount: number; + // 处罚绩效得分率 + punishRate: number; +} + +export interface FactoryPunishOrRewardData { + // 合计金额 + totalAmount: number; + // + items: FactoryPunishOrRewardItem[]; +} + +export interface FactoryPunishOrRewardItem { + // 厂家奖励/处罚id + id: number; + // 文件名称 + fName: string; + // 文件id + fid: string; + // 兑现方式 + cashType: number; + // 处罚类型 + projectTypeName: string; + // 金额 + amount: number; + // 是否门店分摊 + apportion: boolean; + // 是否门店 + shop: boolean; + // 是否人员 + user: boolean; +} + +export interface FactoryPunishOrRewardTypeDetail { + // 对象id + targetId: number; + // 对象名称 + targetName: string; + // 金额 + amount: number; + // 比例 + proportion: number; + // 绩效得分率 + scoringRate: number; + // 岗位名称 + postName: string; + // 在职门店名称 + shopName: string; +} + +export interface ReviewListParams extends PageBaseParams { + applyId: number; + type: DimensionEnum | QueryTypeEnum.USER_PUNISH; + typeId: number; + queryType: QueryTypeEnum; + queryItemType: QueryItemTypeEnum; + keywords?: string; +} + +export interface ReviewListResult { + // 导入数据id + importDataId: number; + // 索赔单号 + claimNo: string; + // 索赔类型 + claimType: number; + // 索赔类型名称 + claimTypeName: string; + // 复核附件 + fid: string; + // 厂家兑现金额 + amount: number; + // 系统生成-总金额 + totalFee: number; + // 系统生成-工时费 + systemManHoursFee: number; + // 系统生成-材料费 + systemPartFee: number; + // 系统生成与厂家差异金额 + differenceAmount: number; + // 提报-总金额 + submitAmount: number; + // 提报-工时费 + submitManHoursFee: number; + // 提报-材料费 + submitPartFee: number; + // 提报-与厂家差异金额 + differenceSubmitAmount: number; + // 上传其他信息:json格式 + importData: string; + // 提报人 + claimer: string; + // 提报商家名称 + submitDealerName: string; + // 提报时间 + commitTime: number; + // VIN + vin: string; + // 车牌号 + plateNo: string; + // 车辆 + carName: string; + // 差异原因 + differenceReasonId: number; + differenceReason: string; + // 是否已复核 1: 未填写差异原因,2: 已填写差异原因 + reviewStatus: number; +} + +export interface DetailListParams { + importDataNo: string; // 导入明细编号,同一文件一致 + claimNo?: string; // 索赔单号 + distributorName?: string; // 经销商 + vin?: string; + claimType?: number; // 索赔类型 + matchStatus?: MatchStatusEnum; // 匹配状态,1:匹配一致,2:无工单记录,3:匹配不一致 + current?: number; + pageSize?: number; +} + +export interface DetailListResult { + id: number; // 复核 id + createTime: number; // 创建时间 + updateTime: number; // 更新时间 + createBy: string; // 创建人 + updateBy: string; // 更新人 + groupId: number; // 集团id + importDataNo: string; // 导入明细编号,同一文件一致 + reviewAmount: number; // 复核后金额 + differenceReason: string; // 差异原因, 1.工时费,材料费差异:2.厂家拒绝:3.漏报:4.其他4s店支付:5.家直接打款到公司账户;6.同一工单分多次提报;7.多个工单一次提报(See: 索赔数据不一致原因) + differenceReasonName: string; // 差异原因名称 + distributorName: string; // 经销商 + claimNo: string; // 索赔单号 + vin: string; // 车架号 + claimType: string; // 索赔类型 + manHoursFee: number; // 工时费 + partFee: number; // 配件费 + rescueFee: number; // 救援费 + totalFee: number; // 总结算费用 + remark: string; // 复核备注 + fids: string; // 附件ids + matchStatus: number; // 匹配状态,1: 匹配一致,2: 匹配不一致,3: 无工单记录(See: 索赔匹配状 +} + +export interface ReviewDetailResult { + claimOrderReviewData: ReviewData; // 复核数据 + claimOrders: ClaimOrder[]; // 索赔单信息 + punishes: ReviewPunish[]; // 处罚信息 +} + +export interface ReviewData { + id: number; // 主键 + createTime: number; // 创建时间 + updateTime: number; // 更新时间 + createBy: string; // 创建人 + updateBy: string; // 更新人 + groupId: number; // 集团id + importDataNo: string; // 导入明细编号,同一文件一致 + reviewAmount: number; // 复核后金额 + differenceReason: string; // 差异原因, 1.工时费,材料费差异;2.厂家拒绝;3.漏报;4.其他4s店支付;5.厂家直接打款到公司账户;6.同一工单分多次提报;7.多个工单一次提报(See: 索赔数据不一致原因) + differenceReasonName: string; // 差异原因名称 + distributorName: string; // 经销商 + claimNo: string; // 索赔单号 + vin: string; // 车架号 + claimType: number; // 索赔类型 + manHoursFee: number; // 工时费 + partFee: number; // 配件费 + rescueFee: number; // 救援费 + totalFee: number; // 总结算费用 + remark: string; // 复核备注 + fids: string; // 附件ids + matchStatus: number; // 匹配状态,1: 匹配一致,2: 匹配不一致,3: 无工单记录(See: 索赔匹配状态) +} + +export interface ClaimOrder { + type: number; // 三包类型(1:首保 2: 三包件5: 市场行动) + claimUsername: number; // 索赔确认/提报员工姓名 + claimSubmitAmount: number; // 提报金额 + claimOrderId: number; // 索赔单id + orderNo: number; // 工单号 + ownerName: number; // 车主姓名 + carName: number; // 车辆名称 + vin: number; // 车辆vin码 + plateNo: number; // 车牌号 + specCode: number; // 车型代码 + receiverName: number; // 接车人姓名 + judgeNames: number; // 故障判断员 +} + +export interface ReviewPunish { + id: number; // 复核id + createTime: number; // 创建时间 + updateTime: number; // 更新时间 + dataId: number; // 索赔复核导入数据id + punishUserId: number; // 处罚人员id + punishUsername: string; // 处罚人员 + punishAmount: number; // 处罚金额 + achievements: number; // 处罚绩效 + punishId: number; // 绩效系统id + shopId: number; + shopName: string; +} + +// 详情 +export function detailApi(applyId: number): Promise { + return http.get(`${host.cas}/erp/claim/review/apply/detail`, { applyId }); +} + +// 复核前数据 +export function dataBeforeReviewApi(params: { applyId: number; type: DimensionEnum }): Promise { + return http.get(`${host.cas}/erp/claim/review/apply/before/review`, params); +} + +// 复核后数据 +export function dataAfterReviewApi(params: { applyId: number; type: DimensionEnum }): Promise { + return http.get(`${host.cas}/erp/claim/review/apply/after/review`, params); +} + +// 处罚信息 +export function punishListApi(applyId: number): Promise { + return http.get(`${host.cas}/erp/claim/review/apply/punish/list`, { applyId }); +} + +// 厂家处罚/奖励信息 1:奖励 2:处罚 +export function factoryPunishOrRewardListApi(params: { applyId: number; type: TypeEnum }): Promise { + return http.get(`${host.cas}/erp/claim/review/apply/factory/info`, params); +} + +// 厂家处罚/奖励类型详情 1:门店分摊,2:门店,3:人员 +export function factoryPunishOrRewardTypeDetailApi(params: { id: number; type: SubTypeEnum }): Promise { + return http.get(`${host.cas}/erp/claim/review/apply/factory/info/detail`, params); +} + +// 复核列表 +export function reviewListApi(params: ReviewListParams): PromisePageResp { + return http.get(`${host.cas}/erp/claim/review/apply/import/data/page`, params); +} + +// 详情明细 +export function detailListApi(params: DetailListParams): PromisePageResp { + return http.get(`${host.cas}/erp/claim/order/review/data/list`, params); +} + +// 复核详情 +export function reviewDetailApi(id: number): Promise { + return http.get(`${host.cas}/erp/claim/order/review/data/${id}`); +} diff --git a/src/routes/demo/page.tsx b/src/routes/demo/page.tsx deleted file mode 100644 index 71797fa..0000000 --- a/src/routes/demo/page.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Helmet } from '@modern-js/runtime/head'; -import { ListRow, Button, Icon, IconFont, Space, PageProvider } from '@feewee/h5app-common'; -import { useSafeState } from 'ahooks'; -import { useEffect } from 'react'; - -const Index = () => { - const [loading, setLoading] = useSafeState(true); - - useEffect(() => { - setTimeout(() => setLoading(false), 2500); - }, []); - - return ( - <> - - {/* 这里面可以映入外部的一些样式或者js之类的,如果有需要的话。也可以调整head里的信息 */} - - - -
-
demo 页面
- console.info('点击事件')} /> - - - - - -
-
- - ); -}; - -export default Index; diff --git a/src/routes/page.tsx b/src/routes/page.tsx index d9f165c..fa5ea5c 100644 --- a/src/routes/page.tsx +++ b/src/routes/page.tsx @@ -1,20 +1,21 @@ -import { Button, Toast } from '@feewee/h5app-common'; -import { Helmet } from '@modern-js/runtime/head'; +import { Button, PageProvider, Toast } from '@feewee/h5app-common'; import { useNavigate } from '@modern-js/runtime/router'; function Index() { const navigate = useNavigate(); return ( - <> - - - +
-
这是首页
-
- +
); } diff --git a/src/style/global.scss b/src/style/global.scss index aa857d8..ab87728 100644 --- a/src/style/global.scss +++ b/src/style/global.scss @@ -51,3 +51,7 @@ body { height: 100%; } } + +.bg-color { + background-color: #f4f6fa; +} diff --git a/src/utils/rmb.ts b/src/utils/rmb.ts new file mode 100644 index 0000000..133ef46 --- /dev/null +++ b/src/utils/rmb.ts @@ -0,0 +1,30 @@ +import currency from 'currency.js'; + +type RMBOption = { price?: number; symbol?: boolean; precision?: boolean }; + +// 人民币格式化 +const RMB = ({ price = 0, symbol = true, precision = true }: RMBOption) => { + return currency(price, { + symbol: symbol ? '¥' : '', + precision: precision ? 2 : 0, + }).format(); +}; + +// 人民币格式化-不包含符号和小数 +const rmb = (price?: number) => RMB({ price, symbol: false, precision: false }); + +// 人民币格式化-包含符号,不包含小数 +const rmbS = (price?: number) => RMB({ price, precision: false }); + +// 人民币格式化-包含小数,不包含符号 +const rmbP = (price?: number) => RMB({ price, symbol: false }); + +// 人民币格式化-包含符号和小数 +const rmbSP = (price?: number) => RMB({ price }); + +export default { + _: rmb, + s: rmbS, + p: rmbP, + sp: rmbSP, +};