- 移除调试日志输出 - 新增 requireQuantityAndPrice 字段支持 - 调整页面样式间距统一为 p-2.5 - 交换工头垫付与产地垫付显示逻辑 - 完善费用弹窗编辑功能,支持数量单价分别输入 - 增加新手引导 tour 功能 - 优化订单成本列表更新逻辑,避免重复项 - 导出新增的费用 section 组件 - 替换 LaborInfoSection为 WorkerAdvanceSection - 引入 ProductionAdvanceSection 组件
605 lines
20 KiB
TypeScript
605 lines
20 KiB
TypeScript
import { ScrollView, Text, View } from "@tarojs/components";
|
||
import { Button, Input, Popup, Radio, SafeArea } from "@nutui/nutui-react-taro";
|
||
import dayjs from "dayjs";
|
||
import { formatCurrency } from "@/utils/format";
|
||
import { useEffect, useState } from "react";
|
||
import businessServices from "@/services/business";
|
||
|
||
export default function BasicInfoSection(props: {
|
||
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
|
||
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
|
||
readOnly?: boolean;
|
||
}) {
|
||
const { purchaseOrderVO, onChange, readOnly } = props;
|
||
|
||
const { orderVehicle } = purchaseOrderVO;
|
||
|
||
// 弹窗可见状态
|
||
const [visiblePopup, setVisiblePopup] = useState({
|
||
basicInfo: false, // 基础信息弹窗
|
||
});
|
||
|
||
// 编辑值的状态(用于弹窗中的临时编辑)
|
||
const [editValues, setEditValues] = useState({
|
||
vehicleNo: orderVehicle?.vehicleNo || "",
|
||
origin: orderVehicle?.origin || "",
|
||
destination: orderVehicle?.destination || "",
|
||
deliveryTime: orderVehicle?.deliveryTime || "",
|
||
plate: orderVehicle?.plate || "",
|
||
phone: orderVehicle?.phone || "",
|
||
priceType: orderVehicle?.priceType || "MAIN_FREIGHT",
|
||
price: orderVehicle?.price || 0,
|
||
openStrawCurtain: orderVehicle?.openStrawCurtain || false,
|
||
strawCurtainPrice: orderVehicle?.strawCurtainPrice || 0,
|
||
});
|
||
|
||
// 上一车次号
|
||
const [lastVehicleNo, setLastVehicleNo] = useState<string | null>(null);
|
||
// 是否正在获取上一车次号
|
||
const [loadingLastVehicleNo, setLoadingLastVehicleNo] = useState(false);
|
||
|
||
// 获取上一车次号
|
||
const fetchLastVehicleNo = async () => {
|
||
// 如果已经有车次号,则不需要获取上一车次号
|
||
if (orderVehicle?.vehicleNo) {
|
||
return;
|
||
}
|
||
|
||
// 避免重复请求
|
||
if (loadingLastVehicleNo || lastVehicleNo) {
|
||
return;
|
||
}
|
||
|
||
setLoadingLastVehicleNo(true);
|
||
try {
|
||
const { data: res } =
|
||
await businessServices.purchaseOrder.getLastVehicleNo({
|
||
lastVehicleNoQry: {},
|
||
});
|
||
|
||
if (res.success && res.data) {
|
||
setLastVehicleNo(res.data);
|
||
|
||
// 解析车次号中的数字部分并加1
|
||
const numberPart = res.data.match(/(\d+)$/);
|
||
if (numberPart) {
|
||
const nextNumber = parseInt(numberPart[1]) + 1;
|
||
const nextVehicleNo = res.data.replace(
|
||
/(\d+)$/,
|
||
nextNumber.toString(),
|
||
);
|
||
|
||
// 更新车次号
|
||
updateVehicleNo(nextVehicleNo);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error("获取上一车次号失败:", error);
|
||
} finally {
|
||
setLoadingLastVehicleNo(false);
|
||
}
|
||
};
|
||
|
||
// 组件加载时获取上一车次号
|
||
useEffect(() => {
|
||
fetchLastVehicleNo();
|
||
}, []);
|
||
|
||
// 打开基础信息弹窗
|
||
const openBasicInfoPopup = () => {
|
||
if (readOnly) return;
|
||
setEditValues({
|
||
vehicleNo: orderVehicle?.vehicleNo || "",
|
||
origin: orderVehicle?.origin || "",
|
||
destination: orderVehicle?.destination || "",
|
||
deliveryTime: orderVehicle?.deliveryTime || "",
|
||
plate: orderVehicle?.plate || "",
|
||
phone: orderVehicle?.phone || "",
|
||
priceType: orderVehicle?.priceType || "MAIN_FREIGHT",
|
||
price: orderVehicle?.price || 0,
|
||
openStrawCurtain: orderVehicle?.openStrawCurtain || false,
|
||
strawCurtainPrice: orderVehicle?.strawCurtainPrice || 0,
|
||
});
|
||
setVisiblePopup((prev) => ({ ...prev, basicInfo: true }));
|
||
};
|
||
|
||
// 保存基础信息
|
||
const saveBasicInfo = () => {
|
||
if (onChange) {
|
||
const updatedOrder = {
|
||
...purchaseOrderVO,
|
||
orderVehicle: {
|
||
...orderVehicle,
|
||
...editValues,
|
||
deliveryTime: editValues.deliveryTime,
|
||
price: Number(editValues.price),
|
||
strawCurtainPrice: Number(editValues.strawCurtainPrice),
|
||
},
|
||
};
|
||
onChange(updatedOrder);
|
||
}
|
||
setVisiblePopup((prev) => ({ ...prev, basicInfo: false }));
|
||
};
|
||
|
||
// 更新车次号
|
||
const updateVehicleNo = (value: string) => {
|
||
if (onChange) {
|
||
const updatedOrder = {
|
||
...purchaseOrderVO,
|
||
orderVehicle: {
|
||
...orderVehicle,
|
||
vehicleNo: value,
|
||
},
|
||
};
|
||
onChange(updatedOrder);
|
||
}
|
||
};
|
||
|
||
// 更新运费类型
|
||
const updatePriceType = (value: BusinessAPI.OrderVehicle["priceType"]) => {
|
||
if (onChange) {
|
||
const updatedOrder = {
|
||
...purchaseOrderVO,
|
||
orderVehicle: {
|
||
...orderVehicle,
|
||
priceType: value,
|
||
},
|
||
};
|
||
onChange(updatedOrder);
|
||
}
|
||
};
|
||
|
||
// 显示的参考车次号
|
||
const displayReferenceVehicleNo = () => {
|
||
if (lastVehicleNo) {
|
||
return lastVehicleNo;
|
||
}
|
||
return "获取中...";
|
||
};
|
||
|
||
return (
|
||
<>
|
||
{/* 基础信息编辑弹窗 */}
|
||
<Popup
|
||
duration={150}
|
||
style={{
|
||
minHeight: "auto",
|
||
}}
|
||
visible={visiblePopup.basicInfo}
|
||
className={"flex flex-col"}
|
||
position="bottom"
|
||
title={"编辑基础信息"}
|
||
onClose={() =>
|
||
setVisiblePopup((prev) => ({ ...prev, basicInfo: false }))
|
||
}
|
||
onOverlayClick={() => {
|
||
setVisiblePopup((prev) => ({ ...prev, basicInfo: false }));
|
||
}}
|
||
lockScroll
|
||
>
|
||
<ScrollView scrollY className="h-96">
|
||
<View className={"flex flex-col gap-3 p-2.5"}>
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
本车次号
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入本车次号"}
|
||
type={"text"}
|
||
value={editValues.vehicleNo}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
vehicleNo: value,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
发货地
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入发货地"}
|
||
type={"text"}
|
||
value={editValues.origin}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
origin: value,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
收货地
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入收货地"}
|
||
type={"text"}
|
||
value={editValues.destination}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
destination: value,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
发货日期
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入发货日期"}
|
||
type={"text"}
|
||
value={editValues.deliveryTime}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
deliveryTime: value,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
车牌号
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入车牌号"}
|
||
type={"text"}
|
||
value={editValues.plate}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
plate: value,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
联系电话
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入联系电话"}
|
||
type={"text"}
|
||
value={editValues.phone}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
phone: value,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
运费类型
|
||
</View>
|
||
<View className="flex flex-row gap-4">
|
||
<View
|
||
className={`flex flex-1 flex-row items-center justify-center rounded-md py-2 ${
|
||
editValues.priceType === "MAIN_FREIGHT"
|
||
? "bg-blue-100 text-blue-600"
|
||
: "border border-gray-300"
|
||
}`}
|
||
onClick={() => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
priceType: "MAIN_FREIGHT",
|
||
}));
|
||
}}
|
||
>
|
||
主运费
|
||
</View>
|
||
<View
|
||
className={`flex flex-1 flex-row items-center justify-center rounded-md py-2 ${
|
||
editValues.priceType === "SHORT_TRANSPORT"
|
||
? "bg-blue-100 text-blue-600"
|
||
: "border border-gray-300"
|
||
}`}
|
||
onClick={() => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
priceType: "SHORT_TRANSPORT",
|
||
}));
|
||
}}
|
||
>
|
||
短驳费
|
||
</View>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
运费(元)
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入运费"}
|
||
type={"digit"}
|
||
value={editValues.price.toString()}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
price: parseFloat(value) || 0,
|
||
}));
|
||
}}
|
||
/>
|
||
<View className="mr-2">元</View>
|
||
</View>
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
草帘费
|
||
</View>
|
||
<View className="flex flex-row gap-4">
|
||
<View
|
||
className={`flex flex-1 flex-row items-center justify-center rounded-md py-2 ${
|
||
editValues.openStrawCurtain
|
||
? "bg-blue-100 text-blue-600"
|
||
: "border border-gray-300"
|
||
}`}
|
||
onClick={() => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
openStrawCurtain: true,
|
||
}));
|
||
}}
|
||
>
|
||
用了
|
||
</View>
|
||
<View
|
||
className={`flex flex-1 flex-row items-center justify-center rounded-md py-2 ${
|
||
!editValues.openStrawCurtain
|
||
? "bg-blue-100 text-blue-600"
|
||
: "border border-gray-300"
|
||
}`}
|
||
onClick={() => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
openStrawCurtain: false,
|
||
}));
|
||
}}
|
||
>
|
||
没用
|
||
</View>
|
||
</View>
|
||
|
||
{editValues.openStrawCurtain && (
|
||
<>
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
草帘费金额(元)
|
||
</View>
|
||
<View
|
||
className={
|
||
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
|
||
}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
placeholder={"请输入草帘费金额"}
|
||
type={"digit"}
|
||
value={editValues.strawCurtainPrice.toString()}
|
||
onChange={(value) => {
|
||
setEditValues((prev) => ({
|
||
...prev,
|
||
strawCurtainPrice: parseFloat(value) || 0,
|
||
}));
|
||
}}
|
||
/>
|
||
<View className="mr-2">元</View>
|
||
</View>
|
||
</>
|
||
)}
|
||
</View>
|
||
</ScrollView>
|
||
<View className={"flex w-full flex-col bg-white"}>
|
||
<View className={"flex flex-row gap-2 p-3"}>
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
size={"large"}
|
||
block
|
||
type="default"
|
||
onClick={() => {
|
||
setVisiblePopup((prev) => ({ ...prev, basicInfo: false }));
|
||
}}
|
||
>
|
||
取消
|
||
</Button>
|
||
</View>
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
size={"large"}
|
||
block
|
||
type="primary"
|
||
onClick={saveBasicInfo}
|
||
>
|
||
保存
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
<SafeArea position={"bottom"} />
|
||
</View>
|
||
</Popup>
|
||
|
||
<View className="rounded-lg border border-solid border-gray-200 bg-white p-3">
|
||
{/* 本车次号 */}
|
||
<View className="mb-4">
|
||
<View className="text-neutral-darkest mb-1 text-sm font-bold">
|
||
本车次号
|
||
</View>
|
||
{readOnly ? (
|
||
<View
|
||
className="flex flex-row items-center justify-between rounded-md p-2.5"
|
||
style={{
|
||
background: "linear-gradient(101deg, #F8FAFF 25%, #F0F5FF 74%)",
|
||
}}
|
||
>
|
||
<View className="text-neutral-darkest text-sm font-medium">
|
||
{orderVehicle?.vehicleNo
|
||
? "第" + orderVehicle?.vehicleNo + "车"
|
||
: "暂未生成车次"}
|
||
</View>
|
||
</View>
|
||
) : (
|
||
<View className="flex flex-row items-center justify-between">
|
||
<View
|
||
className={`flex h-10 flex-1 items-center rounded-md border-4 border-gray-300`}
|
||
>
|
||
<Input
|
||
placeholder="请输入车次号"
|
||
type="text"
|
||
value={orderVehicle?.vehicleNo || ""}
|
||
onChange={(value) => updateVehicleNo(value)}
|
||
/>
|
||
</View>
|
||
<View className="flex-1 text-center text-xs text-gray-500">
|
||
参考上一车:{displayReferenceVehicleNo()}
|
||
</View>
|
||
</View>
|
||
)}
|
||
</View>
|
||
|
||
{/* 运费信息 */}
|
||
<View className="mb-4 flex flex-col gap-2.5">
|
||
<View className="text-neutral-darkest mb-1 text-sm font-bold">
|
||
运费信息
|
||
</View>
|
||
<View
|
||
className="flex flex-row items-center justify-between rounded-md p-2.5"
|
||
style={{
|
||
background: "linear-gradient(101deg, #F8FAFF 25%, #F0F5FF 74%)",
|
||
}}
|
||
>
|
||
<Text className="pb-2 text-3xl font-bold text-red-500">
|
||
{formatCurrency(orderVehicle?.price || 0)}
|
||
</Text>
|
||
<View className="text-neutral-darkest text-sm font-medium">
|
||
{readOnly ? (
|
||
<View>
|
||
{orderVehicle?.priceType === "MAIN_FREIGHT"
|
||
? "主运费"
|
||
: orderVehicle?.priceType === "SHORT_TRANSPORT"
|
||
? "短驳费"
|
||
: "未选择"}
|
||
</View>
|
||
) : (
|
||
<Radio.Group
|
||
direction={"horizontal"}
|
||
value={orderVehicle?.priceType}
|
||
onChange={(value) =>
|
||
updatePriceType(
|
||
value as BusinessAPI.OrderVehicle["priceType"],
|
||
)
|
||
}
|
||
>
|
||
<Radio value="MAIN_FREIGHT">主运费</Radio>
|
||
<Radio value="SHORT_TRANSPORT">短驳费</Radio>
|
||
</Radio.Group>
|
||
)}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 草帘 */}
|
||
<View className="mb-4 flex flex-col gap-2.5">
|
||
<View className="text-neutral-darkest mb-1 text-sm font-bold">
|
||
草帘
|
||
</View>
|
||
|
||
<View
|
||
className="flex flex-row items-center justify-between rounded-md p-2.5"
|
||
style={{
|
||
background: "linear-gradient(101deg, #F8FAFF 25%, #F0F5FF 74%)",
|
||
}}
|
||
>
|
||
{orderVehicle?.openStrawCurtain ? (
|
||
<Text className="pb-2 text-3xl font-bold text-red-500">
|
||
{formatCurrency(orderVehicle?.strawCurtainPrice || 0)}
|
||
</Text>
|
||
) : (
|
||
<View className="text-neutral-darkest text-sm font-medium">
|
||
没用
|
||
</View>
|
||
)}
|
||
</View>
|
||
</View>
|
||
|
||
{/* 路线和其他信息卡片 */}
|
||
<View className="mb-4 rounded-lg bg-gray-50 p-3">
|
||
<View className="mb-2 flex flex-row justify-between">
|
||
<View className="text-neutral-dark text-xs">路线</View>
|
||
<View className="text-neutral-darkest text-xs font-medium">
|
||
{orderVehicle?.origin || "发货地"} {"->"}{" "}
|
||
{orderVehicle?.destination || "收货地"}
|
||
</View>
|
||
</View>
|
||
<View className="flex flex-row justify-between">
|
||
<View className="text-neutral-dark text-xs">信息</View>
|
||
<View className="text-neutral-darkest text-xs font-medium">
|
||
{orderVehicle?.deliveryTime
|
||
? dayjs(orderVehicle.deliveryTime).format("YYYY-MM-DD")
|
||
: "发货日期"}{" "}
|
||
| {orderVehicle?.plate || "车牌号"} |{" "}
|
||
{orderVehicle?.phone || "联系电话"}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
{!readOnly && (
|
||
<View className="flex justify-center border-t border-gray-200 p-2">
|
||
<Button
|
||
size="small"
|
||
type="primary"
|
||
fill="outline"
|
||
onClick={openBasicInfoPopup}
|
||
>
|
||
修改基础信息
|
||
</Button>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</>
|
||
);
|
||
}
|