Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI逻辑优化,数据库改动 #32

Merged
merged 1 commit into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion backend/SqliteUtil.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def createTables():
ItemLevel TEXT,
create_time TIMESTAMP NOT NULL DEFAULT (datetime('now','localtime')),
modify_time TIMESTAMP NOT NULL DEFAULT (datetime('now','localtime')),
Status TEXT DEFAULT '未开始',
FOREIGN KEY (UserID) REFERENCES users (id),
FOREIGN KEY (ActivityID) REFERENCES TodoActivity (ActivityID)
)
Expand Down Expand Up @@ -459,7 +460,8 @@ def getTodoItemsFromData(dataList):
"ActivityID": itemArray[1],
"UserID": itemArray[2],
"ItemContent": itemArray[3],
"ItemLevel": itemArray[4]
"ItemLevel": itemArray[4],
"ItemStatus": itemArray[-1]
}
items.append(item)
return items
Expand Down
21 changes: 20 additions & 1 deletion backend/run_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,31 @@ def deleteMember():
memberID = data['memberID']
captainID = Team.get_team_captain(teamID)[0]
if captainID != userID:
return jsonify({"data": "You are not the captain", "status": 400}), 400
return jsonify({"data": "非队长无权限删除", "status": 400}), 400
if memberID == captainID:
return jsonify({"data": "队长不能被删除", "status": 400}), 400

result = Team.deleteTeamMember(teamID, memberID)
if result is False:
return jsonify({"data": "Failed to delete member", "status": 500}), 500
return jsonify({"data": "Success to delete member", "status": 200}), 200

# updateTeamName
@app.route(apiPrefix + 'updateTeamName', methods=['POST'])
def updateTeamName():
data = request.get_json()
teamID = data['teamID']
userID = data['userID']
userID = int(userID)
teamName = data['teamName']
captainID = Team.get_team_captain(teamID)[0]
if captainID != userID:
return jsonify({"data": "非队长无权限修改", "status": 400}), 400
result = Team.change_team_name(teamID, teamName)
if result is False:
return jsonify({"data": "Failed to update team name", "status": 500}), 500
return jsonify({"data": "Success to update team name", "status": 200}), 200


################## Note接口 ##################
@app.route(apiPrefix + 'updateNote', methods=['POST'])
Expand Down
11 changes: 11 additions & 0 deletions backend/sqlite_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,14 @@ def get_captain_teams(captain_id):
finally:
lock_threading.release()

