diff --git a/src/config/supabase.js b/src/config/supabase.js
index 5f7b4fa..6a073da 100644
--- a/src/config/supabase.js
+++ b/src/config/supabase.js
@@ -21,7 +21,7 @@ export const createSupabase = () => {
detectSessionInUrl: false,
},
db: {
- schema: 'limq'
+ schema: 'limq_dev'
}
}
);
diff --git a/src/pages/auth/Login.jsx b/src/pages/auth/Login.jsx
index 88f9b5e..654dcc3 100644
--- a/src/pages/auth/Login.jsx
+++ b/src/pages/auth/Login.jsx
@@ -22,7 +22,6 @@ const Login = () => {
const handleGoogleLogin = async () => {
try {
await signInWithGoogle();
- navigate("/");
} catch (error) {
console.error("Google login error:", error);
}
diff --git a/src/pages/company/service/detail/index.jsx b/src/pages/company/service/detail/index.jsx
index 01a00c2..aaadb18 100644
--- a/src/pages/company/service/detail/index.jsx
+++ b/src/pages/company/service/detail/index.jsx
@@ -30,9 +30,10 @@ const ServiceForm = () => {
const isEdit = location.search.includes("edit=true");
const [editingSectionIndex, setEditingSectionIndex] = useState(null);
const [availableSections, setAvailableSections] = useState([]);
- const [formValues, setFormValues] = useState({});
- const [sectionNameForm] = Form.useForm();
-
+ const [formValues, setFormValues] = useState({
+ sections: [{ items: [{}] }],
+ currency: "CNY"
+ });
useEffect(() => {
if (id) {
fetchServiceTemplate();
@@ -50,11 +51,13 @@ const ServiceForm = () => {
.single();
if (error) throw error;
- form.setFieldsValue({
+ const formData = {
templateName: data.attributes.templateName,
description: data.attributes.description,
sections: data.attributes.sections,
- });
+ };
+ form.setFieldsValue(formData);
+ setFormValues(formData);
} catch (error) {
console.error("获取服务模版失败:", error);
message.error("获取服务模版失败");
@@ -96,6 +99,7 @@ const ServiceForm = () => {
templateName: values.templateName,
description: values.description,
sections: values.sections,
+ category: values.category || [], // 添加 category 字段
totalAmount,
},
};
@@ -279,7 +283,7 @@ const ServiceForm = () => {
disabled={id && !isEdit}
onValuesChange={handleValuesChange}
>
-
+
基本信息
@@ -327,23 +331,12 @@ const ServiceForm = () => {
-
+
报价明细
- {(!id || isEdit) && (
-
@@ -448,12 +441,7 @@ const ServiceForm = () => {
{...itemField}
name={[itemField.name, "name"]}
className="!mb-0"
- rules={[
- {
- required: true,
- message: "请输入服务项目名称",
- },
- ]}
+
>
diff --git a/src/pages/company/service/index.jsx b/src/pages/company/service/index.jsx
index 35438ad..7d1282d 100644
--- a/src/pages/company/service/index.jsx
+++ b/src/pages/company/service/index.jsx
@@ -1,9 +1,11 @@
-import { Card, Table, Button, Space, Input, message, Popconfirm } from "antd";
-import { EyeOutlined } from "@ant-design/icons";
+import { Card, Table, Button, Space, Input, message, Popconfirm, Form, Tag, Typography } from "antd";
+import { DownOutlined, UpOutlined,EyeOutlined,EditOutlined } from "@ant-design/icons";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { supabase } from "@/config/supabase";
+const { Paragraph } = Typography;
+
const ServicePage = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
@@ -30,32 +32,72 @@ const ServicePage = () => {
}
};
- // 添加保存编辑项目的方法
- const handleSaveItem = async (record, sectionIndex, itemIndex) => {
+ // 修改保存逻辑
+ const handleSaveItem = async (record) => {
try {
setLoading(true);
- const newData = [...data];
- const targetService = newData.find(item => item.id === record.serviceId);
- targetService.attributes.sections[sectionIndex].items[itemIndex] = record;
+ const { serviceId, sectionIndex, itemIndex } = record;
- // 计算新的总金额
- const newTotalAmount = targetService.attributes.sections.reduce((total, section) => {
- return total + section.items.reduce((sectionTotal, item) =>
- sectionTotal + (item.price * item.quantity), 0);
- }, 0);
-
- targetService.attributes.totalAmount = newTotalAmount;
+ // 找到当前服务模板
+ const currentService = data.find(item => item.id === serviceId);
+ if (!currentService) {
+ throw new Error('Service not found');
+ }
+ // 创建新的 attributes 对象,避免直接修改状态
+ const newAttributes = {
+ ...currentService.attributes,
+ templateName: currentService.attributes.templateName,
+ description: currentService.attributes.description,
+ category: currentService.attributes.category || [], // 确保 category 字段存在
+ sections: currentService.attributes.sections.map((section, secIdx) => {
+ if (secIdx === sectionIndex) {
+ return {
+ ...section,
+ items: section.items.map((item, itemIdx) => {
+ if (itemIdx === itemIndex) {
+ return {
+ name: record.name,
+ price: Number(record.price) || 0,
+ quantity: Number(record.quantity) || 0,
+ description: record.description || '',
+ unit: record.unit || ''
+ };
+ }
+ return item;
+ })
+ };
+ }
+ return section;
+ })
+ };
+
+ // 重新计算总金额
+ newAttributes.totalAmount = newAttributes.sections.reduce((total, section) => {
+ return total + section.items.reduce((sectionTotal, item) =>
+ sectionTotal + ((Number(item.price) || 0) * (Number(item.quantity) || 0)), 0);
+ }, 0);
+
+ // 调用 supabase 更新数据
const { error } = await supabase
.from('resources')
- .update({ attributes: targetService.attributes })
- .eq('id', targetService.id);
+ .update({
+ type: "serviceTemplate",
+ attributes: newAttributes
+ })
+ .eq('id', serviceId);
if (error) throw error;
- setData(newData);
+ // 更新本地状态
+ setData(prevData => prevData.map(item =>
+ item.id === serviceId
+ ? { ...item, attributes: newAttributes }
+ : item
+ ));
+
setEditingKey('');
- message.success('更新成功');
+ message.success('保存成功');
} catch (error) {
console.error('保存失败:', error);
message.error('保存失败');
@@ -68,28 +110,73 @@ const ServicePage = () => {
const handleDeleteItem = async (record) => {
try {
setLoading(true);
- const newData = [...data];
- const targetService = newData.find(item => item.id === record.serviceId);
+ const { serviceId, sectionIndex, itemIndex } = record;
- // 删除指定项目
- targetService.attributes.sections[record.sectionIndex].items.splice(record.itemIndex, 1);
+ // 找到目标服务
+ const targetService = data.find(item => item.id === serviceId);
+ if (!targetService) throw new Error('Service not found');
- // 重新计算总金额
- const newTotalAmount = targetService.attributes.sections.reduce((total, section) => {
- return total + section.items.reduce((sectionTotal, item) =>
- sectionTotal + (item.price * item.quantity), 0);
- }, 0);
-
- targetService.attributes.totalAmount = newTotalAmount;
+ // 创建新的 attributes 对象,避免直接修改状态
+ const newAttributes = {
+ ...targetService.attributes,
+ sections: targetService.attributes.sections.map((section, secIdx) => {
+ if (secIdx === sectionIndex) {
+ return {
+ ...section,
+ items: section.items.filter((_, idx) => idx !== itemIndex)
+ };
+ }
+ return section;
+ })
+ };
+ // 重新计算总金额
+ newAttributes.totalAmount = newAttributes.sections.reduce((total, section) => {
+ return total + section.items.reduce((sectionTotal, item) =>
+ sectionTotal + ((Number(item.price) || 0) * (Number(item.quantity) || 0)), 0);
+ }, 0);
+
+ // 调用 supabase 更新数据
const { error } = await supabase
.from('resources')
- .update({ attributes: targetService.attributes })
- .eq('id', targetService.id);
+ .update({
+ attributes: newAttributes
+ })
+ .eq('id', serviceId);
if (error) throw error;
- setData(newData);
+ // 更新本地状态
+ setData(prevData => prevData.map(item =>
+ item.id === serviceId
+ ? { ...item, attributes: newAttributes }
+ : item
+ ));
+
+ message.success('删除成功');
+ } catch (error) {
+ console.error('删除失败:', error);
+ message.error('删���失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 添加删除服务模板的方法
+ const handleDeleteService = async (serviceId) => {
+ try {
+ setLoading(true);
+
+ // 调用 supabase 删除数据
+ const { error } = await supabase
+ .from('resources')
+ .delete()
+ .eq('id', serviceId);
+
+ if (error) throw error;
+
+ // 更新本地状态
+ setData(prevData => prevData.filter(item => item.id !== serviceId));
message.success('删除成功');
} catch (error) {
console.error('删除失败:', error);
@@ -107,10 +194,65 @@ const ServicePage = () => {
key: "templateName",
className: "min-w-[200px]",
},
+ {
+ title: "分类",
+ dataIndex: ["attributes", "category"],
+ key: "category",
+ render: (category) => {
+ if (!category || !Array.isArray(category)) return null;
+ return (
+
+ {category.map((cat, index) => (
+
+ {cat}
+
+ ))}
+
+ )
+ }}
+ className="mb-0"
+ >
+
+ {category.map((cat, index) => (
+
+ {cat}
+
+ ))}
+
+
+ );
+ },
+ },
{
title: "描述",
dataIndex: ["attributes", "description"],
key: "description",
+ width: 300,
+ render: (text) => (
+
+ {text}
+
+ )
},
{
title: "总金额",
@@ -118,7 +260,7 @@ const ServicePage = () => {
key: "totalAmount",
className: "min-w-[150px] text-right",
render: (amount) => (
-
+
¥
{amount?.toLocaleString("zh-CN", {
minimumFractionDigits: 2,
@@ -130,17 +272,40 @@ const ServicePage = () => {
{
title: "操作",
key: "action",
+ fixed: 'right',
+ width: 220,
render: (_, record) => (
-
+ e.stopPropagation()}>
}
- onClick={() =>
- navigate(`/company/serviceTemplateInfo/${record.id}`)
- }
+ onClick={() => navigate(`/company/serviceTemplateInfo/${record.id}`)}
>
查看
+ }
+ onClick={() => navigate(`/company/serviceTemplateInfo/${record.id}?edit=true`)}
+ >
+ 编辑
+
+ handleDeleteService(record.id)}
+ >
+
+
),
},
@@ -152,28 +317,71 @@ const ServicePage = () => {
title: "项目名称",
dataIndex: "name",
key: "name",
- width: "40%",
+ width: "25%",
render: (text, record) => {
const isEditing = record.key === editingKey;
return isEditing ? (
- {
- const newData = [...data];
- const targetService = newData.find(item => item.id === record.serviceId);
- const targetItem = targetService.attributes.sections[record.sectionIndex].items[record.itemIndex];
- targetItem.name = e.target.value;
- setData(newData);
- }}
- />
+
+ {
+ const newData = [...data];
+ const targetService = newData.find(item => item.id === record.serviceId);
+ const targetItem = targetService.attributes.sections[record.sectionIndex].items[record.itemIndex];
+ targetItem.name = e.target.value;
+ setData(newData);
+ }}
+ />
+
) : text;
}
},
+ {
+ title: "描述",
+ dataIndex: "description",
+ key: "description",
+ width: "25%",
+ render: (text, record) => {
+ const isEditing = record.key === editingKey;
+ return isEditing ? (
+
+ {
+ const newData = [...data];
+ const targetService = newData.find(item => item.id === record.serviceId);
+ const targetItem = targetService.attributes.sections[record.sectionIndex].items[record.itemIndex];
+ targetItem.description = e.target.value;
+ setData(newData);
+ }}
+ />
+
+ ) : (
+
+ {text}
+
+ );
+ }
+ },
{
title: "单价",
dataIndex: "price",
key: "price",
- width: "20%",
+ width: "15%",
render: (price, record) => {
const isEditing = record.key === editingKey;
return isEditing ? (
@@ -202,7 +410,7 @@ const ServicePage = () => {
title: "数量",
dataIndex: "quantity",
key: "quantity",
- width: "20%",
+ width: "15%",
render: (quantity, record) => {
const isEditing = record.key === editingKey;
return isEditing ? (
@@ -296,21 +504,24 @@ const ServicePage = () => {
}
];
- // 修改 expandedRowRender 以支持编辑
+ // 修改 expandedRowRender 函数,确保 key 的唯一性
const expandedRowRender = (record) => (
-
+
{record.attributes.sections?.map((section, sectionIndex) => (
- {section.sectionName}
-
+
+
+ {section.sectionName || `服务类型 ${sectionIndex + 1}`}
+
+
总计: ¥
{section.items
.reduce(
- (total, item) => total + item.price * item.quantity,
+ (total, item) =>
+ total + (Number(item.price) || 0) * (Number(item.quantity) || 0),
0
)
.toLocaleString("zh-CN", {
@@ -320,18 +531,26 @@ const ServicePage = () => {
}
+ headStyle={{
+ background: 'rgba(59, 130, 246, 0.05)',
+ borderBottom: '1px solid rgba(59, 130, 246, 0.1)'
+ }}
+ bodyStyle={{
+ background: 'rgba(59, 130, 246, 0.02)'
+ }}
>
({
...item,
- key: `${sectionIndex}-${itemIndex}`,
+ key: `${record.id}-${sectionIndex}-${itemIndex}`,
serviceId: record.id,
- sectionIndex: sectionIndex,
- itemIndex: itemIndex
+ sectionIndex,
+ itemIndex
}))}
pagination={false}
className="rounded-lg overflow-hidden"
+ rowClassName="hover:bg-blue-50/50 dark:hover:bg-blue-900/20 transition-colors"
/>
))}
@@ -343,10 +562,10 @@ const ServicePage = () => {
}, []);
return (
-
+
服务模版管理}
- className="shadow-lg rounded-lg"
+ className="h-full w-full overflow-auto"
extra={