diff --git a/src/components/auth/ProtectedRoute.jsx b/src/components/auth/ProtectedRoute.jsx
index 41ab006..9a96c28 100644
--- a/src/components/auth/ProtectedRoute.jsx
+++ b/src/components/auth/ProtectedRoute.jsx
@@ -3,13 +3,26 @@ import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "@/contexts/AuthContext";
const PUBLIC_PATHS = ['login', '404','home'];
+
export const ProtectedRoute = ({ children }) => {
const { user } = useAuth();
const location = useLocation();
const currentPath = location.pathname.replace(/^\//, '');
+
+ // 如果是公共路径,直接显示
if (PUBLIC_PATHS.includes(currentPath) || currentPath === '*') {
return children;
}
+
+ // 如果用户未登录,重定向到登录页面,并携带当前路径
+ if (!user?.id) {
+ return ;
+ }
+
+ // 如果用户已登录,检查权限
if (user?.id) {
const hasPermission = user.menukeys?.some(key => {
return currentPath === key || currentPath.startsWith(`${key}/`);
diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx
index 3589d9c..07073f0 100644
--- a/src/contexts/AuthContext.jsx
+++ b/src/contexts/AuthContext.jsx
@@ -8,14 +8,13 @@ import { v4 as uuidv4 } from "uuid";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
- const [searchParams, setSearchParams] = useSearchParams();
+ const [searchParams] = useSearchParams();
const navigate = useNavigate();
const location = useLocation();
const [user, setUser] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
- //处理google登录
const hash = window.location.hash.substring(1);
const hashParams = new URLSearchParams(hash);
const accessToken = hashParams.get("access_token");
@@ -62,12 +61,7 @@ export const AuthProvider = ({ children }) => {
}
}, [user]);
- // useEffect(() => {
- // const redirectTo = searchParams.get("redirectTo");
- // if (redirectTo) {
- // navigate(redirectTo);
- // }
- // }, [location.pathname]);
+
//检查时候在管理模块团队中,没有就自动加入
const checkInTeam = async (user) => {
@@ -121,7 +115,21 @@ export const AuthProvider = ({ children }) => {
message.error(error.message || "登录失败,请稍后重试");
return;
}
- setUser(data.user);
+
+ const role = await checkInTeam(data.user);
+ const menukey = await fetchMenuList(role);
+ setUser({ ...data.user, adminRole: role, menukeys: menukey });
+
+ // 获取重定向路径
+ const redirectTo = searchParams.get("redirectTo");
+ if (redirectTo) {
+ // 如果有重定向路径,则导航到该路径
+ navigate(decodeURIComponent(redirectTo), { replace: true });
+ } else {
+ // 如果没有重定向路径,则导航到首页
+ navigate("/home", { replace: true });
+ }
+
return data;
} catch (error) {
message.error(error.message || "登录失败,请稍后重试");
@@ -134,13 +142,12 @@ export const AuthProvider = ({ children }) => {
const signInWithGoogle = async () => {
try {
setLoading(true);
- const redirectTo = searchParams.get("redirectTo");
+ const redirectTo = searchParams.get("redirectTo") || "/home";
+
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
- redirectTo: `${window.location.origin}/login?redirectTo=${
- redirectTo ?? ""
- }`,
+ redirectTo: `${window.location.origin}/login?redirectTo=${encodeURIComponent(redirectTo)}`,
},
});
@@ -182,17 +189,17 @@ export const AuthProvider = ({ children }) => {
}
};
const fetchMenuList = async (role) => {
- console.log(role,'role');
+ if(!role) return;
try {
const { data, error } = await supabase
.from('resources')
.select('*')
.eq('type', 'menuKey')
- .eq('attributes->>roleName', role) // 添加这行来筛选 OWNER 角色
+ .eq('attributes->>roleName', role)
.single();
if (error) throw error;
if(data?.attributes?.menuKeys){
- return data.attributes.menuKeys;
+ return data.attributes.menuKeys;
}else{
return [];
}
@@ -214,7 +221,8 @@ export const AuthProvider = ({ children }) => {
}
setUser({});
message.success("已成功登出");
- navigate(`/login?redirectTo=${location.pathname}`, { replace: true });
+ // 保存当前完整路径作为重定向 URL
+ navigate(`/login?redirectTo=${encodeURIComponent(location.pathname + location.search)}`, { replace: true });
} catch (error) {
message.error(error.message || "登出失败,请稍后重试");
} finally {
diff --git a/src/hooks/supabaseService.js b/src/hooks/supabaseService.js
index 926ca6c..02f49d0 100644
--- a/src/hooks/supabaseService.js
+++ b/src/hooks/supabaseService.js
@@ -6,8 +6,6 @@ class SupabaseService {
let query = supabase
.from(table)
.select(options.select || "*", { count: "exact" });
-
- // 处理精确匹配条件
if (options.match) {
query = query.match(options.match);
}
diff --git a/src/index.css b/src/index.css
index e69de29..139597f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -0,0 +1,2 @@
+
+
diff --git a/src/pages/company/quotation/view/index.jsx b/src/pages/company/quotation/view/index.jsx
index 4adfeda..f04d0bb 100644
--- a/src/pages/company/quotation/view/index.jsx
+++ b/src/pages/company/quotation/view/index.jsx
@@ -7,7 +7,7 @@ import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
const { Title, Text } = Typography;
-import { EXCHANGE_RATE,defaultSymbol } from '@/utils/exchange_rate';
+import { EXCHANGE_RATE, defaultSymbol } from '@/utils/exchange_rate';
const QuotationPreview = () => {
const { id } = useParams();
@@ -114,17 +114,6 @@ const QuotationPreview = () => {
}
};
- // 导出按钮组件
- const ExportPDFButton = () => (
- }
- onClick={exportPDF}
- >
- 导出PDF
-
- );
-
if (loading) {
return (
@@ -139,52 +128,69 @@ const QuotationPreview = () => {
const currencySymbol = EXCHANGE_RATE[attributes.currency]?.symbol || defaultSymbol;
return (
-
+
+
-
- 报价单预览
+
+ 报价单预览
-
+ }
+ onClick={exportPDF}
+ className="bg-blue-500 hover:bg-blue-600 border-none rounded-full shadow-md hover:shadow-lg transition-all duration-200"
+ >
+ 导出PDF
+
- }
- >
-
+
+
+
-
{attributes.quataName}
- 创建日期:{new Date(quotation.created_at).toLocaleDateString()}
+ {attributes.quataName}
+
+ 创建日期:{new Date(quotation.created_at).toLocaleDateString()}
+
-
-
基本信息
-
+
+
基本信息
+
- 客户:
-
+ 客户:
+
{attributes.customers?.map(customer => (
- {customer.name}
+
+ {customer.name}
+
))}
- 货币类型:
- {attributes.currency}
+ 货币类型:
+ {attributes.currency}
{attributes.sections?.map((section, sIndex) => (
-
-
+
+
-
{section.sectionName}
+
{section.sectionName}
-
-
-
+
+
+
| 项目明细 |
描述/备注 |
@@ -194,9 +200,9 @@ const QuotationPreview = () => {
小计 |
-
+
{section.items.map((item, iIndex) => (
-
+
| {item.name} |
{item.description} |
{item.unit} |
@@ -216,9 +222,9 @@ const QuotationPreview = () => {
))}
{/* 金额汇总 */}
-
-
-
+
+
+
税前总计:
{currencySymbol}{attributes.beforeTaxAmount?.toLocaleString()}
@@ -238,9 +244,9 @@ const QuotationPreview = () => {
)}
-
-
最终金额:
-
+
+ 最终金额:
+
{currencySymbol}{(attributes.discount || attributes.afterTaxAmount)?.toLocaleString()}
@@ -251,9 +257,9 @@ const QuotationPreview = () => {
{/* 补充说明 */}
{attributes.description && (
-
补充说明
-
-
{attributes.description}
+
补充说明
+
+ {attributes.description}
)}
diff --git a/src/pages/company/service/index.jsx b/src/pages/company/service/index.jsx
index 1a19d55..1e243a8 100644
--- a/src/pages/company/service/index.jsx
+++ b/src/pages/company/service/index.jsx
@@ -68,11 +68,18 @@ const ServicePage = () => {
},
};
+ // 添加分页相关状态
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ });
+
// 获取服务模板列表
- const fetchServices = async () => {
+ const fetchServices = async (page = pagination.current, pageSize = pagination.pageSize) => {
try {
setLoading(true);
- const { data: services } = await supabaseService.select("resources", {
+ const { data: services, total } = await supabaseService.select("resources", {
filter: {
type: { eq: "serviceTemplate" },
...(selectedType
@@ -83,9 +90,17 @@ const ServicePage = () => {
column: "created_at",
ascending: false,
},
+ page,
+ pageSize,
});
setData(services || []);
+ setPagination((prev) => ({
+ ...prev,
+ current: page,
+ pageSize,
+ total,
+ }));
} catch (error) {
console.error("获取服务模板失败:", error);
message.error("获取服务模板失败");
@@ -93,12 +108,15 @@ const ServicePage = () => {
setLoading(false);
}
};
+
+ useEffect(() => {
+ setPagination((prev) => ({ ...prev, current: 1 }));
+ fetchServices(1, pagination.pageSize);
+ }, [selectedType]);
+
useEffect(() => {
fetchUnits();
}, []);
- useEffect(() => {
- fetchServices();
- }, [selectedType]);
const fetchUnits = async () => {
setloadingUnits(true);
@@ -854,7 +872,7 @@ const ServicePage = () => {
{
rowExpandable: (record) => record.attributes.sections?.length > 0,
}}
pagination={{
- pageSize: 10,
- showTotal: (total) => `共 ${total} 条`,
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ onChange: (page, pageSize) => {
+ // 页码或页大小改变时触发
+ if (pageSize !== pagination.pageSize) {
+ // 如果是页大小改变,重置到第一页
+ fetchServices(1, pageSize);
+ } else {
+ fetchServices(page, pageSize);
+ }
+ },
}}
className="rounded-lg"
/>
diff --git a/src/pages/company/service/itemsManange/classify/index.jsx b/src/pages/company/service/itemsManange/classify/index.jsx
index fed3d4b..07d6b56 100644
--- a/src/pages/company/service/itemsManange/classify/index.jsx
+++ b/src/pages/company/service/itemsManange/classify/index.jsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
-import { Table, Button, Form, Input, Space, message, Popconfirm, Drawer, Select, Segmented, Badge } from 'antd';
-import { PlusOutlined, FileTextOutlined, ProjectOutlined, CheckSquareOutlined } from '@ant-design/icons';
+import { Table, Button, Form, Input, Space, message, Popconfirm, Select, Segmented } from 'antd';
+import { PlusOutlined } from '@ant-design/icons';
import { supabaseService } from '@/hooks/supabaseService';
const Classify = ({activeType,typeList}) => {
const [data, setData] = useState([]);
@@ -8,8 +8,13 @@ const Classify = ({activeType,typeList}) => {
const [editingKey, setEditingKey] = useState('');
const [form] = Form.useForm();
const [filterType, setFilterType] = useState('all'); // 'all', 'common', 'current'
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ });
- const fetchCategories = async (type = activeType, filterTypeValue = filterType) => {
+ const fetchCategories = async (type = activeType, filterTypeValue = filterType, page = pagination.current, pageSize = pagination.pageSize) => {
setLoading(true);
try {
let filterCondition;
@@ -25,7 +30,7 @@ const Classify = ({activeType,typeList}) => {
filterCondition = { in: `(${type},common)` };
}
- const { data: categories } = await supabaseService.select('resources', {
+ const { data: categories, total } = await supabaseService.select('resources', {
filter: {
'type': { eq: 'categories' },
'attributes->>template_type': filterCondition
@@ -33,10 +38,18 @@ const Classify = ({activeType,typeList}) => {
order: {
column: 'created_at',
ascending: false
- }
+ },
+ page,
+ pageSize
});
setData(categories || []);
+ setPagination(prev => ({
+ ...prev,
+ current: page,
+ pageSize,
+ total
+ }));
} catch (error) {
message.error('获取分类数据失败');
console.error(error);
@@ -46,8 +59,9 @@ const Classify = ({activeType,typeList}) => {
};
useEffect(() => {
- fetchCategories(activeType, filterType);
- }, [activeType]);
+ setPagination(prev => ({ ...prev, current: 1 }));
+ fetchCategories(activeType, filterType, 1, pagination.pageSize);
+ }, [activeType, filterType]);
// 新增分类
@@ -273,9 +287,17 @@ const Classify = ({activeType,typeList}) => {
rowKey="id"
loading={loading}
pagination={{
- pageSize: 10,
- showTotal: (total) => `共 ${total} 条`,
- className: "px-4"
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ onChange: (page, pageSize) => {
+ if (pageSize !== pagination.pageSize) {
+ fetchCategories(activeType, filterType, 1, pageSize);
+ } else {
+ fetchCategories(activeType, filterType, page, pageSize);
+ }
+ }
}}
className="rounded-lg"
/>
diff --git a/src/pages/company/service/itemsManange/sections/index.jsx b/src/pages/company/service/itemsManange/sections/index.jsx
index 9ba9f5a..f1a3a8b 100644
--- a/src/pages/company/service/itemsManange/sections/index.jsx
+++ b/src/pages/company/service/itemsManange/sections/index.jsx
@@ -9,8 +9,10 @@ export default function SectionComponent({ activeType }) {
return ;
case "task":
return ;
+ case "project":
+ return
default:
- return ;
+ return 暂无数据
}
};
return <>{renderFn(activeType)}>;
diff --git a/src/pages/company/service/itemsManange/sections/project.jsx b/src/pages/company/service/itemsManange/sections/project.jsx
index 7133884..658f392 100644
--- a/src/pages/company/service/itemsManange/sections/project.jsx
+++ b/src/pages/company/service/itemsManange/sections/project.jsx
@@ -24,7 +24,6 @@ import { supabaseService } from '@/hooks/supabaseService';
import { v4 as uuidv4 } from 'uuid';
import { supabase } from "@/config/supabase";
-const { Text } = Typography;
const TYPE = 'project';
const ProjectSections = () => {
@@ -37,11 +36,15 @@ const ProjectSections = () => {
const [formValues, setFormValues] = useState({});
const [uploadModalVisible, setUploadModalVisible] = useState(false);
const [currentAddItem, setCurrentAddItem] = useState(null);
-
- const fetchSections = async () => {
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ });
+ const fetchSections = async (page = pagination.current, pageSize = pagination.pageSize) => {
setLoading(true);
try {
- const { data: sections } = await supabaseService.select('resources', {
+ const { data: sections, total } = await supabaseService.select('resources', {
filter: {
'type': { eq: 'sections' },
'attributes->>template_type': { eq: TYPE }
@@ -49,9 +52,18 @@ const ProjectSections = () => {
order: {
column: 'created_at',
ascending: false
- }
+ },
+ page,
+ pageSize
});
+
setData(sections || []);
+ setPagination(prev => ({
+ ...prev,
+ current: page,
+ pageSize,
+ total
+ }));
} catch (error) {
message.error('获取模块数据失败');
console.error(error);
@@ -83,9 +95,9 @@ const ProjectSections = () => {
};
useEffect(() => {
- fetchSections();
+ fetchSections(1, pagination.pageSize);
fetchUnits();
- }, []);
+ }, [TYPE]);
const handleAdd = () => {
const newData = {
@@ -151,7 +163,7 @@ const ProjectSections = () => {
message.success('保存成功');
setEditingKey('');
- fetchSections();
+ fetchSections(pagination.current, pagination.pageSize);
} catch (error) {
message.error('保存失败');
console.error(error);
@@ -162,7 +174,11 @@ const ProjectSections = () => {
try {
await supabaseService.delete('resources', { id: record.id });
message.success('删除成功');
- fetchSections();
+ if (data.length === 1 && pagination.current > 1) {
+ fetchSections(pagination.current - 1, pagination.pageSize);
+ } else {
+ fetchSections(pagination.current, pagination.pageSize);
+ }
} catch (error) {
message.error('删除失败');
console.error(error);
@@ -528,9 +544,19 @@ const ProjectSections = () => {
rowKey="id"
loading={loading}
pagination={{
- pageSize: 10,
- showTotal: (total) => `共 ${total} 条`,
- className: "px-4"
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ pageSizeOptions: ['10', '20', '50', '100'],
+ onChange: (page, pageSize) => {
+ if (pageSize !== pagination.pageSize) {
+ setPagination(prev => ({ ...prev, pageSize }));
+ fetchSections(1, pageSize);
+ } else {
+ fetchSections(page, pageSize);
+ }
+ }
}}
className="rounded-lg"
/>
diff --git a/src/pages/company/service/itemsManange/sections/quotation.jsx b/src/pages/company/service/itemsManange/sections/quotation.jsx
index 19e4ca4..cf89d16 100644
--- a/src/pages/company/service/itemsManange/sections/quotation.jsx
+++ b/src/pages/company/service/itemsManange/sections/quotation.jsx
@@ -27,22 +27,34 @@ const SectionsManagement = () => {
const [loadingUnits,setLoadingUnits]=useState(false)
const [units,setUnit]=useState([])
const [formValues, setFormValues] = useState({});
- const fetchSections = async () => {
+const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+});
+ const fetchSections = async (page = pagination.current, pageSize = pagination.pageSize) => {
setLoading(true);
try {
-
- const { data: sections } = await supabaseService.select('resources', {
+ const { data: sections, total } = await supabaseService.select('resources', {
filter: {
'type': { eq: 'sections' },
- 'attributes->>template_type': {eq:TYPE}
+ 'attributes->>template_type': { eq: TYPE }
},
order: {
column: 'created_at',
ascending: false
- }
+ },
+ page,
+ pageSize
});
-
+
setData(sections || []);
+ setPagination(prev => ({
+ ...prev,
+ current: page,
+ pageSize,
+ total
+ }));
} catch (error) {
message.error('获取模块数据失败');
console.error(error);
@@ -72,8 +84,7 @@ const [formValues, setFormValues] = useState({});
}
};
useEffect(() => {
- fetchSections();
-
+ fetchSections(1, pagination.pageSize);
}, [TYPE]);
const handleAdd = () => {
@@ -115,7 +126,7 @@ const [formValues, setFormValues] = useState({});
attributes: {
name: values.name,
template_type: TYPE,
- items: items.filter(item => item.name), // 只保存有名称的项目
+ items: items.filter(item => item.name),
},
schema_version: 1
});
@@ -135,7 +146,7 @@ const [formValues, setFormValues] = useState({});
message.success('保存成功');
setEditingKey('');
- fetchSections();
+ fetchSections(pagination.current, pagination.pageSize);
} catch (error) {
message.error('保存失败');
console.error(error);
@@ -146,7 +157,11 @@ const [formValues, setFormValues] = useState({});
try {
await supabaseService.delete('resources', { id: record.id });
message.success('删除成功');
- fetchSections();
+ if (data.length === 1 && pagination.current > 1) {
+ fetchSections(pagination.current - 1, pagination.pageSize);
+ } else {
+ fetchSections(pagination.current, pagination.pageSize);
+ }
} catch (error) {
message.error('删除失败');
console.error(error);
@@ -450,9 +465,17 @@ const [formValues, setFormValues] = useState({});
rowKey="id"
loading={loading}
pagination={{
- pageSize: 10,
- showTotal: (total) => `共 ${total} 条`,
- className: "px-4"
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ onChange: (page, pageSize) => {
+ if (pageSize !== pagination.pageSize) {
+ fetchSections(1, pageSize);
+ } else {
+ fetchSections(page, pageSize);
+ }
+ }
}}
className="rounded-lg"
/>
diff --git a/src/pages/company/service/itemsManange/sections/task.jsx b/src/pages/company/service/itemsManange/sections/task.jsx
index 4d2387f..af4565c 100644
--- a/src/pages/company/service/itemsManange/sections/task.jsx
+++ b/src/pages/company/service/itemsManange/sections/task.jsx
@@ -18,7 +18,6 @@ import { supabaseService } from '@/hooks/supabaseService';
import { v4 as uuidv4 } from 'uuid';
import dayjs from 'dayjs';
-const { Text } = Typography;
const TYPE = 'task';
const TaskSections = () => {
@@ -29,11 +28,16 @@ const TaskSections = () => {
const [loadingUnits, setLoadingUnits] = useState(false);
const [units, setUnit] = useState([]);
const [formValues, setFormValues] = useState({});
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ });
- const fetchSections = async () => {
+ const fetchSections = async (page = pagination.current, pageSize = pagination.pageSize) => {
setLoading(true);
try {
- const { data: sections } = await supabaseService.select('resources', {
+ const { data: sections, total } = await supabaseService.select('resources', {
filter: {
'type': { eq: 'sections' },
'attributes->>template_type': { eq: TYPE }
@@ -41,9 +45,18 @@ const TaskSections = () => {
order: {
column: 'created_at',
ascending: false
- }
+ },
+ page,
+ pageSize
});
+
setData(sections || []);
+ setPagination(prev => ({
+ ...prev,
+ current: page,
+ pageSize,
+ total
+ }));
} catch (error) {
message.error('获取模块数据失败');
console.error(error);
@@ -75,9 +88,9 @@ const TaskSections = () => {
};
useEffect(() => {
- fetchSections();
+ fetchSections(1, pagination.pageSize);
fetchUnits();
- }, []);
+ }, [TYPE]);
const handleAdd = () => {
const newData = {
@@ -144,7 +157,7 @@ const TaskSections = () => {
message.success('保存成功');
setEditingKey('');
- fetchSections();
+ fetchSections(pagination.current, pagination.pageSize);
} catch (error) {
message.error('保存失败');
console.error(error);
@@ -155,7 +168,11 @@ const TaskSections = () => {
try {
await supabaseService.delete('resources', { id: record.id });
message.success('删除成功');
- fetchSections();
+ if (data.length === 1 && pagination.current > 1) {
+ fetchSections(pagination.current - 1, pagination.pageSize);
+ } else {
+ fetchSections(pagination.current, pagination.pageSize);
+ }
} catch (error) {
message.error('删除失败');
console.error(error);
@@ -440,9 +457,19 @@ const TaskSections = () => {
rowKey="id"
loading={loading}
pagination={{
- pageSize: 10,
- showTotal: (total) => `共 ${total} 条`,
- className: "px-4"
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ pageSizeOptions: ['10', '20', '50', '100'],
+ onChange: (page, pageSize) => {
+ if (pageSize !== pagination.pageSize) {
+ setPagination(prev => ({ ...prev, pageSize }));
+ fetchSections(1, pageSize);
+ } else {
+ fetchSections(page, pageSize);
+ }
+ }
}}
className="rounded-lg"
/>
diff --git a/src/pages/company/service/itemsManange/unit/index.jsx b/src/pages/company/service/itemsManange/unit/index.jsx
index fd3e3fc..d6d6bf3 100644
--- a/src/pages/company/service/itemsManange/unit/index.jsx
+++ b/src/pages/company/service/itemsManange/unit/index.jsx
@@ -9,8 +9,13 @@ const UnitManagement = ({ activeType, typeList }) => {
const [editingKey, setEditingKey] = useState('');
const [form] = Form.useForm();
const [filterType, setFilterType] = useState('all');
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ });
- const fetchUnits = async (type = activeType, filterTypeValue = filterType) => {
+ const fetchUnits = async (type = activeType, filterTypeValue = filterType, page = pagination.current, pageSize = pagination.pageSize) => {
setLoading(true);
try {
let filterCondition;
@@ -26,7 +31,7 @@ const UnitManagement = ({ activeType, typeList }) => {
filterCondition = { in: `(${type},common)` };
}
- const { data: units } = await supabaseService.select('resources', {
+ const { data: units, total } = await supabaseService.select('resources', {
filter: {
'type': { eq: 'units' },
'attributes->>template_type': filterCondition
@@ -34,10 +39,18 @@ const UnitManagement = ({ activeType, typeList }) => {
order: {
column: 'created_at',
ascending: false
- }
+ },
+ page,
+ pageSize
});
setData(units || []);
+ setPagination(prev => ({
+ ...prev,
+ current: page,
+ pageSize,
+ total
+ }));
} catch (error) {
message.error('获取单位数据失败');
console.error(error);
@@ -47,8 +60,9 @@ const UnitManagement = ({ activeType, typeList }) => {
};
useEffect(() => {
- fetchUnits(activeType, filterType);
- }, [activeType]);
+ setPagination(prev => ({ ...prev, current: 1 }));
+ fetchUnits(activeType, filterType, 1, pagination.pageSize);
+ }, [activeType, filterType]);
const handleAdd = () => {
const newData = {
@@ -269,9 +283,17 @@ const UnitManagement = ({ activeType, typeList }) => {
rowKey="id"
loading={loading}
pagination={{
- pageSize: 10,
- showTotal: (total) => `共 ${total} 条`,
- className: "px-4"
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ onChange: (page, pageSize) => {
+ if (pageSize !== pagination.pageSize) {
+ fetchUnits(activeType, filterType, 1, pageSize);
+ } else {
+ fetchUnits(activeType, filterType, page, pageSize);
+ }
+ }
}}
className="rounded-lg"
/>
diff --git a/src/pages/resource/menu/index.jsx b/src/pages/resource/menu/index.jsx
index 793ae50..2dd53bb 100644
--- a/src/pages/resource/menu/index.jsx
+++ b/src/pages/resource/menu/index.jsx
@@ -13,7 +13,7 @@ import {
TreeSelect
} from 'antd';
import { getAllRouteOptions, allRoutes } from '@/routes/routes';
-import { supabase } from '@/config/supabase';
+import { supabaseService } from '@/hooks/supabaseService';
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
const { Title } = Typography;
@@ -25,7 +25,12 @@ export default function MenuManagement() {
const [menuList, setMenuList] = useState([]);
const [form] = Form.useForm();
const [roles, setRoles] = useState([]);
-
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ });
+
// 获取所有路由选项
const routeOptions = getAllRouteOptions();
@@ -81,19 +86,31 @@ export default function MenuManagement() {
];
// 获取菜单数据
- const fetchMenuList = async () => {
+ const fetchMenuList = async (page = pagination.current, pageSize = pagination.pageSize) => {
+ setLoading(true);
try {
- setLoading(true);
- const { data, error } = await supabase
- .from('resources')
- .select('*')
- .eq('type', TYPE)
- .order('created_at', { ascending: false });
-
- if (error) throw error;
- setMenuList(data);
+ const { data: menus, total } = await supabaseService.select('resources', {
+ filter: {
+ 'type': { eq: TYPE }
+ },
+ order: {
+ column: 'created_at',
+ ascending: false
+ },
+ page,
+ pageSize
+ });
+
+ setMenuList(menus || []);
+ setPagination(prev => ({
+ ...prev,
+ current: page,
+ pageSize,
+ total
+ }));
} catch (error) {
- message.error('获取数据失败:' + error.message);
+ message.error('获取菜单数据失败');
+ console.error(error);
} finally {
setLoading(false);
}
@@ -102,12 +119,12 @@ export default function MenuManagement() {
// 获取角色列表
const fetchRoles = async () => {
try {
- const { data, error } = await supabase
- .from('roles')
- .select('*')
- .order('name');
-
- if (error) throw error;
+ const { data } = await supabaseService.select('roles', {
+ order: {
+ column: 'name',
+ ascending: true
+ }
+ });
setRoles(data || []);
} catch (error) {
message.error('获取角色数据失败');
@@ -128,27 +145,20 @@ export default function MenuManagement() {
}
};
- let result;
if (form.getFieldValue('id')) {
- result = await supabase
- .from('resources')
- .update(menuData)
- .eq('id', form.getFieldValue('id'))
- .select();
+ await supabaseService.update('resources',
+ { id: form.getFieldValue('id') },
+ menuData
+ );
} else {
- result = await supabase
- .from('resources')
- .insert([menuData])
- .select();
+ await supabaseService.insert('resources', menuData);
}
- if (result.error) throw result.error;
-
message.success('保存成功');
setIsModalVisible(false);
- fetchMenuList();
+ fetchMenuList(pagination.current, pagination.pageSize);
} catch (error) {
- message.error('保存失败:' + error.message);
+ message.error('保存失败:' + (error.message || '未知错误'));
} finally {
setLoading(false);
}
@@ -169,19 +179,17 @@ export default function MenuManagement() {
const handleDelete = async (id) => {
try {
setLoading(true);
- const {data, error } = await supabase
- .from('resources')
- .delete()
- .eq('id', id);
- if (error) throw error;
- if(data.length>0){
- message.success('删除成功');
- fetchMenuList();
- }else{
- message.error('删除失败');
+ await supabaseService.delete('resources', { id });
+ message.success('删除成功');
+
+ // 如果当前页只有一条数据且不是第一页,删除后自动跳转到上一页
+ if (menuList.length === 1 && pagination.current > 1) {
+ fetchMenuList(pagination.current - 1, pagination.pageSize);
+ } else {
+ fetchMenuList(pagination.current, pagination.pageSize);
}
} catch (error) {
- message.error('删除失败:' + error.message);
+ message.error('删除失败:' + (error.message || '未知错误'));
} finally {
setLoading(false);
}
@@ -197,7 +205,7 @@ export default function MenuManagement() {
};
useEffect(() => {
- fetchMenuList();
+ fetchMenuList(1, pagination.pageSize);
fetchRoles();
}, []);
@@ -222,6 +230,23 @@ export default function MenuManagement() {
columns={columns}
dataSource={menuList}
loading={loading}
+ pagination={{
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ pageSizeOptions: ['10', '20', '50', '100'],
+ onChange: (page, pageSize) => {
+ if (pageSize !== pagination.pageSize) {
+ // 如果是改变每页条数,重置到第一页
+ setPagination(prev => ({ ...prev, pageSize }));
+ fetchMenuList(1, pageSize);
+ } else {
+ // 如果是改变页码,获取对应页数据
+ fetchMenuList(page, pageSize);
+ }
+ }
+ }}
rowKey="id"
/>
diff --git a/src/pages/resource/role/index.jsx b/src/pages/resource/role/index.jsx
index e3342f2..1a4870f 100644
--- a/src/pages/resource/role/index.jsx
+++ b/src/pages/resource/role/index.jsx
@@ -52,7 +52,6 @@ export default function PermissionManagement() {
resources:resource_id(*)
`, { count: 'exact' });
- // 添加排序
if (params.field && params.order) {
const ascending = params.order === 'ascend';
query = query.order(params.field, { ascending });
@@ -544,7 +543,16 @@ export default function PermissionManagement() {
dataSource={permissions}
rowKey="id"
loading={loading}
- pagination={pagination}
+ pagination={{
+ ...pagination,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total) => `共 ${total} 条记录`,
+ pageSizeOptions: ['10', '20', '50', '100'],
+ onChange: (page, pageSize) => {
+ setPagination(prev => ({ ...prev,page, pageSize }));
+ }
+ }}
onChange={handleTableChange}
className='w-full'
/>
diff --git a/src/pages/resource/team/components/TeamTable.jsx b/src/pages/resource/team/components/TeamTable.jsx
index 8718b85..bc9f3cf 100644
--- a/src/pages/resource/team/components/TeamTable.jsx
+++ b/src/pages/resource/team/components/TeamTable.jsx
@@ -3,7 +3,7 @@ import { Table, Form, Button, Space, Popconfirm, Tag, message } from 'antd';
import { EditOutlined, DeleteOutlined, SaveOutlined, CloseOutlined } from '@ant-design/icons';
import { EditableCell } from './EditableCell';
import { ExpandedMemberships } from './ExpandedMemberships';
-export const TeamTable = ({ tableLoading,pagination,dataSource, onTableChange,onDelete ,onUpdate}) => {
+export const TeamTable = ({ tableLoading,pagination,dataSource,setPagination, onTableChange,onDelete ,onUpdate}) => {
const [form] = Form.useForm();
const [editingKey, setEditingKey] = useState('');
const [loading, setLoading] = useState(false);
@@ -182,7 +182,6 @@ export const TeamTable = ({ tableLoading,pagination,dataSource, onTableChange,on