feat(purchase): 优化采购订单界面交互与数据展示

- 调整 CompanyPicker、DealerPicker 等组件的样式与布局
- 更新 BasicInfoSection 中车次号输入框及参考信息展示方式
- 修改 CompanyInfoSection 和 DealerInfoSection 的选择按钮样式与文案
- 增加 costItemVOList 数据字段支持
- 完善审核页面对空数据情况的处理逻辑
- 新增 smart recognition prompt 配置类型定义
- 升级 APP 版本至 v0.0.22
This commit is contained in:
shenyifei 2025-11-19 21:51:14 +08:00
parent a05015fd19
commit 221e3434a0
11 changed files with 174 additions and 161 deletions

View File

@ -1,5 +1,5 @@
import { Popup, SafeArea } from "@nutui/nutui-react-taro"; import { Popup, SafeArea } from "@nutui/nutui-react-taro";
import { ScrollView, View, Image } from "@tarojs/components"; import { Image, ScrollView, View } from "@tarojs/components";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { business } from "@/services"; import { business } from "@/services";
@ -35,6 +35,10 @@ export default function CompanyPicker(props: ICompanyPickerProps) {
return ( return (
<> <>
<View <View
className={"flex flex-1 flex-row items-center justify-between"}
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={(event) => { onClick={(event) => {
setVisible(true); setVisible(true);
event.stopPropagation(); event.stopPropagation();
@ -69,29 +73,38 @@ export default function CompanyPicker(props: ICompanyPickerProps) {
)} )}
{companyVOList?.map((companyVO) => ( {companyVOList?.map((companyVO) => (
<View <View
className={ className={"flex flex-col items-center justify-center p-4"}
"flex flex-col items-center justify-center p-4"
}
key={companyVO.companyId} key={companyVO.companyId}
onClick={async () => { onClick={async () => {
setCompanyVO(companyVO); setCompanyVO(companyVO);
setVisible(false); setVisible(false);
}} }}
> >
<View className={"w-16 h-16 rounded-full bg-white border-2 border-gray-200 flex items-center justify-center shadow-sm"}> <View
className={
"flex h-16 w-16 items-center justify-center rounded-full border-2 border-gray-200 bg-white shadow-sm"
}
>
{companyVO?.logo ? ( {companyVO?.logo ? (
<Image <Image
src={companyVO.logo} src={companyVO.logo}
className={"w-14 h-14 rounded-full"} className={"h-14 w-14 rounded-full"}
mode="aspectFit" mode="aspectFit"
/> />
) : ( ) : (
<View className={"w-14 h-14 rounded-full bg-blue-100 flex items-center justify-center text-blue-500 font-medium text-xl"}> <View
{(companyVO?.shortName || companyVO?.fullName)?.substring(0, 2)} className={
"flex h-14 w-14 items-center justify-center rounded-full bg-blue-100 text-xl font-medium text-blue-500"
}
>
{(companyVO?.shortName || companyVO?.fullName)?.substring(
0,
2,
)}
</View> </View>
)} )}
</View> </View>
<View className={"text-center mt-2 text-base font-medium"}> <View className={"mt-2 text-center text-base font-medium"}>
{companyVO?.shortName} {companyVO?.shortName}
</View> </View>
</View> </View>

View File

@ -64,7 +64,7 @@ export default function DealerPicker(props: IDealerPickerProps) {
return ( return (
<> <>
<View <View
className={"flex flex-1 flex-row items-center justify-between px-5"} className={"flex flex-1 flex-row items-center justify-between"}
style={{ style={{
color: "var(--nutui-color-title, #1a1a1a)", color: "var(--nutui-color-title, #1a1a1a)",
}} }}

View File

@ -756,12 +756,16 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
onFinish={setDealerVO} onFinish={setDealerVO}
enableManualInput enableManualInput
trigger={ trigger={
<> <View
className={
"flex flex-1 flex-row items-center justify-between px-5"
}
>
<View className={"text-sm"}> <View className={"text-sm"}>
{orderVehicle?.dealerName || "选择经销商"} {orderVehicle?.dealerName || "选择经销商"}
</View> </View>
<Icon name={"chevron-down"} /> <Icon name={"chevron-down"} />
</> </View>
} }
/> />
</View> </View>

View File

@ -4,6 +4,7 @@ import dayjs from "dayjs";
import { formatCurrency } from "@/utils/format"; import { formatCurrency } from "@/utils/format";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import businessServices from "@/services/business"; import businessServices from "@/services/business";
import { Icon } from "@/components";
export default function BasicInfoSection(props: { export default function BasicInfoSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
@ -463,11 +464,16 @@ export default function BasicInfoSection(props: {
</View> </View>
</Popup> </Popup>
<View className="rounded-lg border border-solid border-gray-200 bg-white p-3"> <View className="flex flex-col gap-2.5 rounded-lg border border-solid border-gray-200 bg-white p-2.5">
{/* 本车次号 */} {/* 本车次号 */}
<View className="mb-4"> <View className="flex flex-col gap-2.5">
<View className="text-neutral-darkest mb-1 text-sm font-bold"> <View className={"flex flex-row items-center justify-between"}>
<View className="text-neutral-darkest mb-1 text-sm font-bold">
</View>
<View className="text-center text-xs text-gray-500">
{displayReferenceVehicleNo()}
</View>
</View> </View>
{readOnly ? ( {readOnly ? (
<View <View
@ -483,27 +489,22 @@ export default function BasicInfoSection(props: {
</View> </View>
</View> </View>
) : ( ) : (
<View className="flex flex-row items-center justify-between"> <View
<View className={`flex h-10 flex-1 items-center rounded-md border-4 border-gray-300`}
className={`flex h-10 flex-1 items-center rounded-md border-4 border-gray-300`} >
> <Input
<Input placeholder="请输入车次号"
placeholder="请输入车次号" type="text"
type="text" value={orderVehicle?.vehicleNo || ""}
value={orderVehicle?.vehicleNo || ""} onChange={(value) => updateVehicleNo(value)}
onChange={(value) => updateVehicleNo(value)} />
/>
</View>
<View className="flex-1 text-center text-xs text-gray-500">
{displayReferenceVehicleNo()}
</View>
</View> </View>
)} )}
</View> </View>
{/* 运费信息 */} {/* 运费信息 */}
<View className="mb-4 flex flex-col gap-2.5"> <View className="flex flex-col gap-2.5">
<View className="text-neutral-darkest mb-1 text-sm font-bold"> <View className="text-neutral-darkest text-sm font-bold">
</View> </View>
<View <View
@ -543,10 +544,8 @@ export default function BasicInfoSection(props: {
</View> </View>
{/* 草帘 */} {/* 草帘 */}
<View className="mb-4 flex flex-col gap-2.5"> <View className="flex flex-col gap-2.5">
<View className="text-neutral-darkest mb-1 text-sm font-bold"> <View className="text-neutral-darkest text-sm font-bold"></View>
</View>
<View <View
className="flex flex-row items-center justify-between rounded-md p-2.5" className="flex flex-row items-center justify-between rounded-md p-2.5"
@ -567,8 +566,8 @@ export default function BasicInfoSection(props: {
</View> </View>
{/* 路线和其他信息卡片 */} {/* 路线和其他信息卡片 */}
<View className="mb-4 rounded-lg bg-gray-50 p-3"> <View className="flex flex-col gap-2.5 rounded-lg bg-gray-50 p-2.5">
<View className="mb-2 flex flex-row justify-between"> <View className="flex flex-row justify-between">
<View className="text-neutral-dark text-xs">线</View> <View className="text-neutral-dark text-xs">线</View>
<View className="text-neutral-darkest text-xs font-medium"> <View className="text-neutral-darkest text-xs font-medium">
{orderVehicle?.origin || "发货地"} {"->"}{" "} {orderVehicle?.origin || "发货地"} {"->"}{" "}
@ -586,12 +585,15 @@ export default function BasicInfoSection(props: {
</View> </View>
</View> </View>
</View> </View>
{!readOnly && ( {!readOnly && (
<View className="flex justify-center border-t border-gray-200 p-2"> <View className="flex-1 border-t border-gray-200 pt-2.5">
<Button <Button
size="small" icon={<Icon name="pen-to-square" size={18} />}
size="large"
type="primary" type="primary"
fill="outline" fill="outline"
block
onClick={openBasicInfoPopup} onClick={openBasicInfoPopup}
> >

View File

@ -20,9 +20,7 @@ export default function CompanyInfoSection(props: {
}, [purchaseOrderVO]); }, [purchaseOrderVO]);
// 处理公司账户选择完成事件 // 处理公司账户选择完成事件
const handleCompanySelect = ( const handleCompanySelect = (companyVO: BusinessAPI.CompanyVO) => {
companyVO: BusinessAPI.CompanyVO,
) => {
if (readOnly) return; if (readOnly) return;
// 构造新的 orderCompany 对象 // 构造新的 orderCompany 对象
@ -66,53 +64,39 @@ export default function CompanyInfoSection(props: {
}; };
return ( return (
<View> <View className="relative flex flex-col gap-2.5 rounded-lg border border-solid border-gray-200 p-2.5">
{orderCompany ? ( {!readOnly && orderCompany && (
<View className="relative flex flex-col gap-2.5 rounded-lg border border-solid border-gray-200 p-2.5"> <View
{!readOnly && ( className="absolute -top-2 -right-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-gray-100"
<View onClick={handleRemoveCompany}
className="absolute -top-2 -right-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-gray-100" >
onClick={handleRemoveCompany} <Icon name="circle-xmark" size={24} color="#999" />
>
<Icon name="circle-xmark" size={24} color="#999" />
</View>
)}
<View className="flex flex-row items-center justify-between">
<View className="text-neutral-dark text-sm font-medium">
</View>
<View className="text-neutral-darkest text-sm">
{orderCompany?.fullName}
</View>
</View>
{!readOnly && (
<View className="flex justify-center">
<CompanyPicker
onFinish={handleCompanySelect}
trigger={
<Button size="small" type="primary" fill="outline">
</Button>
}
/>
</View>
)}
</View> </View>
) : readOnly ? ( )}
<View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-5">
<View className="text-neutral-darker text-sm"></View> <View className="flex flex-row items-center justify-between">
<View className="text-neutral-dark text-sm font-medium"></View>
<View className="text-neutral-darkest text-sm">
{orderCompany?.fullName || "请先选择销售方"}
</View> </View>
) : ( </View>
<View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-5">
<View className="text-neutral-darker mb-3 text-sm"> {!readOnly && (
<View className="flex justify-center">
</View>
<CompanyPicker <CompanyPicker
onFinish={handleCompanySelect} onFinish={handleCompanySelect}
trigger={ trigger={
<Button size="small" type="primary" fill="outline"> <View className={"flex-1"}>
<Button
</Button> icon={<Icon name="pen-to-square" size={18} />}
size="large"
type="primary"
fill="outline"
block
>
{orderCompany ? "修改我方入账公司" : "选择我方入账公司"}
</Button>
</View>
} }
/> />
</View> </View>

View File

@ -72,7 +72,7 @@ export default function CostDifferenceSection(props: {
<View className="flex"> <View className="flex">
<View className="flex-1"> <View className="flex-1">
{!readOnly ? ( {!readOnly && orderDealer?.shareAdjusted ? (
<View <View
onClick={() => { onClick={() => {
// 打开弹窗时将当前costDifference值同步到临时状态 // 打开弹窗时将当前costDifference值同步到临时状态

View File

@ -64,77 +64,56 @@ export default function (props: {
}; };
return ( return (
<View> <View className="relative flex flex-col gap-2.5 rounded-lg border border-solid border-gray-200 p-2.5">
{orderDealer ? ( {!readOnly && orderDealer && (
<View className="relative flex flex-col gap-2.5 rounded-lg border border-solid border-gray-200 p-2.5"> <View
{!readOnly && ( className="absolute -top-2 -right-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-gray-100"
<View onClick={(e) => {
className="absolute -top-2 -right-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-gray-100" e.stopPropagation();
onClick={(e) => { clearDealerSelection();
e.stopPropagation(); }}
clearDealerSelection(); >
}} <Icon name="circle-xmark" size={24} color="#999" />
> </View>
<Icon name="circle-xmark" size={24} color="#999" /> )}
</View>
)}
<View className="flex flex-row items-center justify-between"> <View className="flex flex-row items-center justify-between">
<View className="text-neutral-dark flex-shrink-0 text-sm"> <View className="text-neutral-dark flex-shrink-0 text-sm">
</View> </View>
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
{orderDealer.shortName} {orderDealer?.shortName || "请先选择经销商"}
</View> </View>
</View> </View>
{!readOnly && ( {dealerNameOnly && (
<DealerPicker <View className="text-xs text-orange-500">
onFinish={(dealer) => { <View>{dealerNameOnly}</View>
handleDealerSelect(dealer); <View className="mt-1"> </View>
}} <View> </View>
trigger={
<View
className={"flex flex-1 flex-row items-center justify-center"}
>
<Button size="small" type="primary" fill="outline">
</Button>
</View>
}
/>
)}
</View>
) : readOnly ? (
<View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-2.5">
<View className="text-neutral-darker text-sm"></View>
</View>
) : (
<View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-2.5">
<View className="text-neutral-darker mb-3 text-sm">
</View>
<View className="flex flex-col items-center">
{dealerNameOnly && (
<View className="mb-3 text-xs text-orange-500">
<View>{dealerNameOnly}</View>
<View className="mt-1">
</View>
<View> </View>
</View>
)}
<DealerPicker
onFinish={handleDealerSelect}
trigger={
<Button size="small" type="primary" fill="outline">
</Button>
}
/>
</View>
</View> </View>
)} )}
{!readOnly && (
<DealerPicker
onFinish={(dealer) => {
handleDealerSelect(dealer);
}}
trigger={
<View className={"flex-1"}>
<Button
icon={<Icon name="pen-to-square" size={18} />}
size="large"
type="primary"
fill="outline"
block
>
{orderDealer ? "修改经销商" : "选择经销商"}
</Button>
</View>
}
/>
)}
</View> </View>
); );
} }

View File

@ -1,2 +1,2 @@
// App 相关常量 // App 相关常量
export const APP_VERSION = "v0.0.21"; export const APP_VERSION = "v0.0.22";

View File

@ -479,6 +479,14 @@ export default hocAuth(function Page(props: CommonComponent) {
return null; return null;
} }
if (
(!purchaseOrderVO.orderPackageList ||
purchaseOrderVO?.orderPackageList.length === 0) &&
sectionKey === "emptyBoxInfo"
) {
return null;
}
return ( return (
<> <>
<View className="text-sm font-bold">{section.title}</View> <View className="text-sm font-bold">{section.title}</View>
@ -486,6 +494,7 @@ export default hocAuth(function Page(props: CommonComponent) {
className={`overflow-x-auto rounded-md rounded-b-lg bg-white p-2.5 shadow-sm`} className={`overflow-x-auto rounded-md rounded-b-lg bg-white p-2.5 shadow-sm`}
> >
<section.component <section.component
readOnly={purchaseOrderVO.state !== "WAITING_AUDIT"}
purchaseOrderVO={purchaseOrderVO} purchaseOrderVO={purchaseOrderVO}
onChange={setPurchaseOrderVO} onChange={setPurchaseOrderVO}
costItemVOList={costItemVOList} costItemVOList={costItemVOList}

View File

@ -1097,6 +1097,8 @@ declare namespace BusinessAPI {
enableCompanyRebate?: boolean; enableCompanyRebate?: boolean;
/** 公司返点比例 */ /** 公司返点比例 */
companyRebateRatio?: number; companyRebateRatio?: number;
/** 是否可调整比例 */
shareAdjusted?: boolean;
}; };
type DealerDestroyCmd = { type DealerDestroyCmd = {
@ -1413,6 +1415,8 @@ declare namespace BusinessAPI {
enableCompanyRebate?: boolean; enableCompanyRebate?: boolean;
/** 公司返点比例 */ /** 公司返点比例 */
companyRebateRatio?: number; companyRebateRatio?: number;
/** 是否可调整比例 */
shareAdjusted?: boolean;
/** 发货单模板 */ /** 发货单模板 */
deliveryTemplate?: string; deliveryTemplate?: string;
}; };
@ -1456,6 +1460,8 @@ declare namespace BusinessAPI {
enableCompanyRebate?: boolean; enableCompanyRebate?: boolean;
/** 公司返点比例 */ /** 公司返点比例 */
companyRebateRatio?: number; companyRebateRatio?: number;
/** 是否可调整比例 */
shareAdjusted?: boolean;
}; };
type DealerWarehouseCreateCmd = { type DealerWarehouseCreateCmd = {
@ -2528,6 +2534,8 @@ declare namespace BusinessAPI {
enableCompanyRebate?: boolean; enableCompanyRebate?: boolean;
/** 公司返点比例 */ /** 公司返点比例 */
companyRebateRatio?: number; companyRebateRatio?: number;
/** 是否可调整比例 */
shareAdjusted?: boolean;
/** 税费补贴 */ /** 税费补贴 */
taxSubsidy?: number; taxSubsidy?: number;
/** 计提税金 */ /** 计提税金 */
@ -2780,8 +2788,6 @@ declare namespace BusinessAPI {
strawCurtainPrice?: number; strawCurtainPrice?: number;
/** 采购日期 */ /** 采购日期 */
deliveryTime: string; deliveryTime: string;
/** 原始数据 */
originalData: string;
}; };
type OssTokenVO = { type OssTokenVO = {
@ -3426,6 +3432,8 @@ declare namespace BusinessAPI {
active?: number; active?: number;
/** 产地负责人 */ /** 产地负责人 */
originPrincipal?: string; originPrincipal?: string;
/** 工头 */
foreman?: string;
/** 备注 */ /** 备注 */
remark?: string; remark?: string;
/** 车辆信息 */ /** 车辆信息 */
@ -3604,6 +3612,8 @@ declare namespace BusinessAPI {
active?: number; active?: number;
/** 产地负责人 */ /** 产地负责人 */
originPrincipal?: string; originPrincipal?: string;
/** 工头 */
foreman?: string;
/** 备注 */ /** 备注 */
remark?: string; remark?: string;
/** 车辆信息 */ /** 车辆信息 */
@ -3818,7 +3828,8 @@ declare namespace BusinessAPI {
| "CHARGING_PILE_PURCHASE_CONFIG" | "CHARGING_PILE_PURCHASE_CONFIG"
| "CUSTOM_THEME_CONFIG" | "CUSTOM_THEME_CONFIG"
| "CUSTOM_MENU_CONFIG" | "CUSTOM_MENU_CONFIG"
| "WX_CP_NOTIFY_CONFIG"; | "WX_CP_NOTIFY_CONFIG"
| "SMART_RECOGNITION_PROMPT";
}; };
type SettingUpdateCmd = { type SettingUpdateCmd = {
@ -3840,13 +3851,15 @@ declare namespace BusinessAPI {
| "CHARGING_PILE_PURCHASE_CONFIG" | "CHARGING_PILE_PURCHASE_CONFIG"
| "CUSTOM_THEME_CONFIG" | "CUSTOM_THEME_CONFIG"
| "CUSTOM_MENU_CONFIG" | "CUSTOM_MENU_CONFIG"
| "WX_CP_NOTIFY_CONFIG"; | "WX_CP_NOTIFY_CONFIG"
| "SMART_RECOGNITION_PROMPT";
/** 系统设置项内容 */ /** 系统设置项内容 */
settingValue: settingValue:
| AliPayConfigValue | AliPayConfigValue
| ChargingPilePurchaseConfig | ChargingPilePurchaseConfig
| CustomMenuConfigValue | CustomMenuConfigValue
| CustomThemeConfigValue | CustomThemeConfigValue
| SmartRecognitionPromptValue
| TencentMapConfigValue | TencentMapConfigValue
| WxCpNotifyConfigValue | WxCpNotifyConfigValue
| WxMaCodeUploadConfigValue | WxMaCodeUploadConfigValue
@ -3879,13 +3892,15 @@ declare namespace BusinessAPI {
| "CHARGING_PILE_PURCHASE_CONFIG" | "CHARGING_PILE_PURCHASE_CONFIG"
| "CUSTOM_THEME_CONFIG" | "CUSTOM_THEME_CONFIG"
| "CUSTOM_MENU_CONFIG" | "CUSTOM_MENU_CONFIG"
| "WX_CP_NOTIFY_CONFIG"; | "WX_CP_NOTIFY_CONFIG"
| "SMART_RECOGNITION_PROMPT";
/** 系统设置项内容 */ /** 系统设置项内容 */
settingValue: settingValue:
| AliPayConfigValue | AliPayConfigValue
| ChargingPilePurchaseConfig | ChargingPilePurchaseConfig
| CustomMenuConfigValue | CustomMenuConfigValue
| CustomThemeConfigValue | CustomThemeConfigValue
| SmartRecognitionPromptValue
| TencentMapConfigValue | TencentMapConfigValue
| WxCpNotifyConfigValue | WxCpNotifyConfigValue
| WxMaCodeUploadConfigValue | WxMaCodeUploadConfigValue
@ -4007,7 +4022,7 @@ declare namespace BusinessAPI {
}; };
type ShipOrderPackage = { type ShipOrderPackage = {
/** 发货单子项ID */ /** 发货单包装信息Id */
orderPackageId: string; orderPackageId: string;
/** 发货单ID */ /** 发货单ID */
shipOrderId: string; shipOrderId: string;
@ -4510,6 +4525,13 @@ declare namespace BusinessAPI {
data?: VehicleExtractionVO; data?: VehicleExtractionVO;
}; };
type SmartRecognitionPromptValue =
// #/components/schemas/SettingValue
SettingValue & {
/** 提示词 */
prompt?: string;
};
type SupplierCheckQry = { type SupplierCheckQry = {
pageSize?: number; pageSize?: number;
pageIndex?: number; pageIndex?: number;

File diff suppressed because one or more lines are too long