refactor(components): 优化采购模块空箱和费用组件实现

- 移除 PageList 组件中对全局 loading 状态的依赖
- 简化 EmptyBoxModule 组件逻辑,使用 PackageList 组件替代原有复杂实现
- 移除冗余的状态管理和弹窗渲染逻辑
- 优化 OrderCost 组件样式和费用项匹配逻辑
- 修复成本项 ID 匹配问题,确保数据正确关联
- 添加边框样式增强视觉效果
- 移除调试日志和无用代码
- 简化组件间数据传递方式
This commit is contained in:
shenyifei 2025-12-11 12:42:01 +08:00
parent 837c27e9bd
commit dfe9a89213
21 changed files with 1033 additions and 1681 deletions

View File

@ -18,7 +18,6 @@ import {
} from "@nutui/nutui-react-taro"; } from "@nutui/nutui-react-taro";
import { Icon } from "@/components"; import { Icon } from "@/components";
import classNames from "classnames"; import classNames from "classnames";
import { globalStore } from "@/store/global-store";
import { ActionType, IRecordListProps, Query, Record } from "./typing"; import { ActionType, IRecordListProps, Query, Record } from "./typing";
export default <T extends {}, Q extends Query = Query>( export default <T extends {}, Q extends Query = Query>(
@ -48,7 +47,6 @@ export default <T extends {}, Q extends Query = Query>(
toolbar?.search?.defaultActiveKey || "", toolbar?.search?.defaultActiveKey || "",
); );
const { loading, setLoading } = globalStore((state: any) => state);
const [lightTheme, setLightTheme] = useState(false); const [lightTheme, setLightTheme] = useState(false);
const [data, setData] = useState<Record<any>[]>(); const [data, setData] = useState<Record<any>[]>();
const [query, setQuery] = useState<Query>(); const [query, setQuery] = useState<Query>();
@ -109,11 +107,10 @@ export default <T extends {}, Q extends Query = Query>(
} }
}) })
.exec(); .exec();
}, [loading, toolbar]); }, [toolbar]);
useEffect(() => { useEffect(() => {
if (query) { if (query) {
setLoading(true);
request(query as any).then((res: Record<any>) => { request(query as any).then((res: Record<any>) => {
const list = res.data; const list = res.data;
if (res.success) { if (res.success) {
@ -132,7 +129,6 @@ export default <T extends {}, Q extends Query = Query>(
if (res.hasMore !== undefined) { if (res.hasMore !== undefined) {
setHasMore(res.hasMore); setHasMore(res.hasMore);
} }
setLoading(false);
} }
}); });
} }
@ -141,8 +137,7 @@ export default <T extends {}, Q extends Query = Query>(
const [hasMore, setHasMore] = useState<boolean>(); const [hasMore, setHasMore] = useState<boolean>();
const loadMore = async () => { const loadMore = async () => {
if (hasMore && !loading && query?.pageIndex) { if (hasMore && query?.pageIndex) {
setLoading(true);
setQuery({ setQuery({
...query, ...query,
pageIndex: query.pageIndex + 1, pageIndex: query.pageIndex + 1,
@ -166,7 +161,7 @@ export default <T extends {}, Q extends Query = Query>(
); );
}; };
if (loading || !data) { if (!data) {
if (skeleton) { if (skeleton) {
return renderSkeleton(); return renderSkeleton();
} }

View File

@ -1,5 +1,6 @@
import { import {
ActionType, ActionType,
CopyText,
DealerPicker, DealerPicker,
Icon, Icon,
PageList, PageList,
@ -167,9 +168,11 @@ export default function PurchaseOrderApprovalList(
<Label className={"text-neutral-dark text-sm"}> <Label className={"text-neutral-dark text-sm"}>
</Label> </Label>
<CopyText copyData={purchaseOrderVO?.orderSn || "-"}>
<Text className={"text-neutral-darkest text-sm"}> <Text className={"text-neutral-darkest text-sm"}>
{purchaseOrderVO.orderSn} {purchaseOrderVO.orderSn}
</Text> </Text>
</CopyText>
</View> </View>
<View <View
className={ className={

View File

@ -1,5 +1,6 @@
import { import {
ActionType, ActionType,
CopyText,
DealerPicker, DealerPicker,
Icon, Icon,
PageList, PageList,
@ -167,9 +168,11 @@ export default function PurchaseOrderAuditList(
<Label className={"text-neutral-dark text-sm"}> <Label className={"text-neutral-dark text-sm"}>
</Label> </Label>
<CopyText copyData={purchaseOrderVO?.orderSn || "-"}>
<Text className={"text-neutral-darkest text-sm"}> <Text className={"text-neutral-darkest text-sm"}>
{purchaseOrderVO.orderSn} {purchaseOrderVO.orderSn}
</Text> </Text>
</CopyText>
</View> </View>
<View <View
className={ className={

View File

@ -52,6 +52,13 @@ export default function CostCard(props: CostCardComponentProps) {
}); });
}; };
if (
orderCostItemList.length == 0 &&
(orderCost.type === "ARTIFICIAL_TYPE" || orderCost.type === "MATERIAL_TYPE")
) {
return;
}
return ( return (
<> <>
<View className="bg-primary/3 rounded-lg border-b border-gray-100 p-2.5"> <View className="bg-primary/3 rounded-lg border-b border-gray-100 p-2.5">

View File

@ -1,24 +1,10 @@
import { ScrollView, View } from "@tarojs/components"; import { View } from "@tarojs/components";
import { Icon } from "@/components"; import { PackageList } from "@/components";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { business } from "@/services"; import { business } from "@/services";
import { import { Checkbox } from "@nutui/nutui-react-taro";
Button,
Checkbox,
Dialog,
Image,
Input,
Popup,
SafeArea,
Toast,
} from "@nutui/nutui-react-taro";
import { BoxBrand, BoxProduct, BoxSpec } from "@/types/typings"; import { BoxBrand, BoxProduct, BoxSpec } from "@/types/typings";
import { import { generateShortId } from "@/utils";
convertBoxBrandToOrderPackages,
convertOrderPackagesToBoxBrands,
generateShortId,
} from "@/utils";
import classNames from "classnames";
export interface IEmptyBoxModuleProps { export interface IEmptyBoxModuleProps {
orderPackageList: BusinessAPI.OrderPackage[]; orderPackageList: BusinessAPI.OrderPackage[];
@ -28,23 +14,9 @@ export interface IEmptyBoxModuleProps {
export default function EmptyBoxModule(props: IEmptyBoxModuleProps) { export default function EmptyBoxModule(props: IEmptyBoxModuleProps) {
const { orderPackageList, onChange } = props; const { orderPackageList, onChange } = props;
// 空箱相关状态 const [hasEmptyBox, setHasEmptyBox] = useState(false);
const [emptyBoxList, setEmptyBoxList] = useState<BusinessAPI.OrderPackage[]>(
[],
);
const [boxBrandList, setBoxBrandList] = useState<BoxBrand[]>(); const [boxBrandList, setBoxBrandList] = useState<BoxBrand[]>();
// 批量添加空箱相关状态
const [selectedBrand, setSelectedBrand] = useState<BoxBrand | null>(null);
const [productCounts, setProductCounts] = useState<Map<string, number>>(
new Map(),
);
const [showBatchModal, setShowBatchModal] = useState(false);
// 编辑空箱弹窗状态
const [showEditModal, setShowEditModal] = useState(false);
const [editingItem, setEditingItem] = useState<BoxBrand | null>(null);
// 初始化空箱品牌数据 // 初始化空箱品牌数据
const initBoxBrandList = async () => { const initBoxBrandList = async () => {
const { data } = await business.boxBrand.listBoxBrand({ const { data } = await business.boxBrand.listBoxBrand({
@ -126,833 +98,51 @@ export default function EmptyBoxModule(props: IEmptyBoxModuleProps) {
// 当传入的value发生变化时重新初始化列表 // 当传入的value发生变化时重新初始化列表
useEffect(() => { useEffect(() => {
initBoxBrandList().then(); initBoxBrandList().then();
setEmptyBoxList(orderPackageList);
if (orderPackageList?.length > 0) { if (orderPackageList?.length > 0) {
// 根据当前供应商确定需要检查的纸箱类型 setHasEmptyBox(true);
let requiredTypes: string[] = ["EMPTY"];
requiredTypes.map((type) => {
setPackageTypeEnabled((prev) => ({
...prev,
[type]: orderPackageList.some((item) => item.boxType === type)
? 1
: 0,
}));
});
} }
}, []); }, []);
// 当空箱列表变化时,通知父组件更新 console.log("orderPackageList", orderPackageList);
useEffect(() => {
onChange(
emptyBoxList?.map((item) => {
return {
...item,
id: generateShortId(),
};
}),
);
}, [emptyBoxList]);
// 处理批量添加时的品牌选择
const handleBatchBrandSelect = (brand: BoxBrand) => {
// 检查是否已存在该品牌
const isBrandAlreadySelected = emptyBoxList.some(
(pkg) => pkg.boxBrandId === brand.boxBrandId,
);
if (isBrandAlreadySelected) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "该空箱品牌已存在,请选择其他品牌或编辑现有信息",
});
return;
}
setSelectedBrand(brand);
// 初始化所有产品的数量为0
const initialCounts = new Map<string, number>();
brand.boxSpecList?.forEach((boxSpec) => {
boxSpec.boxProductList.forEach((product) => {
initialCounts.set(product.id, 0);
});
});
setProductCounts(initialCounts);
};
// 处理产品数量变化
const handleProductCountChange = (productId: string, count: number) => {
setProductCounts((prev) => {
const newCounts = new Map(prev);
newCounts.set(productId, Math.max(0, count)); // 允许为0表示不使用
return newCounts;
});
// 同时更新selectedBrand中的产品数量
if (selectedBrand) {
const updatedBrand = { ...selectedBrand };
updatedBrand.boxSpecList = updatedBrand.boxSpecList?.map((boxSpec) => {
const updatedProducts = boxSpec.boxProductList.map((product) => {
if (product.id === productId) {
return { ...product, boxCount: count };
}
return product;
});
return { ...boxSpec, boxProductList: updatedProducts };
});
setSelectedBrand(updatedBrand);
}
};
// 批量添加空箱信息
const addBatchEmptyBoxInfo = () => {
if (!selectedBrand) {
return;
}
// 使用convertBoxBrandToOrderPackages转换数据
const newOrderPackages = convertBoxBrandToOrderPackages(
selectedBrand,
"EMPTY",
);
// 过滤掉数量为0的项目
const filteredOrderPackages = newOrderPackages.filter((pkg) => {
// 从productCounts中获取对应产品的数量
const product = selectedBrand.boxSpecList
?.flatMap((c) => c.boxProductList)
.find((p) => p.id === pkg.orderPackageId);
const count = product ? productCounts.get(product.id) || 0 : 0;
return count > 0;
});
// 更新数量信息
const updatedOrderPackages = filteredOrderPackages.map((pkg) => {
// 从productCounts中获取对应产品的数量
const product = selectedBrand.boxSpecList
?.flatMap((c) => c.boxProductList)
.find((p) => p.id === pkg.orderPackageId);
const count = product ? productCounts.get(product.id) || 0 : 0;
return {
...pkg,
boxCount: count,
};
});
// 添加到emptyBoxList中
setEmptyBoxList((prev) => [...prev, ...updatedOrderPackages]);
// 重置选择并关闭弹窗
setSelectedBrand(null);
setProductCounts(new Map());
setShowBatchModal(false);
};
// 删除空箱信息
const removeEmptyBoxInfo = (boxBrandId: string) => {
console.log("removeEmptyBoxInfo", boxBrandId);
Dialog.open("dialog", {
title: "提示",
content: "确定要移除此空箱品牌吗?",
confirmText: "确定",
cancelText: "取消",
onConfirm: () => {
const newList = [
...emptyBoxList.filter((item) => item.boxBrandId !== boxBrandId),
];
setEmptyBoxList(newList);
Dialog.close("dialog");
},
onCancel: () => {
Dialog.close("dialog");
},
});
};
// 处理编辑按钮点击
const handleEditItem = (item: BoxBrand) => {
// 从emptyBoxList中找到完整的品牌信息用于编辑
const brandToEdit = convertOrderPackagesToBoxBrands(
emptyBoxList.filter((pkg) => pkg.boxBrandId === item.boxBrandId),
)[0];
// 获取完整品牌信息(包括所有规格)
const fullBrandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === item.boxBrandId,
);
// 合并已有的数据和完整品牌信息
const mergedBrandInfo = fullBrandInfo
? ({
...fullBrandInfo,
boxType: "EMPTY", // 保持boxType
boxSpecList: fullBrandInfo.boxSpecList?.map((boxSpec) => {
// 找到对应的已存在分类数据
const existingSpec = brandToEdit?.boxSpecList?.find(
(spec) => spec.boxSpecId === boxSpec.boxSpecId,
);
return {
...boxSpec,
boxProductList: boxSpec.boxProductList.map((product) => {
// 找到对应的产品数据
const existingProduct = existingSpec?.boxProductList.find(
(prod) => prod.boxProductId === product.boxProductId,
);
return {
...product,
boxCount: existingProduct?.boxCount || 0,
};
}),
};
}),
} as BoxBrand)
: brandToEdit;
if (mergedBrandInfo) {
setEditingItem(mergedBrandInfo);
setShowEditModal(true);
}
};
// 更新编辑项中的数量
const updateEditingItemCount = (
specIndex: number,
detailIndex: number,
count: number,
) => {
if (!editingItem) return;
// 获取完整品牌信息
const fullBrandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === editingItem.boxBrandId,
);
if (!fullBrandInfo) return;
const updatedItem = { ...editingItem };
// 确保boxSpecList存在
if (!updatedItem.boxSpecList) {
updatedItem.boxSpecList = fullBrandInfo.boxSpecList.map((boxSpec) => ({
...boxSpec,
boxProductList: boxSpec.boxProductList.map((product) => ({
...product,
boxCount: 0,
})),
}));
}
// 确保spec存在
if (!updatedItem.boxSpecList[specIndex]) {
updatedItem.boxSpecList[specIndex] = {
...fullBrandInfo.boxSpecList[specIndex],
boxProductList: fullBrandInfo.boxSpecList[specIndex].boxProductList.map(
(product) => ({
...product,
boxCount: 0,
}),
),
};
}
// 更新对应的产品数量
const boxSpec = { ...updatedItem.boxSpecList[specIndex] };
// 确保boxProductList存在
if (!boxSpec.boxProductList) {
boxSpec.boxProductList = fullBrandInfo.boxSpecList[
specIndex
].boxProductList.map((product) => ({
...product,
boxCount: 0,
}));
}
const boxProductList = [...boxSpec.boxProductList];
// 确保产品存在
if (!boxProductList[detailIndex]) {
boxProductList[detailIndex] = {
...fullBrandInfo.boxSpecList[specIndex].boxProductList[detailIndex],
boxCount: 0,
};
}
boxProductList[detailIndex] = {
...boxProductList[detailIndex],
boxCount: count,
};
boxSpec.boxProductList = boxProductList;
const updatedSpecList = [...updatedItem.boxSpecList];
updatedSpecList[specIndex] = boxSpec;
updatedItem.boxSpecList = updatedSpecList;
setEditingItem(updatedItem);
};
// 保存编辑的空箱信息
const saveEditedItem = () => {
if (editingItem) {
// 检查是否尝试更改为已存在的品牌(排除自身)
const isBrandAlreadySelected = emptyBoxList.some(
(pkg) =>
pkg.boxBrandId === editingItem.boxBrandId &&
!(pkg.boxBrandId === editingItem.boxBrandId),
);
if (isBrandAlreadySelected) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "该空箱品牌已存在,请选择其他品牌",
});
return;
}
// 使用convertBoxBrandToOrderPackages转换编辑后的数据
const updatedOrderPackages = convertBoxBrandToOrderPackages(
editingItem,
"EMPTY",
).filter((pkg) => pkg.boxCount > 0);
// 更新emptyBoxList
const newList = [...emptyBoxList];
// 找到对应品牌和类型的现有项目并替换
const startIndex = newList.findIndex(
(pkg) => pkg.boxBrandId === editingItem.boxBrandId,
);
if (startIndex !== -1) {
// 移除旧的项目
const filteredList = newList.filter(
(pkg) => !(pkg.boxBrandId === editingItem.boxBrandId),
);
// 添加更新后的项目
setEmptyBoxList([...filteredList, ...updatedOrderPackages]);
} else {
// 如果没有找到,直接添加
setEmptyBoxList((prev) => [...prev, ...updatedOrderPackages]);
}
}
setShowEditModal(false);
setEditingItem(null);
};
// 渲染单个空箱信息模块
const renderEmptyBoxItem = (item: BoxBrand) => {
// 获取品牌信息用于展示品牌图片
const brandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === item.boxBrandId,
);
return (
<View
className="mb-2.5 overflow-hidden rounded-xl border border-gray-200 bg-white p-2.5 shadow-sm"
key={item.id}
>
{/* 品牌背景水印 */}
{brandInfo?.boxBrandImage && (
<View className="absolute top-2 right-2 opacity-10">
<Image
src={brandInfo.boxBrandImage}
className="h-full w-full"
mode="aspectFit"
/>
</View>
)}
{/* 品牌信息和操作按钮放在同一行 */}
<View className="mb-3 flex items-start justify-between">
<View className="flex items-center">
{brandInfo?.boxBrandImage && (
<View className="mr-3 h-10 w-10 overflow-hidden rounded-lg border border-gray-200">
<Image
src={brandInfo.boxBrandImage}
className="h-full w-full"
mode="aspectFit"
/>
</View>
)}
<View>
<View className="text-lg font-bold text-gray-800">
{item.boxBrandName || "未选择品牌"}
</View>
</View>
</View>
{/* 操作按钮与品牌信息放在同一行 */}
<View className="flex gap-2">
<View
className="cursor-pointer rounded-lg bg-blue-100 px-3 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-200"
onClick={() => handleEditItem(item)}
>
</View>
<View
className="cursor-pointer rounded-lg bg-red-100 px-3 py-1 text-xs font-medium text-red-600 transition-colors hover:bg-red-200"
onClick={() => {
console.log("removeEmptyBoxInfo", item.boxBrandId);
removeEmptyBoxInfo(item.boxBrandId);
}}
>
</View>
</View>
</View>
{/* 详细信息展示,按分类显示 */}
<View className="space-y-2">
{item.boxSpecList &&
item.boxSpecList.map((boxSpec, boxIndex) => (
<View
key={boxIndex}
className="rounded-lg bg-gray-50 p-3 text-sm text-gray-600"
>
<View className="mb-1 font-medium text-gray-700">
{boxSpec.boxSpecName}
</View>
<View>
{boxSpec.boxProductList
.map(
(detail) =>
`${detail.boxProductName}${detail.boxCount}`,
)
.join("")}
</View>
</View>
))}
</View>
</View>
);
};
// 渲染批量添加空箱弹窗
const renderBatchAddModal = () => {
// 检查是否至少有一个产品的数量大于0
const hasAnyProductWithCount = selectedBrand
? Array.from(productCounts.values()).some((count) => count > 0)
: false;
return (
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={showBatchModal}
position="bottom"
onClose={() => {
setShowBatchModal(false);
setSelectedBrand(null);
setProductCounts(new Map());
}}
title={"批量添加空箱"}
round
>
<View className="p-2.5">
<ScrollView
scrollY
style={{
height: "65vh",
width: "100%",
}}
>
{/* 品牌选择 */}
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600"></View>
<ScrollView className="mb-2.5" scrollX>
<View className="flex w-fit flex-row gap-2.5">
{boxBrandList
?.filter((item) => item.boxBrandType !== "FARMER_BOX")
?.map((boxBrand) => (
<View
key={boxBrand.id}
className={"flex flex-col items-center justify-center"}
onClick={() => handleBatchBrandSelect(boxBrand)}
>
<View
className={classNames(
"border-primary box-content !size-16 overflow-hidden rounded-xl border-4 object-cover",
{
"border-primary":
selectedBrand?.id === boxBrand.id,
"border-transparent":
selectedBrand?.id !== boxBrand.id,
},
)}
>
<Image
src={boxBrand.boxBrandImage}
className="h-full w-full"
mode={"aspectFill"}
alt={boxBrand.boxBrandImage}
/>
</View>
<View className="text-center text-xs">
{boxBrand.boxBrandName}
</View>
</View>
))}
</View>
</ScrollView>
</View>
{/* 未选择品牌时的提示 */}
{!selectedBrand && (
<View className="mb-4 rounded-lg bg-yellow-50 p-4 text-center">
<View className="text-yellow-800"></View>
</View>
)}
{/* 产品展示 */}
{selectedBrand && (
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600">
+/- 0使
</View>
{selectedBrand.boxSpecList?.map((boxSpec) => (
<View key={boxSpec.id} className="mb-4">
<View className="mb-2 text-base font-medium">
{boxSpec.boxSpecName}
</View>
<View className="space-y-3">
{boxSpec.boxProductList.map((boxProduct) => {
const currentCount =
productCounts.get(boxProduct.id) || 0;
return (
<View
key={boxProduct.id}
className="flex items-center justify-between rounded-lg bg-gray-50 p-3"
>
<View className="text-gray-800">
{boxProduct.boxProductName}
</View>
<View className="flex items-center">
<View
className="flex h-8 w-8 items-center justify-center rounded-l bg-gray-200"
onClick={() => {
handleProductCountChange(
boxProduct.id,
Math.max(0, currentCount - 1),
);
}}
>
<Icon name="minus" size={16} />
</View>
<View className="flex h-8 w-12 items-center justify-center border-y border-gray-200">
<Input
type="number"
value={currentCount.toString()}
align={"center"}
className="!h-8 !w-12 !p-0 !text-center"
onChange={(value) => {
const num = Number(value);
if (!Number.isNaN(num) && num >= 0) {
handleProductCountChange(
boxProduct.id,
num,
);
}
}}
/>
</View>
<View
className="flex h-8 w-8 items-center justify-center rounded-r bg-gray-200"
onClick={() => {
handleProductCountChange(
boxProduct.id,
currentCount + 1,
);
}}
>
<Icon name="plus" size={16} />
</View>
<View className="ml-2 text-gray-800"></View>
</View>
</View>
);
})}
</View>
</View>
))}
</View>
)}
</ScrollView>
{/* 底部按钮 */}
<View className="flex gap-2 pt-4">
<View className="flex-1">
<Button
type={"default"}
size={"large"}
block
onClick={() => {
setShowBatchModal(false);
setSelectedBrand(null);
setProductCounts(new Map());
}}
>
</Button>
</View>
<View className="flex-1">
<Button
type={"primary"}
size={"large"}
block
disabled={!selectedBrand || !hasAnyProductWithCount}
onClick={addBatchEmptyBoxInfo}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
);
};
// 渲染编辑空箱弹窗
const renderEditModal = () => {
if (!editingItem) return null;
// 获取品牌信息
const brandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === editingItem.boxBrandId,
);
return (
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={showEditModal}
position="bottom"
onClose={() => setShowEditModal(false)}
title={"编辑空箱信息"}
round
>
<View className="p-2.5">
<ScrollView
scrollY
style={{
height: "65vh",
width: "100%",
}}
>
{/* 品牌信息 */}
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600"></View>
<View className="flex items-center rounded-lg bg-gray-50 p-3">
{brandInfo?.boxBrandImage && (
<View className="border-primary mr-3 h-16 w-16 overflow-hidden rounded-xl border-4 object-cover">
<Image
src={brandInfo.boxBrandImage}
className="h-full w-full"
mode="aspectFill"
/>
</View>
)}
<View className="text-base font-medium text-gray-800">
{editingItem.boxBrandName}
</View>
</View>
</View>
{/* 详细信息 */}
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600"></View>
<View className="space-y-3">
{editingItem?.boxSpecList &&
editingItem.boxSpecList.map((boxSpec, specId) => (
<View key={specId} className="rounded-lg bg-gray-50 p-3">
<View className="mb-2 text-base font-medium text-gray-800">
{boxSpec.boxSpecName}
</View>
<View className="space-y-2">
{boxSpec.boxProductList.map((detail, detailIndex) => {
const currentCount = detail?.boxCount || 0;
return (
<View
key={detailIndex}
className="flex items-center justify-between py-1"
>
<View className="text-gray-600">
{detail.boxProductName}
</View>
<View className="flex items-center">
<View
className="flex h-8 w-8 items-center justify-center rounded-l bg-gray-200"
onClick={() => {
const newCount = Math.max(
0,
currentCount - 1,
);
updateEditingItemCount(
specId,
detailIndex,
newCount,
);
}}
>
<Icon name="minus" size={16} />
</View>
<View className="flex h-8 w-12 items-center justify-center border-y border-gray-200">
<Input
type="number"
value={currentCount.toString()}
align={"center"}
className="!h-8 !w-12 !p-0 !text-center"
onChange={(value) => {
const num = Number(value);
if (!Number.isNaN(num) && num >= 0) {
updateEditingItemCount(
specId,
detailIndex,
num,
);
}
}}
/>
</View>
<View
className="flex h-8 w-8 items-center justify-center rounded-r bg-gray-200"
onClick={() => {
updateEditingItemCount(
specId,
detailIndex,
currentCount + 1,
);
}}
>
<Icon name="plus" size={16} />
</View>
<View className="ml-2 font-medium text-gray-800">
</View>
</View>
</View>
);
})}
</View>
</View>
))}
</View>
</View>
</ScrollView>
{/* 底部按钮 */}
<View className="flex gap-2 pt-4">
<View className="flex-1">
<Button
size={"large"}
type={"default"}
block
onClick={() => setShowEditModal(false)}
>
</Button>
</View>
<View className="flex-1">
<Button
type="primary"
size={"large"}
block
onClick={saveEditedItem}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
);
};
// 获取空箱列表
const emptyBoxes = convertOrderPackagesToBoxBrands(emptyBoxList);
// 新增状态:跟踪每种纸箱类型的启用状态
// 0: 未选, 1: 选择是, 2: 选择否
const [packageTypeEnabled, setPackageTypeEnabled] = useState({
EMPTY: 0,
});
// 处理纸箱类型启用状态切换
const togglePackageType = (
type: BusinessAPI.OrderPackage["boxType"],
value: number, // 0: 未选, 1: 选择是, 2: 选择否
) => {
setPackageTypeEnabled((prev) => ({
...prev,
[type]: value,
}));
};
return ( return (
<View className={"flex flex-1 flex-col gap-2.5"}> <View className={"flex flex-1 flex-col gap-2.5"}>
<View className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"> <View className="bg-primary/10 flex flex-col gap-2.5 rounded-lg p-2.5 text-sm text-gray-600">
<View className="flex flex-col gap-2.5">
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<View className="text-sm"></View> <View className="block text-sm font-normal text-[#000000]">
</View>
<View className="flex flex-row items-center"> <View className="flex flex-row items-center">
<Checkbox <Checkbox
className={"flex flex-row items-center"} className={"flex flex-row items-center"}
checked={packageTypeEnabled.EMPTY === 1} checked={hasEmptyBox}
onChange={(checked) => { onChange={(checked) => {
togglePackageType("EMPTY", checked ? 1 : 0); setHasEmptyBox(checked);
if (!checked) { if (!checked) {
setEmptyBoxList([]); onChange([]);
} }
}} }}
> >
<View className={"text-sm font-normal text-[#000000]"}></View> <View className={"text-sm font-normal text-[#000000]"}>
</View>
</Checkbox> </Checkbox>
</View> </View>
</View> </View>
{/* 空箱信息展示 */} {/* 空箱信息展示 */}
{packageTypeEnabled.EMPTY === 1 && ( {hasEmptyBox && (
<View> <PackageList
{emptyBoxes.length > 0 ? ( boxType={"EMPTY"}
emptyBoxes.map((item) => renderEmptyBoxItem(item)) orderPackageList={orderPackageList}
) : ( setOrderPackageList={onChange}
<View className="mb-4 rounded-lg bg-gray-50 p-4 text-center text-gray-500"> boxBrandList={boxBrandList}
/>
</View>
)}
<Button
icon={<Icon name={"plus"} size={20} />}
type={"primary"}
size={"large"}
fill={"outline"}
block
className="border-primary text-primary flex w-full items-center justify-center !border-2 !bg-white"
onClick={() => {
setShowBatchModal(true);
}}
>
<View></View>
</Button>
</View>
)} )}
</View> </View>
</View>
{/* 批量添加弹窗 */}
{renderBatchAddModal()}
{/* 编辑弹窗 */}
{renderEditModal()}
</View> </View>
); );
} }

View File

@ -595,7 +595,7 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
{/* 只有最后一个瓜农才显示是否为最后一个瓜农的选项和添加按钮 */} {/* 只有最后一个瓜农才显示是否为最后一个瓜农的选项和添加按钮 */}
{isLast && ( {isLast && (
<View className="rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
{supplierCount > 1 ? ( {supplierCount > 1 ? (
<View className="text-sm"></View> <View className="text-sm"></View>
@ -711,7 +711,7 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
</View> </View>
{/* 瓜农信息 */} {/* 瓜农信息 */}
<View className="rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="mb-2.5"> <View className="mb-2.5">
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<View className={"text-primary text-sm font-bold"}> <View className={"text-primary text-sm font-bold"}>
@ -910,7 +910,7 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
</View> </View>
{/* 若瓜农无法开发票,则可打款到微信 */} {/* 若瓜农无法开发票,则可打款到微信 */}
<View className="rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={`flex w-full border-gray-300`}> <View className={`flex w-full border-gray-300`}>
<Uploader <Uploader
className={"w-full"} className={"w-full"}

View File

@ -3,6 +3,7 @@ import { Icon } from "@/components";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { Checkbox, Input, Toast } from "@nutui/nutui-react-taro"; import { Checkbox, Input, Toast } from "@nutui/nutui-react-taro";
import { generateShortId } from "@/utils"; import { generateShortId } from "@/utils";
import { business } from "@/services";
// 定义ref暴露的方法接口 // 定义ref暴露的方法接口
export interface OrderCostRef { export interface OrderCostRef {
@ -21,11 +22,14 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
const { value, onChange } = props; const { value, onChange } = props;
const { orderSupplierList, orderCostList } = value; const { orderSupplierList, orderCostList } = value;
console.log("orderCostList", orderCostList);
const supplierVO = orderSupplierList[orderSupplierList.length - 1]; const supplierVO = orderSupplierList[orderSupplierList.length - 1];
const costTemplate = supplierVO?.costTemplate; const costTemplate = supplierVO?.costTemplate;
const costIds = supplierVO?.costIds; const costIds = supplierVO?.costIds;
console.log("costIds", costIds);
// 解析costTemplate为JSON对象 // 解析costTemplate为JSON对象
const parsedCostTemplate = JSON.parse(costTemplate || "[]"); const parsedCostTemplate = JSON.parse(costTemplate || "[]");
console.log("parsedCostTemplate", parsedCostTemplate);
// 初始化函数 // 初始化函数
const init = async (costIds: string[]) => { const init = async (costIds: string[]) => {
@ -43,7 +47,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
// 遍历costList将costList中的项目添加到orderCostList中 // 遍历costList将costList中的项目添加到orderCostList中
parsedCostTemplate.productionTypeList?.forEach((item) => { parsedCostTemplate.productionTypeList?.forEach((item) => {
const existingItem = orderCostMap.get(item.costItemId); const existingItem = orderCostMap.get(item.costId);
if (existingItem) { if (existingItem) {
newOrderCostList.push({ newOrderCostList.push({
@ -62,6 +66,44 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
price: item.price || 0, price: item.price || 0,
unit: item.unit || "元", unit: item.unit || "元",
}); });
orderCostMap.set(item.costId, {
orderCostId: generateShortId(),
costItemIds: item.costItemIds,
costId: item.costId,
name: item.name,
type: item.type,
selected: false,
count: 1,
price: item.price || 0,
unit: item.unit || "元",
});
}
});
const { data } = await business.cost.listCost({
costListQry: {
status: true,
},
});
data.data?.forEach((item) => {
if (
item.costId &&
costIds?.includes(item.costId) &&
!newOrderCostList.some((cost) => cost.costId === item.costId)
) {
newOrderCostList.push({
orderCostId: generateShortId(),
costItemIds: item.costItemIds,
costId: item.costId,
name: item.name,
type: item.type,
selected: false,
count: 1,
price: item.price || 0,
unit: item.unit || "元",
});
} }
}); });
@ -163,8 +205,12 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
const orderCostId = currentItem?.orderCostId || generateShortId(); const orderCostId = currentItem?.orderCostId || generateShortId();
return ( return (
<View key={index}> <View
<View className={"flex flex-col gap-2.5 rounded-lg bg-white p-2.5"}> key={index}
className={
"bg-primary/10 flex flex-col gap-2.5 rounded-lg p-2.5 text-sm text-gray-600"
}
>
<View className={"flex flex-row justify-between"}> <View className={"flex flex-row justify-between"}>
<View className="block text-sm font-normal text-[#000000]"> <View className="block text-sm font-normal text-[#000000]">
{item.name} {item.name}
@ -214,19 +260,19 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
</View> </View>
)} )}
</View> </View>
</View>
); );
}); });
}; };
return ( return (
<View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5 pt-2.5"> <View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5">
{/* 生产费用模块 */} {/* 生产费用模块 */}
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={"flex flex-1 flex-col gap-2.5"}> <View className={"flex flex-1 flex-col gap-2.5"}>
<View className="text-sm font-bold"></View> <View className="text-sm font-bold"></View>
<View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5"> <View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<Icon <Icon
className={"mr-1"} className={"mr-1 leading-4"}
name="circle-info" name="circle-info"
color={"var(--color-blue-700)"} color={"var(--color-blue-700)"}
size={18} size={18}
@ -236,6 +282,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
{renderItemList(parsedCostTemplate.productionTypeList)} {renderItemList(parsedCostTemplate.productionTypeList)}
</View> </View>
</View> </View>
</View>
); );
}, },
); );

View File

@ -302,6 +302,19 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
onChange?.({ onChange?.({
...value, ...value,
orderPackageList, orderPackageList,
orderCostItemList: orderCostItemList?.map((item1) =>
item1.costItemId === item.costItemId
? {
...item1,
selected,
price: orderPackageList.reduce(
(sum, item) =>
sum + (item.boxSalePrice || 0) * item.boxCount,
0,
),
}
: item1,
),
}); });
}} }}
/> />
@ -309,8 +322,12 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
} }
return ( return (
<View key={index}> <View
<View className={"flex flex-col gap-2.5 rounded-lg bg-white p-2.5"}> key={index}
className={
"bg-primary/10 flex flex-col gap-2.5 rounded-lg p-2.5 text-sm text-gray-600"
}
>
<View className={"flex flex-row justify-between"}> <View className={"flex flex-row justify-between"}>
<View className="block text-sm font-normal text-[#000000]"> <View className="block text-sm font-normal text-[#000000]">
{item.name} {item.name}
@ -456,9 +473,7 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
onBlur={() => validateCount(orderCostItemId, price)} onBlur={() => validateCount(orderCostItemId, price)}
/> />
</View> </View>
<View className={"ml-2.5 text-sm text-gray-500"}> <View className={"ml-2.5 text-sm text-gray-500"}></View>
</View>
</View> </View>
</View> </View>
@ -470,7 +485,6 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
</View> </View>
)} )}
</View> </View>
</View>
); );
}); });
}; };
@ -488,29 +502,28 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
}; };
return ( return (
<View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5 pt-2.5"> <View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5">
<View className={"flex flex-1 flex-col gap-2.5"}> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={"flex flex-col gap-2.5"}>
<View className="text-sm font-bold"></View> <View className="text-sm font-bold"></View>
<View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5"> <View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<Icon <Icon
className={"mr-1"} className={"mr-1 leading-4"}
name="circle-info" name="circle-info"
color={"var(--color-blue-700)"} color={"var(--color-blue-700)"}
size={18} size={18}
/> />
<View className={"text-sm text-blue-700"}></View> <View className={"text-sm text-blue-700"}>
</View>
</View> </View>
{renderItemList(
getItemsByCostType("ARTIFICIAL_TYPE"),
"ARTIFICIAL_TYPE",
)}
{/* 总的工头姓名输入框 */} {/* 总的工头姓名输入框 */}
{shouldShowPrincipalInput() && ( {shouldShowPrincipalInput() && (
<View> <View className="flex flex-col gap-2.5">
<View className="mb-1 text-sm font-medium"></View> <View className="text-sm font-medium"></View>
<View <View
className={`flex h-12 items-center rounded-md px-3 ${foremanError ? "border-2 border-red-500 bg-red-100" : "border-2 border-gray-300 bg-white"}`} className={`flex h-12 items-center rounded-md ${foremanError ? "border-2 border-red-500 bg-red-100" : "border-2 border-gray-300 bg-white"}`}
> >
<Input <Input
className="text-base" className="text-base"
@ -530,12 +543,19 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
)} )}
</View> </View>
)} )}
{renderItemList(
getItemsByCostType("ARTIFICIAL_TYPE"),
"ARTIFICIAL_TYPE",
)}
</View> </View>
<View className={"flex flex-1 flex-col gap-2.5"}> </View>
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={"flex flex-col gap-2.5"}>
<View className="text-sm font-bold"></View> <View className="text-sm font-bold"></View>
<View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5"> <View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<Icon <Icon
className={"mr-1"} className={"mr-1 leading-4"}
name="circle-info" name="circle-info"
color={"var(--color-blue-700)"} color={"var(--color-blue-700)"}
size={18} size={18}
@ -543,7 +563,11 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
<View className={"text-sm text-blue-700"}></View> <View className={"text-sm text-blue-700"}></View>
</View> </View>
{/* 渲染辅料费用项(不含纸箱) */} {/* 渲染辅料费用项(不含纸箱) */}
{renderItemList(getItemsByCostType("MATERIAL_TYPE"), "MATERIAL_TYPE")} {renderItemList(
getItemsByCostType("MATERIAL_TYPE"),
"MATERIAL_TYPE",
)}
</View>
</View> </View>
</View> </View>
); );

