feat:v1
This commit is contained in:
420
src/pages/company/task/detail/index.jsx
Normal file
420
src/pages/company/task/detail/index.jsx
Normal file
@@ -0,0 +1,420 @@
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Button,
|
||||
Space,
|
||||
Card,
|
||||
Typography,
|
||||
message,
|
||||
DatePicker,
|
||||
} from "antd";
|
||||
import {
|
||||
ArrowLeftOutlined,
|
||||
SaveOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { supabase } from "@/config/supabase";
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import TaskList from '@/components/TaskList';
|
||||
import dayjs from 'dayjs';
|
||||
import {supabaseService} from '@/hooks/supabaseService'
|
||||
|
||||
const { Title } = Typography;
|
||||
const TYPE = "task";
|
||||
|
||||
export default function TaskForm() {
|
||||
const { id } = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const isEdit = searchParams.get("edit") === "true";
|
||||
const templateId = searchParams.get("templateId");
|
||||
const isView = id && !isEdit;
|
||||
const [form] = Form.useForm();
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [customers, setCustomers] = useState([]);
|
||||
const [formValues, setFormValues] = useState({});
|
||||
const [units, setUnits] = useState([]);
|
||||
const [loadingUnits,setLoadingUnits]=useState(false)
|
||||
const fetchUnits = async () => {
|
||||
setLoadingUnits(true);
|
||||
try {
|
||||
const { data: units } = await supabaseService.select("resources", {
|
||||
filter: {
|
||||
type: { eq: "units" },
|
||||
"attributes->>template_type": { in: `(${TYPE})` },
|
||||
},
|
||||
order: {
|
||||
column: "created_at",
|
||||
ascending: false,
|
||||
},
|
||||
});
|
||||
|
||||
setUnits(units || []);
|
||||
} catch (error) {
|
||||
message.error("获取单位列表失败");
|
||||
console.error(error);
|
||||
} finally {
|
||||
setLoadingUnits(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchUnits();
|
||||
}, []);
|
||||
const initialValues = {
|
||||
sections: [
|
||||
{
|
||||
key: uuidv4(),
|
||||
sectionName: "任务类型 1",
|
||||
items: [
|
||||
{
|
||||
key: uuidv4(),
|
||||
name: "",
|
||||
description: "",
|
||||
timeRange: null,
|
||||
unit: "未开始",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
status: "未开始",
|
||||
timeRange: null,
|
||||
};
|
||||
|
||||
// 处理表单值变化
|
||||
const handleValuesChange = (changedValues, allValues) => {
|
||||
setFormValues(allValues);
|
||||
};
|
||||
|
||||
// 获取任务详情
|
||||
const fetchTaskDetail = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data, error } = await supabase
|
||||
.from("resources")
|
||||
.select("*")
|
||||
.eq("id", id)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
if (data?.attributes) {
|
||||
const formData = {
|
||||
taskName: data.attributes.taskName,
|
||||
customers: data.attributes.customers.map((customer) => customer.id) || [],
|
||||
description: data.attributes.description,
|
||||
sections: data.attributes.sections.map((section) => ({
|
||||
key: uuidv4(),
|
||||
sectionName: section.sectionName,
|
||||
items: section.items.map((item) => ({
|
||||
key: uuidv4(),
|
||||
name: item.name,
|
||||
description: item.description || "",
|
||||
timeRange: item.timeRange,
|
||||
unit: item.unit || "未开始",
|
||||
})),
|
||||
})),
|
||||
status: data.attributes.status || "未开始",
|
||||
timeRange: data.attributes.timeRange
|
||||
? [dayjs(data.attributes.timeRange[0]), dayjs(data.attributes.timeRange[1])]
|
||||
: null,
|
||||
};
|
||||
|
||||
form.setFieldsValue(formData);
|
||||
setFormValues(formData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取任务详情失败:", error);
|
||||
message.error("获取任务详情失败");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取模板数据
|
||||
const fetchTemplateData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data: template, error } = await supabase
|
||||
.from("resources")
|
||||
.select("*")
|
||||
.eq("type", "serviceTemplate")
|
||||
.eq("id", templateId)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
if (template?.attributes) {
|
||||
const taskData = {
|
||||
taskName: template.attributes.templateName,
|
||||
description: template.attributes.description,
|
||||
sections: template.attributes.sections.map((section) => ({
|
||||
key: uuidv4(),
|
||||
sectionName: section.sectionName,
|
||||
items: section.items.map((item) => ({
|
||||
key: uuidv4(),
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
timeRange: null,
|
||||
unit: "未开始",
|
||||
})),
|
||||
})),
|
||||
};
|
||||
form.setFieldsValue(taskData);
|
||||
setFormValues(taskData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取模板数据失败:", error);
|
||||
message.error("获取模板数据失败");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取客户列表
|
||||
const fetchCustomers = async () => {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from("resources")
|
||||
.select("*")
|
||||
.eq("type", "customer");
|
||||
|
||||
if (error) throw error;
|
||||
setCustomers(data || []);
|
||||
} catch (error) {
|
||||
console.error("获取客户列表失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 保存任务
|
||||
const onFinish = async (values) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const taskData = {
|
||||
type: "task",
|
||||
attributes: {
|
||||
taskName: values.taskName,
|
||||
customers: customers
|
||||
.filter(customer => values.customers.includes(customer.id))
|
||||
.map(customer => ({
|
||||
id: customer.id,
|
||||
name: customer.attributes.name
|
||||
})),
|
||||
description: values.description,
|
||||
sections: values.sections.map((section) => ({
|
||||
sectionName: section.sectionName,
|
||||
items: section.items.map((item) => ({
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
timeRange: item.timeRange,
|
||||
unit: item.unit,
|
||||
})),
|
||||
})),
|
||||
status: values.status,
|
||||
timeRange: values.timeRange
|
||||
? [
|
||||
values.timeRange[0].format('YYYY-MM-DD'),
|
||||
values.timeRange[1].format('YYYY-MM-DD')
|
||||
]
|
||||
: null,
|
||||
},
|
||||
};
|
||||
|
||||
let result;
|
||||
if (id) {
|
||||
result = await supabase
|
||||
.from("resources")
|
||||
.update(taskData)
|
||||
.eq("id", id)
|
||||
.select();
|
||||
} else {
|
||||
result = await supabase
|
||||
.from("resources")
|
||||
.insert([taskData])
|
||||
.select();
|
||||
}
|
||||
|
||||
if (result.error) throw result.error;
|
||||
|
||||
message.success("保存成功");
|
||||
navigate("/company/task");
|
||||
} catch (error) {
|
||||
console.error("保存失败:", error);
|
||||
message.error("保存失败");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchCustomers();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
fetchTaskDetail();
|
||||
} else if (templateId) {
|
||||
fetchTemplateData();
|
||||
} else {
|
||||
form.setFieldsValue(initialValues);
|
||||
setFormValues(initialValues);
|
||||
}
|
||||
}, [id, templateId]);
|
||||
|
||||
return (
|
||||
<div className="bg-gradient-to-b from-gray-50 to-white dark:from-gray-800 dark:to-gray-900/90 min-h-screen p-2">
|
||||
<Card
|
||||
className="shadow-lg rounded-lg border-0"
|
||||
title={
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Title level={4} className="mb-0 text-gray-800">
|
||||
{id ? (isEdit ? "编辑任务" : "查看任务") : "新建任务"}
|
||||
</Title>
|
||||
<span className="text-gray-400 text-sm">
|
||||
{id
|
||||
? isEdit
|
||||
? "请修改任务信息"
|
||||
: "任务详情"
|
||||
: "请填写任务信息"}
|
||||
</span>
|
||||
</div>
|
||||
<Space size="middle">
|
||||
<Button
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={() => navigate("/company/task")}
|
||||
>
|
||||
返回
|
||||
</Button>
|
||||
{!isView && (
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
onValuesChange={handleValuesChange}
|
||||
layout="vertical"
|
||||
disabled={isView}
|
||||
initialValues={initialValues}
|
||||
>
|
||||
<Card
|
||||
className="shadow-sm rounded-lg"
|
||||
type="inner"
|
||||
title={
|
||||
<span className="flex items-center space-x-2 text-gray-700">
|
||||
<span className="w-1 h-4 bg-blue-500 rounded-full" />
|
||||
<span>基本信息</span>
|
||||
</span>
|
||||
}
|
||||
bordered={false}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Form.Item
|
||||
name="taskName"
|
||||
label={<span className="text-gray-700 font-medium">任务名称</span>}
|
||||
rules={[{ required: true, message: "请输入任务名称" }]}
|
||||
>
|
||||
<Input
|
||||
placeholder="请输入任务名称"
|
||||
className=" hover:border-blue-400 focus:border-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="customers"
|
||||
label={<span className="text-gray-700 font-medium">客户名称</span>}
|
||||
rules={[{ required: true, message: "请选择至少一个客户" }]}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="请选择客户"
|
||||
className="rounded-md hover:border-blue-400 focus:border-blue-500"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) =>
|
||||
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
options={customers.map((customer) => ({
|
||||
value: customer.id,
|
||||
label: customer.attributes.name,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="status"
|
||||
label={<span className="text-gray-700 font-medium">任务状态</span>}
|
||||
rules={[{ required: true, message: "请选择任务状态" }]}
|
||||
>
|
||||
<Select
|
||||
allowClear
|
||||
loading={loadingUnits}
|
||||
placeholder="请选择任务状态"
|
||||
className="rounded-md hover:border-blue-400 focus:border-blue-500"
|
||||
options={units.map((unit) => ({
|
||||
label: unit.attributes.name,
|
||||
value: unit.attributes.name,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="timeRange"
|
||||
label={<span className="text-gray-700 font-medium">时间范围</span>}
|
||||
rules={[{ required: true, message: "请选择时间范围" }]}
|
||||
>
|
||||
<DatePicker.RangePicker
|
||||
className="w-full rounded-md hover:border-blue-400 focus:border-blue-500"
|
||||
format="YYYY-MM-DD"
|
||||
onChange={(dates) => {
|
||||
if (dates) {
|
||||
form.setFieldValue('timeRange', [
|
||||
dayjs(dates[0]),
|
||||
dayjs(dates[1])
|
||||
]);
|
||||
} else {
|
||||
form.setFieldValue('timeRange', null);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
className="shadow-sm rounded-lg mt-6"
|
||||
type="inner"
|
||||
title={
|
||||
<span className="flex items-center space-x-2 text-gray-700">
|
||||
<span className="w-1 h-4 bg-blue-500 rounded-full" />
|
||||
<span>任务流程</span>
|
||||
</span>
|
||||
}
|
||||
bordered={false}
|
||||
>
|
||||
<TaskList
|
||||
type={TYPE}
|
||||
form={form}
|
||||
isView={isView}
|
||||
formValues={formValues}
|
||||
onValuesChange={handleValuesChange}
|
||||
/>
|
||||
</Card>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user