- 为 ExpenseCostCreate 和 ExpenseProvisionCreate 组件添加必填字段标识 - 使用 generateShortId 替代时间戳生成唯一 ID - 移除备注字段的非必要校验逻辑 - 更新客户选择字段的显示文案与交互样式 - 在 ExpenseProvisionCreate 中移除客户名称必填限制 - 引入 Text 组件支持星号标注必填项 - 修复表单提交按钮的禁用条件判断逻辑 - 集成全局 loading 状态管理 - 实现费用记录保存成功后的提示反馈 - 将 DatePicker 替换为 Calendar 组件用于日期选择 - 为 CostPicker 添加 EXPENSE_TYPE 类型筛选参数 - 在 CostPageQry 类型中新增 name 字段定义 - 优化图标组件使用方式,替换原有 class 样式写法
269 lines
8.1 KiB
TypeScript
269 lines
8.1 KiB
TypeScript
import {
|
||
Button,
|
||
Input,
|
||
Popup,
|
||
SafeArea,
|
||
TextArea,
|
||
} from "@nutui/nutui-react-taro";
|
||
import { DealerPicker, Icon } from "@/components";
|
||
import { ScrollView, Text, View } from "@tarojs/components";
|
||
import { useEffect, useState } from "react";
|
||
import { generateShortId } from "@/utils";
|
||
|
||
interface IExpenseProvisionCreateProps {
|
||
onFinish?: (expenseProvision: BusinessAPI.ExpenseProvision) => void;
|
||
visible: boolean;
|
||
onClose: () => void;
|
||
editMode?: boolean;
|
||
expenseProvision?: BusinessAPI.ExpenseProvision;
|
||
}
|
||
|
||
export default function ExpenseProvisionCreate(
|
||
props: IExpenseProvisionCreateProps,
|
||
) {
|
||
const {
|
||
onFinish,
|
||
visible,
|
||
onClose,
|
||
editMode = false,
|
||
expenseProvision: initialExpenseProvision,
|
||
} = props;
|
||
|
||
const [expenseProvision, setExpenseProvision] =
|
||
useState<BusinessAPI.ExpenseProvision>();
|
||
|
||
// 表单错误状态
|
||
const [formErrors, setFormErrors] = useState<{
|
||
provisionAmount?: boolean;
|
||
}>({});
|
||
|
||
// 初始化表单数据
|
||
useEffect(() => {
|
||
if (visible) {
|
||
if (editMode && initialExpenseProvision) {
|
||
// 编辑模式:回填数据
|
||
setExpenseProvision(initialExpenseProvision);
|
||
} else {
|
||
// 新增模式:清空数据
|
||
setExpenseProvision(undefined);
|
||
}
|
||
// 清空错误状态
|
||
setFormErrors({});
|
||
}
|
||
}, [visible, editMode, initialExpenseProvision]);
|
||
|
||
const validateForm = () => {
|
||
const errors = {
|
||
provisionAmount:
|
||
!expenseProvision?.provisionAmount ||
|
||
expenseProvision.provisionAmount <= 0,
|
||
};
|
||
|
||
setFormErrors(errors);
|
||
|
||
// 检查是否有错误
|
||
return !Object.values(errors).some((error) => error);
|
||
};
|
||
|
||
const saveExpenseProvision = async () => {
|
||
if (!validateForm()) {
|
||
return;
|
||
}
|
||
|
||
if (expenseProvision) {
|
||
// 编辑模式下保留原有ID,新增模式下生成新ID
|
||
const finalExpenseProvision: BusinessAPI.ExpenseProvision = {
|
||
...expenseProvision,
|
||
expenseProvisionId: editMode
|
||
? initialExpenseProvision?.expenseProvisionId ||
|
||
expenseProvision.expenseProvisionId
|
||
: expenseProvision.expenseProvisionId || generateShortId(),
|
||
};
|
||
|
||
onFinish?.(finalExpenseProvision);
|
||
}
|
||
|
||
// 重置表单
|
||
setExpenseProvision(undefined);
|
||
setFormErrors({});
|
||
onClose();
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<Popup
|
||
duration={150}
|
||
style={{
|
||
minHeight: "auto",
|
||
}}
|
||
visible={visible}
|
||
className={"flex flex-col"}
|
||
position="bottom"
|
||
title={editMode ? "编辑计提" : "添加计提"}
|
||
onClose={() => {
|
||
onClose();
|
||
}}
|
||
onOverlayClick={() => {
|
||
onClose();
|
||
}}
|
||
lockScroll
|
||
>
|
||
<ScrollView scrollY className="h-96">
|
||
<View className={"flex flex-col gap-3 p-2.5"}>
|
||
{/* 编辑模式下客户名称不可修改 */}
|
||
{editMode ? (
|
||
<>
|
||
<View className="block text-sm font-normal text-[#000000]">
|
||
客户选择(可选)
|
||
</View>
|
||
<View className="rounded-md bg-gray-100 p-3">
|
||
<View className="text-base font-medium text-gray-800">
|
||
{expenseProvision?.dealerName || "未选客户"}
|
||
</View>
|
||
</View>
|
||
</>
|
||
) : (
|
||
<>
|
||
<View className="block text-sm font-normal text-[#000000]">
|
||
客户名称(可选)
|
||
</View>
|
||
<View
|
||
id={"target2"}
|
||
className={`border-neutral-base flex flex-row items-center rounded-md border-4 border-gray-300`}
|
||
>
|
||
<DealerPicker
|
||
onFinish={(dealerVO) => {
|
||
setExpenseProvision((prev) => {
|
||
return {
|
||
...prev!,
|
||
dealerName: dealerVO.shortName,
|
||
};
|
||
});
|
||
}}
|
||
trigger={
|
||
<View
|
||
className={
|
||
"flex flex-1 flex-row items-center justify-between px-5"
|
||
}
|
||
>
|
||
<View className={"text-sm"}>
|
||
{expenseProvision?.dealerName || "请选择客户"}
|
||
</View>
|
||
<Icon name={"chevron-down"} />
|
||
</View>
|
||
}
|
||
/>
|
||
</View>
|
||
</>
|
||
)}
|
||
|
||
<View className={"text-neutral-darkest text-sm font-medium"}>
|
||
计提金额 <Text className="text-red-500">*</Text>
|
||
</View>
|
||
<View
|
||
className={`border-neutral-base flex flex-row items-center rounded-md ${formErrors.provisionAmount ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Input
|
||
className={"placeholder:text-neutral-dark"}
|
||
type={"digit"}
|
||
value={expenseProvision?.provisionAmount?.toString() || ""}
|
||
placeholder="请输入计提金额"
|
||
onChange={(value) => {
|
||
const num = Number(value);
|
||
if (!Number.isNaN(num) && num >= 0) {
|
||
setExpenseProvision((prev) => {
|
||
return {
|
||
...prev!,
|
||
provisionAmount: num,
|
||
};
|
||
});
|
||
|
||
// 实时清除金额错误状态
|
||
if (num > 0) {
|
||
setFormErrors((prev) => ({
|
||
...prev,
|
||
provisionAmount: false,
|
||
}));
|
||
}
|
||
}
|
||
}}
|
||
/>
|
||
</View>
|
||
{formErrors.provisionAmount && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
请输入正确的计提金额
|
||
</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"
|
||
}
|
||
>
|
||
<TextArea
|
||
className={"flex-1"}
|
||
placeholder="请输入备注信息"
|
||
rows={4}
|
||
maxLength={200}
|
||
showCount
|
||
value={expenseProvision?.remark || ""}
|
||
onChange={(value) => {
|
||
// 清除错误状态
|
||
if (expenseProvision?.remark && value.trim()) {
|
||
setFormErrors((prev) => ({
|
||
...prev,
|
||
remark: false,
|
||
}));
|
||
}
|
||
|
||
setExpenseProvision((prev: any) => {
|
||
return {
|
||
...prev!,
|
||
remark: value,
|
||
};
|
||
});
|
||
}}
|
||
/>
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
<View className={"flex w-full flex-col bg-white"}>
|
||
<View className={"flex flex-row gap-2 p-3"}>
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
size={"large"}
|
||
block
|
||
type="default"
|
||
onClick={() => {
|
||
onClose();
|
||
}}
|
||
>
|
||
取消
|
||
</Button>
|
||
</View>
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
size={"large"}
|
||
block
|
||
type="primary"
|
||
disabled={
|
||
!expenseProvision?.provisionAmount ||
|
||
expenseProvision.provisionAmount <= 0 ||
|
||
Object.values(formErrors).some((item) => item)
|
||
}
|
||
onClick={saveExpenseProvision}
|
||
>
|
||
{editMode ? "保存" : "添加"}
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
<SafeArea position={"bottom"} />
|
||
</View>
|
||
</Popup>
|
||
</>
|
||
);
|
||
}
|