def change_team_name(team_id, new_name):
try:
lock_threading.acquire()
cursor.execute('''UPDATE team SET name = ? WHERE id = ?''', (new_name, team_id))
conn.commit()
return True
except sqlite3.Error as e:
print(e)
return False
finally:
lock_threading.release()
2 changes: 1 addition & 1 deletion frontend/src/pages/AddItemDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const AddItemDialog: React.FC<AddItemDialogProps> = ({ visible, onAdd, onCancel,
return (
<Modal
title="添加事项"
visible={visible}
open={visible}
onOk={handleOk}
onCancel={() => {
form.resetFields();
Expand Down
12 changes: 7 additions & 5 deletions frontend/src/pages/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ interface inviteinfo {
user_id: number
}



const MainLayout: React.FC = () => {
const navigate = useNavigate();
const username = localStorage.getItem('username');
Expand Down Expand Up @@ -54,7 +56,7 @@ const MainLayout: React.FC = () => {

const newinvitations = response.data;
setInvitations(newinvitations);


if (newinvitations.length > 0) {
console.log('invitations:', newinvitations);
Expand All @@ -66,7 +68,7 @@ const MainLayout: React.FC = () => {
message: '团队邀请',
description: `你被邀请加入团队 ${team_id}`,
btn: (
<div>
<div>
<Button type='primary' onClick={() => acceptInvitation(team_id, captain_id, key)}>接受</Button>
<Button onClick={() => rejectInvitation(team_id, captain_id, key)} style={{ marginLeft: '8px' }}>拒绝</Button>
</div>
Expand Down Expand Up @@ -94,7 +96,7 @@ const MainLayout: React.FC = () => {
});
if (notificationKey !== '')
api.destroy(notificationKey); // Destroy the notification
setInvitations(invitations.filter(invitation => invitation.team_id !== teamId));
setInvitations(invitations.filter(invitation => invitation.team_id !== teamId));
} else {
api.error({
message: '接受邀请失败',
Expand All @@ -120,7 +122,7 @@ const MainLayout: React.FC = () => {
});
if (notificationKey !== '')
api.destroy(notificationKey); // Destroy the notification
setInvitations(invitations.filter(invitation => invitation.team_id !== teamId));
setInvitations(invitations.filter(invitation => invitation.team_id !== teamId));
} else {
api.error({
message: '拒绝邀请失败',
Expand Down Expand Up @@ -270,7 +272,7 @@ const MainLayout: React.FC = () => {
<Dropdown
overlay={
<Menu>
{invitations.length==0 ? <a>暂无通知</a>: invitations.map((invitation) => (
{invitations.length == 0 ? <a>暂无通知</a> : invitations.map((invitation) => (
<Menu.Item key={invitation.team_id}>
<div>
团队 {invitation.team_id} 的邀请
Expand Down
65 changes: 42 additions & 23 deletions frontend/src/pages/Reservation/ReservationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ interface Reservation {

interface Member {
member_id: number;
is_captain: string;
avatar: string;
is_captain: number;
}

interface Team {
Expand All @@ -36,7 +35,6 @@ interface Team {
members: Member[];
}


interface ReservationModalProps {
visible: boolean;
onOk: () => void;
Expand All @@ -59,6 +57,9 @@ const ReservationModal: React.FC<ReservationModalProps> = ({
const [sliderValue, setSliderValue] = useState<number[]>([8, 9]);
const [room, setRoom] = useState<number>(meetingRooms[0]?.id || 0);
const [bookedTimes, setBookedTimes] = useState<[number, number][]>([]);
const [teams, setTeams] = useState<Team[]>([]);
const [bookingType, setBookingType] = useState<string>('personal');


useEffect(() => {
form.setFieldsValue({
Expand Down Expand Up @@ -97,16 +98,11 @@ const ReservationModal: React.FC<ReservationModalProps> = ({
}
) as ApiResponse<Team[]>;
if (response.status === 200) {
const teamsWithAvatar = await Promise.all(
response.data.map(async (team) => {
const membersWithAvatar = await Promise.all(
team.members.map(async (member) => {
return { ...member };
})
);
return { ...team, members: membersWithAvatar };
})
);
const userID = localStorage.getItem('userID');
const filteredTeams = response.data.filter((team) => {
return team.members.some((member) => member.member_id.toString() === userID && member.is_captain === 1);
});
setTeams(filteredTeams);
} else {
message.error('获取团队列表失败');
}
Expand Down Expand Up @@ -159,6 +155,7 @@ const ReservationModal: React.FC<ReservationModalProps> = ({
end_time: moment().startOf('day').add(values.time[1], 'hours').format('HH:mm'),
date: values.date.format('YYYY-MM-DD'),
subject: values.title,
type: bookingType,
};
const response = await HttpUtil.post(ApiUtil.API_INSERT_RESERVATION, newReservation) as ApiResponse<any>;
if (response.status === 200) {
Expand All @@ -172,21 +169,44 @@ const ReservationModal: React.FC<ReservationModalProps> = ({
}
};

const handleBookingTypeChange = (value: string) => {
setBookingType(value);
if (value === 'team') {
fetchTeams();
}
};

return (
<Modal title="创建预约" open={visible} onOk={form.submit} onCancel={onCancel}>
<Form form={form} layout="vertical" onFinish={handleSubmit}>
<Form.Item name="title" label="会议主题" rules={[{ required: true, message: '请输入会议主题' }]}>
<Form form={form} layout="vertical" onFinish={handleSubmit}>
<Form.Item name="title" label="会议主题" rules={[{ required: true, message: '请输入会议主题' }]}>
<Input />
<Form.Item name="room_id" label="会议室" rules={[{ required: true, message: '请选择会议室' }]}>
<Select onChange={(value: number) => setRoom(value)} value={room}>
{meetingRooms.map(room => (
<Option key={room.id} value={room.id}>{room.name}</Option>
</Form.Item>
<Form.Item name="bookingType" label="预约类型" rules={[{ required: true, message: '请选择预约类型' }]}>
<Select onChange={handleBookingTypeChange}>
<Option value="personal">个人预约</Option>
<Option value="team">团队预约</Option>
</Select>
</Form.Item>
{bookingType === 'team' && (
<Form.Item name="teamId" label="选择团队" rules={[{ required: true, message: '请选择团队' }]}>
<Select>
{teams.map(team => (
<Option key={team.team_id} value={team.team_id}>{team.team_name}</Option>
))}
</Select>
</Form.Item>
<Form.Item name="date" label="日期" rules={[{ required: true, message: '请选择日期' }]}>
<DatePicker onChange={handleDateChange} />
</Form.Item>
)}
<Form.Item name="room_id" label="会议室" rules={[{ required: true, message: '请选择会议室' }]}>
<Select onChange={(value: number) => setRoom(value)} value={room}>
{meetingRooms.map(room => (
<Option key={room.id} value={room.id}>{room.name}</Option>
))}
</Select>
</Form.Item>
<Form.Item name="date" label="日期" rules={[{ required: true, message: '请选择日期' }]}>
<DatePicker onChange={handleDateChange} />
</Form.Item>
<Form.Item name="time" label="选择时间段" rules={[{ required: true, message: '请选择时间段' }]}>
<Slider
range
Expand All @@ -201,7 +221,6 @@ const ReservationModal: React.FC<ReservationModalProps> = ({
/>
</Form.Item>

</Form.Item>
</Form>
</Modal>
);
Expand Down
40 changes: 36 additions & 4 deletions frontend/src/pages/Team/TeamCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Card, Avatar, Button, message, Popconfirm, Tooltip } from 'antd';
import React, { useState } from 'react';
import { Card, Avatar, Button, message, Popconfirm, Tooltip, Input } from 'antd';
import { CloseOutlined, DeleteOutlined, UserAddOutlined, UserOutlined } from '@ant-design/icons';
import HttpUtil from '../../utils/HttpUtil';
import ApiUtil from '../../utils/ApiUtil';
Expand All @@ -17,14 +17,46 @@ interface TeamCardProps {
onAddMember: () => void;
onDeleteTeam: () => void;
onDeleteMember: (memberId: number) => void;
onUpdateTeamName: (teamId: number, newName: string) => void;
}

const TeamCard: React.FC<TeamCardProps> = ({ teamName, members, teamId, onAddMember, onDeleteTeam, onDeleteMember }) => {
const TeamCard: React.FC<TeamCardProps> = ({ teamName, members, teamId, onAddMember, onDeleteTeam, onDeleteMember, onUpdateTeamName}) => {
const [isEditing, setIsEditing] = useState(false);
const [editedTeamName, setEditedTeamName] = useState(teamName);

const sortedMembers = [...members].sort((a, b) => b.is_captain - a.is_captain);

const handleTeamNameDoubleClick = () => {
setIsEditing(true);
};

const handleTeamNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditedTeamName(e.target.value);
};

const handleTeamNameSave = () => {
onUpdateTeamName(teamId, editedTeamName);
setIsEditing(false);
};


return (
<Card
title={<span>{teamName} <span style={{ fontSize: 'small' }}>共 {members.length} 人</span></span>}
title={
isEditing ? (
<Input
value={editedTeamName}
onChange={handleTeamNameChange}
onPressEnter={handleTeamNameSave}
onBlur={handleTeamNameSave}
autoFocus
/>
) : (
<span onDoubleClick={handleTeamNameDoubleClick}>
{teamName} <span style={{ fontSize: 'small' }}>共 {members.length} 人</span>
</span>
)
}
bordered={true}
style={{ width: 400, height: 200 }}
actions={[
Expand Down
18 changes: 17 additions & 1 deletion frontend/src/pages/Team/TeamManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ const TeamManagement: React.FC = () => {
}
};

const updateTeamName = async (teamId: number, newName: string) => {
try {
const userId = localStorage.getItem('userID') || 1;
const response = await HttpUtil.post(ApiUtil.API_UPDATE_TEAM_NAME, { userID: userId, teamID: teamId, teamName: newName }) as ApiResponse<string>;
if (response.status === 200) {
message.success('更新团队名称成功');
fetchTeams();
} else {
message.error('更新团队名称失败');
}
} catch (error) {
message.error('更新团队名称失败');
}
};

const deleteTeamMember = async (teamId: number, memberId: number) => {
try {
const userId = localStorage.getItem('userID') || 1;
Expand All @@ -63,7 +78,7 @@ const TeamManagement: React.FC = () => {
message.success('删除团队成员成功');
fetchTeams();
} else if (response.status === 400) {
message.error('你没有权限删除该团队');
message.error(response.data);
} else {
message.error('删除团队失败');
}
Expand Down Expand Up @@ -210,6 +225,7 @@ const TeamManagement: React.FC = () => {
onAddMember={() => handleAddMember(team.team_id)}
onDeleteTeam={() => deleteTeam(team.team_id)}
onDeleteMember={(memberId) => deleteTeamMember(team.team_id, memberId)}
onUpdateTeamName={updateTeamName}
/>
</Col>
))}
Expand Down
Loading
Loading