Compare commits
10 Commits
362b13ac43
...
debed766d5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
debed766d5 | ||
|
|
abc0a6051d | ||
|
|
513e113ea9 | ||
|
|
07c74c956e | ||
|
|
29f6a6e696 | ||
|
|
e2158286be | ||
|
|
db6a8bc97f | ||
|
|
41ce02512a | ||
|
|
d09bfc4ec6 | ||
|
|
f75e19cf85 |
15
package-lock.json
generated
15
package-lock.json
generated
@@ -76,6 +76,7 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -1835,6 +1836,7 @@
|
|||||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
@@ -1845,6 +1847,7 @@
|
|||||||
"integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==",
|
"integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -1905,6 +1908,7 @@
|
|||||||
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
|
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.47.0",
|
"@typescript-eslint/scope-manager": "8.47.0",
|
||||||
"@typescript-eslint/types": "8.47.0",
|
"@typescript-eslint/types": "8.47.0",
|
||||||
@@ -2153,6 +2157,7 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -2367,6 +2372,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.25",
|
"baseline-browser-mapping": "^2.8.25",
|
||||||
"caniuse-lite": "^1.0.30001754",
|
"caniuse-lite": "^1.0.30001754",
|
||||||
@@ -2775,6 +2781,7 @@
|
|||||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -3888,6 +3895,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -4089,6 +4097,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -4098,6 +4107,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -4493,6 +4503,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -4552,6 +4563,7 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -4645,6 +4657,7 @@
|
|||||||
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
|
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -4738,6 +4751,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -4810,6 +4824,7 @@
|
|||||||
"integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
|
"integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "export NODE_ENV=development && vite --host 0.0.0.0",
|
"dev": "export NODE_ENV=development && vite --host 0.0.0.0 --port 5777",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import { RouterProvider } from 'react-router-dom';
|
|||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return <RouterProvider router={router} />;
|
return (
|
||||||
|
<div className='h-screen max-h-screen overflow-hidden'>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const API_CONFIG = {
|
|||||||
// 开发环境使用内网,生产环境使用外网
|
// 开发环境使用内网,生产环境使用外网
|
||||||
BASE_URL: import.meta.env.NODE_ENV === 'development'
|
BASE_URL: import.meta.env.NODE_ENV === 'development'
|
||||||
? 'http://10.1.5.118:8077/platform-api'
|
? 'http://10.1.5.118:8077/platform-api'
|
||||||
: 'http://apihis.circleharmonyhospital.cn:8982/platform-api',
|
: '/platform-api',
|
||||||
// 请求超时时间(120秒)
|
// 请求超时时间(120秒)
|
||||||
TIMEOUT: 120000,
|
TIMEOUT: 120000,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -548,6 +548,8 @@ export interface InputPhysicalExamAddItem {
|
|||||||
scrm_account_name?: string | null;
|
scrm_account_name?: string | null;
|
||||||
/** 项目名称(默认空值,传入项目名称过滤数据) */
|
/** 项目名称(默认空值,传入项目名称过滤数据) */
|
||||||
item_name?: string;
|
item_name?: string;
|
||||||
|
/** 折扣率 */
|
||||||
|
discount_rate?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -720,11 +722,11 @@ export type PhysicalExamQrcodeCreateResponse = CommonActionResult<string>;
|
|||||||
*/
|
*/
|
||||||
export interface InputAddItemCombinationInfo {
|
export interface InputAddItemCombinationInfo {
|
||||||
/** 体检组合项目代码 */
|
/** 体检组合项目代码 */
|
||||||
combination_item_code: string;
|
combination_item_code?: string | null;
|
||||||
/** 体检组合项目价格 */
|
/** 体检组合项目价格 */
|
||||||
combination_item_price: number;
|
combination_item_price?: number | null;
|
||||||
/** 折扣比例 */
|
/** 折扣比例 */
|
||||||
discount_rate: number;
|
discount_rate?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -741,6 +743,8 @@ export interface InputOrderPaymentInfo {
|
|||||||
pay_type: number;
|
pay_type: number;
|
||||||
/** 挂账公司ID(挂账公司传对应的ID,其他传0) */
|
/** 挂账公司ID(挂账公司传对应的ID,其他传0) */
|
||||||
company_id: number;
|
company_id: number;
|
||||||
|
/** 订单总金额 */
|
||||||
|
orderAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1396,6 +1400,15 @@ export interface InputCustomSettlementApplyApprove {
|
|||||||
add_item_id?: string;
|
add_item_id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 体检加项自定义结算申请-加项组合项
|
||||||
|
*/
|
||||||
|
export interface OutputCustomSettlementApplyApproveItem {
|
||||||
|
combination_item_code?: string | null;
|
||||||
|
combination_item_price?: number | null;
|
||||||
|
discount_rate?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 体检加项自定义结算申请状态编辑出参
|
* 体检加项自定义结算申请状态编辑出参
|
||||||
*/
|
*/
|
||||||
@@ -1408,6 +1421,12 @@ export interface OutputCustomSettlementApplyApprove {
|
|||||||
apply_status_name?: string | null;
|
apply_status_name?: string | null;
|
||||||
/** 最终结算金额 */
|
/** 最终结算金额 */
|
||||||
final_settlement_price?: number | null;
|
final_settlement_price?: number | null;
|
||||||
|
/** 申请理由 */
|
||||||
|
apply_reason?: string;
|
||||||
|
/** 折扣 */
|
||||||
|
discount_ratio?: number | null;
|
||||||
|
/** 加项组合列表 */
|
||||||
|
listAddItemCombination?: OutputCustomSettlementApplyApproveItem[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1427,6 +1446,8 @@ export interface InputAddItemCustomSettlementDetail {
|
|||||||
original_price: number;
|
original_price: number;
|
||||||
/** 结算金额 */
|
/** 结算金额 */
|
||||||
settlement_price: number;
|
settlement_price: number;
|
||||||
|
/** 折扣率 */
|
||||||
|
discount_ratio?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1447,6 +1468,8 @@ export interface InputCustomSettlementApply {
|
|||||||
final_settlement_price?: number;
|
final_settlement_price?: number;
|
||||||
/** 申请理由 */
|
/** 申请理由 */
|
||||||
apply_reason?: string;
|
apply_reason?: string;
|
||||||
|
/** 申请人 */
|
||||||
|
apply_user: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1470,6 +1493,8 @@ export interface InputCustomSettlementApplyCancel {
|
|||||||
physical_exam_id: number;
|
physical_exam_id: number;
|
||||||
/** 体检加项组合ID(多个逗号分隔,例如:123,456) */
|
/** 体检加项组合ID(多个逗号分隔,例如:123,456) */
|
||||||
add_item_id: string;
|
add_item_id: string;
|
||||||
|
/** 申请人 */
|
||||||
|
cancel_user: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState, useRef, useCallback } from 'react';
|
|||||||
|
|
||||||
import type { ExamClient } from '../../data/mockData';
|
import type { ExamClient } from '../../data/mockData';
|
||||||
import { searchPhysicalExamAddItem, getAddItemCustomerInfo, getChannelCompanyList, createNativePaymentQrcode, checkNativePaymentStatus, getAddItemBillPdf, getCustomSettlementApproveStatus, customSettlementApply, customSettlementApplyCancel } from '../../api';
|
import { searchPhysicalExamAddItem, getAddItemCustomerInfo, getChannelCompanyList, createNativePaymentQrcode, checkNativePaymentStatus, getAddItemBillPdf, getCustomSettlementApproveStatus, customSettlementApply, customSettlementApplyCancel } from '../../api';
|
||||||
|
import type { InputAddItemCombinationInfo, OutputCustomSettlementApplyApproveItem } from '../../api/types';
|
||||||
import { Button, Input } from '../ui';
|
import { Button, Input } from '../ui';
|
||||||
import { cls } from '../../utils/cls';
|
import { cls } from '../../utils/cls';
|
||||||
import nozImage from '../../assets/image/noz.png';
|
import nozImage from '../../assets/image/noz.png';
|
||||||
@@ -20,6 +21,7 @@ interface AddonItem {
|
|||||||
combinationItemCode?: number | null;
|
combinationItemCode?: number | null;
|
||||||
isEnjoyDiscount?: number | null;
|
isEnjoyDiscount?: number | null;
|
||||||
discount_name?: string | null;
|
discount_name?: string | null;
|
||||||
|
discount_ratio?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExamAddonPanelProps {
|
interface ExamAddonPanelProps {
|
||||||
@@ -29,6 +31,17 @@ interface ExamAddonPanelProps {
|
|||||||
|
|
||||||
export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
||||||
const [addonList, setAddonList] = useState<AddonItem[]>([]);
|
const [addonList, setAddonList] = useState<AddonItem[]>([]);
|
||||||
|
const allAddons = useMemo(() => addonList, [addonList]);
|
||||||
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
const currentAddItemId = useMemo(() => {
|
||||||
|
const currentSelectedItems = allAddons.filter(item => selectedIds.has(item.id || item.name));
|
||||||
|
return currentSelectedItems
|
||||||
|
.map(item => item.combinationItemCode)
|
||||||
|
.filter((code): code is number => code !== null && code !== undefined)
|
||||||
|
.join(',');
|
||||||
|
}, [allAddons, selectedIds]);
|
||||||
|
|
||||||
// 防抖:内部输入值(用于显示)
|
// 防抖:内部输入值(用于显示)
|
||||||
const [addonSearchInput, setAddonSearchInput] = useState('');
|
const [addonSearchInput, setAddonSearchInput] = useState('');
|
||||||
// 防抖:实际用于 API 调用的值(延迟更新)
|
// 防抖:实际用于 API 调用的值(延迟更新)
|
||||||
@@ -82,15 +95,51 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
apply_status?: number;
|
apply_status?: number;
|
||||||
apply_status_name?: string | null;
|
apply_status_name?: string | null;
|
||||||
final_settlement_price?: number | null;
|
final_settlement_price?: number | null;
|
||||||
|
apply_reason?: string | null;
|
||||||
|
settlement_type?: number | null;
|
||||||
|
discount_ratio?: number | null;
|
||||||
|
add_item_id?: string | null;
|
||||||
|
listAddItemCombination?: OutputCustomSettlementApplyApproveItem[] | null;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [customSettlementLoading, setCustomSettlementLoading] = useState(false);
|
const [customSettlementLoading, setCustomSettlementLoading] = useState(false);
|
||||||
const [customSettlementType, setCustomSettlementType] = useState<1 | 2>(1); // 1-按比例折扣 2-自定义结算价
|
const [customSettlementType, setCustomSettlementType] = useState<1 | 2>(1); // 1-按比例折扣 2-自定义结算价
|
||||||
const [customDiscountRatio, setCustomDiscountRatio] = useState<number>(100); // 折扣比例(如100代表10折,即原价)
|
const [customDiscountRatio, setCustomDiscountRatio] = useState<number | null>(null); // 折扣比例(如100代表10折,即原价)
|
||||||
const [customFinalPrice, setCustomFinalPrice] = useState<number>(0); // 最终结算价
|
const [customFinalPrice, setCustomFinalPrice] = useState<number | null>(null); // 最终结算价
|
||||||
const [customApplyReason, setCustomApplyReason] = useState<string>(''); // 申请理由
|
const [customApplyReason, setCustomApplyReason] = useState<string>(''); // 申请理由
|
||||||
const [waitingSeconds, setWaitingSeconds] = useState<number>(0); // 等待审核的秒数
|
const [waitingSeconds, setWaitingSeconds] = useState<number>(0); // 等待审核的秒数
|
||||||
const waitingTimerRef = useRef<number | null>(null); // 等待计时器
|
const waitingTimerRef = useRef<number | null>(null); // 等待计时器
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showCustomSettlementModal && customSettlementStatus) {
|
||||||
|
// 已通过(3) 或 已拒绝(4) 且 add_item_id 匹配才需要回显
|
||||||
|
if ((customSettlementStatus.apply_status === 3 || customSettlementStatus.apply_status === 4) &&
|
||||||
|
customSettlementStatus.add_item_id === currentAddItemId) {
|
||||||
|
// 如果 discount_ratio 为 0,说明是自定义结算价模式
|
||||||
|
if (customSettlementStatus.discount_ratio === 0) {
|
||||||
|
setCustomSettlementType(2);
|
||||||
|
} else if (customSettlementStatus.settlement_type === 1 || customSettlementStatus.settlement_type === 2) {
|
||||||
|
setCustomSettlementType(customSettlementStatus.settlement_type as 1 | 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof customSettlementStatus.discount_ratio === 'number') {
|
||||||
|
setCustomDiscountRatio(customSettlementStatus.discount_ratio || null);
|
||||||
|
}
|
||||||
|
if (typeof customSettlementStatus.final_settlement_price === 'number') {
|
||||||
|
setCustomFinalPrice(customSettlementStatus.final_settlement_price);
|
||||||
|
}
|
||||||
|
if (customSettlementStatus.apply_reason) {
|
||||||
|
setCustomApplyReason(customSettlementStatus.apply_reason);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 其他状态(如取消后再点开)重置表单为默认值
|
||||||
|
setCustomSettlementType(1);
|
||||||
|
setCustomDiscountRatio(null);
|
||||||
|
setCustomFinalPrice(null);
|
||||||
|
setCustomApplyReason('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [showCustomSettlementModal, customSettlementStatus, currentAddItemId]);
|
||||||
|
|
||||||
// 点击外部关闭下拉框
|
// 点击外部关闭下拉框
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
@@ -123,24 +172,15 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
try {
|
try {
|
||||||
const res = await getAddItemCustomerInfo({ physical_exam_id });
|
const res = await getAddItemCustomerInfo({ physical_exam_id });
|
||||||
if (res.Status === 200) {
|
if (res.Status === 200) {
|
||||||
// 保存客户信息
|
// 保存客户信息,渠道信息使用 listChannelDiscount 中的 channel_id / channel_name
|
||||||
if (res.Data?.customerInfo) {
|
if (res.Data?.customerInfo) {
|
||||||
const channelInfo = res.Data.listChannelDiscount?.[0];
|
const firstChannel = res.Data.listChannelDiscount?.[0];
|
||||||
const scrm_account_id =
|
|
||||||
res.Data.customerInfo.scrm_account_id ??
|
|
||||||
channelInfo?.channel_id ??
|
|
||||||
null;
|
|
||||||
const scrm_account_name =
|
|
||||||
res.Data.customerInfo.scrm_account_name ??
|
|
||||||
channelInfo?.channel_name ??
|
|
||||||
null;
|
|
||||||
|
|
||||||
setCustomerInfo({
|
setCustomerInfo({
|
||||||
patient_id: res.Data.customerInfo.patient_id,
|
patient_id: res.Data.customerInfo.patient_id,
|
||||||
customer_name: res.Data.customerInfo.customer_name,
|
customer_name: res.Data.customerInfo.customer_name,
|
||||||
phone: res.Data.customerInfo.phone,
|
phone: res.Data.customerInfo.phone,
|
||||||
scrm_account_id,
|
scrm_account_id: firstChannel?.channel_id ?? null,
|
||||||
scrm_account_name,
|
scrm_account_name: firstChannel?.channel_name ?? null,
|
||||||
});
|
});
|
||||||
customerInfoLoadedRef.current = true;
|
customerInfoLoadedRef.current = true;
|
||||||
// 设置挂账公司默认值
|
// 设置挂账公司默认值
|
||||||
@@ -237,11 +277,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
setAddonLoading(true);
|
setAddonLoading(true);
|
||||||
setAddonError(null);
|
setAddonError(null);
|
||||||
try {
|
try {
|
||||||
|
console.log("请求数据 1", customerInfo, customerInfo?.scrm_account_id);
|
||||||
|
|
||||||
|
const selectedChannel = channelDiscounts.find(
|
||||||
|
(item) => item.discount_rate === discountRatio
|
||||||
|
);
|
||||||
|
const discountRate = selectedChannel?.discount_rate ?? null;
|
||||||
|
|
||||||
const res = await searchPhysicalExamAddItem({
|
const res = await searchPhysicalExamAddItem({
|
||||||
physical_exam_id: Number(client.id),
|
physical_exam_id: Number(client.id),
|
||||||
scrm_account_id: customerInfo?.scrm_account_id || null,
|
scrm_account_id: customerInfo?.scrm_account_id || null,
|
||||||
scrm_account_name: customerInfo?.scrm_account_name || null,
|
scrm_account_name: customerInfo?.scrm_account_name || null,
|
||||||
item_name: debouncedAddonSearch.trim() || "",
|
item_name: debouncedAddonSearch.trim() || "",
|
||||||
|
discount_rate: discountRate,
|
||||||
});
|
});
|
||||||
if (res.Status === 200 && Array.isArray(res.Data)) {
|
if (res.Status === 200 && Array.isArray(res.Data)) {
|
||||||
const list: AddonItem[] = res.Data.map((item) => ({
|
const list: AddonItem[] = res.Data.map((item) => ({
|
||||||
@@ -254,12 +302,11 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
currentPrice:
|
currentPrice:
|
||||||
item.actual_received_amount !== undefined && item.actual_received_amount !== null
|
item.actual_received_amount !== undefined && item.actual_received_amount !== null
|
||||||
? Number(item.actual_received_amount).toFixed(2)
|
? Number(item.actual_received_amount).toFixed(2)
|
||||||
: item.original_price !== undefined && item.original_price !== null
|
|
||||||
? Number(item.original_price).toFixed(2)
|
|
||||||
: '0.00',
|
: '0.00',
|
||||||
combinationItemCode: item.combination_item_code ?? null,
|
combinationItemCode: item.combination_item_code ?? null,
|
||||||
isEnjoyDiscount: item.is_enjoy_discount ?? null,
|
isEnjoyDiscount: item.is_enjoy_discount ?? null,
|
||||||
discount_name: item.discount_rate ?? null,
|
discount_name: item.discount_rate ?? null,
|
||||||
|
discount_ratio: item.discount_ratio ?? null,
|
||||||
tags: [],
|
tags: [],
|
||||||
paid: false,
|
paid: false,
|
||||||
}));
|
}));
|
||||||
@@ -277,11 +324,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchList();
|
fetchList();
|
||||||
}, [debouncedAddonSearch, customerInfo?.scrm_account_id, customerInfo?.scrm_account_name, client.id, customerInfo]);
|
}, [debouncedAddonSearch, customerInfo?.scrm_account_id, customerInfo?.scrm_account_name, client.id, customerInfo, channelDiscounts, discountRatio]);
|
||||||
|
|
||||||
const allAddons = useMemo(() => addonList, [addonList]);
|
|
||||||
|
|
||||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
|
||||||
|
|
||||||
const maxSelect = 15;
|
const maxSelect = 15;
|
||||||
const selectedCount = selectedIds.size;
|
const selectedCount = selectedIds.size;
|
||||||
@@ -337,14 +380,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
return item.discount_name;
|
return item.discount_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 构建折扣选项列表
|
// 构建折扣选项列表(带上渠道ID/名称,便于联动设置 scrm_account_id / scrm_account_name)
|
||||||
const discountOptions = useMemo(() => {
|
const discountOptions = useMemo(() => {
|
||||||
const options: Array<{ value: number; label: string }> = [];
|
const options: Array<{ value: number; label: string; channel_id?: string | null; channel_name?: string | null }> = [];
|
||||||
channelDiscounts.forEach((item) => {
|
channelDiscounts.forEach((item) => {
|
||||||
const rate = typeof item.discount_rate === 'number' && item.discount_rate > 0 ? item.discount_rate : 1;
|
const rate = typeof item.discount_rate === 'number' && item.discount_rate > 0 ? item.discount_rate : 1;
|
||||||
const percent = Math.round(rate * 100);
|
const percent = Math.round(rate * 100);
|
||||||
const label = item.discount_name || `${percent}%`;
|
const label = item.discount_name || `${percent}%`;
|
||||||
options.push({ value: rate, label });
|
options.push({
|
||||||
|
value: rate,
|
||||||
|
label,
|
||||||
|
channel_id: item.channel_id ?? null,
|
||||||
|
channel_name: item.channel_name ?? null,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return options;
|
return options;
|
||||||
}, [channelDiscounts]);
|
}, [channelDiscounts]);
|
||||||
@@ -355,9 +403,21 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
return option?.label;
|
return option?.label;
|
||||||
}, [discountRatio, discountOptions]);
|
}, [discountRatio, discountOptions]);
|
||||||
|
|
||||||
// 处理折扣选择
|
// 处理折扣选择,同时更新 scrm_account_id / scrm_account_name
|
||||||
const handleDiscountSelect = (value: number) => {
|
const handleDiscountSelect = (value: number) => {
|
||||||
setDiscountRatio(value);
|
setDiscountRatio(value);
|
||||||
|
const matched = discountOptions.find(opt => opt.value === value);
|
||||||
|
if (matched && (matched.channel_id || matched.channel_name)) {
|
||||||
|
setCustomerInfo((prev) =>
|
||||||
|
prev
|
||||||
|
? {
|
||||||
|
...prev,
|
||||||
|
scrm_account_id: matched.channel_id ?? null,
|
||||||
|
scrm_account_name: matched.channel_name ?? null,
|
||||||
|
}
|
||||||
|
: prev
|
||||||
|
);
|
||||||
|
}
|
||||||
setIsDiscountDropdownOpen(false);
|
setIsDiscountDropdownOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -419,19 +479,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
// 获取自定义结算审批状态(带节流,最多每秒一次)
|
// 获取自定义结算审批状态(带节流,最多每秒一次)
|
||||||
const fetchCustomSettlementStatus = useCallback(async () => {
|
const fetchCustomSettlementStatus = useCallback(async () => {
|
||||||
const physical_exam_id = Number(client.id);
|
const physical_exam_id = Number(client.id);
|
||||||
const currentSelectedItems = allAddons.filter(item => selectedIds.has(item.id || item.name));
|
|
||||||
|
|
||||||
if (!physical_exam_id || currentSelectedItems.length === 0) {
|
if (!physical_exam_id || !currentAddItemId) {
|
||||||
setCustomSettlementStatus(null);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const add_item_id = currentSelectedItems
|
|
||||||
.map(item => item.combinationItemCode)
|
|
||||||
.filter((code): code is number => code !== null && code !== undefined)
|
|
||||||
.join(',');
|
|
||||||
|
|
||||||
if (!add_item_id) {
|
|
||||||
setCustomSettlementStatus(null);
|
setCustomSettlementStatus(null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -440,7 +489,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const timeSinceLastCall = now - lastFetchStatusTimeRef.current;
|
const timeSinceLastCall = now - lastFetchStatusTimeRef.current;
|
||||||
if (timeSinceLastCall < 800) {
|
if (timeSinceLastCall < 800) {
|
||||||
// 返回当前状态是否需要轮询
|
// 从 ref 获取当前状态判断是否需要轮询
|
||||||
return customSettlementStatus?.apply_status === 1;
|
return customSettlementStatus?.apply_status === 1;
|
||||||
}
|
}
|
||||||
lastFetchStatusTimeRef.current = now;
|
lastFetchStatusTimeRef.current = now;
|
||||||
@@ -448,19 +497,28 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
try {
|
try {
|
||||||
const res = await getCustomSettlementApproveStatus({
|
const res = await getCustomSettlementApproveStatus({
|
||||||
physical_exam_id,
|
physical_exam_id,
|
||||||
add_item_id,
|
add_item_id: currentAddItemId,
|
||||||
});
|
});
|
||||||
if (res.Status === 200 && res.Data) {
|
if (res.Status === 200 && res.Data) {
|
||||||
const status = {
|
const status = {
|
||||||
apply_status: res.Data.apply_status,
|
apply_status: res.Data.apply_status,
|
||||||
apply_status_name: res.Data.apply_status_name,
|
apply_status_name: res.Data.apply_status_name,
|
||||||
final_settlement_price: res.Data.final_settlement_price,
|
final_settlement_price: res.Data.final_settlement_price,
|
||||||
|
apply_reason: (res.Data as any).apply_reason,
|
||||||
|
settlement_type: (res.Data as any).settlement_type,
|
||||||
|
discount_ratio: (res.Data as any).discount_ratio,
|
||||||
|
add_item_id: currentAddItemId,
|
||||||
|
listAddItemCombination: res.Data.listAddItemCombination,
|
||||||
};
|
};
|
||||||
setCustomSettlementStatus(status);
|
|
||||||
// 如果状态变为审核中,重置等待时间
|
// 获取旧状态用于判断是否刚进入审核中
|
||||||
if (res.Data.apply_status === 1 && customSettlementStatus?.apply_status !== 1) {
|
setCustomSettlementStatus(prev => {
|
||||||
|
if (status.apply_status === 1 && prev?.apply_status !== 1) {
|
||||||
setWaitingSeconds(0);
|
setWaitingSeconds(0);
|
||||||
}
|
}
|
||||||
|
return status;
|
||||||
|
});
|
||||||
|
|
||||||
// 返回是否需要继续轮询(1-审核中 需要轮询)
|
// 返回是否需要继续轮询(1-审核中 需要轮询)
|
||||||
return res.Data.apply_status === 1;
|
return res.Data.apply_status === 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -472,12 +530,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
setCustomSettlementStatus(null);
|
setCustomSettlementStatus(null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}, [client.id, selectedIds, allAddons, customSettlementStatus]);
|
}, [client.id, currentAddItemId]);
|
||||||
|
|
||||||
// 当选中加项变化时,获取审批状态
|
// 当选中加项变化时,获取审批状态
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentSelectedItems = allAddons.filter(item => selectedIds.has(item.id || item.name));
|
// 切换选中项时,先清零状态,避免错误回显旧数据
|
||||||
if (currentSelectedItems.length > 0) {
|
setCustomSettlementStatus(null);
|
||||||
|
setCustomSettlementType(1);
|
||||||
|
setCustomDiscountRatio(null);
|
||||||
|
setCustomFinalPrice(null);
|
||||||
|
setCustomApplyReason('');
|
||||||
|
lastFetchStatusTimeRef.current = 0; // 强制立即发起新请求
|
||||||
|
|
||||||
|
if (currentAddItemId) {
|
||||||
fetchCustomSettlementStatus().then((shouldPoll) => {
|
fetchCustomSettlementStatus().then((shouldPoll) => {
|
||||||
// 如果需要轮询(审核中),开始轮询
|
// 如果需要轮询(审核中),开始轮询
|
||||||
if (shouldPoll) {
|
if (shouldPoll) {
|
||||||
@@ -487,10 +552,9 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setCustomSettlementStatus(null);
|
|
||||||
stopCustomSettlementPolling();
|
stopCustomSettlementPolling();
|
||||||
}
|
}
|
||||||
}, [selectedIds, client.id, allAddons, fetchCustomSettlementStatus]);
|
}, [currentAddItemId, client.id]); // 移除 fetchCustomSettlementStatus 依赖,避免由于状态更新导致的死循环
|
||||||
|
|
||||||
// 开始轮询自定义结算审批状态
|
// 开始轮询自定义结算审批状态
|
||||||
const startCustomSettlementPolling = useCallback(() => {
|
const startCustomSettlementPolling = useCallback(() => {
|
||||||
@@ -505,7 +569,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
stopCustomSettlementPolling();
|
stopCustomSettlementPolling();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1500);
|
||||||
}, [fetchCustomSettlementStatus]);
|
}, [fetchCustomSettlementStatus]);
|
||||||
|
|
||||||
// 停止轮询自定义结算审批状态
|
// 停止轮询自定义结算审批状态
|
||||||
@@ -550,6 +614,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
|
|
||||||
// 提交自定义结算申请
|
// 提交自定义结算申请
|
||||||
const handleSubmitCustomSettlement = async () => {
|
const handleSubmitCustomSettlement = async () => {
|
||||||
|
|
||||||
const physical_exam_id = Number(client.id);
|
const physical_exam_id = Number(client.id);
|
||||||
if (!physical_exam_id || selectedItems.length === 0) {
|
if (!physical_exam_id || selectedItems.length === 0) {
|
||||||
setPaymentMessage('请先选择加项项目');
|
setPaymentMessage('请先选择加项项目');
|
||||||
@@ -561,6 +626,12 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const discountRatioValue = customSettlementType === 1 ? (customDiscountRatio ?? 0) : 0;
|
||||||
|
if (customSettlementType === 1 && (discountRatioValue <= 0 || discountRatioValue > 100)) {
|
||||||
|
setPaymentMessage('请输入有效折扣比例');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setCustomSettlementLoading(true);
|
setCustomSettlementLoading(true);
|
||||||
setPaymentMessage(null);
|
setPaymentMessage(null);
|
||||||
|
|
||||||
@@ -573,41 +644,52 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const originalPrice = parseFloat(item.originalPrice || '0');
|
const originalPrice = parseFloat(item.originalPrice || '0');
|
||||||
let settlementPrice = originalPrice;
|
|
||||||
|
|
||||||
if (customSettlementType === 1) {
|
// if (customSettlementType === 1) {
|
||||||
// 按比例折扣
|
// // 按比例折扣
|
||||||
settlementPrice = originalPrice * (customDiscountRatio / 100);
|
// settlementPrice = originalPrice * (discountRatioValue / 100);
|
||||||
} else {
|
// } else {
|
||||||
// 自定义结算价
|
// // 自定义结算价
|
||||||
settlementPrice = customFinalPrice / selectedItems.length; // 平均分配
|
// settlementPrice = (customFinalPrice ?? 0) / selectedItems.length; // 平均分配
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
const discount_ratio = (() => {
|
||||||
|
const r = item.discount_ratio;
|
||||||
|
if (r == null || typeof r !== 'number') return 0;
|
||||||
|
return r > 1 ? r : Math.round(r * 100);
|
||||||
|
})();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
combination_item_code: String(combinationItemCode),
|
combination_item_code: String(combinationItemCode),
|
||||||
combination_item_name: item.name,
|
combination_item_name: item.name,
|
||||||
original_price: originalPrice,
|
original_price: originalPrice,
|
||||||
settlement_price: settlementPrice,
|
settlement_price: originalPrice * (discount_ratio / 100),
|
||||||
|
discount_ratio: discount_ratio,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((item): item is { combination_item_code: string; combination_item_name: string; original_price: number; settlement_price: number } => item !== null);
|
.filter((item): item is { combination_item_code: string; combination_item_name: string; original_price: number; settlement_price: number; discount_ratio: number } => item !== null);
|
||||||
|
|
||||||
const original_settlement_price = selectedItems.reduce((sum, item) => {
|
const original_settlement_price = listAddItemDetail.reduce((sum, item) => sum + item.original_price, 0);
|
||||||
return sum + parseFloat(item.currentPrice || item.originalPrice || '0');
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
const final_settlement_price = customSettlementType === 1
|
const final_settlement_price = customSettlementType === 1
|
||||||
? original_settlement_price * (customDiscountRatio / 100)
|
? totalCurrent * ((customDiscountRatio ?? 0) / 100)
|
||||||
: customFinalPrice;
|
: (customFinalPrice ?? 0);
|
||||||
|
|
||||||
|
const apply_user = localStorage.getItem('operatorName');
|
||||||
|
if (!apply_user) {
|
||||||
|
alert('请先登录');
|
||||||
|
window.location.href = '/home';
|
||||||
|
return;
|
||||||
|
}
|
||||||
const res = await customSettlementApply({
|
const res = await customSettlementApply({
|
||||||
physical_exam_id,
|
physical_exam_id,
|
||||||
listAddItemDetail,
|
listAddItemDetail,
|
||||||
original_settlement_price,
|
original_settlement_price,
|
||||||
settlement_type: customSettlementType,
|
settlement_type: customSettlementType,
|
||||||
discount_ratio: customSettlementType === 1 ? customDiscountRatio : undefined,
|
discount_ratio: customSettlementType === 1 ? discountRatioValue : undefined,
|
||||||
final_settlement_price,
|
final_settlement_price: final_settlement_price ?? undefined,
|
||||||
apply_reason: customApplyReason.trim(),
|
apply_reason: customApplyReason.trim(),
|
||||||
|
apply_user,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.Status === 200) {
|
if (res.Status === 200) {
|
||||||
@@ -616,7 +698,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
// 重置表单
|
// 重置表单
|
||||||
setCustomSettlementType(1);
|
setCustomSettlementType(1);
|
||||||
setCustomDiscountRatio(100);
|
setCustomDiscountRatio(100);
|
||||||
setCustomFinalPrice(0);
|
setCustomFinalPrice(null);
|
||||||
setCustomApplyReason('');
|
setCustomApplyReason('');
|
||||||
// 重新获取审批状态并开始轮询
|
// 重新获取审批状态并开始轮询
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -649,6 +731,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopCustomSettlementPolling();
|
||||||
setCustomSettlementLoading(true);
|
setCustomSettlementLoading(true);
|
||||||
setPaymentMessage(null);
|
setPaymentMessage(null);
|
||||||
|
|
||||||
@@ -663,13 +746,21 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cancel_user = localStorage.getItem('operatorName');
|
||||||
|
if (!cancel_user) {
|
||||||
|
setPaymentMessage('请先登录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await customSettlementApplyCancel({
|
const res = await customSettlementApplyCancel({
|
||||||
physical_exam_id,
|
physical_exam_id,
|
||||||
add_item_id,
|
add_item_id,
|
||||||
|
cancel_user,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.Status === 200 && res.Data?.is_success === 1) {
|
if (res.Status === 200 && res.Data?.is_success === 1) {
|
||||||
setPaymentMessage('取消申请成功');
|
setPaymentMessage('取消申请成功');
|
||||||
|
setCustomDiscountRatio(null);
|
||||||
// 停止轮询
|
// 停止轮询
|
||||||
stopCustomSettlementPolling();
|
stopCustomSettlementPolling();
|
||||||
// 重新获取审批状态
|
// 重新获取审批状态
|
||||||
@@ -712,14 +803,11 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
const startPaymentPolling = (
|
const startPaymentPolling = (
|
||||||
physical_exam_id: number,
|
physical_exam_id: number,
|
||||||
patient_id: number,
|
patient_id: number,
|
||||||
listAddItemCombination: Array<{
|
listAddItemCombination: InputAddItemCombinationInfo[],
|
||||||
combination_item_code: string;
|
|
||||||
combination_item_price: number;
|
|
||||||
discount_rate: number;
|
|
||||||
}>,
|
|
||||||
pay_type: number,
|
pay_type: number,
|
||||||
company_id: number,
|
company_id: number,
|
||||||
combinationItemCodes: string
|
combinationItemCodes: string,
|
||||||
|
orderAmount: number
|
||||||
) => {
|
) => {
|
||||||
if (pollingTimerRef.current) {
|
if (pollingTimerRef.current) {
|
||||||
clearInterval(pollingTimerRef.current);
|
clearInterval(pollingTimerRef.current);
|
||||||
@@ -733,6 +821,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
listAddItemCombination,
|
listAddItemCombination,
|
||||||
pay_type,
|
pay_type,
|
||||||
company_id,
|
company_id,
|
||||||
|
orderAmount,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.Status === 200) {
|
if (res.Status === 200) {
|
||||||
@@ -805,26 +894,32 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
const selectedItems = allAddons.filter((item) => selectedIds.has(item.id || item.name));
|
const selectedItems = allAddons.filter((item) => selectedIds.has(item.id || item.name));
|
||||||
|
|
||||||
// 构建加项组合项目信息列表
|
// 构建加项组合项目信息列表
|
||||||
const listAddItemCombination = selectedItems
|
let listAddItemCombination: InputAddItemCombinationInfo[] = [];
|
||||||
|
|
||||||
|
// 如果自定义结算审核通过,直接使用接口返回的项目列表
|
||||||
|
if (customSettlementStatus?.apply_status === 3 && customSettlementStatus.listAddItemCombination) {
|
||||||
|
listAddItemCombination = customSettlementStatus.listAddItemCombination.map((item: OutputCustomSettlementApplyApproveItem) => ({
|
||||||
|
combination_item_code: item.combination_item_code,
|
||||||
|
combination_item_price: item.combination_item_price,
|
||||||
|
discount_rate: item.discount_rate
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
listAddItemCombination = selectedItems
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
const combinationItemCode = item.combinationItemCode;
|
const combinationItemCode = item.combinationItemCode;
|
||||||
if (combinationItemCode === null || combinationItemCode === undefined) {
|
if (combinationItemCode === null || combinationItemCode === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let combinationItemPrice = parseFloat(item.currentPrice || item.originalPrice || '0');
|
const combinationItemPrice = parseFloat(item.currentPrice || item.originalPrice || '0');
|
||||||
|
|
||||||
// 如果自定义结算审核通过,传申请后的均价(按项平摊)
|
|
||||||
if (customSettlementStatus?.apply_status === 3 && typeof customSettlementStatus.final_settlement_price === 'number') {
|
|
||||||
combinationItemPrice = customSettlementStatus.final_settlement_price / selectedItems.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
combination_item_code: String(combinationItemCode),
|
combination_item_code: String(combinationItemCode),
|
||||||
combination_item_price: combinationItemPrice,
|
combination_item_price: combinationItemPrice,
|
||||||
discount_rate: discountRatio,
|
discount_rate: discountRatio,
|
||||||
};
|
} as InputAddItemCombinationInfo;
|
||||||
})
|
})
|
||||||
.filter((item): item is { combination_item_code: string; combination_item_price: number; discount_rate: number } => item !== null);
|
.filter((item): item is InputAddItemCombinationInfo => item !== null);
|
||||||
|
}
|
||||||
|
|
||||||
if (listAddItemCombination.length === 0) {
|
if (listAddItemCombination.length === 0) {
|
||||||
setPaymentMessage('缺少加项信息,请稍后重试');
|
setPaymentMessage('缺少加项信息,请稍后重试');
|
||||||
@@ -835,6 +930,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
// 获取组合项目代码(用于生成二维码接口 & 生成加项单,多个加项逗号分隔)
|
// 获取组合项目代码(用于生成二维码接口 & 生成加项单,多个加项逗号分隔)
|
||||||
const combinationItemCodes = listAddItemCombination
|
const combinationItemCodes = listAddItemCombination
|
||||||
.map((item) => item.combination_item_code)
|
.map((item) => item.combination_item_code)
|
||||||
|
.filter(Boolean)
|
||||||
.join(',');
|
.join(',');
|
||||||
|
|
||||||
// 获取 patient_id(必须从接口返回的客户信息中获取)
|
// 获取 patient_id(必须从接口返回的客户信息中获取)
|
||||||
@@ -846,6 +942,20 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
const patient_id = customerInfo.patient_id;
|
const patient_id = customerInfo.patient_id;
|
||||||
|
|
||||||
if (paymentMethod === 'self') {
|
if (paymentMethod === 'self') {
|
||||||
|
// 自费模式:如果是 0 元支付,直接查询状态,不生成二维码
|
||||||
|
if (totalCurrent === 0) {
|
||||||
|
startPaymentPolling(
|
||||||
|
physical_exam_id,
|
||||||
|
patient_id,
|
||||||
|
listAddItemCombination,
|
||||||
|
12, // 微信支付
|
||||||
|
0, // 自费模式,company_id 传 0
|
||||||
|
combinationItemCodes,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 自费模式:生成二维码
|
// 自费模式:生成二维码
|
||||||
const res = await createNativePaymentQrcode({
|
const res = await createNativePaymentQrcode({
|
||||||
physical_exam_id,
|
physical_exam_id,
|
||||||
@@ -870,7 +980,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
listAddItemCombination,
|
listAddItemCombination,
|
||||||
12, // 微信支付
|
12, // 微信支付
|
||||||
0, // 自费模式,company_id 传 0
|
0, // 自费模式,company_id 传 0
|
||||||
combinationItemCodes
|
combinationItemCodes,
|
||||||
|
totalCurrent
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setPaymentMessage(res.Message || '生成支付二维码失败');
|
setPaymentMessage(res.Message || '生成支付二维码失败');
|
||||||
@@ -902,6 +1013,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
listAddItemCombination,
|
listAddItemCombination,
|
||||||
pay_type: 13, // 挂账公司
|
pay_type: 13, // 挂账公司
|
||||||
company_id: selectedCompany.company_id,
|
company_id: selectedCompany.company_id,
|
||||||
|
orderAmount: totalCurrent,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.Status === 200) {
|
if (res.Status === 200) {
|
||||||
@@ -939,6 +1051,10 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isApprovedOrRejected =
|
||||||
|
(customSettlementStatus?.apply_status === 3 || customSettlementStatus?.apply_status === 4) &&
|
||||||
|
customSettlementStatus?.add_item_id === currentAddItemId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='space-y-4'>
|
<div className='space-y-4'>
|
||||||
{/* 标题和说明 */}
|
{/* 标题和说明 */}
|
||||||
@@ -1011,8 +1127,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 加项网格 */}
|
{/* 加项网格 */}
|
||||||
<div className='overflow-y-auto overflow-x-hidden max-h-[366px]'>
|
<div className='overflow-y-auto overflow-x-hidden max-h-[clamp(248px,calc(100vh-520px),366px)]'>
|
||||||
<div className='grid grid-cols-5 gap-3 min-h-[142px]'>
|
<div className='grid grid-cols-5 gap-2'>
|
||||||
{addonError && (
|
{addonError && (
|
||||||
<div className='col-span-5 text-xs text-amber-600'>{addonError}</div>
|
<div className='col-span-5 text-xs text-amber-600'>{addonError}</div>
|
||||||
)}
|
)}
|
||||||
@@ -1031,7 +1147,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={id}
|
key={id}
|
||||||
className='border rounded-lg p-3 cursor-pointer transition-all flex flex-col relative'
|
className='border rounded-lg p-3 cursor-pointer transition-all flex flex-col relative h-[120px] overflow-hidden'
|
||||||
onClick={() => toggleSelect(id)}
|
onClick={() => toggleSelect(id)}
|
||||||
>
|
>
|
||||||
{/* 无折扣标签图片 - 浮动在右上角(当 is_enjoy_discount 为 0 或 null 时显示) */}
|
{/* 无折扣标签图片 - 浮动在右上角(当 is_enjoy_discount 为 0 或 null 时显示) */}
|
||||||
@@ -1077,14 +1193,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
<div className='mt-auto pt-1'>
|
<div className='mt-auto pt-1'>
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col'>
|
||||||
{origPrice >= 0 && origPrice >= currPrice && (
|
{origPrice >= 0 && origPrice >= currPrice && (
|
||||||
<span className='text-xs text-gray-400 line-through'>¥{origPrice.toFixed(2)}</span>
|
<span className='text-xs text-gray-400 line-through'>原价:¥{origPrice.toFixed(2)}</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<span className='text-[13px] font-bold text-[#447955]'>{getDiscountText(item)}</span>
|
||||||
|
|
||||||
|
<span className='text-[13px] font-bold text-red-600'>折后价:¥{currPrice.toFixed(2)}</span>
|
||||||
|
{/*
|
||||||
<div className='flex items-center justify-between gap-2'>
|
<div className='flex items-center justify-between gap-2'>
|
||||||
<span className='text-[14px] font-bold text-red-600'>¥{currPrice.toFixed(2)}</span>
|
<span></span>
|
||||||
<span className={`text-[10px] px-2 rounded-full bg-[#EAFCF1] text-[#447955] whitespace-nowrap`}>
|
<span className={`text-[10px] px-2 rounded-full bg-[#EAFCF1] text-[#447955] whitespace-nowrap`}>
|
||||||
{getDiscountText(item)}
|
{getDiscountText(item)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1095,55 +1216,41 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
|
|
||||||
{/* 底部汇总和支付 */}
|
{/* 底部汇总和支付 */}
|
||||||
<div className='border-t pt-4 mt-6 flex items-center justify-between'>
|
<div className='border-t pt-4 mt-6 flex items-center justify-between'>
|
||||||
|
<div className='flex items-center gap-2'>
|
||||||
<div className='space-y-1 text-sm'>
|
<div className='space-y-1 text-sm'>
|
||||||
<div className='text-gray-600'>
|
<div className='text-gray-600'>
|
||||||
加项原价合计: <span className='text-gray-900'>¥{totalOriginal.toFixed(2)}</span>
|
原价: <span className='text-gray-900'>¥{totalOriginal.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-gray-600'>
|
<div className='text-gray-600'>
|
||||||
渠道折扣价: <span className='text-xl font-bold text-red-600'>¥{totalCurrent.toFixed(2)}</span>
|
优惠: <span className='font-bold text-red-600'>¥{discount.toFixed(2)}</span>
|
||||||
{discount > 0 && (
|
</div>
|
||||||
<span className='text-gray-500 ml-1'>已优惠 ¥{discount.toFixed(2)}</span>
|
<div className='text-gray-600'>
|
||||||
)}
|
结算价: <span className='font-bold text-red-600'>¥{(totalCurrent).toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className='text-xs text-gray-500'>结算方式: 个人支付 (微信 / 支付宝)</div> */}
|
{/* <div className='text-xs text-gray-500'>结算方式: 个人支付 (微信 / 支付宝)</div> */}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{/* 添加自定义结算 */}
|
{/* 添加自定义结算 */}
|
||||||
{localStorage.getItem('authCode')?.includes('HisTijianPad_Btn_Tongji') && selectedItems.length > 0 && (
|
{localStorage.getItem('authCode')?.includes('HisTijian_Btn_JiesuanShenqing') && selectedItems.length > 0 && (
|
||||||
<div className='flex flex-col items-end gap-2 relative'>
|
<div className='flex flex-col items-end gap-2 relative'>
|
||||||
{customSettlementStatus && (
|
|
||||||
<div className='text-xs text-gray-600'>
|
|
||||||
审批状态: <span className={cls(
|
|
||||||
'font-semibold',
|
|
||||||
customSettlementStatus.apply_status === 1 && 'text-blue-600', // 审核中
|
|
||||||
customSettlementStatus.apply_status === 3 && 'text-green-600', // 审核通过
|
|
||||||
customSettlementStatus.apply_status === 4 && 'text-red-600', // 审核不通过
|
|
||||||
customSettlementStatus.apply_status === 2 && 'text-gray-600' // 取消申请
|
|
||||||
)}>
|
|
||||||
{customSettlementStatus.apply_status_name || '未知'}
|
|
||||||
{customSettlementStatus.apply_status === 1 && ' (审核中...)'}
|
|
||||||
</span>
|
|
||||||
{customSettlementStatus.final_settlement_price !== null && customSettlementStatus.final_settlement_price !== undefined && (
|
|
||||||
<span className='ml-2'>
|
|
||||||
结算价: <span className='font-semibold text-red-600'>¥{customSettlementStatus.final_settlement_price.toFixed(2)}</span>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setShowCustomSettlementModal(true)}
|
onClick={() => setShowCustomSettlementModal(true)}
|
||||||
disabled={customSettlementStatus?.apply_status === 1}
|
className='px-3 py-1.5 text-xs text-white bg-blue-600'
|
||||||
className={cls(
|
|
||||||
'px-3 py-1.5 text-xs text-white',
|
|
||||||
customSettlementStatus?.apply_status === 1
|
|
||||||
? 'bg-gray-400 cursor-not-allowed'
|
|
||||||
: 'bg-blue-600 hover:bg-blue-700'
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
自定义结算
|
{!customSettlementStatus ||
|
||||||
|
!customSettlementStatus.apply_status ||
|
||||||
|
customSettlementStatus.apply_status === 1 ||
|
||||||
|
customSettlementStatus.add_item_id !== currentAddItemId
|
||||||
|
? '申请自定义结算'
|
||||||
|
: customSettlementStatus.apply_status === 3
|
||||||
|
? '自定义结算(已通过)'
|
||||||
|
: '自定义结算(未通过)'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{paymentMessage && (
|
{paymentMessage && (
|
||||||
@@ -1263,7 +1370,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className='bg-[#269745] hover:bg-[#269745]/80 rounded-3xl text-white px-6 py-3 text-base font-medium'
|
className='bg-blue-600 rounded-3xl text-white px-6 py-3 text-base font-medium'
|
||||||
disabled={selectedCount === 0 || paymentLoading}
|
disabled={selectedCount === 0 || paymentLoading}
|
||||||
onClick={handlePayment}
|
onClick={handlePayment}
|
||||||
>
|
>
|
||||||
@@ -1272,7 +1379,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 二维码支付弹窗 */}
|
{/* 二维码支付弹窗 */}
|
||||||
{showQrcodeModal && qrcodeUrl && (
|
{
|
||||||
|
showQrcodeModal && qrcodeUrl && (
|
||||||
<div className='fixed inset-0 z-[80] bg-black/80 flex items-center justify-center px-6'>
|
<div className='fixed inset-0 z-[80] bg-black/80 flex items-center justify-center px-6'>
|
||||||
<div className='bg-white rounded-2xl w-full max-w-md shadow-2xl p-6 flex flex-col gap-4'>
|
<div className='bg-white rounded-2xl w-full max-w-md shadow-2xl p-6 flex flex-col gap-4'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
@@ -1306,10 +1414,12 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{/* 自定义结算弹窗 */}
|
{/* 自定义结算弹窗 */}
|
||||||
{showCustomSettlementModal && (
|
{
|
||||||
|
showCustomSettlementModal && (
|
||||||
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/30' onClick={() => setShowCustomSettlementModal(false)}>
|
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/30' onClick={() => setShowCustomSettlementModal(false)}>
|
||||||
<div
|
<div
|
||||||
className='w-[500px] max-w-[95vw] bg-white rounded-2xl shadow-xl overflow-hidden'
|
className='w-[500px] max-w-[95vw] bg-white rounded-2xl shadow-xl overflow-hidden'
|
||||||
@@ -1349,24 +1459,28 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
<div className='flex gap-3'>
|
<div className='flex gap-3'>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
|
disabled={isApprovedOrRejected}
|
||||||
onClick={() => setCustomSettlementType(1)}
|
onClick={() => setCustomSettlementType(1)}
|
||||||
className={cls(
|
className={cls(
|
||||||
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors',
|
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors',
|
||||||
customSettlementType === 1
|
customSettlementType === 1
|
||||||
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium'
|
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium'
|
||||||
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400'
|
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400',
|
||||||
|
isApprovedOrRejected && 'opacity-70 cursor-not-allowed'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
按比例折扣
|
按比例折扣
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
|
disabled={isApprovedOrRejected}
|
||||||
onClick={() => setCustomSettlementType(2)}
|
onClick={() => setCustomSettlementType(2)}
|
||||||
className={cls(
|
className={cls(
|
||||||
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors',
|
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors',
|
||||||
customSettlementType === 2
|
customSettlementType === 2
|
||||||
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium'
|
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium'
|
||||||
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400'
|
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400',
|
||||||
|
isApprovedOrRejected && 'opacity-70 cursor-not-allowed'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
自定义结算价
|
自定义结算价
|
||||||
@@ -1378,39 +1492,41 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
{customSettlementType === 1 ? (
|
{customSettlementType === 1 ? (
|
||||||
<div className='space-y-3'>
|
<div className='space-y-3'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<label className='text-sm text-gray-700 font-medium'>折扣比例</label>
|
<label className='text-sm text-gray-700 font-medium'>折扣比例 (%)</label>
|
||||||
<span className='text-sm font-semibold text-blue-600'>{customDiscountRatio / 10}折</span>
|
<span className='text-sm font-semibold text-blue-600'>
|
||||||
</div>
|
{((customDiscountRatio ?? 0) / 10).toFixed(1)}折
|
||||||
<div className='relative'>
|
|
||||||
<input
|
|
||||||
type='range'
|
|
||||||
min='10'
|
|
||||||
max='100'
|
|
||||||
step='5'
|
|
||||||
value={customDiscountRatio}
|
|
||||||
onChange={(e) => {
|
|
||||||
setCustomDiscountRatio(Number(e.target.value));
|
|
||||||
}}
|
|
||||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-600'
|
|
||||||
style={{
|
|
||||||
background: `linear-gradient(to right, #3b82f6 0%, #3b82f6 ${((customDiscountRatio - 10) / (100 - 10)) * 100}%, #e5e7eb ${((customDiscountRatio - 10) / (100 - 10)) * 100}%, #e5e7eb 100%)`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/* 刻度标记 */}
|
|
||||||
<div className='flex justify-between mt-1 px-1'>
|
|
||||||
{[10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100].map((value) => (
|
|
||||||
<span
|
|
||||||
key={value}
|
|
||||||
className='text-[10px] text-gray-400'
|
|
||||||
style={{ width: '10px' }}
|
|
||||||
>
|
|
||||||
{value / 10}
|
|
||||||
</span>
|
</span>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className='flex items-center gap-2'>
|
||||||
|
<Input
|
||||||
|
type='number'
|
||||||
|
min='0'
|
||||||
|
max='100'
|
||||||
|
step='1'
|
||||||
|
disabled={isApprovedOrRejected}
|
||||||
|
value={customDiscountRatio ?? ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const raw = e.target.value;
|
||||||
|
if (raw === '') {
|
||||||
|
setCustomDiscountRatio(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const val = Number(raw);
|
||||||
|
if (Number.isNaN(val)) return;
|
||||||
|
if (val < 1) {
|
||||||
|
setCustomDiscountRatio(1);
|
||||||
|
} else if (val > 100) {
|
||||||
|
setCustomDiscountRatio(100);
|
||||||
|
} else {
|
||||||
|
setCustomDiscountRatio(val);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className='w-28 text-sm'
|
||||||
|
/>
|
||||||
|
<span className='text-xs text-gray-500'>1-100,100 表示不打折</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-xs text-gray-500'>
|
<div className='text-xs text-gray-500'>
|
||||||
最终结算价: <span className='font-semibold text-red-600'>¥{(totalCurrent * (customDiscountRatio / 100)).toFixed(2)}</span>
|
最终结算价: <span className='font-semibold text-red-600'>¥{(totalCurrent * ((customDiscountRatio ?? 0) / 100)).toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -1420,9 +1536,17 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
type='number'
|
type='number'
|
||||||
min='0'
|
min='0'
|
||||||
step='0.01'
|
step='0.01'
|
||||||
value={customFinalPrice || ''}
|
disabled={isApprovedOrRejected}
|
||||||
|
value={customFinalPrice ?? ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const val = Number(e.target.value);
|
const rawValue = e.target.value;
|
||||||
|
|
||||||
|
if (rawValue === '') {
|
||||||
|
setCustomFinalPrice(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const val = Number(rawValue);
|
||||||
if (val >= 0) {
|
if (val >= 0) {
|
||||||
setCustomFinalPrice(val);
|
setCustomFinalPrice(val);
|
||||||
}
|
}
|
||||||
@@ -1438,10 +1562,14 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
<label className='text-sm text-gray-700 font-medium'>申请理由 *</label>
|
<label className='text-sm text-gray-700 font-medium'>申请理由 *</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={customApplyReason}
|
value={customApplyReason}
|
||||||
|
disabled={isApprovedOrRejected}
|
||||||
onChange={(e) => setCustomApplyReason(e.target.value)}
|
onChange={(e) => setCustomApplyReason(e.target.value)}
|
||||||
placeholder='请输入申请理由'
|
placeholder='请输入申请理由'
|
||||||
rows={3}
|
rows={3}
|
||||||
className='w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 resize-none'
|
className={cls(
|
||||||
|
'w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 resize-none',
|
||||||
|
isApprovedOrRejected && 'bg-gray-50 opacity-70 cursor-not-allowed'
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1451,8 +1579,9 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
onClick={() => setShowCustomSettlementModal(false)}
|
onClick={() => setShowCustomSettlementModal(false)}
|
||||||
className='flex-1 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700'
|
className='flex-1 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700'
|
||||||
>
|
>
|
||||||
取消
|
{isApprovedOrRejected ? '关闭' : '取消'}
|
||||||
</Button>
|
</Button>
|
||||||
|
{!isApprovedOrRejected && (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSubmitCustomSettlement}
|
onClick={handleSubmitCustomSettlement}
|
||||||
disabled={customSettlementLoading || !customApplyReason.trim()}
|
disabled={customSettlementLoading || !customApplyReason.trim()}
|
||||||
@@ -1465,14 +1594,17 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
>
|
>
|
||||||
{customSettlementLoading ? '提交中...' : '提交申请'}
|
{customSettlementLoading ? '提交中...' : '提交申请'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{/* 审核中全屏遮罩 */}
|
{/* 审核中全屏遮罩 */}
|
||||||
{customSettlementStatus?.apply_status === 1 && (
|
{
|
||||||
|
customSettlementStatus?.apply_status === 1 && (
|
||||||
<div className='fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50'>
|
<div className='fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50'>
|
||||||
<div className='bg-white rounded-2xl p-6 max-w-md w-full mx-4 shadow-xl'>
|
<div className='bg-white rounded-2xl p-6 max-w-md w-full mx-4 shadow-xl'>
|
||||||
<div className='text-center mb-6'>
|
<div className='text-center mb-6'>
|
||||||
@@ -1491,7 +1623,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
</div >
|
</div >
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export const ExamDeliveryPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{viewMode === 'form' ? (
|
{viewMode === 'form' ? (
|
||||||
<>
|
<div className='overflow-y-auto overflow-x-hidden max-h-[clamp(260px,calc(100vh-520px),560px)] pr-1'>
|
||||||
{infoLoading && (
|
{infoLoading && (
|
||||||
<div className='mb-3 text-xs text-gray-500'>正在加载地址信息...</div>
|
<div className='mb-3 text-xs text-gray-500'>正在加载地址信息...</div>
|
||||||
)}
|
)}
|
||||||
@@ -261,7 +261,7 @@ export const ExamDeliveryPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
{saveLoading ? '保存中...' : '保存寄送信息'}
|
{saveLoading ? '保存中...' : '保存寄送信息'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className='flex flex-col items-center justify-center py-8'>
|
<div className='flex flex-col items-center justify-center py-8'>
|
||||||
{qrcodeLoading ? (
|
{qrcodeLoading ? (
|
||||||
@@ -271,7 +271,7 @@ export const ExamDeliveryPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
<img
|
<img
|
||||||
src={qrcodeUrl.startsWith('data:') ? qrcodeUrl : `data:image/png;base64,${qrcodeUrl}`}
|
src={qrcodeUrl.startsWith('data:') ? qrcodeUrl : `data:image/png;base64,${qrcodeUrl}`}
|
||||||
alt='报告寄送登记二维码'
|
alt='报告寄送登记二维码'
|
||||||
className='max-w-full max-h-[400px] object-contain'
|
className='max-w-full max-h-[clamp(240px,calc(100vh-520px),400px)] object-contain'
|
||||||
/>
|
/>
|
||||||
<div className='text-xs text-gray-500'>请扫描二维码进行报告寄送登记</div>
|
<div className='text-xs text-gray-500'>请扫描二维码进行报告寄送登记</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface ExamDetailPanelProps {
|
|||||||
addItemInfoList: CustomerExamAddItem[] | null;
|
addItemInfoList: CustomerExamAddItem[] | null;
|
||||||
progressList: PhysicalExamProgressItem[] | null;
|
progressList: PhysicalExamProgressItem[] | null;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
onCustomerUpdated?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getMaritalCodeFromText = (text: string): number => {
|
const getMaritalCodeFromText = (text: string): number => {
|
||||||
@@ -33,6 +34,7 @@ export const ExamDetailPanel = ({
|
|||||||
addItemInfoList,
|
addItemInfoList,
|
||||||
progressList,
|
progressList,
|
||||||
loading,
|
loading,
|
||||||
|
onCustomerUpdated,
|
||||||
}: ExamDetailPanelProps) => {
|
}: ExamDetailPanelProps) => {
|
||||||
const basePhone = customerInfo?.phone || (client['mobile' as keyof ExamClient] as string | undefined) || '';
|
const basePhone = customerInfo?.phone || (client['mobile' as keyof ExamClient] as string | undefined) || '';
|
||||||
const baseMaritalText =
|
const baseMaritalText =
|
||||||
@@ -108,6 +110,7 @@ export const ExamDetailPanel = ({
|
|||||||
if (res.Status === 200) {
|
if (res.Status === 200) {
|
||||||
setEditMessage('保存成功');
|
setEditMessage('保存成功');
|
||||||
setPhoneEditing(false);
|
setPhoneEditing(false);
|
||||||
|
onCustomerUpdated?.();
|
||||||
setTimeout(() => setEditMessage(null), 2000);
|
setTimeout(() => setEditMessage(null), 2000);
|
||||||
} else {
|
} else {
|
||||||
setEditMessage(res.Message || '保存失败');
|
setEditMessage(res.Message || '保存失败');
|
||||||
@@ -140,6 +143,7 @@ export const ExamDetailPanel = ({
|
|||||||
if (res.Status === 200) {
|
if (res.Status === 200) {
|
||||||
setEditMessage('保存成功');
|
setEditMessage('保存成功');
|
||||||
setMaritalEditing(false);
|
setMaritalEditing(false);
|
||||||
|
onCustomerUpdated?.();
|
||||||
setTimeout(() => setEditMessage(null), 2000);
|
setTimeout(() => setEditMessage(null), 2000);
|
||||||
} else {
|
} else {
|
||||||
setEditMessage(res.Message || '保存失败');
|
setEditMessage(res.Message || '保存失败');
|
||||||
|
|||||||
@@ -76,6 +76,16 @@ export const ExamModal = ({ client, tab, onTabChange, onClose }: ExamModalProps)
|
|||||||
.finally(() => setDetailLoading(false));
|
.finally(() => setDetailLoading(false));
|
||||||
}, [client.id]);
|
}, [client.id]);
|
||||||
|
|
||||||
|
const refetchDetail = () => {
|
||||||
|
const physical_exam_id = Number(client.id);
|
||||||
|
if (!physical_exam_id) return;
|
||||||
|
getCustomerDetail({ physical_exam_id }).then((detailRes) => {
|
||||||
|
setCustomerInfo(detailRes.Data?.customerInfo ?? null);
|
||||||
|
setAppointmentInfo(detailRes.Data?.appointmentInfo ?? null);
|
||||||
|
setAddItemInfoList(detailRes.Data?.addItemInfoList ?? null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='fixed inset-0 z-40 flex items-center justify-center bg-black/50'
|
className='fixed inset-0 z-40 flex items-center justify-center bg-black/50'
|
||||||
@@ -153,6 +163,7 @@ export const ExamModal = ({ client, tab, onTabChange, onClose }: ExamModalProps)
|
|||||||
addItemInfoList={addItemInfoList}
|
addItemInfoList={addItemInfoList}
|
||||||
progressList={progressList}
|
progressList={progressList}
|
||||||
loading={detailLoading}
|
loading={detailLoading}
|
||||||
|
onCustomerUpdated={refetchDetail}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{tab === 'sign' && <ExamSignPanel examId={Number(client.id)} onBusyChange={setSignBusy} />}
|
{tab === 'sign' && <ExamSignPanel examId={Number(client.id)} onBusyChange={setSignBusy} />}
|
||||||
|
|||||||
@@ -22,10 +22,18 @@ const NAV_ITEMS = [
|
|||||||
{ key: 'support', icon: IconSupport, label: '客服咨询' },
|
{ key: 'support', icon: IconSupport, label: '客服咨询' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
const toggleFullscreen = () => {
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
document.documentElement.requestFullscreen?.();
|
||||||
|
} else {
|
||||||
|
document.exitFullscreen?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const Sidebar = ({ active, onNavigate, onQuickAction }: SidebarProps) => (
|
export const Sidebar = ({ active, onNavigate, onQuickAction }: SidebarProps) => (
|
||||||
<aside className='bg-white border-r p-4 flex flex-col gap-4'>
|
<aside className='h-screen max-h-screen bg-white border-r p-4 flex flex-col gap-4 overflow-hidden'>
|
||||||
<div>
|
<div>
|
||||||
<div className='text-base font-semibold'>圆和医疗 · 体检中心</div>
|
<button type='button' onClick={toggleFullscreen} className='text-base font-semibold cursor-pointer hover:opacity-80 text-left'>圆和医疗 · 体检中心</button>
|
||||||
<div className='text-xs text-gray-500 mt-1'></div>
|
<div className='text-xs text-gray-500 mt-1'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -73,13 +81,13 @@ export const Sidebar = ({ active, onNavigate, onQuickAction }: SidebarProps) =>
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className='mt-auto p-3 rounded-2xl border bg-gray-50/70 text-xs flex flex-col gap-2'>
|
{/* <section className='mt-auto p-3 rounded-2xl border bg-gray-50/70 text-xs flex flex-col gap-2'>
|
||||||
<div className='text-sm font-medium flex items-center gap-2'>
|
<div className='text-sm font-medium flex items-center gap-2'>
|
||||||
<span>💻</span>
|
<span>💻</span>
|
||||||
<span>客服 / IT 支持</span>
|
<span>客服 / IT 支持</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-gray-600'>遇到系统问题可一键联系 IT / 运营支持。</div>
|
<div className='text-gray-600'>遇到系统问题可一键联系 IT / 运营支持。</div>
|
||||||
</section>
|
</section> */}
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -28,18 +28,18 @@ export const TopBar = ({ enableSearch = true, operatorName, onLoginClick, onLogo
|
|||||||
return (
|
return (
|
||||||
<header className='flex items-center gap-3 p-2 border-b bg-white'>
|
<header className='flex items-center gap-3 p-2 border-b bg-white'>
|
||||||
<div className='flex-1 flex items-center gap-3'>
|
<div className='flex-1 flex items-center gap-3'>
|
||||||
{enableSearch ? (
|
{/* {enableSearch ? (
|
||||||
<div className='w-[420px] max-w-[60vw] h-[36px] flex items-center'>
|
<div className='w-[420px] max-w-[60vw] h-[36px] flex items-center'>
|
||||||
{/* <Input
|
<Input
|
||||||
placeholder='搜索 姓名 / 证件号 / 手机号'
|
placeholder='搜索 姓名 / 证件号 / 手机号'
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => onSearch(e.target.value)}
|
onChange={(e) => onSearch(e.target.value)}
|
||||||
className='text-sm'
|
className='text-sm'
|
||||||
/> */}
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className='text-sm text-gray-500 flex items-center p-[9px] pl-[14px]'>圆和医疗 · 体检驾驶舱</div>
|
<div className='text-sm text-gray-500 flex items-center p-[9px] pl-[14px]'>圆和医疗 · 体检驾驶舱</div>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-3 text-xs relative'>
|
<div className='flex items-center gap-3 text-xs relative'>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -95,9 +95,9 @@ export const NoteModal = ({ noteText, onNoteChange, onClose }: NoteModalProps) =
|
|||||||
{saving ? '保存中...' : '保存'}
|
{saving ? '保存中...' : '保存'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-right text-[11px] text-gray-500'>
|
{/* <div className='text-right text-[11px] text-gray-500'>
|
||||||
备注内容会同步至客户详情页,供前台和导检护士查看。
|
备注内容会同步至客户详情页,供前台和导检护士查看。
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { Card, CardContent, CardHeader } from '../ui';
|
|||||||
|
|
||||||
export const SupportSection = () => (
|
export const SupportSection = () => (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>客服咨询 · 圆圆客服台卡</CardHeader>
|
{/* <CardHeader>客服咨询 · 圆圆客服台卡</CardHeader> */}
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className='grid grid-cols-[1.2fr_1fr] gap-6 items-center'>
|
<div className='items-center'>
|
||||||
<div className='space-y-3 text-sm text-gray-700'>
|
{/* <div className='space-y-3 text-sm text-gray-700'>
|
||||||
<p>通过「圆圆客服」二维码,客户可获得一站式健康服务:包含体检预约、报告查询、报告解读等。</p>
|
<p>通过「圆圆客服」二维码,客户可获得一站式健康服务:包含体检预约、报告查询、报告解读等。</p>
|
||||||
<ul className='list-disc ml-5 space-y-1 text-xs text-gray-600'>
|
<ul className='list-disc ml-5 space-y-1 text-xs text-gray-600'>
|
||||||
<li>支持体检当天现场扫码添加,绑定客户信息</li>
|
<li>支持体检当天现场扫码添加,绑定客户信息</li>
|
||||||
@@ -13,19 +13,19 @@ export const SupportSection = () => (
|
|||||||
<li>提供一对一健康咨询与报告解读服务</li>
|
<li>提供一对一健康咨询与报告解读服务</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='text-xs text-gray-500'>注:实际系统中可上传设计好的「圆圆客服二维码台卡」图片,用于前台展示与打印。</div>
|
<div className='text-xs text-gray-500'>注:实际系统中可上传设计好的「圆圆客服二维码台卡」图片,用于前台展示与打印。</div>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<div className='h-64 rounded-3xl overflow-hidden shadow-inner flex items-center justify-center bg-gradient-to-b from-[#152749] to-[#c73545]'>
|
<div className='h-[70vh] rounded-3xl overflow-hidden shadow-inner flex items-center justify-center bg-gradient-to-b from-[#152749] to-[#c73545]'>
|
||||||
<div className='flex flex-col items-center gap-3 text-white'>
|
<div className='flex flex-col items-center gap-5 text-white'>
|
||||||
<div className='text-[11px] tracking-[0.2em] opacity-80'>CIRCLE HARMONY · 圆和医疗</div>
|
<div className='text-base tracking-[0.2em] opacity-80'>CIRCLE HARMONY · 圆和医疗</div>
|
||||||
<div className='text-sm font-medium'>圆圆客服 · 一站式健康服务</div>
|
<div className='text-lg font-medium'>圆圆客服 · 一站式健康服务</div>
|
||||||
<div className='w-28 h-28 bg-none flex items-center justify-center'>
|
<div className='w-72 h-72 bg-none flex items-center justify-center'>
|
||||||
<div className='w-30 h-30 rounded-md overflow-hidden'>
|
<div className='w-72 h-72 rounded-md overflow-hidden'>
|
||||||
<img src="https://datacenter-open.oss-cn-hangzhou.aliyuncs.com/his/kefu-zixun.png" alt='圆圆客服二维码' className='w-full h-full object-cover' />
|
<img src="https://datacenter-open.oss-cn-hangzhou.aliyuncs.com/his/kefu-zixun.png" alt='圆圆客服二维码' className='w-full h-full object-cover' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-sm font-semibold'>一对一专属服务</div>
|
<div className='text-lg font-semibold'>一对一专属服务</div>
|
||||||
<div className='px-4 py-1.5 rounded-full border border-white/70 text-[11px] flex gap-2'>
|
<div className='px-6 py-2 rounded-full border border-white/70 text-sm flex gap-2'>
|
||||||
<span>服务预约</span>
|
<span>服务预约</span>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
<span>报告查询</span>
|
<span>报告查询</span>
|
||||||
|
|||||||
@@ -7,17 +7,21 @@
|
|||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
scrollbar-color: #cbd5e1 #f8fafc;
|
scrollbar-color: #cbd5e1 #f8fafc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scroll::-webkit-scrollbar {
|
.custom-scroll::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scroll::-webkit-scrollbar-track {
|
.custom-scroll::-webkit-scrollbar-track {
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scroll::-webkit-scrollbar-thumb {
|
.custom-scroll::-webkit-scrollbar-thumb {
|
||||||
background: #cbd5e1;
|
background: #cbd5e1;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scroll::-webkit-scrollbar-thumb:hover {
|
.custom-scroll::-webkit-scrollbar-thumb:hover {
|
||||||
background: #94a3b8;
|
background: #94a3b8;
|
||||||
}
|
}
|
||||||
@@ -30,9 +34,13 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export const ExamPage = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('accessToken');
|
const token = localStorage.getItem('accessToken');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
// 跳转登录
|
||||||
|
window.location.href = '/home';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getUserOwnedMenus({ app_id: APP_ID })
|
getUserOwnedMenus({ app_id: APP_ID })
|
||||||
|
|||||||
@@ -4,4 +4,7 @@ import react from '@vitejs/plugin-react-swc'
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
allowedHosts: ['ipad.shenynet.com'],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user