feat(components): 添加发票批量上传功能并优化列表组件
- 添加 InvoiceBatchUpload 组件支持发票批量上传 - 修改 PageList 组件类型定义支持泛型 - 在 PageList 组件中添加 toolbar footer 支持 - 优化发票页面 UI 布局和交互体验 - 更新应用版本号到 v0.0.53 - 重构发票上传页面实现批量选择功能
This commit is contained in:
parent
ebd955de97
commit
ff39dc01d2
@ -48,7 +48,7 @@ export default <T extends {}, Q extends Query = Query>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [lightTheme, setLightTheme] = useState(false);
|
const [lightTheme, setLightTheme] = useState(false);
|
||||||
const [data, setData] = useState<Record<any>[]>();
|
const [data, setData] = useState<T[]>([]);
|
||||||
const [query, setQuery] = useState<Query>();
|
const [query, setQuery] = useState<Query>();
|
||||||
|
|
||||||
const [selectRows, setSelectRows] = useState<any[]>([]);
|
const [selectRows, setSelectRows] = useState<any[]>([]);
|
||||||
@ -345,7 +345,7 @@ export default <T extends {}, Q extends Query = Query>(
|
|||||||
>
|
>
|
||||||
{toolbar?.actions && toolbar?.actions.map((item) => item)}
|
{toolbar?.actions && toolbar?.actions.map((item) => item)}
|
||||||
|
|
||||||
{toolbar?.selectRow?.onClick && (
|
{toolbar?.selectRow?.onClick && !toolbar?.footer && (
|
||||||
<View key={"confirm"} className={"flex justify-between gap-2 p-2.5"}>
|
<View key={"confirm"} className={"flex justify-between gap-2 p-2.5"}>
|
||||||
<View className={"flex flex-1 items-center justify-between gap-1"}>
|
<View className={"flex flex-1 items-center justify-between gap-1"}>
|
||||||
<View className={"flex items-center gap-2"}>
|
<View className={"flex items-center gap-2"}>
|
||||||
@ -393,6 +393,7 @@ export default <T extends {}, Q extends Query = Query>(
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{toolbar?.footer && toolbar?.footer?.(data || [])}
|
||||||
{tabbar ? tabbar : <SafeArea position="bottom" />}
|
{tabbar ? tabbar : <SafeArea position="bottom" />}
|
||||||
|
|
||||||
<Picker
|
<Picker
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export type TabPane = {
|
|||||||
title: string | number;
|
title: string | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ToolBar = {
|
export type ToolBar<T> = {
|
||||||
selectRow?: {
|
selectRow?: {
|
||||||
onClick: (selectRow: any[]) => void;
|
onClick: (selectRow: any[]) => void;
|
||||||
};
|
};
|
||||||
@ -38,6 +38,7 @@ export type ToolBar = {
|
|||||||
items: any[];
|
items: any[];
|
||||||
};
|
};
|
||||||
render?: () => React.ReactNode | undefined;
|
render?: () => React.ReactNode | undefined;
|
||||||
|
footer?: (record: T[]) => React.ReactNode | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IRecordListProps<T = {}, Q extends Query = Query> = {
|
export type IRecordListProps<T = {}, Q extends Query = Query> = {
|
||||||
@ -47,7 +48,7 @@ export type IRecordListProps<T = {}, Q extends Query = Query> = {
|
|||||||
type: "virtual" | "infinite";
|
type: "virtual" | "infinite";
|
||||||
skeleton?: React.ReactNode;
|
skeleton?: React.ReactNode;
|
||||||
actionRef: React.MutableRefObject<any>;
|
actionRef: React.MutableRefObject<any>;
|
||||||
toolbar?: ToolBar;
|
toolbar?: ToolBar<T>;
|
||||||
render: (record: T, index: number) => React.ReactNode;
|
render: (record: T, index: number) => React.ReactNode;
|
||||||
request: (params: Q) => Promise<Record<T>>;
|
request: (params: Q) => Promise<Record<T>>;
|
||||||
pagination: {
|
pagination: {
|
||||||
|
|||||||
@ -9,3 +9,4 @@ export * from "./delivery";
|
|||||||
export * from "./cost";
|
export * from "./cost";
|
||||||
export * from "./expenses";
|
export * from "./expenses";
|
||||||
export * from "./audit";
|
export * from "./audit";
|
||||||
|
export * from "./invoce";
|
||||||
|
|||||||
298
packages/app-client/src/components/invoce/InvoiceBatchUpload.tsx
Normal file
298
packages/app-client/src/components/invoce/InvoiceBatchUpload.tsx
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
import { Image, Text, View } from "@tarojs/components";
|
||||||
|
import { Button, Popup, SafeArea, Toast } from "@nutui/nutui-react-taro";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import { uploadFile } from "@/utils";
|
||||||
|
import { Icon } from "@/components";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { business } from "@/services";
|
||||||
|
import { globalStore } from "@/store/global-store";
|
||||||
|
import { DecimalUtils } from "@/utils/classes/calculators/core/DecimalUtils";
|
||||||
|
|
||||||
|
interface IInvoiceBatchUploadProps {
|
||||||
|
visible: boolean;
|
||||||
|
setVisible: (visible: boolean) => void;
|
||||||
|
onFinish: () => void;
|
||||||
|
selectedOrders: BusinessAPI.OrderSupplierVO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function InvoiceBatchUpload(props: IInvoiceBatchUploadProps) {
|
||||||
|
const { visible, setVisible, onFinish, selectedOrders } = props;
|
||||||
|
|
||||||
|
const { setLoading } = globalStore((state: any) => state);
|
||||||
|
const [invoiceImg, setInvoiceImg] = useState<
|
||||||
|
BusinessAPI.OrderSupplier["invoiceImg"]
|
||||||
|
>([]);
|
||||||
|
// 提交申请
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
// 这里添加提交申请的逻辑
|
||||||
|
const {
|
||||||
|
data: { success },
|
||||||
|
} = await business.orderSupplier.batchUploadInvoice({
|
||||||
|
orderSupplierIdList: selectedOrders.map((order) => order.orderSupplierId),
|
||||||
|
invoiceImg: invoiceImg,
|
||||||
|
invoiceUpload: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
Toast.show("toast", {
|
||||||
|
icon: "success",
|
||||||
|
title: "提交成功",
|
||||||
|
content: "发票已批量上传",
|
||||||
|
});
|
||||||
|
onFinish?.();
|
||||||
|
setVisible(false);
|
||||||
|
setInvoiceImg([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算选中车次的总重量和总金额
|
||||||
|
const calculateTotals = () => {
|
||||||
|
return selectedOrders.reduce(
|
||||||
|
(totals, order) => {
|
||||||
|
totals.totalWeight = DecimalUtils.add(
|
||||||
|
totals.totalWeight,
|
||||||
|
order.netWeight || 0,
|
||||||
|
);
|
||||||
|
totals.totalAmount = DecimalUtils.add(
|
||||||
|
totals.totalAmount,
|
||||||
|
order.invoiceAmount || 0,
|
||||||
|
);
|
||||||
|
return totals;
|
||||||
|
},
|
||||||
|
{ totalWeight: 0, totalAmount: 0 },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { totalWeight, totalAmount } = calculateTotals();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
duration={150}
|
||||||
|
style={{
|
||||||
|
minHeight: "auto",
|
||||||
|
}}
|
||||||
|
closeable
|
||||||
|
destroyOnClose
|
||||||
|
visible={visible}
|
||||||
|
title={"批量上传发票"}
|
||||||
|
position="bottom"
|
||||||
|
onClose={() => setVisible(false)}
|
||||||
|
onOverlayClick={() => setVisible(false)}
|
||||||
|
lockScroll
|
||||||
|
round
|
||||||
|
>
|
||||||
|
<View className="flex flex-col">
|
||||||
|
{/* 统计信息 */}
|
||||||
|
<View className="mb-2 flex items-center justify-between p-2.5">
|
||||||
|
<View className="text-sm font-medium text-gray-800">
|
||||||
|
已选{" "}
|
||||||
|
<View className="text-primary inline">{selectedOrders.length}</View>{" "}
|
||||||
|
车次
|
||||||
|
</View>
|
||||||
|
<View className="text-sm font-medium text-gray-800">
|
||||||
|
总重量:
|
||||||
|
<View className="text-primary inline">{totalWeight}</View> 斤
|
||||||
|
</View>
|
||||||
|
<View className="text-sm font-medium text-gray-800">
|
||||||
|
总金额:{" "}
|
||||||
|
<View className="text-primary inline">
|
||||||
|
{totalAmount.toFixed(2)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 上传发票 */}
|
||||||
|
<View className="rounded-lg bg-white p-2.5">
|
||||||
|
<View className="flex flex-col gap-2.5">
|
||||||
|
{invoiceImg && invoiceImg?.length > 0 ? (
|
||||||
|
invoiceImg?.map((uploadFileItem, index) => (
|
||||||
|
<View
|
||||||
|
key={uploadFileItem.fileName || index}
|
||||||
|
className="bg-primary/10 flex flex-col gap-2.5 rounded-lg p-2.5"
|
||||||
|
>
|
||||||
|
<View className="flex items-center">
|
||||||
|
<View className="relative mr-3 h-16 w-16 overflow-hidden rounded-lg">
|
||||||
|
<Image
|
||||||
|
className="h-full w-full object-cover"
|
||||||
|
src={uploadFileItem.filePath!}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View className="flex-1">
|
||||||
|
<View className="font-medium" id="invoice-filename">
|
||||||
|
{uploadFileItem.fileName}
|
||||||
|
</View>
|
||||||
|
<View className="text-sm text-gray-500">
|
||||||
|
{Number(uploadFileItem.fileSize! / 1024 / 1024).toFixed(
|
||||||
|
2,
|
||||||
|
)}{" "}
|
||||||
|
MB
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="flex flex-row gap-2.5">
|
||||||
|
<View className={"flex-1"}>
|
||||||
|
<Button
|
||||||
|
type={"primary"}
|
||||||
|
size={"large"}
|
||||||
|
fill={"outline"}
|
||||||
|
block
|
||||||
|
onClick={() => {
|
||||||
|
setInvoiceImg([]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View>重新上传</View>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
<View className={"flex-1"}>
|
||||||
|
<Button
|
||||||
|
type={"warning"}
|
||||||
|
size={"large"}
|
||||||
|
fill={"outline"}
|
||||||
|
block
|
||||||
|
onClick={() => {
|
||||||
|
setInvoiceImg([]);
|
||||||
|
Toast.show("toast", {
|
||||||
|
title: "删除成功",
|
||||||
|
icon: "success",
|
||||||
|
content: "发票已删除",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View>删除</View>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<View className={`flex w-full flex-row items-center gap-2.5`}>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
"border-primary bg-primary/5 flex h-24 flex-1 flex-col items-center justify-center rounded-md border-2 border-dashed p-2.5"
|
||||||
|
}
|
||||||
|
onClick={async () => {
|
||||||
|
await Taro.chooseMessageFile({
|
||||||
|
type: "file",
|
||||||
|
count: 1,
|
||||||
|
success: (res) => {
|
||||||
|
setLoading(true);
|
||||||
|
const file = res.tempFiles[0];
|
||||||
|
uploadFile(file.path).then(({ url }) => {
|
||||||
|
setInvoiceImg([
|
||||||
|
{
|
||||||
|
fileName: url.split("/").pop(),
|
||||||
|
filePath: url,
|
||||||
|
fileSize: file.size,
|
||||||
|
fileType: url.split(".").pop(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setLoading(false);
|
||||||
|
Toast.show("toast", {
|
||||||
|
title: "上传成功",
|
||||||
|
icon: "success",
|
||||||
|
content: "发票已添加",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
Toast.show("toast", {
|
||||||
|
title: "上传失败",
|
||||||
|
icon: "fail",
|
||||||
|
content: err.errMsg,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name={"file-pdf"} size={28} className="text-primary" />
|
||||||
|
<Text className="text-primary text-sm font-medium">
|
||||||
|
PDF文档
|
||||||
|
</Text>
|
||||||
|
<Text className="mt-1 text-xs text-gray-400">
|
||||||
|
点击上传PDF
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View className={"text-primary text-sm font-medium"}>或</View>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
"border-primary bg-primary/5 flex h-24 flex-1 flex-col items-center justify-center rounded-md border-2 border-dashed p-2.5"
|
||||||
|
}
|
||||||
|
onClick={async () => {
|
||||||
|
await Taro.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
success: (res) => {
|
||||||
|
setLoading(true);
|
||||||
|
const file = res.tempFiles[0];
|
||||||
|
uploadFile(file.path).then(({ url }) => {
|
||||||
|
setInvoiceImg([
|
||||||
|
{
|
||||||
|
fileName: url.split("/").pop(),
|
||||||
|
filePath: url,
|
||||||
|
fileSize: file.size,
|
||||||
|
fileType: url.split(".").pop(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setLoading(false);
|
||||||
|
Toast.show("toast", {
|
||||||
|
title: "上传成功",
|
||||||
|
icon: "success",
|
||||||
|
content: "发票已添加",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
Toast.show("toast", {
|
||||||
|
title: "上传失败",
|
||||||
|
icon: "fail",
|
||||||
|
content: err.errMsg,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name={"camera"} size={28} className="text-primary" />
|
||||||
|
<Text className="text-primary text-sm font-medium">
|
||||||
|
拍照/相册
|
||||||
|
</Text>
|
||||||
|
<Text className="mt-1 text-xs text-gray-400">
|
||||||
|
点击上传照片
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 操作按钮 */}
|
||||||
|
<View className={"flex flex-row gap-2 p-2.5"}>
|
||||||
|
<View className={"flex-1"}>
|
||||||
|
<Button
|
||||||
|
size={"large"}
|
||||||
|
block
|
||||||
|
type="default"
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(false);
|
||||||
|
setInvoiceImg([]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
{/* 提交按钮 */}
|
||||||
|
<View className={"flex-1"}>
|
||||||
|
<Button
|
||||||
|
size={"large"}
|
||||||
|
block
|
||||||
|
type="primary"
|
||||||
|
disabled={invoiceImg?.length === 0}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
确认上传
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<SafeArea position={"bottom"} />
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
packages/app-client/src/components/invoce/index.ts
Normal file
1
packages/app-client/src/components/invoce/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as InvoiceBatchUpload } from "./InvoiceBatchUpload";
|
||||||
@ -1,2 +1,2 @@
|
|||||||
// App 相关常量
|
// App 相关常量
|
||||||
export const APP_VERSION = "v0.0.52";
|
export const APP_VERSION = "v0.0.53";
|
||||||
|
|||||||
@ -11,10 +11,11 @@ import { useRef, useState } from "react";
|
|||||||
import { business } from "@/services";
|
import { business } from "@/services";
|
||||||
import hocAuth from "@/hocs/auth";
|
import hocAuth from "@/hocs/auth";
|
||||||
import { CommonComponent } from "@/types/typings";
|
import { CommonComponent } from "@/types/typings";
|
||||||
import { View } from "@tarojs/components";
|
import { Label, Text, View } from "@tarojs/components";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import order from "@/constant/order";
|
import order from "@/constant/order";
|
||||||
import { Button, Toast } from "@nutui/nutui-react-taro";
|
import { Button, Toast } from "@nutui/nutui-react-taro";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
export default hocAuth(function Page(props: CommonComponent) {
|
export default hocAuth(function Page(props: CommonComponent) {
|
||||||
const { shareOptions } = props;
|
const { shareOptions } = props;
|
||||||
@ -94,21 +95,45 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
type={"infinite"}
|
type={"infinite"}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
render={(orderSupplierVO: BusinessAPI.OrderSupplierVO, index) => (
|
render={(orderSupplierVO: BusinessAPI.OrderSupplierVO, index) => (
|
||||||
<View className={"mb-2.5"} key={index}>
|
<View className={"mb-2"} key={index}>
|
||||||
<View
|
<View
|
||||||
className={
|
className={
|
||||||
"relative flex flex-col divide-y-2 divide-neutral-100 rounded-lg bg-white px-2.5"
|
"relative flex flex-col divide-y divide-neutral-100 overflow-hidden rounded-xl bg-white shadow-sm"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View className={"flex flex-col divide-y-2 divide-neutral-100"}>
|
{/* 头部区域:名称 + 状态 */}
|
||||||
<View className={"py-2.5"}>
|
<View
|
||||||
<View className="flex items-start justify-between">
|
className={
|
||||||
<View className="text-base font-medium text-gray-800">
|
"flex cursor-pointer items-center justify-between gap-2 px-4 py-3"
|
||||||
{orderSupplierVO.name} (
|
}
|
||||||
{orderSupplierVO.orderVO?.orderVehicle?.origin}-
|
onClick={() => {
|
||||||
{orderSupplierVO.orderVO?.orderVehicle?.destination})
|
const imageUrls =
|
||||||
|
orderSupplierVO.invoiceImg?.map((file) => file.filePath!) ||
|
||||||
|
[];
|
||||||
|
// 使用 Taro.previewImage 预览图片
|
||||||
|
if (imageUrls?.length > 0) {
|
||||||
|
Taro.previewImage({
|
||||||
|
urls: imageUrls,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.show("toast", {
|
||||||
|
title: "暂无发票图片",
|
||||||
|
icon: "none",
|
||||||
|
content: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View className={"flex min-w-0 flex-1 flex-col gap-1.5"}>
|
||||||
|
<Text className={"text-neutral-darkest text-lg font-bold"}>
|
||||||
|
{orderSupplierVO.name}
|
||||||
|
</Text>
|
||||||
|
<Text className={"text-neutral-dark text-xs"}>
|
||||||
|
{orderSupplierVO.orderVO?.orderVehicle?.origin} →{" "}
|
||||||
|
{orderSupplierVO.orderVO?.orderVehicle?.destination}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={"flex-shrink-0"}>
|
<View className={"flex flex-col items-end gap-1"}>
|
||||||
{orderSupplierVO.poState && (
|
{orderSupplierVO.poState && (
|
||||||
<State
|
<State
|
||||||
position="relative"
|
position="relative"
|
||||||
@ -118,36 +143,86 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className="my-1 flex flex-row flex-wrap gap-1 text-sm text-gray-600">
|
|
||||||
<View className="text-primary">
|
{/* 详细信息区域 */}
|
||||||
|
<View
|
||||||
|
className={"flex cursor-pointer flex-col gap-2 px-4 py-3"}
|
||||||
|
onClick={() => {
|
||||||
|
const imageUrls =
|
||||||
|
orderSupplierVO.invoiceImg?.map((file) => file.filePath!) ||
|
||||||
|
[];
|
||||||
|
// 使用 Taro.previewImage 预览图片
|
||||||
|
if (imageUrls?.length > 0) {
|
||||||
|
Taro.previewImage({
|
||||||
|
urls: imageUrls,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.show("toast", {
|
||||||
|
title: "暂无发票图片",
|
||||||
|
icon: "none",
|
||||||
|
content: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
|
<Label className={"text-neutral-dark text-xs"}>
|
||||||
|
车次信息
|
||||||
|
</Label>
|
||||||
|
<Text
|
||||||
|
className={classNames(
|
||||||
|
"text-xs font-medium",
|
||||||
|
"text-primary",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{orderSupplierVO.orderVO?.orderVehicle?.vehicleNo
|
{orderSupplierVO.orderVO?.orderVehicle?.vehicleNo
|
||||||
? "第" +
|
? `第${orderSupplierVO.orderVO?.orderVehicle?.vehicleNo}车`
|
||||||
orderSupplierVO.orderVO?.orderVehicle?.vehicleNo +
|
|
||||||
"车"
|
|
||||||
: "暂未生成车次"}
|
: "暂未生成车次"}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
|
<Label className={"text-neutral-dark text-xs"}>
|
||||||
|
发货时间
|
||||||
|
</Label>
|
||||||
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
{dayjs(
|
{dayjs(
|
||||||
orderSupplierVO.orderVO?.orderVehicle?.deliveryTime,
|
orderSupplierVO.orderVO?.orderVehicle?.deliveryTime,
|
||||||
).format("MM-DD")}
|
).format("YYYY-MM-DD")}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View>|</View>
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
<View>{orderSupplierVO.netWeight}斤</View>
|
<Label className={"text-neutral-dark text-xs"}>重量</Label>
|
||||||
<View>|</View>
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
<View>¥{orderSupplierVO.invoiceAmount}</View>
|
{orderSupplierVO.netWeight} 斤
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-neutral-darker text-xs">
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
{`品种:${orderSupplierVO.productName}`}
|
<Label className={"text-neutral-dark text-xs"}>
|
||||||
|
开票金额
|
||||||
|
</Label>
|
||||||
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
|
¥{orderSupplierVO.invoiceAmount}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
|
<Label className={"text-neutral-dark text-xs"}>品种</Label>
|
||||||
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
|
{orderSupplierVO.productName}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className={"py-2.5"}>
|
{/* 操作按钮区域 */}
|
||||||
<View className={"flex flex-row justify-end gap-2"}>
|
<View
|
||||||
|
className={
|
||||||
|
"flex justify-end gap-2 border-t border-neutral-100 bg-gray-50 px-4 py-3"
|
||||||
|
}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
size={"small"}
|
size={"small"}
|
||||||
type={"primary"}
|
type={"primary"}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
const imageUrls =
|
const imageUrls =
|
||||||
orderSupplierVO.invoiceImg?.map(
|
orderSupplierVO.invoiceImg?.map(
|
||||||
(file) => file.filePath!,
|
(file) => file.filePath!,
|
||||||
@ -159,7 +234,7 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Toast.show("toast", {
|
Toast.show("toast", {
|
||||||
title: "暂无数据",
|
title: "暂无发票图片",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
content: "",
|
content: "",
|
||||||
});
|
});
|
||||||
@ -171,7 +246,6 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
|
|||||||
@ -1,37 +1,31 @@
|
|||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
Icon,
|
Icon,
|
||||||
|
InvoiceBatchUpload,
|
||||||
PageList,
|
PageList,
|
||||||
State,
|
State,
|
||||||
SupplierPicker,
|
SupplierPicker,
|
||||||
ToolBar,
|
ToolBar,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import Taro, { useShareAppMessage } from "@tarojs/taro";
|
import { useShareAppMessage } from "@tarojs/taro";
|
||||||
import { useRef, useState } from "react";
|
|
||||||
import { business } from "@/services";
|
import { business } from "@/services";
|
||||||
import hocAuth from "@/hocs/auth";
|
import hocAuth from "@/hocs/auth";
|
||||||
import { CommonComponent } from "@/types/typings";
|
import { CommonComponent } from "@/types/typings";
|
||||||
import { Image, View } from "@tarojs/components";
|
import { Label, Text, View } from "@tarojs/components";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Button, Popup, SafeArea, Toast } from "@nutui/nutui-react-taro";
|
import { Button } from "@nutui/nutui-react-taro";
|
||||||
import { uploadFile } from "@/utils";
|
|
||||||
import { globalStore } from "@/store/global-store";
|
|
||||||
import { DecimalUtils } from "@/utils/classes/calculators/core/DecimalUtils";
|
|
||||||
import order from "@/constant/order";
|
import order from "@/constant/order";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
export default hocAuth(function Page(props: CommonComponent) {
|
export default hocAuth(function Page(props: CommonComponent) {
|
||||||
const { shareOptions } = props;
|
const { shareOptions } = props;
|
||||||
|
|
||||||
const [supplierVO, setSupplierVO] = useState<BusinessAPI.SupplierVO>();
|
const [supplierVO, setSupplierVO] = useState<BusinessAPI.SupplierVO>();
|
||||||
const [popupVisible, setPopupVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [selectedOrders, setSelectedOrders] = useState<
|
const [selectedOrders, setSelectedOrders] = useState<
|
||||||
BusinessAPI.OrderSupplierVO[]
|
BusinessAPI.OrderSupplierVO[]
|
||||||
>([]);
|
>([]);
|
||||||
const { setLoading } = globalStore((state: any) => state);
|
|
||||||
|
|
||||||
const [invoiceImg, setInvoiceImg] = useState<
|
|
||||||
BusinessAPI.OrderSupplier["invoiceImg"]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const toolbar: ToolBar = {
|
const toolbar: ToolBar = {
|
||||||
@ -53,220 +47,14 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
},
|
},
|
||||||
render: () => (
|
render: () => (
|
||||||
<>
|
<>
|
||||||
{/* Popup 弹窗 */}
|
<InvoiceBatchUpload
|
||||||
<Popup
|
visible={visible}
|
||||||
duration={150}
|
setVisible={setVisible}
|
||||||
style={{
|
selectedOrders={selectedOrders}
|
||||||
minHeight: "auto",
|
onFinish={() => {
|
||||||
|
actionRef.current?.reload();
|
||||||
}}
|
}}
|
||||||
closeable
|
|
||||||
destroyOnClose
|
|
||||||
visible={popupVisible}
|
|
||||||
title={"上传发票"}
|
|
||||||
position="bottom"
|
|
||||||
onClose={() => setPopupVisible(false)}
|
|
||||||
onOverlayClick={() => setPopupVisible(false)}
|
|
||||||
lockScroll
|
|
||||||
round
|
|
||||||
>
|
|
||||||
<View className="flex flex-col gap-2.5">
|
|
||||||
{/* 统计信息 */}
|
|
||||||
<View className="mb-2 flex items-center justify-between p-2.5">
|
|
||||||
<View className="text-sm font-medium text-gray-800">
|
|
||||||
已选{" "}
|
|
||||||
<View className="text-primary inline">
|
|
||||||
{selectedOrders.length}
|
|
||||||
</View>{" "}
|
|
||||||
车次
|
|
||||||
</View>
|
|
||||||
<View className="text-sm font-medium text-gray-800">
|
|
||||||
总重量:
|
|
||||||
<View className="text-primary inline">{totalWeight}</View> 斤
|
|
||||||
</View>
|
|
||||||
<View className="text-sm font-medium text-gray-800">
|
|
||||||
总金额:{" "}
|
|
||||||
<View className="text-primary inline">
|
|
||||||
{totalAmount.toFixed(2)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 上传发票 */}
|
|
||||||
<View className="rounded-lg bg-white p-2.5">
|
|
||||||
<View className="flex flex-col gap-2.5">
|
|
||||||
{invoiceImg && invoiceImg?.length > 0 ? (
|
|
||||||
invoiceImg?.map((uploadFileItem, index) => (
|
|
||||||
<View
|
|
||||||
key={uploadFileItem.fileName || index}
|
|
||||||
className="bg-primary/10 flex flex-col gap-2.5 rounded-lg p-2.5"
|
|
||||||
>
|
|
||||||
<View className="flex items-center">
|
|
||||||
<View className="relative mr-3 h-16 w-16 overflow-hidden rounded-lg">
|
|
||||||
<Image
|
|
||||||
className="h-full w-full object-cover"
|
|
||||||
src={uploadFileItem.filePath!}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
|
||||||
<View className="flex-1">
|
|
||||||
<View className="font-medium" id="invoice-filename">
|
|
||||||
{uploadFileItem.fileName}
|
|
||||||
</View>
|
|
||||||
<View className="text-sm text-gray-500">
|
|
||||||
{Number(
|
|
||||||
uploadFileItem.fileSize! / 1024 / 1024,
|
|
||||||
).toFixed(2)}{" "}
|
|
||||||
MB
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View className="flex flex-row gap-2.5">
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
type={"primary"}
|
|
||||||
size={"large"}
|
|
||||||
fill={"outline"}
|
|
||||||
block
|
|
||||||
onClick={() => {
|
|
||||||
setInvoiceImg([]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View>重新上传</View>
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
type={"warning"}
|
|
||||||
size={"large"}
|
|
||||||
fill={"outline"}
|
|
||||||
block
|
|
||||||
onClick={() => {
|
|
||||||
setInvoiceImg([]);
|
|
||||||
Toast.show("toast", {
|
|
||||||
title: "删除成功",
|
|
||||||
icon: "success",
|
|
||||||
content: "发票已删除",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View>删除</View>
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<View className={`flex w-full flex-row items-center gap-2.5`}>
|
|
||||||
<View
|
|
||||||
className={
|
|
||||||
"border-primary bg-primary/5 flex h-24 flex-1 flex-col items-center justify-center rounded-md border-2 border-dashed p-2.5"
|
|
||||||
}
|
|
||||||
onClick={async () => {
|
|
||||||
await Taro.chooseMessageFile({
|
|
||||||
type: "file",
|
|
||||||
count: 1,
|
|
||||||
success: (res) => {
|
|
||||||
setLoading(true);
|
|
||||||
const file = res.tempFiles[0];
|
|
||||||
uploadFile(file.path).then(({ url }) => {
|
|
||||||
setInvoiceImg([
|
|
||||||
{
|
|
||||||
fileName: url.split("/").pop(),
|
|
||||||
filePath: url,
|
|
||||||
fileSize: file.size,
|
|
||||||
fileType: url.split(".").pop(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
Toast.show("toast", {
|
|
||||||
title: "上传失败",
|
|
||||||
icon: "fail",
|
|
||||||
content: err.errMsg,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name={"camera"} size={24} />
|
|
||||||
<View className="text-primary text-sm">添加文档</View>
|
|
||||||
<View className="mt-1 text-xs text-gray-400">
|
|
||||||
上传PDF文档
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View className={"text-primary text-base"}>或</View>
|
|
||||||
<View
|
|
||||||
className={
|
|
||||||
"border-primary bg-primary/5 flex h-24 flex-1 flex-col items-center justify-center rounded-md border-2 border-dashed p-2.5"
|
|
||||||
}
|
|
||||||
onClick={async () => {
|
|
||||||
await Taro.chooseImage({
|
|
||||||
count: 1,
|
|
||||||
success: (res) => {
|
|
||||||
setLoading(true);
|
|
||||||
const file = res.tempFiles[0];
|
|
||||||
uploadFile(file.path).then(({ url }) => {
|
|
||||||
setInvoiceImg([
|
|
||||||
{
|
|
||||||
fileName: url.split("/").pop(),
|
|
||||||
filePath: url,
|
|
||||||
fileSize: file.size,
|
|
||||||
fileType: url.split(".").pop(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
Toast.show("toast", {
|
|
||||||
title: "上传失败",
|
|
||||||
icon: "fail",
|
|
||||||
content: err.errMsg,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name={"camera"} size={24} />
|
|
||||||
<View className="text-primary text-sm">添加照片</View>
|
|
||||||
<View className="mt-1 text-xs text-gray-400">
|
|
||||||
上传单张照片
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className={"flex flex-1 flex-row gap-2.5 p-2.5"}>
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
size={"xlarge"}
|
|
||||||
block
|
|
||||||
type="default"
|
|
||||||
onClick={() => setPopupVisible(false)}
|
|
||||||
>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
{/* 提交按钮 */}
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
size={"xlarge"}
|
|
||||||
block
|
|
||||||
type="primary"
|
|
||||||
disabled={invoiceImg?.length === 0}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
>
|
|
||||||
提交申请
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<SafeArea position={"bottom"} />
|
|
||||||
</View>
|
|
||||||
</Popup>
|
|
||||||
|
|
||||||
<View className={"flex flex-row gap-2.5"}>
|
<View className={"flex flex-row gap-2.5"}>
|
||||||
<SupplierPicker
|
<SupplierPicker
|
||||||
@ -313,50 +101,6 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算选中车次的总重量和总金额
|
|
||||||
const calculateTotals = () => {
|
|
||||||
return selectedOrders.reduce(
|
|
||||||
(totals, order) => {
|
|
||||||
totals.totalWeight = DecimalUtils.add(
|
|
||||||
totals.totalWeight,
|
|
||||||
order.netWeight || 0,
|
|
||||||
);
|
|
||||||
totals.totalAmount = DecimalUtils.add(
|
|
||||||
totals.totalAmount,
|
|
||||||
order.invoiceAmount || 0,
|
|
||||||
);
|
|
||||||
return totals;
|
|
||||||
},
|
|
||||||
{ totalWeight: 0, totalAmount: 0 },
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { totalWeight, totalAmount } = calculateTotals();
|
|
||||||
|
|
||||||
// 提交申请
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
// 这里添加提交申请的逻辑
|
|
||||||
const {
|
|
||||||
data: { success },
|
|
||||||
} = await business.orderSupplier.batchUploadInvoice({
|
|
||||||
orderSupplierIdList: selectedOrders.map((order) => order.orderSupplierId),
|
|
||||||
invoiceImg: invoiceImg,
|
|
||||||
invoiceUpload: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
Toast.show("toast", {
|
|
||||||
icon: "success",
|
|
||||||
title: "提交成功",
|
|
||||||
content: "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
actionRef.current?.reload();
|
|
||||||
setPopupVisible(false);
|
|
||||||
setInvoiceImg([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageList<BusinessAPI.OrderSupplierVO, BusinessAPI.OrderSupplierPageQry>
|
<PageList<BusinessAPI.OrderSupplierVO, BusinessAPI.OrderSupplierPageQry>
|
||||||
@ -365,21 +109,28 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
type={"infinite"}
|
type={"infinite"}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
render={(orderSupplierVO: BusinessAPI.OrderSupplierVO, index) => (
|
render={(orderSupplierVO: BusinessAPI.OrderSupplierVO, index) => (
|
||||||
<View className={"mb-2.5"} key={index}>
|
<View className={"mb-2"} key={index}>
|
||||||
<View
|
<View
|
||||||
className={
|
className={
|
||||||
"relative flex flex-col divide-y-2 divide-neutral-100 rounded-lg bg-white px-2.5"
|
"relative flex flex-col divide-y divide-neutral-100 overflow-hidden rounded-xl bg-white shadow-sm"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View className={"flex flex-col divide-y-2 divide-neutral-100"}>
|
{/* 头部区域:名称 + 状态 */}
|
||||||
<View className={"py-2.5"}>
|
<View
|
||||||
<View className="flex items-start justify-between">
|
className={
|
||||||
<View className="text-base font-medium text-gray-800">
|
"flex cursor-pointer items-center justify-between gap-2 px-4 py-3"
|
||||||
{orderSupplierVO.name} (
|
}
|
||||||
{orderSupplierVO.orderVO?.orderVehicle?.origin}-
|
>
|
||||||
{orderSupplierVO.orderVO?.orderVehicle?.destination})
|
<View className={"flex min-w-0 flex-1 flex-col gap-1.5"}>
|
||||||
|
<Text className={"text-neutral-darkest text-lg font-bold"}>
|
||||||
|
{orderSupplierVO.name}
|
||||||
|
</Text>
|
||||||
|
<Text className={"text-neutral-dark text-xs"}>
|
||||||
|
{orderSupplierVO.orderVO?.orderVehicle?.origin} →{" "}
|
||||||
|
{orderSupplierVO.orderVO?.orderVehicle?.destination}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={"flex-shrink-0"}>
|
<View className={"flex flex-col items-end gap-1"}>
|
||||||
{orderSupplierVO.poState && (
|
{orderSupplierVO.poState && (
|
||||||
<State
|
<State
|
||||||
position="relative"
|
position="relative"
|
||||||
@ -389,31 +140,78 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className="my-1 flex flex-row flex-wrap gap-1 text-sm text-gray-600">
|
|
||||||
<View className="text-primary">
|
{/* 详细信息区域 */}
|
||||||
|
<View className={"flex flex-col gap-2 px-4 py-3"}>
|
||||||
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
|
<Label className={"text-neutral-dark text-xs"}>
|
||||||
|
车次信息
|
||||||
|
</Label>
|
||||||
|
<Text
|
||||||
|
className={classNames(
|
||||||
|
"text-xs font-medium",
|
||||||
|
"text-primary",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{orderSupplierVO.orderVO?.orderVehicle?.vehicleNo
|
{orderSupplierVO.orderVO?.orderVehicle?.vehicleNo
|
||||||
? "第" +
|
? `第${orderSupplierVO.orderVO?.orderVehicle?.vehicleNo}车`
|
||||||
orderSupplierVO.orderVO?.orderVehicle?.vehicleNo +
|
|
||||||
"车"
|
|
||||||
: "暂未生成车次"}
|
: "暂未生成车次"}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
|
<Label className={"text-neutral-dark text-xs"}>
|
||||||
|
发货时间
|
||||||
|
</Label>
|
||||||
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
{dayjs(
|
{dayjs(
|
||||||
orderSupplierVO.orderVO?.orderVehicle?.deliveryTime,
|
orderSupplierVO.orderVO?.orderVehicle?.deliveryTime,
|
||||||
).format("MM-DD")}
|
).format("YYYY-MM-DD")}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View>|</View>
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
<View>{orderSupplierVO.netWeight}斤</View>
|
<Label className={"text-neutral-dark text-xs"}>重量</Label>
|
||||||
<View>|</View>
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
<View>¥{orderSupplierVO.invoiceAmount}</View>
|
{orderSupplierVO.netWeight} 斤
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-neutral-darker text-xs">
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
{`品种:${orderSupplierVO.productName}`}
|
<Label className={"text-neutral-dark text-xs"}>
|
||||||
|
开票金额
|
||||||
|
</Label>
|
||||||
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
|
¥{orderSupplierVO.invoiceAmount}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
|
<Label className={"text-neutral-dark text-xs"}>品种</Label>
|
||||||
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
|
{orderSupplierVO.productName}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
{/* 发票状态提示 */}
|
||||||
|
{orderSupplierVO.invoiceUpload ? (
|
||||||
|
<View className="mt-1 flex items-center gap-1 rounded-full bg-green-50 px-2 py-1">
|
||||||
|
<Icon name="check-circle" size={12} color={"#2E7D32"} />
|
||||||
|
<Text className="text-xs font-medium text-green-700">
|
||||||
|
已上传发票
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={"py-2.5"}>
|
) : (
|
||||||
<View className={"flex flex-row justify-end gap-2"}>
|
<View className="mt-1 flex items-center gap-1 rounded-full bg-orange-50 px-2 py-1">
|
||||||
|
<Icon name="clock" size={12} color={"#D97A05"} />
|
||||||
|
<Text className="text-xs font-medium text-orange-700">
|
||||||
|
待上传发票
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 操作按钮区域 */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
"flex justify-end gap-2 border-t border-neutral-100 bg-gray-50 px-4 py-3"
|
||||||
|
}
|
||||||
|
>
|
||||||
{!orderSupplierVO.invoiceUpload &&
|
{!orderSupplierVO.invoiceUpload &&
|
||||||
(orderSupplierVO.poState === "WAITING_AUDIT" ||
|
(orderSupplierVO.poState === "WAITING_AUDIT" ||
|
||||||
orderSupplierVO.poState === "COMPLETED") && (
|
orderSupplierVO.poState === "COMPLETED") && (
|
||||||
@ -422,7 +220,7 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
type={"primary"}
|
type={"primary"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedOrders([orderSupplierVO]);
|
setSelectedOrders([orderSupplierVO]);
|
||||||
setPopupVisible(true);
|
setVisible(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
上传发票
|
上传发票
|
||||||
@ -431,7 +229,6 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
|
|||||||
@ -1,48 +1,87 @@
|
|||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
Icon,
|
Icon,
|
||||||
|
InvoiceBatchUpload,
|
||||||
PageList,
|
PageList,
|
||||||
State,
|
State,
|
||||||
SupplierPicker,
|
SupplierPicker,
|
||||||
ToolBar,
|
ToolBar,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import Taro, { useShareAppMessage } from "@tarojs/taro";
|
import { useShareAppMessage } from "@tarojs/taro";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { business } from "@/services";
|
import { business } from "@/services";
|
||||||
import hocAuth from "@/hocs/auth";
|
import hocAuth from "@/hocs/auth";
|
||||||
import { CommonComponent } from "@/types/typings";
|
import { CommonComponent } from "@/types/typings";
|
||||||
import { Image, View } from "@tarojs/components";
|
import { Text, View } from "@tarojs/components";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Button, Popup, SafeArea, Toast } from "@nutui/nutui-react-taro";
|
|
||||||
import { uploadFile } from "@/utils";
|
|
||||||
import { globalStore } from "@/store/global-store";
|
|
||||||
import { DecimalUtils } from "@/utils/classes/calculators/core/DecimalUtils";
|
|
||||||
import order from "@/constant/order";
|
import order from "@/constant/order";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { Button, Checkbox, SafeArea } from "@nutui/nutui-react-taro";
|
||||||
|
import { DecimalUtils } from "@/utils/classes/calculators/core/DecimalUtils";
|
||||||
|
|
||||||
export default hocAuth(function Page(props: CommonComponent) {
|
export default hocAuth(function Page(props: CommonComponent) {
|
||||||
const { shareOptions } = props;
|
const { shareOptions } = props;
|
||||||
|
|
||||||
const [supplierVO, setSupplierVO] = useState<BusinessAPI.SupplierVO>();
|
const [supplierVO, setSupplierVO] = useState<BusinessAPI.SupplierVO>();
|
||||||
const [popupVisible, setPopupVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [selectedOrders, setSelectedOrders] = useState<
|
const [selectedOrders, setSelectedOrders] = useState<
|
||||||
BusinessAPI.OrderSupplierVO[]
|
BusinessAPI.OrderSupplierVO[]
|
||||||
>([]);
|
>([]);
|
||||||
const { setLoading } = globalStore((state: any) => state);
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
const [invoiceImg, setInvoiceImg] = useState<
|
|
||||||
BusinessAPI.OrderSupplier["invoiceImg"]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const toolbar: ToolBar = {
|
|
||||||
selectRow: {
|
// 切换选中状态
|
||||||
onClick: async (orderSupplierVOList: BusinessAPI.OrderSupplierVO[]) => {
|
const toggleSelect = (orderSupplierVO: BusinessAPI.OrderSupplierVO) => {
|
||||||
console.log("orderSupplierVOList", orderSupplierVOList);
|
const newSelectedIds = new Set(selectedIds);
|
||||||
// 点击弹出popup
|
if (newSelectedIds.has(orderSupplierVO.orderSupplierId)) {
|
||||||
setSelectedOrders(orderSupplierVOList);
|
newSelectedIds.delete(orderSupplierVO.orderSupplierId);
|
||||||
setPopupVisible(true);
|
setSelectedOrders(
|
||||||
},
|
selectedOrders.filter(
|
||||||
|
(orderSupplierVO) =>
|
||||||
|
orderSupplierVO.orderSupplierId !== orderSupplierVO.orderSupplierId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
newSelectedIds.add(orderSupplierVO.orderSupplierId);
|
||||||
|
setSelectedOrders([...selectedOrders, orderSupplierVO]);
|
||||||
|
}
|
||||||
|
setSelectedIds(newSelectedIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 全选/取消全选
|
||||||
|
const toggleSelectAll = (allIds: string[]) => {
|
||||||
|
if (selectedIds.size === allIds.length) {
|
||||||
|
// 取消全选
|
||||||
|
setSelectedIds(new Set());
|
||||||
|
} else {
|
||||||
|
// 全选
|
||||||
|
setSelectedIds(new Set(allIds));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算选中订单的总重量和总金额
|
||||||
|
const calculateSelectedTotals = (data: BusinessAPI.OrderSupplierVO[]) => {
|
||||||
|
const selected = data.filter((item) =>
|
||||||
|
selectedIds.has(item.orderSupplierId),
|
||||||
|
);
|
||||||
|
return selected.reduce(
|
||||||
|
(totals, order) => {
|
||||||
|
totals.totalWeight = DecimalUtils.add(
|
||||||
|
totals.totalWeight,
|
||||||
|
order.netWeight || 0,
|
||||||
|
);
|
||||||
|
totals.totalAmount = DecimalUtils.add(
|
||||||
|
totals.totalAmount,
|
||||||
|
order.invoiceAmount || 0,
|
||||||
|
);
|
||||||
|
return totals;
|
||||||
},
|
},
|
||||||
|
{ totalWeight: 0, totalAmount: 0 },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toolbar: ToolBar<BusinessAPI.OrderSupplierVO> = {
|
||||||
search: {
|
search: {
|
||||||
activeKey: "vehicleNo",
|
activeKey: "vehicleNo",
|
||||||
defaultActiveKey: "vehicleNo",
|
defaultActiveKey: "vehicleNo",
|
||||||
@ -59,222 +98,19 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
render: () => (
|
render: () => {
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Popup 弹窗 */}
|
{/* Popup 弹窗 */}
|
||||||
<Popup
|
<InvoiceBatchUpload
|
||||||
duration={150}
|
selectedOrders={selectedOrders}
|
||||||
style={{
|
visible={visible}
|
||||||
minHeight: "auto",
|
setVisible={setVisible}
|
||||||
|
onFinish={() => {
|
||||||
|
actionRef.current?.reload();
|
||||||
|
setSelectedIds(new Set());
|
||||||
}}
|
}}
|
||||||
closeable
|
|
||||||
destroyOnClose
|
|
||||||
visible={popupVisible}
|
|
||||||
title={"上传发票"}
|
|
||||||
position="bottom"
|
|
||||||
onClose={() => setPopupVisible(false)}
|
|
||||||
onOverlayClick={() => setPopupVisible(false)}
|
|
||||||
lockScroll
|
|
||||||
round
|
|
||||||
>
|
|
||||||
<View className="flex flex-col gap-2.5">
|
|
||||||
{/* 统计信息 */}
|
|
||||||
<View className="mb-2 flex items-center justify-between p-2.5">
|
|
||||||
<View className="text-sm font-medium text-gray-800">
|
|
||||||
已选{" "}
|
|
||||||
<View className="text-primary inline">
|
|
||||||
{selectedOrders.length}
|
|
||||||
</View>{" "}
|
|
||||||
车次
|
|
||||||
</View>
|
|
||||||
<View className="text-sm font-medium text-gray-800">
|
|
||||||
总重量:
|
|
||||||
<View className="text-primary inline">{totalWeight}</View> 斤
|
|
||||||
</View>
|
|
||||||
<View className="text-sm font-medium text-gray-800">
|
|
||||||
总金额:{" "}
|
|
||||||
<View className="text-primary inline">
|
|
||||||
{totalAmount.toFixed(2)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 上传发票 */}
|
|
||||||
<View className="rounded-lg bg-white p-2.5">
|
|
||||||
<View className="flex flex-col gap-2.5">
|
|
||||||
{invoiceImg && invoiceImg?.length > 0 ? (
|
|
||||||
invoiceImg?.map((uploadFileItem, index) => (
|
|
||||||
<View
|
|
||||||
key={uploadFileItem.fileName || index}
|
|
||||||
className="bg-primary/10 flex flex-col gap-2.5 rounded-lg p-2.5"
|
|
||||||
>
|
|
||||||
<View className="flex items-center">
|
|
||||||
<View className="relative mr-3 h-16 w-16 overflow-hidden rounded-lg">
|
|
||||||
<Image
|
|
||||||
className="h-full w-full object-cover"
|
|
||||||
src={uploadFileItem.filePath!}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
|
||||||
<View className="flex-1">
|
|
||||||
<View className="font-medium" id="invoice-filename">
|
|
||||||
{uploadFileItem.fileName}
|
|
||||||
</View>
|
|
||||||
<View className="text-sm text-gray-500">
|
|
||||||
{Number(
|
|
||||||
uploadFileItem.fileSize! / 1024 / 1024,
|
|
||||||
).toFixed(2)}{" "}
|
|
||||||
MB
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View className="flex flex-row gap-2.5">
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
type={"primary"}
|
|
||||||
size={"large"}
|
|
||||||
fill={"outline"}
|
|
||||||
block
|
|
||||||
onClick={() => {
|
|
||||||
setInvoiceImg([]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View>重新上传</View>
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
type={"warning"}
|
|
||||||
size={"large"}
|
|
||||||
fill={"outline"}
|
|
||||||
block
|
|
||||||
onClick={() => {
|
|
||||||
setInvoiceImg([]);
|
|
||||||
Toast.show("toast", {
|
|
||||||
title: "删除成功",
|
|
||||||
icon: "success",
|
|
||||||
content: "发票已删除",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View>删除</View>
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<View className={`flex w-full flex-row items-center gap-2.5`}>
|
|
||||||
<View
|
|
||||||
className={
|
|
||||||
"border-primary bg-primary/5 flex h-24 flex-1 flex-col items-center justify-center rounded-md border-2 border-dashed p-2.5"
|
|
||||||
}
|
|
||||||
onClick={async () => {
|
|
||||||
await Taro.chooseMessageFile({
|
|
||||||
type: "file",
|
|
||||||
count: 1,
|
|
||||||
success: (res) => {
|
|
||||||
setLoading(true);
|
|
||||||
const file = res.tempFiles[0];
|
|
||||||
uploadFile(file.path).then(({ url }) => {
|
|
||||||
setInvoiceImg([
|
|
||||||
{
|
|
||||||
fileName: url.split("/").pop(),
|
|
||||||
filePath: url,
|
|
||||||
fileSize: file.size,
|
|
||||||
fileType: url.split(".").pop(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
Toast.show("toast", {
|
|
||||||
title: "上传失败",
|
|
||||||
icon: "fail",
|
|
||||||
content: err.errMsg,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name={"camera"} size={24} />
|
|
||||||
<View className="text-primary text-sm">添加文档</View>
|
|
||||||
<View className="mt-1 text-xs text-gray-400">
|
|
||||||
上传PDF文档
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View className={"text-primary text-base"}>或</View>
|
|
||||||
<View
|
|
||||||
className={
|
|
||||||
"border-primary bg-primary/5 flex h-24 flex-1 flex-col items-center justify-center rounded-md border-2 border-dashed p-2.5"
|
|
||||||
}
|
|
||||||
onClick={async () => {
|
|
||||||
await Taro.chooseImage({
|
|
||||||
count: 1,
|
|
||||||
success: (res) => {
|
|
||||||
setLoading(true);
|
|
||||||
const file = res.tempFiles[0];
|
|
||||||
uploadFile(file.path).then(({ url }) => {
|
|
||||||
setInvoiceImg([
|
|
||||||
{
|
|
||||||
fileName: url.split("/").pop(),
|
|
||||||
filePath: url,
|
|
||||||
fileSize: file.size,
|
|
||||||
fileType: url.split(".").pop(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
Toast.show("toast", {
|
|
||||||
title: "上传失败",
|
|
||||||
icon: "fail",
|
|
||||||
content: err.errMsg,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name={"camera"} size={24} />
|
|
||||||
<View className="text-primary text-sm">添加照片</View>
|
|
||||||
<View className="mt-1 text-xs text-gray-400">
|
|
||||||
上传单张照片
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className={"flex flex-1 flex-row gap-2.5 p-2.5"}>
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
size={"xlarge"}
|
|
||||||
block
|
|
||||||
type="default"
|
|
||||||
onClick={() => setPopupVisible(false)}
|
|
||||||
>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
{/* 提交按钮 */}
|
|
||||||
<View className={"flex-1"}>
|
|
||||||
<Button
|
|
||||||
size={"xlarge"}
|
|
||||||
block
|
|
||||||
type="primary"
|
|
||||||
disabled={invoiceImg?.length === 0}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
>
|
|
||||||
提交申请
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<SafeArea position={"bottom"} />
|
|
||||||
</View>
|
|
||||||
</Popup>
|
|
||||||
|
|
||||||
<View className={"flex flex-row gap-2.5"}>
|
<View className={"flex flex-row gap-2.5"}>
|
||||||
<SupplierPicker
|
<SupplierPicker
|
||||||
@ -308,7 +144,79 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
),
|
);
|
||||||
|
},
|
||||||
|
footer: (data: BusinessAPI.OrderSupplierVO[]) => {
|
||||||
|
const allIds = data.map((item) => item.orderSupplierId);
|
||||||
|
const isSelectedAll =
|
||||||
|
selectedIds.size === allIds.length && allIds.length > 0;
|
||||||
|
const totals = calculateSelectedTotals(data);
|
||||||
|
return (
|
||||||
|
<View className="sticky bottom-0 z-10 bg-white">
|
||||||
|
{/* 批量操作栏 */}
|
||||||
|
{selectedIds.size > 0 && (
|
||||||
|
<View className="flex items-center justify-between p-2.5">
|
||||||
|
<View className="flex flex-1 flex-row items-center gap-2">
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelectedAll}
|
||||||
|
onChange={() => toggleSelectAll(allIds)}
|
||||||
|
/>
|
||||||
|
<View className="flex flex-col">
|
||||||
|
<Text className="text-sm font-medium text-gray-700">
|
||||||
|
已选 {selectedIds.size} 项
|
||||||
|
</Text>
|
||||||
|
<Text className="text-xs text-gray-500">
|
||||||
|
总重: {totals.totalWeight}斤 | 金额: ¥
|
||||||
|
{totals.totalAmount.toFixed(2)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="flex flex-row items-center gap-2">
|
||||||
|
<Button
|
||||||
|
type={"default"}
|
||||||
|
size={"large"}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedIds(new Set());
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
取消选择
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type={"primary"}
|
||||||
|
size={"large"}
|
||||||
|
disabled={selectedIds.size === 0}
|
||||||
|
onClick={() => {
|
||||||
|
if (selectedIds.size > 0) {
|
||||||
|
const selected = data.filter((item) =>
|
||||||
|
selectedIds.has(item.orderSupplierId),
|
||||||
|
);
|
||||||
|
setSelectedOrders(selected);
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
批量上传
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 全选按钮 */}
|
||||||
|
{selectedIds.size === 0 && data.length > 0 && (
|
||||||
|
<View className="p-2.5">
|
||||||
|
<View
|
||||||
|
className="flex cursor-pointer items-center gap-2 rounded-lg bg-gray-50 px-3 py-2"
|
||||||
|
onClick={() => toggleSelectAll(allIds)}
|
||||||
|
>
|
||||||
|
<Checkbox checked={false} />
|
||||||
|
<Text className="text-sm text-gray-600">全选当前页</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<SafeArea position={"bottom"} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
useShareAppMessage((res) => {
|
useShareAppMessage((res) => {
|
||||||
@ -321,70 +229,65 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算选中车次的总重量和总金额
|
|
||||||
const calculateTotals = () => {
|
|
||||||
return selectedOrders.reduce(
|
|
||||||
(totals, order) => {
|
|
||||||
totals.totalWeight = DecimalUtils.add(
|
|
||||||
totals.totalWeight,
|
|
||||||
order.netWeight || 0,
|
|
||||||
);
|
|
||||||
totals.totalAmount = DecimalUtils.add(
|
|
||||||
totals.totalAmount,
|
|
||||||
order.invoiceAmount || 0,
|
|
||||||
);
|
|
||||||
return totals;
|
|
||||||
},
|
|
||||||
{ totalWeight: 0, totalAmount: 0 },
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { totalWeight, totalAmount } = calculateTotals();
|
|
||||||
|
|
||||||
// 提交申请
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
// 这里添加提交申请的逻辑
|
|
||||||
const {
|
|
||||||
data: { success },
|
|
||||||
} = await business.orderSupplier.batchUploadInvoice({
|
|
||||||
orderSupplierIdList: selectedOrders.map((order) => order.orderSupplierId),
|
|
||||||
invoiceImg: invoiceImg,
|
|
||||||
invoiceUpload: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
Toast.show("toast", {
|
|
||||||
icon: "success",
|
|
||||||
title: "提交成功",
|
|
||||||
content: "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
actionRef.current?.reload();
|
|
||||||
setPopupVisible(false);
|
|
||||||
setInvoiceImg([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageList<BusinessAPI.OrderSupplierVO, BusinessAPI.OrderSupplierPageQry>
|
<PageList<BusinessAPI.OrderSupplierVO, BusinessAPI.OrderSupplierPageQry>
|
||||||
rowId={"orderSupplierId"}
|
rowId={"orderSupplierId"}
|
||||||
itemHeight={182}
|
itemHeight={100}
|
||||||
type={"infinite"}
|
type={"infinite"}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
render={(orderSupplierVO: BusinessAPI.OrderSupplierVO, index) => (
|
render={(orderSupplierVO: BusinessAPI.OrderSupplierVO, index) => {
|
||||||
<View className={"flex-1"} key={index}>
|
const isSelected = selectedIds.has(orderSupplierVO.orderSupplierId);
|
||||||
|
return (
|
||||||
<View
|
<View
|
||||||
className={"relative flex flex-col divide-y-2 divide-neutral-100"}
|
className={classNames("mb-2", isSelected && "p-0.5")}
|
||||||
|
key={index}
|
||||||
>
|
>
|
||||||
<View className="flex-1">
|
<View
|
||||||
<View className="flex items-start justify-between">
|
className={classNames(
|
||||||
<View className="text-base font-medium text-gray-800">
|
"relative flex flex-col divide-y divide-neutral-100 overflow-hidden rounded-lg bg-white shadow-sm transition-all",
|
||||||
{orderSupplierVO.name} (
|
isSelected && "ring-primary ring-2 ring-offset-1",
|
||||||
{orderSupplierVO.orderVO?.orderVehicle.origin}-
|
)}
|
||||||
{orderSupplierVO.orderVO?.orderVehicle.destination})
|
>
|
||||||
|
{/* 选中遮罩层 */}
|
||||||
|
{isSelected && (
|
||||||
|
<View className="bg-primary/5 pointer-events-none absolute inset-0 z-10" />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 复选框 + 头部区域 */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
"flex cursor-pointer items-center justify-between gap-2 px-3 py-2"
|
||||||
|
}
|
||||||
|
onClick={(e) => {
|
||||||
|
// 点击卡片切换选中状态
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleSelect(orderSupplierVO);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View className={"flex flex-1 flex-row items-center gap-2"}>
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelected}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleSelect(orderSupplierVO);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View className={"flex min-w-0 flex-1 flex-col gap-1"}>
|
||||||
|
<Text
|
||||||
|
className={"text-neutral-darkest text-sm font-bold"}
|
||||||
|
>
|
||||||
|
{orderSupplierVO.name}
|
||||||
|
</Text>
|
||||||
|
<View className={"flex flex-row items-center gap-2"}>
|
||||||
|
<Text className={"text-neutral-dark text-xs"}>
|
||||||
|
{orderSupplierVO.orderVO?.orderVehicle?.origin} →{" "}
|
||||||
|
{orderSupplierVO.orderVO?.orderVehicle?.destination}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={"flex-shrink-0"}>
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className={"flex flex-col items-end gap-1"}>
|
||||||
{orderSupplierVO.poState && (
|
{orderSupplierVO.poState && (
|
||||||
<State
|
<State
|
||||||
position="relative"
|
position="relative"
|
||||||
@ -392,33 +295,66 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
stateMap={order.stateMap}
|
stateMap={order.stateMap}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{/* 发票状态提示 */}
|
||||||
|
{orderSupplierVO.invoiceUpload ? (
|
||||||
|
<View className="flex items-center gap-1">
|
||||||
|
<Icon name="check-circle" size={10} color={"#2E7D32"} />
|
||||||
|
<Text className="text-xs text-green-700">已上传</Text>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<View className="flex items-center gap-1">
|
||||||
|
<Icon name="clock" size={10} color={"#D97A05"} />
|
||||||
|
<Text className="text-xs text-orange-700">待上传</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className="my-1 flex flex-row flex-wrap gap-1 text-sm text-gray-600">
|
|
||||||
<View className="text-primary">
|
{/* 详细信息区域 - 紧凑一行显示 */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
"flex flex-row items-center justify-between gap-2 px-3 py-2 text-xs"
|
||||||
|
}
|
||||||
|
onClick={(e) => {
|
||||||
|
// 点击详细信息区域也切换选中状态
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleSelect(orderSupplierVO);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
"flex flex-1 flex-row items-center gap-1 text-gray-600"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className={classNames(
|
||||||
|
"text-xs font-medium",
|
||||||
|
"text-primary",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{orderSupplierVO.orderVO?.orderVehicle?.vehicleNo
|
{orderSupplierVO.orderVO?.orderVehicle?.vehicleNo
|
||||||
? "第" +
|
? `第${orderSupplierVO.orderVO?.orderVehicle?.vehicleNo}车`
|
||||||
orderSupplierVO.orderVO?.orderVehicle?.vehicleNo +
|
|
||||||
"车"
|
|
||||||
: "暂未生成车次"}
|
: "暂未生成车次"}
|
||||||
</View>
|
</Text>
|
||||||
<View>
|
|
|
||||||
|
<Text>
|
||||||
{dayjs(
|
{dayjs(
|
||||||
orderSupplierVO.orderVO?.orderVehicle?.deliveryTime,
|
orderSupplierVO.orderVO?.orderVehicle?.deliveryTime,
|
||||||
).format("MM-DD")}
|
).format("MM-DD")}
|
||||||
|
</Text>
|
||||||
|
<Text>|</Text>
|
||||||
|
<Text>{orderSupplierVO.netWeight}斤</Text>
|
||||||
|
<Text>|</Text>
|
||||||
|
<Text>¥{orderSupplierVO.invoiceAmount}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View>|</View>
|
<Text className={"text-neutral-darkest font-medium"}>
|
||||||
<View>{orderSupplierVO.netWeight}斤</View>
|
{orderSupplierVO.productName}
|
||||||
<View>|</View>
|
</Text>
|
||||||
<View>¥{orderSupplierVO.invoiceAmount}</View>
|
|
||||||
</View>
|
|
||||||
<View className="text-neutral-darker text-xs">
|
|
||||||
{`品种:${orderSupplierVO.productName}`}
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
);
|
||||||
)}
|
}}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -60,31 +60,40 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
type={"infinite"}
|
type={"infinite"}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
render={(supplierVO: BusinessAPI.SupplierVO, index) => (
|
render={(supplierVO: BusinessAPI.SupplierVO, index) => (
|
||||||
<View className={"mb-2.5"} key={index}>
|
<View className={"mb-2"} key={index}>
|
||||||
<View
|
<View
|
||||||
className={
|
className={
|
||||||
"relative flex flex-col divide-y-2 divide-neutral-100 rounded-lg bg-white px-2.5"
|
"relative flex flex-col divide-y divide-neutral-100 overflow-hidden rounded-xl bg-white shadow-sm"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View className={"flex flex-col divide-y-2 divide-neutral-100"}>
|
{/* 头部区域:名称 + 编辑按钮 */}
|
||||||
<View className={"py-2.5"}>
|
<View
|
||||||
<View className={"flex flex-row items-center"}>
|
className={
|
||||||
<View className={"flex flex-1 flex-col gap-2"}>
|
"flex cursor-pointer items-center justify-between gap-2 px-4 py-3"
|
||||||
<View className={"flex flex-row gap-1"}>
|
}
|
||||||
{/* 复制 */}
|
onClick={() => {
|
||||||
<Text
|
Taro.navigateTo({
|
||||||
className={"text-neutral-darkest text-xl font-bold"}
|
url: buildUrl("/pages/supplier/create", {
|
||||||
|
supplierId: supplierVO.supplierId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
<View className={"flex min-w-0 flex-1 flex-col gap-1.5"}>
|
||||||
|
<Text className={"text-neutral-darkest text-lg font-bold"}>
|
||||||
{supplierVO?.name}
|
{supplierVO?.name}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
<Text className={"text-neutral-dark text-xs"}>
|
||||||
|
瓜农 ID: {supplierVO.supplierId}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
size={"small"}
|
size={"small"}
|
||||||
type={"primary"}
|
type={"primary"}
|
||||||
fill={"none"}
|
fill={"none"}
|
||||||
icon={<Icon name={"pen-to-square"} size={18} />}
|
icon={<Icon name={"pen-to-square"} size={16} />}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
Taro.navigateTo({
|
Taro.navigateTo({
|
||||||
url: buildUrl("/pages/supplier/create", {
|
url: buildUrl("/pages/supplier/create", {
|
||||||
supplierId: supplierVO.supplierId,
|
supplierId: supplierVO.supplierId,
|
||||||
@ -93,29 +102,26 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
<View className={"py-2.5"}>
|
{/* 详细信息区域 */}
|
||||||
<View className={"flex flex-col gap-2"}>
|
|
||||||
<View
|
<View
|
||||||
className={
|
className={"flex cursor-pointer flex-col gap-2 px-4 py-3"}
|
||||||
"flex flex-row items-center justify-between gap-2.5"
|
onClick={() => {
|
||||||
}
|
Taro.navigateTo({
|
||||||
|
url: buildUrl("/pages/supplier/create", {
|
||||||
|
supplierId: supplierVO.supplierId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Label className={"text-neutral-dark text-sm"}>
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
登记时间
|
<Label className={"text-neutral-dark text-xs"}>登记时间</Label>
|
||||||
</Label>
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
<Text className={"text-neutral-darkest text-sm"}>
|
{dayjs(supplierVO.createdAt).format("YYYY-MM-DD HH:mm")}
|
||||||
{dayjs(supplierVO.createdAt).format("MM-DD HH:mm")}
|
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
className={
|
<Label className={"text-neutral-dark text-xs"}>联系方式</Label>
|
||||||
"flex flex-row items-center justify-between gap-2.5"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Label className={"text-neutral-dark text-sm"}>
|
|
||||||
联系方式
|
|
||||||
</Label>
|
|
||||||
<View
|
<View
|
||||||
className={
|
className={
|
||||||
"flex flex-1 flex-row items-center justify-end gap-1"
|
"flex flex-1 flex-row items-center justify-end gap-1"
|
||||||
@ -124,33 +130,26 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
{supplierVO.phone && <Phone phone={supplierVO.phone} />}
|
{supplierVO.phone && <Phone phone={supplierVO.phone} />}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View className={"flex flex-row items-center justify-between"}>
|
||||||
className={
|
<Label className={"text-neutral-dark text-xs"}>开票信息</Label>
|
||||||
"flex flex-row items-center justify-between gap-2.5"
|
<Text className={"text-neutral-darkest text-xs font-medium"}>
|
||||||
}
|
|
||||||
>
|
|
||||||
<Label className={"text-neutral-dark text-sm"}>
|
|
||||||
开票信息
|
|
||||||
</Label>
|
|
||||||
<View
|
|
||||||
className={
|
|
||||||
"flex flex-1 flex-row items-center justify-end gap-1"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
已开票 12 张 | 待开票 2 张
|
已开票 12 张 | 待开票 2 张
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
</View>
|
{/* 操作按钮区域 */}
|
||||||
</View>
|
<View
|
||||||
<View className={"py-2.5"}>
|
className={
|
||||||
<View className={"flex flex-row justify-end gap-2"}>
|
"flex justify-end gap-2 border-t border-neutral-100 bg-gray-50 px-4 py-3"
|
||||||
{/* 联系 */}
|
}
|
||||||
|
>
|
||||||
{supplierVO.phone && (
|
{supplierVO.phone && (
|
||||||
<Button
|
<Button
|
||||||
size={"small"}
|
size={"small"}
|
||||||
type={"default"}
|
type={"default"}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
Taro.makePhoneCall({
|
Taro.makePhoneCall({
|
||||||
phoneNumber: supplierVO.phone!,
|
phoneNumber: supplierVO.phone!,
|
||||||
});
|
});
|
||||||
@ -159,11 +158,11 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
联系
|
联系
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{/* 新建采购 */}
|
|
||||||
<Button
|
<Button
|
||||||
size={"small"}
|
size={"small"}
|
||||||
type={"default"}
|
type={"default"}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
Taro.navigateTo({
|
Taro.navigateTo({
|
||||||
url: buildUrl("/pages/purchase/made/create", {
|
url: buildUrl("/pages/purchase/made/create", {
|
||||||
supplierId: supplierVO.supplierId,
|
supplierId: supplierVO.supplierId,
|
||||||
@ -173,11 +172,11 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
>
|
>
|
||||||
新建采购
|
新建采购
|
||||||
</Button>
|
</Button>
|
||||||
{/* 协助开票 */}
|
|
||||||
<Button
|
<Button
|
||||||
size={"small"}
|
size={"small"}
|
||||||
type={"primary"}
|
type={"primary"}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
Taro.navigateTo({
|
Taro.navigateTo({
|
||||||
url: buildUrl("/pages/invoice/upload", {
|
url: buildUrl("/pages/invoice/upload", {
|
||||||
supplierId: supplierVO.supplierId,
|
supplierId: supplierVO.supplierId,
|
||||||
@ -190,7 +189,6 @@ export default hocAuth(function Page(props: CommonComponent) {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user