Merge branch 'main' of github.com:xuqssq/uppmkt-admin
This commit is contained in:
@@ -42,12 +42,35 @@ export const useTeams = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 创建团队前检查类型是否已存在
|
||||||
|
const checkTeamTypeExists = async (type) => {
|
||||||
|
try {
|
||||||
|
const {data} = await supabaseService.select('teams', {
|
||||||
|
filter: {
|
||||||
|
deleted_at: { is: null },
|
||||||
|
'attributes->>type': { eq: type }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data.length > 0;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查团队类型失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 创建团队
|
// 创建团队
|
||||||
const createTeam = async (values) => {
|
const createTeam = async (values) => {
|
||||||
try {
|
try {
|
||||||
|
if (values.type) {
|
||||||
|
const result = await checkTeamTypeExists(values.type);
|
||||||
|
if (result) {
|
||||||
|
throw new Error(`团队类型 "${values.type}" 已存在,请使用其他类型名称`);
|
||||||
|
}
|
||||||
|
}
|
||||||
const newTeam = await supabaseService.insert('teams', {
|
const newTeam = await supabaseService.insert('teams', {
|
||||||
name: values.name,
|
name: values.name,
|
||||||
description: values.description
|
description: values.description,
|
||||||
|
attributes: { type: values.type }
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建团队成员关系
|
// 创建团队成员关系
|
||||||
|
|||||||
@@ -41,7 +41,12 @@ export const TeamForm = ({ form }) => {
|
|||||||
>
|
>
|
||||||
<Input placeholder="请输入团队名称" />
|
<Input placeholder="请输入团队名称" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="type"
|
||||||
|
label="项目归属"
|
||||||
|
>
|
||||||
|
<Input placeholder="请输入项目归属" />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="avatarUrl"
|
name="avatarUrl"
|
||||||
label="团队头像"
|
label="团队头像"
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { Modal,Button, Form, Input, Select, message } from 'antd';
|
import { Modal,Button, Form, Input, Select, message } from 'antd';
|
||||||
import { MembershipTable } from './MembershipTable';
|
import { MembershipTable } from './MembershipTable';
|
||||||
import { supabaseService } from '@/hooks/supabaseService';
|
import { supabaseService } from '@/hooks/supabaseService';
|
||||||
|
import { supabase } from '@/config/supabase';
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
export const ExpandedMemberships = ({ teamId }) => {
|
export const ExpandedMemberships = ({ teamId }) => {
|
||||||
@@ -69,17 +70,26 @@ export const ExpandedMemberships = ({ teamId }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = (value) => {
|
||||||
|
handleModalOk(value)
|
||||||
setIsModalVisible(true);
|
setIsModalVisible(true);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalOk = async () => {
|
const handleModalOk = async (values) => {
|
||||||
try {
|
try {
|
||||||
const values = await form.validateFields();
|
const { data: users, error: userError } = await supabase
|
||||||
|
.from('users')
|
||||||
|
.select('id, email')
|
||||||
|
.eq('email', values.email)
|
||||||
|
.single();
|
||||||
|
if (userError || users.length===0) {
|
||||||
|
throw new Error('未找到该用户');
|
||||||
|
}
|
||||||
await supabaseService.insert('team_membership', {
|
await supabaseService.insert('team_membership', {
|
||||||
|
id:uuidv4(),
|
||||||
team_id: teamId,
|
team_id: teamId,
|
||||||
user_id: values.user_id,
|
user_id: users.id,
|
||||||
role: values.role,
|
role: values.role,
|
||||||
is_creator: false
|
is_creator: false
|
||||||
});
|
});
|
||||||
@@ -88,7 +98,7 @@ export const ExpandedMemberships = ({ teamId }) => {
|
|||||||
message.success('成员已添加');
|
message.success('成员已添加');
|
||||||
await loadMemberships();
|
await loadMemberships();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('添加成员失败');
|
message.error(error.message || '添加成员失败');
|
||||||
console.error('Add failed:', error);
|
console.error('Add failed:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Table, Button, Space, Popconfirm, Tag, Form } from 'antd';
|
import { Table, Button, Space, Popconfirm, Tag, Form, Modal, Input, Select } from 'antd';
|
||||||
import { EditOutlined, DeleteOutlined, SaveOutlined, CloseOutlined, PlusOutlined } from '@ant-design/icons';
|
import { EditOutlined, DeleteOutlined, SaveOutlined, CloseOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
import { EditableMembershipCell } from './EditableMembershipCell';
|
import { EditableMembershipCell } from './EditableMembershipCell';
|
||||||
import { roleColors } from '../constants/teamConstants';
|
import { roleColors } from '../constants/teamConstants';
|
||||||
|
import { message } from 'antd';
|
||||||
|
|
||||||
export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [editingKey, setEditingKey] = useState('');
|
const [editingKey, setEditingKey] = useState('');
|
||||||
|
const [addModalVisible, setAddModalVisible] = useState(false);
|
||||||
|
|
||||||
const isEditing = (record) => record.id === editingKey;
|
const isEditing = (record) => record.id === editingKey;
|
||||||
|
|
||||||
@@ -38,7 +40,6 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
|||||||
render: (user) => (
|
render: (user) => (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="font-medium">{user?.email}</span>
|
<span className="font-medium">{user?.email}</span>
|
||||||
<span className="text-gray-500 text-sm">{user?.email}</span>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -137,16 +138,71 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const AddMemberModal = ({ visible, onCancel, onAdd }) => {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const values = await form.validateFields();
|
||||||
|
await onAdd(values);
|
||||||
|
form.resetFields();
|
||||||
|
onCancel();
|
||||||
|
} catch (error) {
|
||||||
|
message.error('添加失败: ' + error.message);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="添加成员"
|
||||||
|
open={visible}
|
||||||
|
onCancel={onCancel}
|
||||||
|
onOk={handleSubmit}
|
||||||
|
confirmLoading={loading}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="email"
|
||||||
|
label="邮箱"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入邮箱' },
|
||||||
|
{ type: 'email', message: '请输入有效的邮箱地址' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder='请输入邮箱' />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="role"
|
||||||
|
label="角色"
|
||||||
|
rules={[{ required: true, message: '请选择角色' }]}
|
||||||
|
initialValue="MEMBER"
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
<Select.Option value="OWNER">管理员</Select.Option>
|
||||||
|
<Select.Option value="MEMBER">成员</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* <Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={onAdd}
|
onClick={() => setAddModalVisible(true)}
|
||||||
className="mb-4"
|
className="mb-4"
|
||||||
>
|
>
|
||||||
添加成员
|
添加成员
|
||||||
</Button> */}
|
</Button>
|
||||||
<Form form={form} component={false}>
|
<Form form={form} component={false}>
|
||||||
<Table
|
<Table
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
@@ -161,6 +217,12 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
|
|||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
|
<AddMemberModal
|
||||||
|
visible={addModalVisible}
|
||||||
|
onCancel={() => setAddModalVisible(false)}
|
||||||
|
onAdd={onAdd}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -48,7 +48,7 @@ export const TeamForm = ({ form }) => {
|
|||||||
>
|
>
|
||||||
<Input placeholder="请输入团队名称" />
|
<Input placeholder="请输入团队名称" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="avatarUrl"
|
name="avatarUrl"
|
||||||
label="团队头像"
|
label="团队头像"
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ export const TeamTable = ({ tableLoading,pagination,dataSource, onTableChange,on
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '归属',
|
||||||
|
dataIndex: 'type',
|
||||||
|
dataIndex: ["attributes", "type"],
|
||||||
|
key: "type",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '描述',
|
title: '描述',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ const TeamManagement = () => {
|
|||||||
await loadTeams({ current: 1 });
|
await loadTeams({ current: 1 });
|
||||||
message.success('创建团队成功');
|
message.success('创建团队成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('创建团队失败');
|
message.error(error.message);
|
||||||
} finally {
|
} finally {
|
||||||
setConfirmLoading(false);
|
setConfirmLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user