View File

@ -168,8 +168,10 @@ export default forwardRef<OrderOptionRef, IOrderOptionProps>(
orderId: value.orderId, orderId: value.orderId,
active: Number(active + 1), active: Number(active + 1),
foreman: value.foreman, foreman: value.foreman,
orderCostList: value.orderCostList, // 空箱
orderPackageList: value.orderPackageList, orderPackageList: value.orderPackageList,
// 费用
orderCostList: value.orderCostList.filter((item) => item.selected),
orderCostItemList: value.orderCostItemList.filter( orderCostItemList: value.orderCostItemList.filter(
(item) => item.selected, (item) => item.selected,
), ),

View File

@ -45,21 +45,18 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
if (supplierVO.isPaper && supplierVO.isLast) { if (supplierVO.isPaper && supplierVO.isLast) {
setPackageTypeEnabled({ setPackageTypeEnabled({
USED: usageMap["USED"] || 0,
EXTRA: 0, EXTRA: 0,
EXTRA_USED: usageMap["EXTRA_USED"] || 0, EXTRA_USED: usageMap["EXTRA_USED"] || 0,
REMAIN: usageMap["REMAIN"] || 0, REMAIN: usageMap["REMAIN"] || 0,
}); });
} else if (supplierVO.isPaper && !supplierVO.isLast) { } else if (supplierVO.isPaper && !supplierVO.isLast) {
setPackageTypeEnabled({ setPackageTypeEnabled({
USED: usageMap["USED"] || 0,
EXTRA: usageMap["EXTRA"] || 0, EXTRA: usageMap["EXTRA"] || 0,
EXTRA_USED: 0, EXTRA_USED: 0,
REMAIN: 0, REMAIN: 0,
}); });
} else if (!supplierVO.isPaper) { } else if (!supplierVO.isPaper) {
setPackageTypeEnabled({ setPackageTypeEnabled({
USED: usageMap["USED"] || 0,
EXTRA: 0, EXTRA: 0,
EXTRA_USED: 0, EXTRA_USED: 0,
REMAIN: 0, REMAIN: 0,
@ -100,16 +97,13 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
// 确定当前供应商需要检查哪些纸箱类型 // 确定当前供应商需要检查哪些纸箱类型
if (supplierVO.isPaper && supplierVO.isLast) { if (supplierVO.isPaper && supplierVO.isLast) {
// 空磅包含纸箱 + 最后一个瓜农 // 空磅包含纸箱 + 最后一个瓜农
requiredTypes = ["USED", "EXTRA_USED", "REMAIN"]; requiredTypes = ["EXTRA_USED", "REMAIN"];
} else if (supplierVO.isPaper && !supplierVO.isLast) { } else if (supplierVO.isPaper && !supplierVO.isLast) {
requiredTypes = ["USED", "EXTRA"]; requiredTypes = ["EXTRA"];
} else if (!supplierVO.isPaper) {
// 空磅不包含纸箱
requiredTypes = ["USED"];
} }
console.log("requiredTypes", requiredTypes); console.log("requiredTypes", requiredTypes);
if (requiredTypes.length > 0) {
// 检查所需选项是否都已做出选择不是1就是2不能是0 // 检查所需选项是否都已做出选择不是1就是2不能是0
const allRequiredAnswered = requiredTypes.every( const allRequiredAnswered = requiredTypes.every(
(type) => (type) =>
@ -124,6 +118,9 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
}); });
return false; return false;
} }
}
requiredTypes.push("USED");
// 检查所有启用的纸箱类型是否都填写了信息 // 检查所有启用的纸箱类型是否都填写了信息
const enabledTypes = Object.entries(packageTypeEnabled) const enabledTypes = Object.entries(packageTypeEnabled)
@ -141,9 +138,7 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
if (!hasPackagesOfType) { if (!hasPackagesOfType) {
const typeLabels = { const typeLabels = {
USED: "记录装车用的纸箱", USED: "记录装车用的纸箱",
EXTRA_USED: supplierVO.isPaper EXTRA_USED: "记录用的额外运输纸箱",
? "记录用的额外运输纸箱"
: "记录装车用的纸箱",
EXTRA: "记录额外运输来的所有纸箱", EXTRA: "记录额外运输来的所有纸箱",
REMAIN: "记录车上没用完的纸箱", REMAIN: "记录车上没用完的纸箱",
}; };
@ -250,7 +245,6 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
// 新增状态:跟踪每种纸箱类型的启用状态 // 新增状态:跟踪每种纸箱类型的启用状态
// 0: 未选, 1: 选择是, 2: 选择否 // 0: 未选, 1: 选择是, 2: 选择否
const [packageTypeEnabled, setPackageTypeEnabled] = useState({ const [packageTypeEnabled, setPackageTypeEnabled] = useState({
USED: 0,
EXTRA_USED: 0, EXTRA_USED: 0,
EXTRA: 0, EXTRA: 0,
REMAIN: 0, REMAIN: 0,
@ -269,7 +263,6 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
packageUsage: [ packageUsage: [
...(Object.entries( ...(Object.entries(
packageTypeEnabled || { packageTypeEnabled || {
USED: 0,
EXTRA_USED: 0, EXTRA_USED: 0,
EXTRA: 0, EXTRA: 0,
REMAIN: 0, REMAIN: 0,
@ -297,7 +290,8 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
return ( return (
<View className="flex flex-1 flex-col gap-2.5 p-2.5"> <View className="flex flex-1 flex-col gap-2.5 p-2.5">
{/* 替换原来的Tab导航为问答式选择 */} {/* 替换原来的Tab导航为问答式选择 */}
<View className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="flex flex-col gap-2.5">
<View className="text-base font-bold"> <View className="text-base font-bold">
{supplierVO.name}使 {supplierVO.name}使
</View> </View>
@ -309,11 +303,13 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
boxBrandList={boxBrandList} boxBrandList={boxBrandList}
/> />
</View> </View>
</View>
{/* 根据供应商属性显示不同的问题 */} {/* 根据供应商属性显示不同的问题 */}
{supplierVO.isPaper && supplierVO.isLast && ( {supplierVO.isPaper && supplierVO.isLast && (
<> <>
<View className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="flex flex-col gap-2.5">
{/* 空车带来的纸箱用完了吗? */} {/* 空车带来的纸箱用完了吗? */}
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<View className="text-sm"></View> <View className="text-sm"></View>
@ -366,8 +362,10 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
/> />
)} )}
</View> </View>
</View>
<View className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="flex flex-col gap-2.5">
{/* 用额外运输纸箱的了吗? */} {/* 用额外运输纸箱的了吗? */}
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<View className="text-sm"></View> <View className="text-sm"></View>
@ -424,19 +422,23 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
/> />
)} )}
</View> </View>
</View>
</> </>
)} )}
{supplierVO.isPaper && !supplierVO.isLast && ( {supplierVO.isPaper && !supplierVO.isLast && (
/* 非最后一个瓜农 */ /* 非最后一个瓜农 */
<View className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="flex flex-col gap-2.5">
{/* 有额外运输纸箱过来吗? */} {/* 有额外运输纸箱过来吗? */}
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<View className="text-sm"></View> <View className="text-sm"></View>
<View className="flex flex-shrink-0 gap-2"> <View className="flex flex-shrink-0 gap-2">
<Button <Button
size="small" size="small"
type={packageTypeEnabled.EXTRA === 1 ? "primary" : "default"} type={
packageTypeEnabled.EXTRA === 1 ? "primary" : "default"
}
onClick={() => { onClick={() => {
setPackageTypeEnabled({ setPackageTypeEnabled({
...packageTypeEnabled, ...packageTypeEnabled,
@ -448,7 +450,9 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
</Button> </Button>
<Button <Button
size="small" size="small"
type={packageTypeEnabled.EXTRA === 2 ? "primary" : "default"} type={
packageTypeEnabled.EXTRA === 2 ? "primary" : "default"
}
onClick={() => { onClick={() => {
setPackageTypeEnabled({ setPackageTypeEnabled({
...packageTypeEnabled, ...packageTypeEnabled,
@ -478,17 +482,6 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
/> />
)} )}
</View> </View>
)}
{!supplierVO.isPaper && (
/* 空磅不包含纸箱 */
<View className="rounded-lg bg-white p-2.5 shadow-sm">
<PackageList
boxType={"USED"}
orderPackageList={orderPackageList}
setOrderPackageList={setOrderPackageList}
boxBrandList={boxBrandList}
/>
</View> </View>
)} )}
</View> </View>

View File

@ -535,7 +535,8 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
return ( return (
<View className="flex flex-1 flex-col gap-2.5 p-2.5"> <View className="flex flex-1 flex-col gap-2.5 p-2.5">
<View className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="flex flex-col gap-2.5">
<View className={"flex flex-1 flex-col gap-2.5"}> <View className={"flex flex-1 flex-col gap-2.5"}>
<View className="block text-sm font-normal text-[#000000]"> <View className="block text-sm font-normal text-[#000000]">
@ -618,7 +619,9 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
)} )}
</View> </View>
</View> </View>
<View className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"> </View>
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className="flex flex-col gap-2.5">
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
<View className="block text-sm font-normal text-[#000000]"> <View className="block text-sm font-normal text-[#000000]">
@ -835,14 +838,18 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
> >
<View className={"text-sm"}> <View className={"text-sm"}>
{orderVehicle?.deliveryTime {orderVehicle?.deliveryTime
? dayjs(orderVehicle?.deliveryTime).format("YYYY年MM月DD日") ? dayjs(orderVehicle?.deliveryTime).format(
"YYYY年MM月DD日",
)
: "请选择发货时间"} : "请选择发货时间"}
</View> </View>
<Icon name={"chevron-down"} /> <Icon name={"chevron-down"} />
</View> </View>
</View> </View>
{deliveryTimeError && ( {deliveryTimeError && (
<View className="mt-1 text-xs text-red-500"></View> <View className="mt-1 text-xs text-red-500">
</View>
)} )}
<DatePicker <DatePicker
title="发货时间选择" title="发货时间选择"
@ -858,6 +865,7 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
</View> </View>
</View> </View>
</View> </View>
</View>
); );
}, },
); );

View File

@ -49,7 +49,7 @@ export default function PackageCard(props: IPackageCardProps) {
return ( return (
<View <View
className="mb-2.5 overflow-hidden rounded-xl border border-gray-200 bg-white p-2.5 shadow-sm" className="overflow-hidden rounded-xl border border-gray-200 bg-white p-2.5 shadow-sm"
key={boxBrand.id} key={boxBrand.id}
> >
{/* 品牌背景水印 */} {/* 品牌背景水印 */}

View File

@ -21,7 +21,7 @@ export default function PackageList(props: IPackageListProps) {
// 获取该类型下所有的纸箱 // 获取该类型下所有的纸箱
const packagesOfType = convertOrderPackagesToBoxBrands( const packagesOfType = convertOrderPackagesToBoxBrands(
orderPackageList.filter((pkg) => pkg.boxType === boxType), orderPackageList?.filter((pkg) => pkg.boxType === boxType),
); );
// 根据类型设置显示标题 // 根据类型设置显示标题
@ -30,6 +30,7 @@ export default function PackageList(props: IPackageListProps) {
EXTRA_USED: "用的额外运输纸箱", EXTRA_USED: "用的额外运输纸箱",
EXTRA: "额外运输来的所有纸箱", EXTRA: "额外运输来的所有纸箱",
REMAIN: "车上没用完的纸箱", REMAIN: "车上没用完的纸箱",
EMPTY: "空箱",
}; };
if (!boxBrandList) { if (!boxBrandList) {
@ -37,8 +38,8 @@ export default function PackageList(props: IPackageListProps) {
} }
return ( return (
<View key={boxType} className="mb-2.5"> <>
<View className="mb-3 flex items-center justify-between"> <View className="flex items-center justify-between">
<View className="text-base font-bold text-gray-800"> <View className="text-base font-bold text-gray-800">
{typeLabels[boxType]} {typeLabels[boxType]}
</View> </View>
@ -56,7 +57,7 @@ export default function PackageList(props: IPackageListProps) {
/> />
)) ))
) : ( ) : (
<View className="mb-4 rounded-lg bg-gray-50 p-4 text-center text-gray-500"> <View className="rounded-lg bg-gray-50 p-4 text-center text-gray-500">
{typeLabels[boxType]} {typeLabels[boxType]}
</View> </View>
)} )}
@ -83,6 +84,6 @@ export default function PackageList(props: IPackageListProps) {
onClose={() => setShowBatchModal(false)} onClose={() => setShowBatchModal(false)}
onSave={setOrderPackageList} onSave={setOrderPackageList}
/> />
</View> </>
); );
} }

View File

@ -41,7 +41,6 @@ export default hocAuth(function Page(props: CommonComponent) {
) => { ) => {
if (!menus || menus.length === 0) return null; if (!menus || menus.length === 0) return null;
console.log("level", level);
if (level === 1) { if (level === 1) {
return ( return (
<View className="grid grid-cols-4"> <View className="grid grid-cols-4">

View File

@ -6,6 +6,7 @@ import { Button, SafeArea, Toast } from "@nutui/nutui-react-taro";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { business } from "@/services"; import { business } from "@/services";
import { buildUrl } from "@/utils"; import { buildUrl } from "@/utils";
import { CopyText } from "@/components";
export default hocAuth(function Page(props: CommonComponent) { export default hocAuth(function Page(props: CommonComponent) {
const { router, setLoading } = props; const { router, setLoading } = props;
@ -123,9 +124,11 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="mb-2.5 flex flex-col gap-2.5"> <View className="mb-2.5 flex flex-col gap-2.5">
<View className="flex flex-row justify-between"> <View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text> <Text className="text-sm text-gray-600">:</Text>
<CopyText copyData={purchaseOrder?.orderSn || "-"}>
<Text className="text-sm font-medium"> <Text className="text-sm font-medium">
{purchaseOrder?.orderSn || "-"} {purchaseOrder?.orderSn || "-"}
</Text> </Text>
</CopyText>
</View> </View>
<View className="flex flex-row justify-between"> <View className="flex flex-row justify-between">

View File

@ -33,6 +33,7 @@ import {
PurchaseFormSection, PurchaseFormSection,
RebateCalcSection, RebateCalcSection,
State, State,
SupplierInfoSection,
TaxProvisionSection, TaxProvisionSection,
TaxSubsidySection, TaxSubsidySection,
WorkerAdvanceSection, WorkerAdvanceSection,
@ -40,108 +41,169 @@ import {
import { buildUrl, formatCurrency, PurchaseOrderCalculator } from "@/utils"; import { buildUrl, formatCurrency, PurchaseOrderCalculator } from "@/utils";
import classNames from "classnames"; import classNames from "classnames";
const sections = { const defaultSections = [
"marketPrice",
"supplierInfo",
"dealerInfo",
"basicInfo",
"packageInfo",
"emptyBoxInfo",
"foremanAdvance",
"materialCost",
"originAdvance",
"otherCost",
"productionLoss",
"taxSubsidy",
"taxProvision",
"costDifference",
"costSummary",
"rebateCalc",
"deliveryForm",
];
const xylSections = [
"marketPrice",
"supplierInfo",
"dealerInfo",
"basicInfo",
"purchaseCostInfo",
"purchaseForm",
"packageInfo",
"emptyBoxInfo",
"foremanAdvance",
"materialCost",
"originAdvance",
"otherCost",
"productionLoss",
"taxSubsidy",
"taxProvision",
"costDifference",
"costSummary",
"rebateCalc",
];
const fullSections = [
// 市场报价 // 市场报价
marketPrice: { {
name: "marketPrice",
component: MarketPriceSection, component: MarketPriceSection,
title: "市场报价", title: "市场报价",
}, },
// 我方入账公司 // 我方入账公司
supplierInfo: { {
name: "supplierInfo",
component: CompanyInfoSection, component: CompanyInfoSection,
title: "我方入账公司", title: "我方入账公司",
}, },
// 下游经销商信息 // 下游经销商信息
dealerInfo: { {
name: "dealerInfo",
component: DealerInfoSection, component: DealerInfoSection,
title: "下游经销商", title: "下游经销商",
}, },
// 基础信息 // 基础信息
basicInfo: { {
name: "basicInfo",
component: BasicInfoSection, component: BasicInfoSection,
title: "基础信息", title: "基础信息",
}, },
// // 瓜农信息 // 瓜农信息
// farmerInfo: { {
// component: SupplierInfoSection, name: "farmerInfo",
// title: "瓜农信息", component: SupplierInfoSection,
// }, title: "瓜农信息",
},
// 采购成本 // 采购成本
purchaseCostInfo: { {
name: "purchaseCostInfo",
component: PurchaseCostInfoSection, component: PurchaseCostInfoSection,
title: "采购成本复核", title: "采购成本复核",
}, },
// 分单发货复核 // 分单发货复核
purchaseForm: { {
name: "purchaseForm",
component: PurchaseFormSection, component: PurchaseFormSection,
title: "分单发货复核", title: "分单发货复核",
}, },
// 包装纸箱费 // 包装纸箱费
packageInfo: { {
name: "packageInfo",
component: PackageInfoSection, component: PackageInfoSection,
title: "包装纸箱费复核", title: "包装纸箱费复核",
}, },
// 空箱费用 // 空箱费用
emptyBoxInfo: { {
name: "emptyBoxInfo",
component: EmptyBoxInfoSection, component: EmptyBoxInfoSection,
title: "空箱费用复核", title: "空箱费用复核",
}, },
// 人工费用复核 // 人工费用复核
foremanAdvance: { {
name: "foremanAdvance",
component: WorkerAdvanceSection, component: WorkerAdvanceSection,
title: "人工费用复核", title: "人工费用复核",
}, },
// 辅料费用复核 // 辅料费用复核
materialCost: { {
name: "materialCost",
component: MaterialCostSection, component: MaterialCostSection,
title: "辅料费复核", title: "辅料费复核",
}, },
// 产地费用复核 // 产地费用复核
originAdvance: { {
name: "originAdvance",
component: ProductionAdvanceSection, component: ProductionAdvanceSection,
title: "产地费用复核", title: "产地费用复核",
}, },
// 其他费用复核 // 其他费用复核
otherCost: { {
name: "otherCost",
component: PackagingCostSection, component: PackagingCostSection,
title: "其他费用复核", title: "其他费用复核",
}, },
// 产地损耗复核 // 产地损耗复核
productionLoss: { {
name: "productionLoss",
component: ProductionLossSection, component: ProductionLossSection,
title: "产地损耗复核", title: "产地损耗复核",
}, },
// 公司返点复核 // 公司返点复核
taxSubsidy: { {
name: "taxSubsidy",
component: TaxSubsidySection, component: TaxSubsidySection,
title: "公司返点复核", title: "公司返点复核",
}, },
// 计提税金复核 // 计提税金复核
taxProvision: { {
name: "taxProvision",
component: TaxProvisionSection, component: TaxProvisionSection,
title: "计提税金复核", title: "计提税金复核",
}, },
// 调诚信志远分红 // 调诚信志远分红
costDifference: { {
name: "costDifference",
component: CostDifferenceSection, component: CostDifferenceSection,
title: "调诚信志远分红", title: "调诚信志远分红",
}, },
// 成本合计 // 成本合计
costSummary: { {
name: "costSummary",
component: CostSummarySection, component: CostSummarySection,
title: "成本合计", title: "成本合计",
}, },
// 个人返点复核 // 个人返点复核
rebateCalc: { {
name: "rebateCalc",
component: RebateCalcSection, component: RebateCalcSection,
title: "个人返点复核", title: "个人返点复核",
}, },
// 发货单复核 // 发货单复核
deliveryForm: { {
name: "deliveryForm",
component: DeliveryFormSection, component: DeliveryFormSection,
title: "发货单复核", title: "发货单复核",
}, },
}; ];
export default hocAuth(function Page(props: CommonComponent) { export default hocAuth(function Page(props: CommonComponent) {
const { router, isInitialized, setIsInitialized, userRoleVO, setLoading } = const { router, isInitialized, setIsInitialized, userRoleVO, setLoading } =
@ -417,6 +479,11 @@ export default hocAuth(function Page(props: CommonComponent) {
const calculator = new PurchaseOrderCalculator(purchaseOrderVO, true); const calculator = new PurchaseOrderCalculator(purchaseOrderVO, true);
const personalProfit = calculator.getPersonalProfit(); const personalProfit = calculator.getPersonalProfit();
const sections =
purchaseOrderVO.orderDealer.shortName === "信誉楼"
? xylSections
: defaultSections;
return ( return (
<> <>
<View <View
@ -475,8 +542,15 @@ export default hocAuth(function Page(props: CommonComponent) {
</View> </View>
{/* 循环渲染各部分内容 */} {/* 循环渲染各部分内容 */}
{Object.keys(sections).map((sectionKey) => { {sections.map((sectionKey) => {
const section = sections[sectionKey]; const section = fullSections.find(
(section) => section.name === sectionKey,
);
if (!section) {
return null;
}
const orderDealer = purchaseOrderVO.orderDealer; const orderDealer = purchaseOrderVO.orderDealer;
if ( if (
@ -538,6 +612,7 @@ export default hocAuth(function Page(props: CommonComponent) {
readOnly={purchaseOrderVO.state !== "WAITING_AUDIT"} readOnly={purchaseOrderVO.state !== "WAITING_AUDIT"}
purchaseOrderVO={purchaseOrderVO} purchaseOrderVO={purchaseOrderVO}
onChange={setPurchaseOrderVO} onChange={setPurchaseOrderVO}
//@ts-ignore
costList={costList} costList={costList}
calculator={calculator} calculator={calculator}
/> />

View File

@ -6,6 +6,7 @@ import { Button, SafeArea, Toast } from "@nutui/nutui-react-taro";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { business } from "@/services"; import { business } from "@/services";
import { buildUrl } from "@/utils"; import { buildUrl } from "@/utils";
import { CopyText } from "@/components";
export default hocAuth(function Page(props: CommonComponent) { export default hocAuth(function Page(props: CommonComponent) {
const { router, setLoading } = props; const { router, setLoading } = props;
@ -81,9 +82,11 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="mb-2.5 flex flex-col gap-2.5"> <View className="mb-2.5 flex flex-col gap-2.5">
<View className="flex flex-row justify-between"> <View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text> <Text className="text-sm text-gray-600">:</Text>
<CopyText copyData={purchaseOrder?.orderSn || "-"}>
<Text className="text-sm font-medium"> <Text className="text-sm font-medium">
{purchaseOrder?.orderSn || "-"} {purchaseOrder?.orderSn || "-"}
</Text> </Text>
</CopyText>
</View> </View>
<View className="flex flex-row justify-between"> <View className="flex flex-row justify-between">

View File

@ -114,6 +114,7 @@ export default hocAuth(function Page(props: CommonComponent) {
// 如果订单状态不是草稿,则跳转到预览页面 // 如果订单状态不是草稿,则跳转到预览页面
if ( if (
purchaseOrder.state !== "DRAFT" && purchaseOrder.state !== "DRAFT" &&
purchaseOrder.state !== "REJECTED" &&
userRoleVO.slug === "origin-entry" userRoleVO.slug === "origin-entry"
) { ) {
Taro.redirectTo({ Taro.redirectTo({
@ -411,6 +412,7 @@ export default hocAuth(function Page(props: CommonComponent) {
...prev!, ...prev!,
foreman: purchaseOrder.foreman, foreman: purchaseOrder.foreman,
orderCostItemList: purchaseOrder.orderCostItemList, orderCostItemList: purchaseOrder.orderCostItemList,
orderPackageList: purchaseOrder.orderPackageList,
}; };
}); });
}} }}
@ -437,7 +439,7 @@ export default hocAuth(function Page(props: CommonComponent) {
!purchaseOrder?.orderSupplierList[ !purchaseOrder?.orderSupplierList[
purchaseOrder?.orderSupplierList.length - 1 purchaseOrder?.orderSupplierList.length - 1
]?.isLast && ( ]?.isLast && (
<View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5 pt-2.5"> <View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5">
<View className={"flex flex-1 flex-col gap-2.5"}> <View className={"flex flex-1 flex-col gap-2.5"}>
<View className="text-sm font-bold"></View> <View className="text-sm font-bold"></View>
<Button <Button

View File

@ -6,6 +6,7 @@ import { Button, SafeArea, Toast } from "@nutui/nutui-react-taro";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { business } from "@/services"; import { business } from "@/services";
import { buildUrl } from "@/utils"; import { buildUrl } from "@/utils";
import { CopyText } from "@/components";
export default hocAuth(function Page(props: CommonComponent) { export default hocAuth(function Page(props: CommonComponent) {
const { router, setLoading } = props; const { router, setLoading } = props;
@ -81,9 +82,11 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="mb-2.5 flex flex-col gap-2.5"> <View className="mb-2.5 flex flex-col gap-2.5">
<View className="flex flex-row justify-between"> <View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text> <Text className="text-sm text-gray-600">:</Text>
<CopyText copyData={purchaseOrder?.orderSn || "-"}>
<Text className="text-sm font-medium"> <Text className="text-sm font-medium">
{purchaseOrder?.orderSn || "-"} {purchaseOrder?.orderSn || "-"}
</Text> </Text>
</CopyText>
</View> </View>
<View className="flex flex-row justify-between"> <View className="flex flex-row justify-between">

View File

@ -1,11 +1,5 @@
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
import { import { Button, Input, Toast, Uploader, UploaderFileItem } from "@nutui/nutui-react-taro";
Button,
Input,
Toast,
Uploader,
UploaderFileItem,
} from "@nutui/nutui-react-taro";
import { Icon } from "@/components"; import { Icon } from "@/components";
import hocAuth from "@/hocs/auth"; import hocAuth from "@/hocs/auth";
import { CommonComponent } from "@/types/typings"; import { CommonComponent } from "@/types/typings";
@ -368,7 +362,7 @@ export default hocAuth(function Page(props: CommonComponent) {
{/* 功能提醒 */} {/* 功能提醒 */}
<View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5"> <View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<Icon <Icon
className={"mr-1"} className={"mr-1 leading-4"}
name="circle-info" name="circle-info"
color={"var(--color-blue-700)"} color={"var(--color-blue-700)"}
size={18} size={18}

View File

@ -913,7 +913,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */ /** 状态1_启用0_禁用 */
status: boolean; status: boolean;
/** 成本项ID */ /** 成本项ID */
costItemIds?: number[]; costItemIds?: string[];
}; };
type CostDestroyCmd = { type CostDestroyCmd = {
@ -1123,7 +1123,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */ /** 状态1_启用0_禁用 */
status: boolean; status: boolean;
/** 成本项ID */ /** 成本项ID */
costItemIds?: number[]; costItemIds?: string[];
}; };
type CostVO = { type CostVO = {
@ -1150,7 +1150,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */ /** 状态1_启用0_禁用 */
status: boolean; status: boolean;
/** 项目id集合 */ /** 项目id集合 */
costItemIds?: number[]; costItemIds?: string[];
/** 创建时间 */ /** 创建时间 */
createdAt?: string; createdAt?: string;
/** 项目列表 */ /** 项目列表 */
@ -2648,7 +2648,7 @@ declare namespace BusinessAPI {
| "PRODUCTION_TYPE" | "PRODUCTION_TYPE"
| "OTHER_TYPE"; | "OTHER_TYPE";
/** 关联项目id */ /** 关联项目id */
costItemIds?: number[]; costItemIds?: string[];
/** 是否选中 */ /** 是否选中 */
selected: boolean; selected: boolean;
}; };