feat(purchase): 优化采购模块人工费用和车辆信息处理逻辑

- 调整API域名配置,区分h5和小程序环境
- 重构OrderCost组件,支持多种费用类型筛选和展示
- 优化OrderVehicle组件,简化经销商信息赋值逻辑
- Weigh组件新增西瓜品种选择功能,包含弹窗和校验逻辑
- 重写LaborInfoSection组件,支持新增和编辑人工费用项
- 改进费用承担方和工头信息的处理流程
- 优化UI布局和交互体验
This commit is contained in:
shenyifei 2025-11-16 19:14:15 +08:00
parent a62ca1fb95
commit 3f8c6d962a
32 changed files with 4867 additions and 669 deletions

View File

@ -10,7 +10,10 @@ export default {
},
defineConstants: {
"process.env.TARO_APP_ID": '"wxa080848726642f73"',
"process.env.TARO_API_DOMAIN": '"/api"',
"process.env.TARO_API_DOMAIN":
process.env.TARO_ENV === "h5"
? '"/api"'
: '"https://api.erp.qilincloud168.com"',
},
mini: {
miniCssExtractPluginOption: {

View File

@ -18,7 +18,10 @@ export default {
},
defineConstants: {
"process.env.TARO_APP_ID": '"wxa080848726642f73"',
"process.env.TARO_API_DOMAIN": '"https://api.erp.qilincloud168.com"',
"process.env.TARO_API_DOMAIN":
process.env.TARO_ENV === "h5"
? '"/api"'
: '"https://api.erp.qilincloud168.com"',
},
mini: {
miniCssExtractPluginOption: {

View File

@ -18,4 +18,12 @@ export default [
projectName: "business",
namespace: "BusinessAPI",
},
{
requestLibPath: "import request from '../poster-request';",
// schemaPath: 'http://localhost:8080/mdb-business/v3/api-docs',
schemaPath: path.resolve(__dirname, "../../swagger/poster.json"),
serversPath: "./src/services",
projectName: "poster",
namespace: "PosterAPI",
},
];

View File

@ -40,7 +40,7 @@ config = {
// 发货单
{
root: "pages/delivery",
pages: ["list", "document"],
pages: ["list", "document/delivery", "document/purchase"],
},
],
permission: {
@ -49,7 +49,7 @@ config = {
},
},
tabBar: {
custom: false,
custom: process.env.TARO_ENV === "weapp",
color: "#000000",
selectedColor: "#DC143C",
backgroundColor: "#ffffff",

View File

@ -36,38 +36,75 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
showInEntry: true,
},
});
const initialList =
data.data?.map((item) => {
// 查找是否在purchaseOrder中已有该item的记录
const existingItem = value?.find(
(costItem) => costItem.itemId === item.itemId,
);
const costItemList = data.data;
return {
orderCostId: generateShortId(),
itemId: item.itemId,
name: item.name,
price: item.price,
unit: item.unit,
selected: existingItem ? existingItem.selected : false,
count: existingItem ? existingItem.count : 1,
payerType: existingItem ? existingItem.payerType : undefined,
principal: existingItem ? existingItem.principal : "",
costType: item.costType,
};
}) || [];
if (costItemList) {
// 人工辅料选中
const initialList =
costItemList
?.filter(
(item) =>
item.costType === "HUMAN_COST" ||
item.costType === "PACKAGING_MATERIALS",
)
.map((item) => {
// 查找是否在purchaseOrder中已有该item的记录
const existingItem = value?.find(
(costItem) => costItem.itemId === item.itemId,
);
setCostItemVOList(initialList as CostItem[]);
console.log("existingItem", existingItem, value)
// 初始化总的工头姓名(如果有启用且费用承担方为"我方"的项目)
const enabledUsItems = initialList.filter(
(item) =>
item.selected &&
item.payerType === "US" &&
item.costType === "HUMAN_COST",
);
if (enabledUsItems.length > 0 && enabledUsItems[0].principal) {
setPrincipal(enabledUsItems[0].principal || "");
return {
orderCostId: existingItem
? existingItem.orderCostId
: generateShortId(),
itemId: item.itemId,
name: item.name,
price: item.price,
unit: item.unit,
selected: existingItem ? existingItem.selected : false,
count: existingItem ? existingItem.count : 1,
payerType: existingItem ? existingItem.payerType : undefined,
principal: existingItem ? existingItem.principal : "",
costType: item.costType,
};
}) || [];
setCostItemVOList([
...(initialList || []),
...(value
?.filter(
(item) =>
item.costType === "WORKER_ADVANCE" ||
item.costType === "PRODUCTION_ADVANCE",
)
.map((item) => {
return {
orderCostId: item.orderCostId,
itemId: item.itemId,
name: item.name,
price: item.price,
unit: item.unit,
selected: item.selected,
count: item.count,
payerType: item.payerType,
principal: item.principal,
costType: item.costType,
};
}) || []),
]);
// 初始化总的工头姓名(如果有启用且费用承担方为"我方"的项目)
const enabledUsItems = initialList.filter(
(item) =>
item.selected &&
item.payerType === "US" &&
item.costType === "HUMAN_COST",
);
if (enabledUsItems.length > 0 && enabledUsItems[0].principal) {
setPrincipal(enabledUsItems[0].principal || "");
}
}
};
@ -253,7 +290,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
// 渲染项目列表
const renderItemList = (items: CostItem[], type: string) => {
return items.map((item) => (
<View className="mb-2.5" key={item.orderCostId}>
<View key={item.orderCostId}>
<View className={"flex flex-col gap-2.5 rounded-lg bg-white p-2.5"}>
<View className={"flex flex-row justify-between"}>
<View className="block text-sm font-normal text-[#000000]">
@ -384,10 +421,10 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
};
return (
<View className="flex flex-1 flex-col bg-[#D1D5DB] px-2.5 pt-2.5">
<View className={"mb-2.5"}>
<View className="mb-2.5 text-sm font-bold"></View>
<View className="mb-2.5 flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<View className="flex flex-1 flex-col bg-[#D1D5DB] p-2.5 pt-2.5 gap-2.5">
<View className={"flex flex-1 flex-col gap-2.5"}>
<View className="text-sm font-bold"></View>
<View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<Icon
className={"mr-1"}
name="circle-info"
@ -400,7 +437,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
{/* 总的工头姓名输入框 */}
{shouldShowPrincipalInput() && (
<View className="mt-2.5">
<View>
<View className="mb-1 text-sm font-medium"></View>
<View
className={`flex h-12 items-center rounded-md px-3 ${principalError ? "border-2 border-red-500 bg-red-100" : "border-2 border-gray-300 bg-white"}`}
@ -424,9 +461,9 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
</View>
)}
</View>
<View className={"mb-2.5"}>
<View className="mb-2.5 text-sm font-bold"></View>
<View className="mb-2.5 flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<View className={"flex flex-1 flex-col gap-2.5"}>
<View className="text-sm font-bold"></View>
<View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
<Icon
className={"mr-1"}
name="circle-info"

View File

@ -77,15 +77,7 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
setOrderDealer({
...orderDealer,
dealerId: dealerVO.dealerId,
shortName: dealerVO?.shortName!,
dealerType: dealerVO.dealerType!,
enableShare: dealerVO.enableShare,
shareRatio: dealerVO.shareRatio,
freightCostFlag: dealerVO.freightCostFlag,
strawMatCostFlag: dealerVO.strawMatCostFlag,
includePackingFlag: dealerVO.includePackingFlag,
documentTypes: dealerVO.documentTypes,
...dealerVO,
});
// 校验经销商信息
@ -485,7 +477,7 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
const { data } = await business.extraction.vehicleExtraction({
message: message,
dealerNames: '',
dealerNames: "",
});
const newVehicle = data.data as BusinessAPI.VehicleExtractionVO;

View File

@ -5,12 +5,14 @@ import {
Toast,
Uploader,
UploaderFileItem,
Popup,
} from "@nutui/nutui-react-taro";
import { Icon } from "@/components";
import { SupplierVO } from "@/types/typings";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { uploadFile } from "@/utils/uploader";
import { validatePrice } from "@/utils/format";
import { business } from "@/services";
// 定义ref暴露的方法接口
export interface WeighRef {
@ -22,13 +24,30 @@ interface IWeightProps {
onChange: (supplierVO: SupplierVO) => void;
isFirst?: boolean;
changePaper: (isPaper: boolean) => void;
changeProduct: (productVO: BusinessAPI.ProductVO) => void;
}
export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
const { value, onChange, isFirst, changePaper } = props;
const { value, onChange, isFirst, changePaper, changeProduct } = props;
const [supplierVO, setSupplierVO] = useState<SupplierVO>();
useEffect(() => {
// 获取产品列表
business.product
.listProduct({
productListQry: {
status: true,
},
})
.then(({ data }) => {
if (data.success) {
const products = data.data || [];
setProductList(products);
}
});
}, []);
// 初始化数据
useEffect(() => {
setSupplierVO(value);
@ -69,10 +88,19 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
UploaderFileItem[]
>([]);
const [isProductIdError, setProductIdError] = useState<{
[key: string]: boolean;
}>({}); // 添加瓜农是否选择西瓜品牌的错误状态
const [isPaperError, setIsPaperError] = useState<{
[key: string]: boolean;
}>({}); // 添加是否为最后一个瓜农的错误状态
const [productPopupVisible, setProductPopupVisible] = useState(false);
const [selectedProduct, setSelectedProduct] = useState("");
const [customProduct, setCustomProduct] = useState("");
const [productList, setProductList] = useState<BusinessAPI.ProductVO[]>([]);
// 添加三个字段的错误状态
const [emptyWeightError, setEmptyWeightError] = useState<{
[key: string]: boolean;
@ -136,6 +164,23 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
return isPaper !== undefined && isPaper !== null;
};
// 校验西瓜品种
const validateProductId = (productId: any) => {
if (productId === "" || productId === undefined || productId === null) {
setProductIdError((prev) => ({
...prev,
[supplierVO.orderSupplierId]: true,
}));
return false;
}
setProductIdError((prev) => ({
...prev,
[supplierVO.orderSupplierId]: false,
}));
return true;
};
// 校验空磅重量
const validateEmptyWeight = (value: any) => {
if (value === "" || value === undefined || value === null) {
@ -225,6 +270,7 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
supplierVO.purchasePrice,
);
const isPaperValid = validateIsPaper(supplierVO.isPaper);
const isProductIdValid = validateProductId(supplierVO.productId);
// 更新错误状态
setIsPaperError((prev) => ({
@ -232,6 +278,12 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
[id]: !isPaperValid,
}));
// 更新错误状态
setProductIdError((prev) => ({
...prev,
[id]: !isProductIdValid,
}));
setPriceError((prev) => ({
...prev,
[id]: !isPurchasePriceValid,
@ -251,6 +303,7 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
isEmptyWeightValid &&
isTotalWeightValid &&
isPurchasePriceValid &&
isProductIdValid &&
isPaperValid;
if (!isValid) {
@ -266,53 +319,222 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
return (
<View className="flex flex-1 flex-col gap-2.5 p-2.5">
{isFirst && (
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={"flex items-center justify-between gap-2.5"}>
<View className="text-sm"></View>
<View className="text-neutral-darkest text-sm font-medium">
<Radio.Group
direction="horizontal"
value={
supplierVO.isPaper === true
? "true"
: supplierVO.isPaper === false
? "false"
: undefined
}
onChange={(value) => {
// 清除错误状态
setIsPaperError((prev) => ({
...prev,
[supplierVO.orderSupplierId]: false,
}));
// 根据用户选择设置是否包含空纸箱
const isPaperValue =
value === "true"
? true
: value === "false"
? false
: undefined;
setSupplierVO({
...supplierVO,
isPaper: isPaperValue,
});
changePaper(isPaperValue!);
{/* 西瓜品种选择弹窗 */}
<Popup
visible={productPopupVisible}
position="bottom"
title="请选择具体品种"
onClose={() => setProductPopupVisible(false)}
round
closeable
>
<View className="p-4">
<View id="sub-options" className="slide-in mt-3">
<View className="mb-3 text-sm font-medium text-gray-500">
</View>
<View className="grid grid-cols-2 gap-2">
{productList.map((product) => (
<View
className={`cursor-pointer rounded-lg border p-3 text-center hover:bg-gray-50 ${selectedProduct === product.name ? "border-primary border-2" : "border-gray-200"}`}
key={product.productId}
onClick={() => setSelectedProduct(product.name)}
>
<View
className={
selectedProduct === product.name
? "text-primary font-medium"
: ""
}
>
{product.name}
</View>
</View>
))}
</View>
{selectedProduct === "其他(请注明)" && (
<View className="mt-4">
<View
className={`flex h-10 w-full items-center rounded-md ${selectedProduct === "其他(请注明)" && customProduct ? "border-primary border-4" : "border-4 border-gray-300"}`}
>
<Input
placeholder="请输入品种"
type="text"
value={customProduct}
onChange={(value) => {
setCustomProduct(value);
}}
/>
</View>
</View>
)}
<View className="mt-6 flex gap-3">
<View
className="border-primary text-primary flex-1 cursor-pointer rounded-md border py-3 text-center"
onClick={() => {
setProductPopupVisible(false);
setSelectedProduct("");
setCustomProduct("");
}}
>
<Radio value="true"></Radio>
<Radio value="false"></Radio>
</Radio.Group>
</View>
<View
className="bg-primary flex-1 cursor-pointer rounded-md py-3 text-center text-white"
onClick={() => {
let finalProduct = "";
if (selectedProduct === "其他(请注明)") {
finalProduct = customProduct;
} else if (selectedProduct) {
// 从产品列表中找到选中产品的ID
const selectedProductObj = productList.find(
(p) => p.name === selectedProduct,
);
if (selectedProductObj) {
finalProduct = selectedProductObj.productId;
}
}
if (finalProduct) {
// 找到选中的产品对象
const productObj = productList.find(
(p) => p.name === selectedProduct,
);
if (productObj) {
setSupplierVO({
...supplierVO,
productId: productObj.productId,
productName:
selectedProduct === "其他(请注明)"
? customProduct
: productObj.name,
});
changeProduct({
...productObj,
name:
selectedProduct === "其他(请注明)"
? customProduct
: productObj.name,
});
}
setProductPopupVisible(false);
setSelectedProduct("");
setCustomProduct("");
}
}}
>
</View>
</View>
</View>
{isPaperError[supplierVO.orderSupplierId] && (
<View className="mt-1 text-xs text-red-500">
</View>
</Popup>
{isFirst && (
<>
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={"flex items-center justify-between gap-2.5"}>
<View className="text-sm"></View>
<View className="text-neutral-darkest text-sm font-medium">
<Radio.Group
direction="horizontal"
value={
supplierVO.isPaper === true
? "true"
: supplierVO.isPaper === false
? "false"
: undefined
}
onChange={(value) => {
// 清除错误状态
setIsPaperError((prev) => ({
...prev,
[supplierVO.orderSupplierId]: false,
}));
// 根据用户选择设置是否包含空纸箱
const isPaperValue =
value === "true"
? true
: value === "false"
? false
: undefined;
setSupplierVO({
...supplierVO,
isPaper: isPaperValue,
});
changePaper(isPaperValue!);
}}
>
<Radio value="true"></Radio>
<Radio value="false"></Radio>
</Radio.Group>
</View>
</View>
{isPaperError[supplierVO.orderSupplierId] && (
<View className="mt-1 text-xs text-red-500">
</View>
)}
</View>
{productList.length > 0 && (
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={"flex items-center justify-between gap-2.5"}>
<View className="text-sm">西</View>
<View className="text-neutral-darkest text-sm font-medium">
<Radio.Group
direction="horizontal"
value={supplierVO.productId}
onChange={(value) => {
// 清除错误状态
setProductIdError((prev) => ({
...prev,
[supplierVO.orderSupplierId]: false,
}));
if (value === "other") {
// 点击"其他"时打开弹窗
setProductPopupVisible(true);
} else {
// 根据用户选择设置西瓜品种
const productId = value as string;
const productVO = productList.find(
(p) => p.productId === productId,
);
if (productVO) {
setSupplierVO({
...supplierVO,
productId: productVO?.productId,
productName: productVO?.name,
});
changeProduct(productVO);
}
}
}}
>
<Radio
value={supplierVO.productId || productList?.[0].productId}
>
{supplierVO.productName || productList?.[0].name}
</Radio>
<Radio value="other"></Radio>
</Radio.Group>
</View>
</View>
{isProductIdError[supplierVO.orderSupplierId] && (
<View className="mt-1 text-xs text-red-500">
西
</View>
)}
</View>
)}
</View>
</>
)}
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">

View File

@ -1,7 +1,16 @@
import { Text, View } from "@tarojs/components";
import { Button, Input, Popup, SafeArea } from "@nutui/nutui-react-taro";
import {
Button,
Input,
Picker,
Popup,
SafeArea,
} from "@nutui/nutui-react-taro";
import { useEffect, useState } from "react";
import { validatePrice } from "@/utils/format";
import { generateShortId } from "@/utils/generateShortId";
import { business } from "@/services";
import { Icon } from "@/components";
export default function MaterialCostSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
@ -15,6 +24,28 @@ export default function MaterialCostSection(props: {
{},
);
// 新增辅料费弹窗状态
const [showAddCostPopup, setShowAddCostPopup] = useState(false);
// 新增辅料费表单数据
const [newCostData, setNewCostData] = useState<any>({
itemId: "", // 费用项目ID
name: "", // 费用名称
quantity: 0, // 数量
unit: "", // 单位
unitPrice: "", // 单价
amount: "", // 金额
requireQuantityAndPrice: false,
});
// Picker可见状态
const [pickerVisible, setPickerVisible] = useState({
costItem: false, // 费用项目Picker
});
// 费用项目列表
const [costItems, setCostItems] = useState<BusinessAPI.CostItemVO[]>([]);
// 编辑值的状态
const [editValues, setEditValues] = useState<{
[key: string]: {
@ -81,6 +112,26 @@ export default function MaterialCostSection(props: {
return updates;
};
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
showInEntry: true,
costType: "PACKAGING_MATERIALS", // 只获取辅料费用项目
},
});
setCostItems(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
// 辅料费(商标费和网套费)
const packagingMaterials =
purchaseOrderVO.orderCostList?.filter(
@ -230,7 +281,7 @@ export default function MaterialCostSection(props: {
<Text className="text-sm text-gray-500"></Text>
</View>
<View className="relative flex">
<Text className="text-red-500 border-red-500 w-full border-b-2 pb-2 text-3xl font-bold focus:outline-none">
<Text className="w-full border-b-2 border-red-500 pb-2 text-3xl font-bold text-red-500 focus:outline-none">
{amount.toFixed(2) || "0.00"}
</Text>
</View>
@ -268,8 +319,292 @@ export default function MaterialCostSection(props: {
</View>
);
})}
{!readOnly && (
<Button
type={"primary"}
block
size={"large"}
onClick={() => setShowAddCostPopup(true)}
>
</Button>
)}
</View>
{/* 新增其他辅料费 */}
<Popup
visible={showAddCostPopup}
position="bottom"
title="新增其他辅料费用"
onClose={() => setShowAddCostPopup(false)}
onOverlayClick={() => setShowAddCostPopup(false)}
lockScroll
>
<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 relative flex h-10 w-full items-center rounded-md border border-solid"
onClick={() =>
setPickerVisible((prev) => ({ ...prev, costItem: true }))
}
>
<Input
type="text"
placeholder="请选择辅料项目"
value={newCostData.name || ""}
disabled
/>
<Icon name="chevron-down" className="absolute -right-1 mr-4" />
</View>
<Picker
title="请选择辅料项目"
visible={pickerVisible.costItem}
options={[
costItems
.filter((item) => {
// 检查该项目是否已经被选择
return !packagingMaterials.some(
(hc) => hc.itemId === item.itemId,
);
})
.map((item) => ({
label: item.name,
value: item.itemId,
})),
]}
onConfirm={(_, values) => {
const selectedValue = values[0] as string;
const selectedItem = costItems.find(
(item) => item.itemId === selectedValue,
);
if (selectedItem) {
setNewCostData((prev) => ({
...prev,
itemId: selectedValue,
name: selectedItem.name,
quantity: selectedItem.requireQuantityAndPrice ? 0 : 1, // 数量
unit: selectedItem.requireQuantityAndPrice
? selectedItem.unit
: "元", // 单位
unitPrice: "", // 单价
amount: "", // 金额
requireQuantityAndPrice: selectedItem.requireQuantityAndPrice,
}));
}
setPickerVisible((prev) => ({ ...prev, costItem: false }));
}}
onCancel={() =>
setPickerVisible((prev) => ({ ...prev, costItem: false }))
}
onClose={() =>
setPickerVisible((prev) => ({ ...prev, costItem: false }))
}
/>
{/* 数量输入 */}
{newCostData.itemId && newCostData.requireQuantityAndPrice && (
<>
<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 flex-1"
placeholder="请输入数量"
type="digit"
value={newCostData.quantity?.toString() || ""}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setNewCostData((prev) => ({
...prev,
quantity: numValue as any,
}));
}
}}
/>
<View className="mr-2">{newCostData.unit}</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 flex-1"
placeholder="请输入单价"
type="digit"
value={newCostData.unitPrice?.toString() || ""}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setNewCostData((prev) => ({
...prev,
unitPrice: numValue as any,
}));
}
}}
/>
<View className="mr-2"></View>
</View>
{/* 金额展示 */}
<View className="flex items-center justify-between rounded-md bg-gray-100 p-3">
<View className="text-neutral-darkest text-sm font-medium">
</View>
<View className="text-neutral-darkest text-sm font-medium">
{(
Number(newCostData.quantity || 0) *
Number(newCostData.unitPrice || 0)
).toFixed(2)}{" "}
</View>
</View>
</>
)}
{newCostData.itemId && !newCostData.requireQuantityAndPrice && (
<>
<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 flex-1"
placeholder="请输入金额"
type="digit"
value={newCostData.amount?.toString() || ""}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setNewCostData((prev) => ({
...prev,
unitPrice: numValue as any,
amount: numValue as any,
}));
}
}}
/>
<View className="mr-2"></View>
</View>
</>
)}
</View>
<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={() => {
setShowAddCostPopup(false);
// 重置表单
setNewCostData({
itemId: "",
name: "", // 费用名称
quantity: 0, // 数量
unit: "", // 单位
unitPrice: "", // 单价
amount: "", // 金额
requireQuantityAndPrice: false,
});
}}
>
</Button>
</View>
<View className="flex-1">
<Button
size="large"
block
type="primary"
onClick={() => {
// 检查必填项
if (!newCostData.itemId) {
console.log("请选择辅料项目");
return;
}
// 检查数量和单价或金额
if (
newCostData.requireQuantityAndPrice &&
(!newCostData.quantity || !newCostData.unitPrice)
) {
console.log("请填写数量和单价");
return;
}
if (
!newCostData.requireQuantityAndPrice &&
!newCostData.amount
) {
console.log("请填写金额");
return;
}
// 创建新的费用项
const newOrderCost: BusinessAPI.OrderCost = {
orderCostId: generateShortId(),
itemId: newCostData.itemId || "",
name: newCostData.name,
price: Number(
newCostData.requireQuantityAndPrice
? newCostData.unitPrice || 0
: newCostData.amount || 0,
),
unit: newCostData.unit,
count: Number(
newCostData.requireQuantityAndPrice
? newCostData.quantity || 1
: 1,
),
payerType: "US",
costType: "PACKAGING_MATERIALS",
principal: "",
};
// 更新purchaseOrderVO将新费用添加到orderCostList中
if (onChange) {
const updatedOrderCostList = [
...(purchaseOrderVO.orderCostList || []),
newOrderCost,
];
const newPurchaseOrderVO = {
...purchaseOrderVO,
orderCostList: updatedOrderCostList,
};
onChange(newPurchaseOrderVO);
}
setShowAddCostPopup(false);
// 重置表单
setNewCostData({
itemId: "",
name: "", // 费用名称
quantity: 0, // 数量
unit: "", // 单位
unitPrice: "", // 单价
amount: "", // 金额
requireQuantityAndPrice: false,
});
}}
>
</Button>
</View>
</View>
</View>
<SafeArea position="bottom" />
</Popup>
{/* 辅料费编辑弹窗 */}
{packagingMaterials.map((item) => (
<Popup

View File

@ -151,7 +151,7 @@ export default function PackagingCostSection(props: {
onChange(newPurchaseOrderVO as any);
}
}
}, [purchaseOrderVO.orderCostList]);
}, [purchaseOrderVO.orderCostList, onChange, fixedCosts]);
// 当editValues发生变化时更新purchaseOrderVO
useEffect(() => {
@ -204,7 +204,7 @@ export default function PackagingCostSection(props: {
return (
<>
<View className="grid grid-cols-1 gap-2.5">
<View className="flex flex-col gap-2.5">
{/* 固定费用 */}
{fixedCosts.map((item, index) => {
// 初始化编辑值
@ -212,7 +212,7 @@ export default function PackagingCostSection(props: {
return (
<View
className="flex items-center justify-between gap-2.5 p-2.5"
className="flex items-center justify-between gap-2.5"
key={item.orderCostId}
onClick={() =>
setVisiblePopup((prev) => ({
@ -254,7 +254,7 @@ export default function PackagingCostSection(props: {
return (
<View
className="flex items-center justify-between gap-2.5 p-2.5"
className="flex items-center justify-between gap-2.5"
key={item.orderCostId}
onClick={() =>
setVisiblePopup((prev) => ({
@ -283,18 +283,18 @@ export default function PackagingCostSection(props: {
</View>
);
})}
</View>
{!readOnly && (
<Button
type={"primary"}
block
size={"large"}
onClick={() => setShowAddCostPopup(true)}
>
</Button>
)}
{!readOnly && (
<Button
type={"primary"}
block
size={"large"}
onClick={() => setShowAddCostPopup(true)}
>
</Button>
)}
</View>
{/* 新增其他费用*/}
<Popup
@ -311,49 +311,32 @@ export default function PackagingCostSection(props: {
<View className="text-neutral-darkest text-sm font-medium">
</View>
<View
className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid"
onClick={() =>
setPickerVisible((prev) => ({ ...prev, costType: true }))
}
>
<Input
type="text"
placeholder="请选择费用类型"
value={
costTypeOptions.find(
(option) => option.value === newCostData.costType,
)?.label || ""
}
disabled
/>
<Icon name="chevron-down" className="absolute -right-1 mr-4" />
<View className="flex flex-row gap-4">
{costTypeOptions.map((option) => (
<View
key={option.value}
className={`flex flex-1 flex-row items-center justify-center rounded-md py-2 ${
newCostData.costType === option.value
? "bg-blue-100 text-blue-600"
: "border border-gray-300"
}`}
onClick={() => {
setNewCostData((prev) => ({
...prev,
costType: option.value,
itemId: "",
name: "",
payee: "",
quantity: "",
unitPrice: "",
amount: "",
}));
}}
>
{option.label}
</View>
))}
</View>
<Picker
title="请选择费用类型"
visible={pickerVisible.costType}
options={[costTypeOptions]}
onConfirm={(_, values) => {
const selectedValue = values[0] as string;
setNewCostData((prev) => ({
...prev,
costType: selectedValue,
itemId: "",
name: "",
payee: "",
quantity: "",
unitPrice: "",
amount: "",
}));
setPickerVisible((prev) => ({ ...prev, costType: false }));
}}
onCancel={() =>
setPickerVisible((prev) => ({ ...prev, costType: false }))
}
onClose={() =>
setPickerVisible((prev) => ({ ...prev, costType: false }))
}
/>
{/* 固定费用子类型 */}
{newCostData.costType === "FIXED_COST" && (
@ -381,7 +364,14 @@ export default function PackagingCostSection(props: {
<Picker
title="请选择固定费用类型"
visible={pickerVisible.fixedCost}
options={[fixedCostOptions]}
options={[
fixedCostOptions.filter((option) => {
// 检查该固定费用类型是否已存在于fixedCosts中
return !fixedCosts.some(
(cost) => cost.name === option.label,
);
}),
]}
onConfirm={(_, values) => {
const selectedValue = values[0] as string;
const selectedItem = fixedCostOptions.find(
@ -479,7 +469,21 @@ export default function PackagingCostSection(props: {
size="large"
block
type="default"
onClick={() => setShowAddCostPopup(false)}
onClick={() => {
setShowAddCostPopup(false);
// 重置表单
setNewCostData({
payerType: "",
principal: "",
costType: "",
itemId: generateShortId(),
name: "",
payee: "",
quantity: "",
unitPrice: "",
amount: "",
});
}}
>
</Button>

View File

@ -2265,7 +2265,6 @@ export default hocAuth(function Page(props: CommonComponent) {
type="default"
size="large"
block
className="max-w-[150px] flex-1"
onClick={handleDownload}
>
@ -2279,7 +2278,6 @@ export default hocAuth(function Page(props: CommonComponent) {
type="default"
size="large"
block
className="max-w-[150px] flex-1"
onClick={handleView}
>
@ -2290,7 +2288,6 @@ export default hocAuth(function Page(props: CommonComponent) {
type="primary"
size="large"
block
className="max-w-[150px] flex-1"
onClick={handleShare}
>

View File

@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: "采购底单单据生成",
navigationBarBackgroundColor: "#fff",
});

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,12 @@ export default hocAuth(function Page(props: CommonComponent) {
const [state, setState] = useState<BusinessAPI.ShipOrderPageQry["state"]>();
const [dealerVO, setDealerVO] = useState<BusinessAPI.DealerVO>();
const [deliveryTemplate, setDeliveryTemplate] = useState<BusinessAPI.DealerVO['deliveryTemplate']>();
const [canGenerateDocuments, setCanGenerateDocuments] = useState({
shipDocument: false,
purchaseDocument: false,
});
const [deliveryTemplate, setDeliveryTemplate] =
useState<BusinessAPI.DealerVO["deliveryTemplate"]>();
// 控制弹窗显示状态
const [popupVisible, setPopupVisible] = useState(false);
@ -31,6 +36,66 @@ export default hocAuth(function Page(props: CommonComponent) {
const [currentShipOrder, setCurrentShipOrder] =
useState<BusinessAPI.ShipOrderVO | null>(null);
// 生成发货单据
const generateShipDocument = async () => {
if (!currentShipOrder?.shipOrderId) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "未找到关联的发货单",
});
return;
}
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/delivery/document/delivery", {
shipOrderId: currentShipOrder.shipOrderId,
}),
success: () => {
setPopupVisible(false);
},
});
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "跳转发货单据页面失败",
});
}
};
// 生成采购底单单据
const generatePurchaseDocument = () => {
if (!currentShipOrder?.shipOrderId) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "未找到关联的发货单",
});
return;
}
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/delivery/document/purchase", {
shipOrderId: currentShipOrder.shipOrderId,
}),
success: () => {
setPopupVisible(false);
},
});
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "跳转发货单据页面失败",
});
}
};
const actionRef = useRef<ActionType>();
const toolbar: ToolBar = {
search: {
@ -60,7 +125,7 @@ export default hocAuth(function Page(props: CommonComponent) {
},
render: () => (
<>
{/* 生成发货单据弹窗 */}
{/* 生成单据弹窗 */}
<Popup
duration={150}
style={{
@ -69,7 +134,7 @@ export default hocAuth(function Page(props: CommonComponent) {
visible={popupVisible}
className={"flex flex-col"}
position="bottom"
title={"生成发货单据"}
title={"生成单据"}
onClose={() => setPopupVisible(false)}
onOverlayClick={() => setPopupVisible(false)}
lockScroll
@ -104,19 +169,44 @@ export default hocAuth(function Page(props: CommonComponent) {
</View>
)}
{deliveryTemplate ? (
{currentShipOrder && (
<View className="border-t border-gray-200 pt-2.5">
<View className="mb-2">
<Text className="text-lg font-semibold"></Text>
<Text className="text-lg font-semibold"></Text>
</View>
<View className="text-sm text-gray-600">
</View>
</View>
) : (
<View className="flex flex-col gap-3">
<View className="mt-2.5 text-center text-sm text-red-500">
<View className="flex flex-col gap-3">
{canGenerateDocuments.shipDocument && (
<>
{!deliveryTemplate ? (
<View className="border-t border-gray-200 pt-2.5">
<View className="flex flex-col gap-3">
<View className="mt-2.5 text-center text-sm text-red-500">
</View>
</View>
</View>
) : (
<View className="border-t border-gray-200 pt-2.5">
<View className="flex flex-col gap-3">
<View className="text-primary mt-2.5 text-center text-sm">
</View>
</View>
</View>
)}
</>
)}
{!canGenerateDocuments.purchaseDocument &&
!canGenerateDocuments.shipDocument && (
<View className="border-t border-gray-200 pt-2.5">
<View className="flex flex-col gap-3">
<View className="mt-2.5 text-center text-sm text-red-500">
</View>
</View>
</View>
)}
</View>
</View>
)}
@ -124,27 +214,44 @@ export default hocAuth(function Page(props: CommonComponent) {
<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={() => setPopupVisible(false)}
>
</Button>
</View>
<View className={"flex-1"}>
<Button
size={"large"}
block
disabled={!deliveryTemplate}
type="primary"
onClick={confirmGenerateDocument}
>
</Button>
</View>
{canGenerateDocuments.purchaseDocument && currentShipOrder && (
<View className={"flex-1"}>
<Button
type="default"
size={"xlarge"}
block
onClick={generatePurchaseDocument}
>
</Button>
</View>
)}
{canGenerateDocuments.shipDocument && currentShipOrder && (
<View className={"flex-1"}>
<Button
type="primary"
size={"xlarge"}
disabled={!deliveryTemplate}
block
onClick={generateShipDocument}
>
</Button>
</View>
)}
{!canGenerateDocuments.purchaseDocument &&
!canGenerateDocuments.shipDocument && (
<View className={"flex-1"}>
<Button
type="default"
size={"xlarge"}
block
onClick={() => setPopupVisible(false)}
>
</Button>
</View>
)}
</View>
<SafeArea position={"bottom"} />
</View>
@ -194,7 +301,7 @@ export default hocAuth(function Page(props: CommonComponent) {
});
// 生成发货单据
const generateShipDocument = async (shipOrderVO: BusinessAPI.ShipOrderVO) => {
const generateDocument = async (shipOrderVO: BusinessAPI.ShipOrderVO) => {
setCurrentShipOrder(shipOrderVO);
const { data: dealerData } = await business.dealer.showDealer({
@ -205,40 +312,24 @@ export default hocAuth(function Page(props: CommonComponent) {
if (dealerData.success) {
const deliveryTemplate = dealerData.data?.deliveryTemplate;
setDeliveryTemplate(deliveryTemplate)
setDeliveryTemplate(deliveryTemplate);
const documentTypes = dealerData.data?.documentTypes;
if (documentTypes) {
setCanGenerateDocuments({
shipDocument: documentTypes.includes("delivery"),
purchaseDocument: documentTypes.includes("purchase"),
});
} else {
setCanGenerateDocuments({
shipDocument: false,
purchaseDocument: false,
});
}
}
setPopupVisible(true);
};
// 确认生成发货单据
const confirmGenerateDocument = () => {
if (!currentShipOrder?.shipOrderId) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "未找到关联的发货单",
});
return;
}
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/delivery/document", {
shipOrderId: currentShipOrder.shipOrderId,
}),
});
setPopupVisible(false);
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "跳转发货单据页面失败",
});
}
};
return (
<PageList<BusinessAPI.ShipOrderVO, BusinessAPI.ShipOrderPageQry>
rowId={"shipOrderId"}
@ -323,11 +414,11 @@ export default hocAuth(function Page(props: CommonComponent) {
type={"primary"}
size={"small"}
onClick={async (e) => {
await generateShipDocument(shipOrderVO);
await generateDocument(shipOrderVO);
e.stopPropagation();
}}
>
</Button>
</View>
</View>

View File

@ -1,16 +1,9 @@
import hocAuth from "@/hocs/auth";
import { CommonComponent } from "@/types/typings";
import { View, Text } from "@tarojs/components";
import { Text, View } from "@tarojs/components";
import { useEffect, useState } from "react";
import { auth } from "@/services";
import {
Button,
Dialog,
Image,
Popup,
SafeArea,
Toast,
} from "@nutui/nutui-react-taro";
import { Button, Dialog, Image, Popup, Toast } from "@nutui/nutui-react-taro";
import dayjs from "dayjs";
import Taro from "@tarojs/taro";
import buildUrl from "@/utils/buildUrl";
@ -382,8 +375,6 @@ export default hocAuth(function Page(props: CommonComponent) {
</Button>
</View>
<SafeArea position={"bottom"} />
</Popup>
</>
);

View File

@ -15,10 +15,11 @@ export default hocAuth(function Page(props: CommonComponent) {
useState<BusinessAPI.PurchaseOrderVO>();
const [canGenerateDocuments, setCanGenerateDocuments] = useState({
shipDocument: false,
costDocument: false,
purchaseDocument: false,
});
const [shipOrder, setShipOrder] = useState<BusinessAPI.ShipOrderVO>();
const [deliveryTemplate, setDeliveryTemplate] = useState<BusinessAPI.DealerVO['deliveryTemplate']>();
const [deliveryTemplate, setDeliveryTemplate] =
useState<BusinessAPI.DealerVO["deliveryTemplate"]>();
const init = async (orderId: string) => {
setLoading(true);
@ -59,7 +60,7 @@ export default hocAuth(function Page(props: CommonComponent) {
setDeliveryTemplate(dealerData.data.deliveryTemplate);
setCanGenerateDocuments({
shipDocument: documentTypes.includes("delivery"),
costDocument: documentTypes.includes("cost"),
purchaseDocument: documentTypes.includes("purchase"),
});
}
}
@ -104,7 +105,7 @@ export default hocAuth(function Page(props: CommonComponent) {
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/delivery/document", {
url: buildUrl("/pages/delivery/document/delivery", {
shipOrderId: shipOrder.shipOrderId,
}),
});
@ -117,22 +118,40 @@ export default hocAuth(function Page(props: CommonComponent) {
}
};
// 生成成本单
const generateCostDocument = () => {
if (!canGenerateDocuments.costDocument) {
// 生成采购底单单据
const generatePurchaseDocument = () => {
if (!canGenerateDocuments.purchaseDocument) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "当前经销商不支持生成成本单",
content: "当前经销商不支持生成采购底单单据",
});
return;
}
Toast.show("toast", {
icon: "success",
title: "提示",
content: "成本单生成成功",
});
if (!shipOrder?.shipOrderId) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "未找到关联的发货单",
});
return;
}
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/delivery/document/purchase", {
shipOrderId: shipOrder.shipOrderId,
}),
});
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "跳转发货单据页面失败",
});
}
};
return (
@ -189,53 +208,40 @@ export default hocAuth(function Page(props: CommonComponent) {
</View>
</View>
{deliveryTemplate ? (
{shipOrder && (
<View className="border-t border-gray-200 pt-2.5">
<View className="mb-2">
<Text className="text-lg font-semibold"></Text>
</View>
<View className="flex flex-col gap-3">
{canGenerateDocuments.shipDocument && shipOrder && (
<Button
type="primary"
size={"xlarge"}
block
onClick={generateShipDocument}
>
</Button>
)}
{canGenerateDocuments.shipDocument &&
(deliveryTemplate ? (
<Button
type="primary"
size={"xlarge"}
block
onClick={generateShipDocument}
>
</Button>
) : (
<View className="mt-2.5 text-center text-sm text-red-500">
</View>
))}
{canGenerateDocuments.costDocument && (
{canGenerateDocuments.purchaseDocument && shipOrder && (
<Button
type="default"
size={"xlarge"}
block
onClick={generateCostDocument}
onClick={generatePurchaseDocument}
>
</Button>
)}
</View>
{!shipOrder && (
<View className="mt-2.5 text-center text-sm text-red-500">
</View>
)}
</View>
) : (
<View className="border-t border-gray-200 pt-2.5">
<View className="mb-2">
<Text className="text-lg font-semibold"></Text>
</View>
<View className="flex flex-col gap-3">
<View className="mt-2.5 text-center text-sm text-red-500">
</View>
</View>
</View>
)}
</View>

View File

@ -139,7 +139,8 @@ export default hocAuth(function Page(props: CommonComponent) {
const orderCostList1 = purchaseOrder.orderCostList.map(
(item: CostItem) => ({
...item,
selected: true,
// 设置默认选中,
selected: item.count > 0,
}),
) as any;
setOrderCostList(orderCostList1);
@ -308,6 +309,7 @@ export default hocAuth(function Page(props: CommonComponent) {
orderId: orderId,
active: active,
orderSupplierList: purchaseOrder.orderSupplierList,
orderCostList: purchaseOrder.orderCostList,
});
if (data.success) {
@ -611,6 +613,42 @@ export default hocAuth(function Page(props: CommonComponent) {
}),
]);
}}
changeProduct={(productVO: BusinessAPI.ProductVO) => {
setOrderSupplierList([
...orderSupplierList.map((item: SupplierVO) => {
item.productId = productVO.productId;
item.productName = productVO.name;
return item;
}),
]);
setPurchaseOrder((prev) => {
return {
...prev!,
orderCostList: [
...(prev?.orderCostList?.filter((item) => {
return (
item.costType !== "WORKER_ADVANCE" &&
item.costType !== "PRODUCTION_ADVANCE"
);
}) || []),
...(productVO.costItemVOList?.map((item) => {
return {
orderCostId: generateShortId(),
orderId: purchaseOrder?.orderId,
itemId: item.itemId,
name: item.name,
price: item.price,
unit: item.unit,
count: 0,
payerType: "US" as any,
principal: "",
costType: item.costType,
};
}) || []),
],
};
});
}}
isFirst={index === 0}
key={item.orderSupplierId}
ref={(ref) => {
@ -778,7 +816,6 @@ export default hocAuth(function Page(props: CommonComponent) {
const selectedIndex = orderSupplierList.findIndex(
(supplier) => supplier.selected,
);
console.log("selectedIndex", selectedIndex);
if (
selectedIndex !== -1 &&
orderPackageRefs.current[selectedIndex]?.validate()

View File

@ -24,7 +24,6 @@ import {
MaterialCostSection,
PackageInfoSection,
PackagingCostSection,
PurchaseCostInfoSection,
PurchaseOrderRejectApprove,
RebateCalcSection,
State,
@ -70,13 +69,13 @@ const sections = {
// containerClass: "border-l-8 border-l-purple-500",
// contentClass: "p-4 bg-white rounded-b-lg shadow-sm overflow-x-auto",
// },
// 采购成本
purchaseCostInfo: {
component: PurchaseCostInfoSection,
title: "采购成本",
containerClass: "border-l-8 border-l-red-500",
contentClass: "p-4 bg-white rounded-b-lg shadow-sm overflow-x-auto",
},
// // 采购成本
// purchaseCostInfo: {
// component: PurchaseCostInfoSection,
// title: "采购成本",
// containerClass: "border-l-8 border-l-red-500",
// contentClass: "p-4 bg-white rounded-b-lg shadow-sm overflow-x-auto",
// },
// 包装纸箱费
packageInfo: {
component: PackageInfoSection,

View File

@ -7,6 +7,7 @@ import * as supplier from "./supplier";
import * as shipOrder from "./shipOrder";
import * as setting from "./setting";
import * as purchaseOrder from "./purchaseOrder";
import * as product from "./product";
import * as platform from "./platform";
import * as menu from "./menu";
import * as material from "./material";
@ -34,6 +35,7 @@ export default {
shipOrder,
setting,
purchaseOrder,
product,
platform,
menu,
material,

View File

@ -0,0 +1,156 @@
// @ts-ignore
/* eslint-disable */
import request from "../request";
/** 创建产品 POST /operation/createProduct */
export async function createProduct(
body: BusinessAPI.ProductCreateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseProductVO>(
"/operation/createProduct",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
},
);
}
/** 产品删除 DELETE /operation/destroyProduct */
export async function destroyProduct(
body: BusinessAPI.ProductDestroyCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>("/operation/destroyProduct", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
});
}
/** 产品拖拽排序 PUT /operation/dragProduct */
export async function dragProduct(
body: BusinessAPI.ProductDragCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>("/operation/dragProduct", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
});
}
/** 产品拖拽排序 PATCH /operation/dragProduct */
export async function dragProduct1(
body: BusinessAPI.ProductDragCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>("/operation/dragProduct", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
});
}
/** 产品列表 GET /operation/listProduct */
export async function listProduct(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.listProductParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.MultiResponseProductVO>("/operation/listProduct", {
method: "GET",
params: {
...params,
productListQry: undefined,
...params["productListQry"],
},
...(options || {}),
});
}
/** 产品列表 GET /operation/pageProduct */
export async function pageProduct(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.pageProductParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.PageResponseProductVO>("/operation/pageProduct", {
method: "GET",
params: {
...params,
productPageQry: undefined,
...params["productPageQry"],
},
...(options || {}),
});
}
/** 产品详情 GET /operation/showProduct */
export async function showProduct(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.showProductParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseProductVO>(
"/operation/showProduct",
{
method: "GET",
params: {
...params,
productShowQry: undefined,
...params["productShowQry"],
},
...(options || {}),
},
);
}
/** 产品更新 PUT /operation/updateProduct */
export async function updateProduct(
body: BusinessAPI.ProductUpdateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseProductVO>(
"/operation/updateProduct",
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
},
);
}
/** 产品更新 PATCH /operation/updateProduct */
export async function updateProduct1(
body: BusinessAPI.ProductUpdateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseProductVO>(
"/operation/updateProduct",
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
},
);
}

View File

@ -798,12 +798,14 @@ declare namespace BusinessAPI {
type CostItemCreateCmd = {
/** 项目ID */
itemId: string;
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用 */
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用5_工头垫付6_产地垫付 */
costType:
| "PACKAGING_MATERIALS"
| "HUMAN_COST"
| "OTHER_COST"
| "FIXED_COST";
| "FIXED_COST"
| "WORKER_ADVANCE"
| "PRODUCTION_ADVANCE";
/** 项目名称 */
name: string;
/** 单位 */
@ -812,6 +814,8 @@ declare namespace BusinessAPI {
price: number;
/** 是否在录入时显示 */
showInEntry: boolean;
/** 是否需要填写数量和单价 */
requireQuantityAndPrice: boolean;
/** 备注 */
remark?: string;
/** 状态1_启用0_禁用 */
@ -837,12 +841,14 @@ declare namespace BusinessAPI {
status?: boolean;
/** 费用项目ID */
itemId?: string;
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用 */
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用5_工头垫付6_产地垫付 */
costType?:
| "PACKAGING_MATERIALS"
| "HUMAN_COST"
| "OTHER_COST"
| "FIXED_COST";
| "FIXED_COST"
| "WORKER_ADVANCE"
| "PRODUCTION_ADVANCE";
/** 是否在录入时显示 */
showInEntry?: boolean;
};
@ -864,12 +870,14 @@ declare namespace BusinessAPI {
status?: boolean;
/** 费用项目ID */
itemId?: string;
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用 */
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用5_工头垫付6_产地垫付 */
costType?:
| "PACKAGING_MATERIALS"
| "HUMAN_COST"
| "OTHER_COST"
| "FIXED_COST";
| "FIXED_COST"
| "WORKER_ADVANCE"
| "PRODUCTION_ADVANCE";
/** 项目名称 */
name?: string;
offset?: number;
@ -885,12 +893,14 @@ declare namespace BusinessAPI {
type CostItemUpdateCmd = {
/** 费用项目ID */
itemId: string;
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用 */
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用5_工头垫付6_产地垫付 */
costType:
| "PACKAGING_MATERIALS"
| "HUMAN_COST"
| "OTHER_COST"
| "FIXED_COST";
| "FIXED_COST"
| "WORKER_ADVANCE"
| "PRODUCTION_ADVANCE";
/** 项目名称 */
name: string;
/** 单位 */
@ -899,6 +909,8 @@ declare namespace BusinessAPI {
price: number;
/** 是否在录入时显示 */
showInEntry: boolean;
/** 是否需要填写数量和单价 */
requireQuantityAndPrice: boolean;
/** 备注 */
remark?: string;
/** 状态1_启用0_禁用 */
@ -908,12 +920,14 @@ declare namespace BusinessAPI {
type CostItemVO = {
/** 项目ID */
itemId: string;
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用 */
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用5_工头垫付6_产地垫付 */
costType:
| "PACKAGING_MATERIALS"
| "HUMAN_COST"
| "OTHER_COST"
| "FIXED_COST";
| "FIXED_COST"
| "WORKER_ADVANCE"
| "PRODUCTION_ADVANCE";
/** 项目名称 */
name: string;
/** 单位 */
@ -922,6 +936,8 @@ declare namespace BusinessAPI {
price: number;
/** 是否在录入时显示 */
showInEntry: boolean;
/** 是否需要填写数量和单价 */
requireQuantityAndPrice: boolean;
/** 备注 */
remark?: string;
/** 状态1_启用0_禁用 */
@ -1859,6 +1875,10 @@ declare namespace BusinessAPI {
platformListQry: PlatformListQry;
};
type listProductParams = {
productListQry: ProductListQry;
};
type listPurchaseOrderParams = {
purchaseOrderListQry: PurchaseOrderListQry;
};
@ -2251,6 +2271,15 @@ declare namespace BusinessAPI {
notEmpty?: boolean;
};
type MultiResponseProductVO = {
success?: boolean;
errCode?: string;
errMessage?: string;
data?: ProductVO[];
empty?: boolean;
notEmpty?: boolean;
};
type MultiResponsePurchaseOrderVO = {
success?: boolean;
errCode?: string;
@ -2352,15 +2381,17 @@ declare namespace BusinessAPI {
/** 数量 */
count: number;
/** 付款方类型:1-我方,2-对方 */
payerType: "US" | "OTHER";
payerType?: "US" | "OTHER";
/** 负责人 */
principal?: string;
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用 */
/** 费用类型1_包装材料2_人工费用3_其他费用4_固定费用5_工头垫付6_产地垫付 */
costType:
| "PACKAGING_MATERIALS"
| "HUMAN_COST"
| "OTHER_COST"
| "FIXED_COST";
| "FIXED_COST"
| "WORKER_ADVANCE"
| "PRODUCTION_ADVANCE";
};
type OrderDealer = {
@ -2501,6 +2532,10 @@ declare namespace BusinessAPI {
contractUpload: boolean;
/** 合同 */
contractImg?: string[];
/** 产品ID */
productId?: string;
/** 产品名称 */
productName?: string;
/** 采购订单包装箱信息 */
orderPackageList?: OrderPackage[];
};
@ -2615,6 +2650,10 @@ declare namespace BusinessAPI {
platformPageQry: PlatformPageQry;
};
type pageProductParams = {
productPageQry: ProductPageQry;
};
type pagePurchaseOrderParams = {
purchaseOrderPageQry: PurchaseOrderPageQry;
};
@ -2840,6 +2879,19 @@ declare namespace BusinessAPI {
totalPages?: number;
};
type PageResponseProductVO = {
success?: boolean;
errCode?: string;
errMessage?: string;
totalCount?: number;
pageSize?: number;
pageIndex?: number;
data?: ProductVO[];
empty?: boolean;
notEmpty?: boolean;
totalPages?: number;
};
type PageResponsePurchaseOrderVO = {
success?: boolean;
errCode?: string;
@ -3021,6 +3073,107 @@ declare namespace BusinessAPI {
homePage: string;
};
type ProductCreateCmd = {
/** 产品ID */
productId: string;
/** 产品名称 */
name: string;
/** 关联成本费用id */
costItemIds?: number[];
/** 备注 */
remark?: string;
/** 排序号 */
sort: number;
/** 状态1_启用0_禁用 */
status: boolean;
/** 创建时间 */
createdAt?: string;
};
type ProductDestroyCmd = {
/** 产品表ID */
productId: string;
};
type ProductDragCmd = {
/** 相邻元素前 */
prevId?: number;
/** 相邻元素后 */
nextId?: number;
/** 当前元素 */
currentId?: number;
};
type ProductListQry = {
/** 状态1_启用0_禁用 */
status?: boolean;
/** 产品表ID */
productId?: string;
};
type ProductPageQry = {
pageSize?: number;
pageIndex?: number;
orderBy?: string;
orderDirection?: string;
groupBy?: string;
needTotalCount?: boolean;
/** 自定义字段key */
customFieldKey?: string;
/** 自定义字段value */
customFieldValue?: string;
/** 备注 */
remark?: string;
/** 状态1_启用0_禁用 */
status?: boolean;
/** 产品表ID */
productId?: string;
offset?: number;
};
type ProductShowQry = {
/** 状态1_启用0_禁用 */
status?: boolean;
/** 产品表ID */
productId?: string;
};
type ProductUpdateCmd = {
/** 产品表ID */
productId: string;
/** 产品名称 */
name: string;
/** 关联成本费用id */
costItemIds?: number[];
/** 备注 */
remark?: string;
/** 排序号 */
sort: number;
/** 状态1_启用0_禁用 */
status: boolean;
/** 创建时间 */
createdAt?: string;
};
type ProductVO = {
/** 产品ID */
productId: string;
/** 产品名称 */
name: string;
/** 关联成本费用id */
costItemIds?: number[];
/** 备注 */
remark?: string;
/** 排序号 */
sort: number;
/** 状态1_启用0_禁用 */
status: boolean;
/** 成本费用 */
costItemVOList?: CostItemVO[];
/** 创建时间 */
createdAt?: string;
};
type PurchaseOrderApproveCmd = {
/** 采购订单ID */
orderId: string;
@ -3169,6 +3322,8 @@ declare namespace BusinessAPI {
active?: number;
/** 供应商信息 */
orderSupplierList: OrderSupplier[];
/** 采购订单费用信息 */
orderCostList: OrderCost[];
};
type PurchaseOrderStep3Cmd = {
@ -3838,6 +3993,10 @@ declare namespace BusinessAPI {
platformShowQry: PlatformShowQry;
};
type showProductParams = {
productShowQry: ProductShowQry;
};
type showPurchaseOrderParams = {
purchaseOrderShowQry: PurchaseOrderShowQry;
};
@ -4009,6 +4168,13 @@ declare namespace BusinessAPI {
data?: PlatformVO;
};
type SingleResponseProductVO = {
success?: boolean;
errCode?: string;
errMessage?: string;
data?: ProductVO;
};
type SingleResponsePurchaseOrderVO = {
success?: boolean;
errCode?: string;

View File

@ -0,0 +1,107 @@
import axios from "axios";
import Taro from "@tarojs/taro";
import { Toast } from "@nutui/nutui-react-taro";
const request = axios.create({
baseURL: process.env.TARO_API_DOMAIN,
});
// 创建全局的axios实例并安装请求拦截器
request.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么
const saToken = Taro.getStorageSync("saToken");
if (saToken) {
config.headers.saToken = saToken;
}
const slug = Taro.getStorageSync("userRoleVO")?.slug;
if (slug) {
config.headers["Xh-Role-Slug"] = slug;
}
return config;
},
(error) => {
// 处理请求错误
return Promise.reject(error);
},
);
// 在获取到响应后,安装响应拦截器
request.interceptors.response.use(
async (response) => {
console.log("response", response);
const {
data: { success, data },
} = response;
// 处理响应成功的情况
if (success) {
const saToken = data?.meta?.accessToken;
if (saToken) {
Taro.setStorageSync("saToken", saToken);
Taro.setStorageSync("user", data.userVO);
Taro.setStorageSync("userRoleVO", data.userRoleVOList?.[0]);
}
} else {
// 异常处理
if (response.data.errCode === "403") {
Toast.show("toast", {
icon: "fail",
title: "暂无权限",
content: response.data.errMessage,
});
}
// 如果是401则重新登录然后重新发起请求
if (response.data.errCode === "401") {
// 移除缓存
Taro.removeStorageSync("saToken");
Taro.removeStorageSync("user");
Taro.removeStorageSync("userRoleVO");
// const { code } = await Taro.login();
// const data = await wxMaLogin(code);
// if (data) {
// response.config.headers.saToken = `${Taro.getStorageSync("saToken")}`;
// return request(response.config);
// }
// 跳转到登录页面
Taro.navigateTo({
url: "/pages/public/login/index",
});
return response;
}
console.log("response.data.errCode", response.data.errCode);
if (response.data.errCode === "400") {
Toast.show("toast", {
icon: "fail",
title: "请求失败",
content: response.data.errMessage,
});
}
if (response.data.errCode === "500") {
Toast.show("toast", {
icon: "fail",
title: "服务器错误",
content: response.data.errMessage,
});
} else {
Toast.show("toast", {
icon: "fail",
title: "请求失败",
content: response.data.errMessage,
});
}
}
return response;
},
(error) => {
// 处理响应错误的情况
return Promise.reject(error);
},
);
export default request;

View File

@ -0,0 +1,16 @@
// @ts-ignore
/* eslint-disable */
import request from "../poster-request";
/** 健康检查 检查服务是否正常运行 GET /status */
export async function getStatus(options?: { [key: string]: any }) {
return request<{
success?: boolean;
data?: Record<string, any>;
message?: string;
code?: number;
}>("/status", {
method: "GET",
...(options || {}),
});
}

View File

@ -0,0 +1,12 @@
// @ts-ignore
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
import * as health from "./health";
import * as poster from "./poster";
import * as pdf from "./pdf";
export default {
health,
poster,
pdf,
};

View File

@ -0,0 +1,34 @@
// @ts-ignore
/* eslint-disable */
import request from "../poster-request";
/** 生成PDF 从网页URL或HTML内容生成PDF文档 POST /api/v1/pdf */
export async function postApiV1Pdf(
body: {
/** 要生成PDF的网页URL */
webpage?: string;
/** 要生成PDF的HTML内容可选优先级高于webpage */
html?: string;
/** 设备缩放因子 */
device?: number;
/** PDF宽度 */
width?: number;
/** PDF高度 */
height?: number;
},
options?: { [key: string]: any },
) {
return request<{
success?: boolean;
data?: { name?: string; path?: string };
message?: string;
code?: number;
}>("/api/v1/pdf", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
});
}

View File

@ -0,0 +1,38 @@
// @ts-ignore
/* eslint-disable */
import request from "../poster-request";
/** 生成海报 从网页URL或HTML内容生成海报图像 POST /api/v1/poster */
export async function postApiV1Poster(
body: {
/** 要生成海报的网页URL */
webpage?: string;
/** 要生成海报的HTML内容可选优先级高于webpage */
html?: string;
/** 设备缩放因子 */
device?: number;
/** 海报宽度 */
width?: number;
/** 海报高度 */
height?: number;
/** 输出图像类型 */
type?: string;
/** 编码类型 */
encoding?: string;
},
options?: { [key: string]: any },
) {
return request<{
success?: boolean;
data?: { name?: string; path?: string };
message?: string;
code?: number;
}>("/api/v1/poster", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
});
}

View File

@ -0,0 +1 @@
declare namespace PosterAPI {}

View File

@ -26,7 +26,7 @@ async function ocr(tempFilePath: string, path: string) {
name: "file",
header: {
saToken: Taro.getStorageSync("saToken"),
"Content-Type": "multipart/form-data",
// "Content-Type": "multipart/form-data",
},
});

View File

@ -10,7 +10,7 @@ export const uploadFile = async (file: File | string) => {
name: "file",
header: {
saToken: Taro.getStorageSync("saToken"),
"Content-Type": "multipart/form-data",
// "Content-Type": "multipart/form-data",
},
});

File diff suppressed because one or more lines are too long

1
swagger/poster.json Normal file

File diff suppressed because one or more lines are too long