feat:1
This commit is contained in:
@@ -44,9 +44,9 @@ const QuotationPage = () => {
|
|||||||
const [selectedTemplateId, setSelectedTemplateId] = useState(null);
|
const [selectedTemplateId, setSelectedTemplateId] = useState(null);
|
||||||
const [templates, setTemplates] = useState([]);
|
const [templates, setTemplates] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [selectedCategory, setSelectedCategory] = useState("all");
|
const [customers,setCustomers]=useState([])
|
||||||
const [categories, setCategories] = useState([]);
|
const [loadingCustomers, setLoadingCustomers] = useState(false);
|
||||||
|
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
||||||
const {
|
const {
|
||||||
resources: quotations,
|
resources: quotations,
|
||||||
loading: loadingQuotations,
|
loading: loadingQuotations,
|
||||||
@@ -56,9 +56,18 @@ const QuotationPage = () => {
|
|||||||
} = useResources(pagination, sorter, "quota");
|
} = useResources(pagination, sorter, "quota");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchQuotations();
|
fetchQuotations({
|
||||||
}, []);
|
current: pagination.current,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
field: sorter.field,
|
||||||
|
order: sorter.order,
|
||||||
|
search: selectedCustomer ? JSON.stringify({ customerId: selectedCustomer }) : "" // 添加搜索参数
|
||||||
|
});
|
||||||
|
|
||||||
|
}, [selectedCustomer, pagination.current, pagination.pageSize, sorter]); // 添加更多依赖项
|
||||||
|
useEffect(()=>{
|
||||||
|
fetchCustomers();
|
||||||
|
},[])
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setPagination(pagination);
|
setPagination(pagination);
|
||||||
setSorter(sorter);
|
setSorter(sorter);
|
||||||
@@ -106,11 +115,7 @@ const QuotationPage = () => {
|
|||||||
}
|
}
|
||||||
}, [isModalVisible]);
|
}, [isModalVisible]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (templates.length > 0) {
|
|
||||||
setCategories(getAllCategories(templates));
|
|
||||||
}
|
|
||||||
}, [templates]);
|
|
||||||
|
|
||||||
const handleTemplateSelect = (templateId) => {
|
const handleTemplateSelect = (templateId) => {
|
||||||
setSelectedTemplateId(templateId);
|
setSelectedTemplateId(templateId);
|
||||||
@@ -126,23 +131,6 @@ const QuotationPage = () => {
|
|||||||
setSelectedTemplateId(null);
|
setSelectedTemplateId(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAllCategories = (templates) => {
|
|
||||||
const categorySet = new Set();
|
|
||||||
templates.forEach((template) => {
|
|
||||||
template.attributes.category?.forEach((cat) => {
|
|
||||||
categorySet.add(JSON.stringify(cat));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return Array.from(categorySet).map((cat) => JSON.parse(cat));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFilteredTemplates = () => {
|
|
||||||
if (selectedCategory === "all") return templates;
|
|
||||||
return templates.filter((template) =>
|
|
||||||
template.attributes.category?.some((cat) => cat.id === selectedCategory)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: "报价单名称",
|
title: "报价单名称",
|
||||||
@@ -333,6 +321,24 @@ const QuotationPage = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const fetchCustomers = async () => {
|
||||||
|
setLoadingCustomers(true);
|
||||||
|
try {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('resources')
|
||||||
|
.select('*')
|
||||||
|
.eq('type', 'customer')
|
||||||
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
setCustomers(data || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取客户列表失败:', error);
|
||||||
|
message.error('获取客户列表失败');
|
||||||
|
} finally {
|
||||||
|
setLoadingCustomers(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card
|
<Card
|
||||||
@@ -344,15 +350,29 @@ const QuotationPage = () => {
|
|||||||
}
|
}
|
||||||
className="h-full w-full overflow-auto"
|
className="h-full w-full overflow-auto"
|
||||||
extra={
|
extra={
|
||||||
<div className="flex justify-between my-4">
|
<Space>
|
||||||
<Button
|
<Select
|
||||||
type="primary"
|
allowClear
|
||||||
icon={<PlusOutlined />}
|
loading={loadingCustomers}
|
||||||
onClick={() => setIsModalVisible(true)}
|
placeholder="按客户筛选"
|
||||||
>
|
style={{ width: 200 }}
|
||||||
新增报价单
|
options={customers.map(c => ({
|
||||||
</Button>
|
label: c.attributes.name,
|
||||||
</div>
|
value: c.id
|
||||||
|
}))}
|
||||||
|
onChange={(value) => {
|
||||||
|
setSelectedCustomer(value);
|
||||||
|
setPagination({ current: 1, pageSize: pagination.pageSize });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => setIsModalVisible(true)}
|
||||||
|
>
|
||||||
|
新增报价单
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ const TaskTemplate = ({ id, isView, onCancel,isEdit }) => {
|
|||||||
title={
|
title={
|
||||||
<span className="flex items-center space-x-2 text-gray-700">
|
<span className="flex items-center space-x-2 text-gray-700">
|
||||||
<span className="w-1 h-4 bg-blue-500 rounded-full" />
|
<span className="w-1 h-4 bg-blue-500 rounded-full" />
|
||||||
<span>服务明细</span>
|
<span>相关任务</span>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Spin,
|
Spin,
|
||||||
Modal,
|
Modal,
|
||||||
Empty,
|
Empty,
|
||||||
|
Select,
|
||||||
Typography,
|
Typography,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import {
|
import {
|
||||||
@@ -37,7 +38,8 @@ const TaskPage = () => {
|
|||||||
const [selectedTemplateId, setSelectedTemplateId] = useState(null);
|
const [selectedTemplateId, setSelectedTemplateId] = useState(null);
|
||||||
const [templates, setTemplates] = useState([]);
|
const [templates, setTemplates] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [customers,setCustomers]=useState([])
|
||||||
|
const [loadingCustomers, setLoadingCustomers] = useState(false);
|
||||||
const {
|
const {
|
||||||
resources: tasks,
|
resources: tasks,
|
||||||
loading: loadingTasks,
|
loading: loadingTasks,
|
||||||
@@ -45,11 +47,30 @@ const TaskPage = () => {
|
|||||||
fetchResources: fetchTasks,
|
fetchResources: fetchTasks,
|
||||||
deleteResource: deleteTask,
|
deleteResource: deleteTask,
|
||||||
} = useResources(pagination, sorter, "task");
|
} = useResources(pagination, sorter, "task");
|
||||||
|
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
|
fetchCustomers()
|
||||||
}, []);
|
}, []);
|
||||||
|
const fetchCustomers = async () => {
|
||||||
|
setLoadingCustomers(true);
|
||||||
|
try {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('resources')
|
||||||
|
.select('*')
|
||||||
|
.eq('type', 'customer')
|
||||||
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
setCustomers(data || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取客户列表失败:', error);
|
||||||
|
message.error('获取客户列表失败');
|
||||||
|
} finally {
|
||||||
|
setLoadingCustomers(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: "任务名称",
|
title: "任务名称",
|
||||||
@@ -58,6 +79,24 @@ const TaskPage = () => {
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
className: "dark:text-gray-200",
|
className: "dark:text-gray-200",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "相关人员",
|
||||||
|
dataIndex: ["attributes", "customers"],
|
||||||
|
key: "customers",
|
||||||
|
render: (customers, record) => (
|
||||||
|
<Space>
|
||||||
|
{customers?.map((customer) => (
|
||||||
|
<Tag
|
||||||
|
key={customer.id}
|
||||||
|
color="blue"
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
{customer.name}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "开始时间",
|
title: "开始时间",
|
||||||
dataIndex: ["attributes", "timeRange"],
|
dataIndex: ["attributes", "timeRange"],
|
||||||
@@ -258,6 +297,7 @@ const TaskPage = () => {
|
|||||||
pageSize: pagination.pageSize,
|
pageSize: pagination.pageSize,
|
||||||
field: sorter.field,
|
field: sorter.field,
|
||||||
order: sorter.order,
|
order: sorter.order,
|
||||||
|
search: selectedCustomer ? JSON.stringify({ customerId: selectedCustomer }) : undefined
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -307,6 +347,29 @@ const TaskPage = () => {
|
|||||||
}
|
}
|
||||||
className="h-full w-full overflow-auto dark:bg-gray-800 dark:border-gray-700"
|
className="h-full w-full overflow-auto dark:bg-gray-800 dark:border-gray-700"
|
||||||
extra={
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
loading={loadingCustomers}
|
||||||
|
placeholder="按客户筛选"
|
||||||
|
variant="filled"
|
||||||
|
style={{ width: 200 }}
|
||||||
|
options={customers.map(c => ({
|
||||||
|
label: c.attributes.name,
|
||||||
|
value: c.id
|
||||||
|
}))}
|
||||||
|
onChange={(value) => {
|
||||||
|
setSelectedCustomer(value);
|
||||||
|
setPagination({ current: 1, pageSize: pagination.pageSize });
|
||||||
|
fetchTasks({
|
||||||
|
current: 1,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
field: sorter.field,
|
||||||
|
order: sorter.order,
|
||||||
|
search: value ? JSON.stringify({ customerId: value }) : undefined
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
@@ -315,6 +378,7 @@ const TaskPage = () => {
|
|||||||
>
|
>
|
||||||
新增任务
|
新增任务
|
||||||
</Button>
|
</Button>
|
||||||
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
|
|||||||
@@ -629,11 +629,11 @@ const StorageManager = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Search
|
<Input
|
||||||
|
variant="filled"
|
||||||
placeholder="搜索文件名..."
|
placeholder="搜索文件名..."
|
||||||
allowClear
|
allowClear
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
className="w-full"
|
|
||||||
size="large"
|
size="large"
|
||||||
/>
|
/>
|
||||||
<div className=" p-3 rounded-lg shadow-sm">
|
<div className=" p-3 rounded-lg shadow-sm">
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
|||||||
try {
|
try {
|
||||||
const row = await form.validateFields();
|
const row = await form.validateFields();
|
||||||
setEditingKey('');
|
setEditingKey('');
|
||||||
|
console.log(row,'row');
|
||||||
|
|
||||||
onUpdate(key, row);
|
onUpdate(key, row);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save failed:', error);
|
console.error('Save failed:', error);
|
||||||
@@ -93,16 +95,20 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
|||||||
</Space>
|
</Space>
|
||||||
) : (
|
) : (
|
||||||
<Space>
|
<Space>
|
||||||
<Button
|
|
||||||
|
{!record.is_creator && (
|
||||||
|
<>
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
disabled={editingKey !== '' || record.isCreator}
|
disabled={editingKey !== '' || record.isCreator}
|
||||||
icon={<EditOutlined />}
|
icon={<EditOutlined />}
|
||||||
onClick={() => edit(record)}
|
onClick={() => edit(record)}
|
||||||
type="link"
|
type="link"
|
||||||
size='small'
|
size='small'
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
{!record.isCreator && (
|
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定要删除该成员吗?"
|
title="确定要删除该成员吗?"
|
||||||
onConfirm={() => onDelete(record.id)}
|
onConfirm={() => onDelete(record.id)}
|
||||||
@@ -116,6 +122,7 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
|||||||
删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ const TeamManagement = () => {
|
|||||||
try {
|
try {
|
||||||
await updateTeam(id, values);
|
await updateTeam(id, values);
|
||||||
await loadTeams();
|
await loadTeams();
|
||||||
message.success('更新团队成功');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('更新团队失败');
|
message.error('更新团队失败');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,19 @@ export const resourceService = {
|
|||||||
.eq('type', type)
|
.eq('type', type)
|
||||||
|
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
query = query.or(`external_id.ilike.%${searchQuery}%`);
|
const searchParams = JSON.parse(searchQuery);
|
||||||
|
if (searchParams.customerId) {
|
||||||
|
query = query.filter('attributes->customers', 'cs',
|
||||||
|
JSON.stringify([{id: searchParams.customerId}])
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (orderBy) {
|
if (orderBy) {
|
||||||
query = query.order(orderBy, { ascending });
|
query = query.order(orderBy, { ascending });
|
||||||
}
|
}
|
||||||
const from = (page - 1) * pageSize;
|
const from = (page - 1) * pageSize;
|
||||||
query = query.range(from, from + pageSize - 1);
|
const to = from + pageSize - 1;
|
||||||
|
query = query.range(from, to);
|
||||||
|
|
||||||
const { data, count, error } = await query;
|
const { data, count, error } = await query;
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
@@ -25,6 +31,7 @@ export const resourceService = {
|
|||||||
total: count || 0,
|
total: count || 0,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("获取资源列表失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user