Skip to content

Commit

Permalink
UI进一步优化,逻辑优化
Browse files Browse the repository at this point in the history
  • Loading branch information
Moujuruo committed Jun 19, 2024
1 parent 972979f commit 98de8c3
Show file tree
Hide file tree
Showing 9 changed files with 1,790 additions and 75 deletions.
5 changes: 4 additions & 1 deletion backend/SqliteUtil.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,13 @@ def insertOrUpdateTodoItem(item):
try:
lock_threading.acquire()
item = json.loads(item)
print(item)
# 把item的key ItemStatus 换成 Status
if 'ItemStatus' in item:
item['Status'] = item.pop('ItemStatus')
item_id = item.get('ItemID', 0)
ItemContent = item.get('ItemContent', '')
ItemLevel = item.get('ItemLevel', 0)
ItemStatus = item.get('ItemStatus', '未开始')
# UserID = int(item.get('UserID', 0))
print(item_id,ItemContent,ItemLevel)
if item_id == 0: # 新增
Expand Down
1,462 changes: 1,462 additions & 0 deletions frontend/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"homepage": "https://moujuruo.github.io/AI_Office/",
"proxy": "http://127.0.0.1:5001",
"dependencies": {
"@ant-design/charts": "^2.1.1",
"@ant-design/icons": "^5.3.7",
"@ant-design/pro-chat": "^1.13.9",
"@ant-design/pro-components": "^2.7.9",
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { ConfigProvider } from 'antd';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
// import from './pages/TodoList'
import TodoList from './pages/TodoList';
import TodoList from './pages/TodoList/TodoList';
import Login from './pages/Login/Login';
import Register from './pages/Login/Register';
import ProtectedRoute from './router/ProtectedRoute';
Expand All @@ -11,6 +11,7 @@ import Homepage from './pages/HomePage/HomePage';
import NoteList from './pages/NotePage';
import RoomBooking from './pages/Reservation/ReservationPage';
import TeamManagement from './pages/Team/TeamManagement';
import DataAnalysis from './pages/DataAnalysis/DataAnalysis';

const App: React.FC = () => {
return (
Expand All @@ -20,6 +21,7 @@ const App: React.FC = () => {
<Route path="/register" element={<Register />} />
<Route path="/" element={<ProtectedRoute><MainLayout /></ProtectedRoute>}>
<Route index element={<Homepage />} />
<Route path="data-analysis" element={<DataAnalysis />} />
<Route path="staff-list" element={<TodoList />} />
<Route path="notelist-page" element={<NoteList />} />
<Route path="reservation-page" element={<RoomBooking />} />
Expand Down
210 changes: 210 additions & 0 deletions frontend/src/pages/DataAnalysis/DataAnalysis.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { ProCard, StatisticCard } from '@ant-design/pro-components';
import RcResizeObserver from 'rc-resize-observer';
import React, { useEffect } from 'react';
import { useState } from 'react';
import dayjs from 'dayjs';
import { TodoItem, TodoActivity } from '../TodoList/TodoList';
import HttpUtil from '../../utils/HttpUtil';
import ApiUtil from '../../utils/ApiUtil';
import { message } from 'antd';
import { Pie } from '@ant-design/plots';

const { Statistic } = StatisticCard;

export default () => {
const [responsive, setResponsive] = useState(false);
const [activityListWithItems, setActivityListWithItems] = useState<TodoActivity[]>([]);

const Today: string = dayjs().format('YYYY年MM月DD日 dddd');


const getItemsByActivity = async (activityID: number) => {
try {
const response = await HttpUtil.get(`${ApiUtil.API_Item_LIST_BY_ACTIVITY}${activityID}`);
// console.log("getItemsByActivity:" + response);
return response as TodoItem[];
} catch (error: any) {
message.error(error);
return [];
}
};

const getData = async () => {
const response = await HttpUtil.get(ApiUtil.API_Activity_LIST + localStorage.getItem("userID"));
const activityList = response as TodoActivity[];
const activityListWithItems = await Promise.all(
activityList.map(async (activity) => {
const items = await getItemsByActivity(activity.ActivityID);
return { ...activity, items };
})
);
setActivityListWithItems(activityListWithItems);
};

useEffect(() => {
getData();
}, []);

console.log('Activity List With Items:', activityListWithItems);
// 计算今日 Activity 总数
const todayActivityCount = activityListWithItems.filter((activity) => {
const today = dayjs();
const startDate = dayjs(activity.ActivityBeginDate);
const endDate = dayjs(activity.ActivityEndDate);
return today.isSame(startDate, 'day') || today.isSame(endDate, 'day') || (today.isAfter(startDate) && today.isBefore(endDate));
}).length;

// 计算当月日平均 Activity 数
const currentMonthActivityCount = activityListWithItems.filter((activity) => {
const currentMonth = dayjs();
const startDate = dayjs(activity.ActivityBeginDate);
const endDate = dayjs(activity.ActivityEndDate);
return currentMonth.isSame(startDate, 'month') || currentMonth.isSame(endDate, 'month') || (currentMonth.isAfter(startDate) && currentMonth.isBefore(endDate));
}).length;
const currentMonthDays = dayjs().daysInMonth();
const averageActivityPerDay = currentMonthActivityCount / currentMonthDays;

// 计算今日 Item 总数
const todayItemCount = activityListWithItems
.filter((activity) => {
const today = dayjs();
const startDate = dayjs(activity.ActivityBeginDate);
const endDate = dayjs(activity.ActivityEndDate);
return today.isSame(startDate, 'day') || today.isSame(endDate, 'day') || (today.isAfter(startDate) && today.isBefore(endDate));
})
.reduce((total, activity) => {
const unfinishedItems = activity.items.filter((item) => item.ItemStatus === '未开始' || item.ItemStatus === '进行中');
return total + unfinishedItems.length;
}, 0);

// 计算当月日平均 Item 数
const currentMonthItemCount = activityListWithItems
.filter((activity) => {
const currentMonth = dayjs();
const startDate = dayjs(activity.ActivityBeginDate);
const endDate = dayjs(activity.ActivityEndDate);
return currentMonth.isSame(startDate, 'month') || currentMonth.isSame(endDate, 'month') || (currentMonth.isAfter(startDate) && currentMonth.isBefore(endDate));
})
.reduce((total, activity) => {
return total + activity.items.length;
}, 0);
console.log('Current Month Item Count:', currentMonthItemCount);
const averageItemPerDay = currentMonthItemCount / currentMonthDays;

const todayItemData = activityListWithItems
.filter((activity) => {
const today = dayjs();
const startDate = dayjs(activity.ActivityBeginDate);
const endDate = dayjs(activity.ActivityEndDate);
return today.isSame(startDate, 'day') || today.isSame(endDate, 'day') || (today.isAfter(startDate) && today.isBefore(endDate));
})
.reduce((data, activity) => {
activity.items.forEach((item) => {
const existingType = data.find((d) => d.type === item.ItemLevel);
if (existingType) {
existingType.value += 1;
} else {
data.push({ type: item.ItemLevel, value: 1 });
}
});
return data;
}, [] as { type: string; value: number }[]);

console.log('Today Item Data:', todayItemData);

// 饼图配置
const config = {
appendPadding: 10,
data: todayItemData,
angleField: 'value',
colorField: 'type',
radius: 0.8,
label: {
// type: 'outer',
content: '{name} {percentage}',
},
interactions: [{ type: 'pie-legend-active' }, { type: 'element-active' }],
};


return (
<RcResizeObserver
key="resize-observer"
onResize={(offset) => {
setResponsive(offset.width < 596);
}}
>
<ProCard
title="日程数据概览"
extra={Today}
split={responsive ? 'horizontal' : 'vertical'}
headerBordered
bordered
>
<ProCard split="horizontal">
<ProCard split="horizontal">
<ProCard split="vertical">
<StatisticCard
statistic={{
title: '今日活动总数',
value: todayActivityCount,
description: (
<Statistic
title="较当月日平均数"
value={`${((todayActivityCount - averageActivityPerDay) / averageActivityPerDay * 100).toFixed(2)}%`}
trend={todayActivityCount >= averageActivityPerDay ? 'up' : 'down'}
/>
),
}}
/>
<StatisticCard
statistic={{
title: '今日事项总数',
value: todayItemCount,
description: (
<Statistic
title="较当月日平均数"
value={`${((todayItemCount - averageItemPerDay) / averageItemPerDay * 100).toFixed(2)}%`}
trend={todayItemCount >= averageItemPerDay ? 'up' : 'down'}
/>
),
}}
/>
</ProCard>
<ProCard split="vertical">
<StatisticCard
statistic={{
title: '当月活动总数',
value: currentMonthActivityCount,
suffix: '个',
}}
/>
<StatisticCard
statistic={{
title: '当月事项总数',
value: currentMonthItemCount,
suffix: '个',
}}
/>
</ProCard>
</ProCard>


<StatisticCard
title="流量走势"
chart={
<img
src="https://gw.alipayobjects.com/zos/alicdn/_dZIob2NB/zhuzhuangtu.svg"
width="100%"
/>
}
/>
</ProCard>
<StatisticCard
title="今日事项类型分布"
chart={<Pie {...config} />}
/>
</ProCard>
</RcResizeObserver>
);
};
52 changes: 33 additions & 19 deletions frontend/src/pages/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useEffect, useState } from 'react';
import { ProLayout } from '@ant-design/pro-components';
import { MenuDataItem, ProLayout } from '@ant-design/pro-components';
import { Link, Outlet, useNavigate } from 'react-router-dom';
import { Layout, Button, Dropdown, Menu, message, Upload, notification, Badge } from 'antd';
import type { MenuProps } from 'antd';
import type { UploadRequestOption } from 'rc-upload/lib/interface';
import { UserOutlined, DownOutlined, SmileOutlined, UploadOutlined, BellOutlined } from '@ant-design/icons';
import { UserOutlined, DownOutlined, SmileOutlined, UploadOutlined, BellOutlined, HomeOutlined, AreaChartOutlined, CalendarOutlined, FileTextOutlined, TeamOutlined, ContainerOutlined } from '@ant-design/icons';
import ApiUtil from '../utils/ApiUtil';
import HttpUtil from '../utils/HttpUtil';
import { ApiResponse } from '../utils/ApiUtil';
Expand Down Expand Up @@ -236,12 +236,31 @@ const MainLayout: React.FC = () => {
},
]

const [collapsed, setCollapsed] = useState(false);

const menuData: MenuDataItem[] = [
{ path: "/", name: "🏠 首页", default: true, icon: <HomeOutlined /> },
{ path: "/data-analysis", name: "📊 数据分析", icon: <AreaChartOutlined /> },
{ path: "/staff-list", name: "📆 日程表", icon: <CalendarOutlined /> },
{ path: "/notelist-page", name: "📒 笔记备忘录", icon: <FileTextOutlined /> },
{ path: "/reservation-page", name: "🚪 会议室预定", icon: <ContainerOutlined /> },
{ path: "/team-page", name: "👨‍👩‍👧‍👦 团队管理", icon: <TeamOutlined /> },
];
const handleMenuDataRender = (menuData: MenuDataItem[]): MenuDataItem[] => {
return menuData.map(item => ({
...item,
icon: collapsed ? item.icon : null,
}));
};

return (
<ProLayout
title="智能办公管理系统"
logo={<div className="logo" />}
layout="mix"
navTheme="light"
inlineCollapsed={collapsed}
onCollapse={() => setCollapsed(!collapsed)}
token={{
header: {
heightLayoutHeader: 80, // 调整Header的高度
Expand Down Expand Up @@ -270,10 +289,12 @@ const MainLayout: React.FC = () => {
</div>
<div style={{ display: "flex", alignItems: "center" }}>
<Dropdown
overlay={
<Menu>
{invitations.length == 0 ? <a>暂无通知</a> : invitations.map((invitation) => (
<Menu.Item key={invitation.team_id}>
menu={{
items: invitations.length === 0
? [{ key: 'empty', label: '暂无通知' }]
: invitations.map((invitation) => ({
key: invitation.team_id,
label: (
<div>
团队 {invitation.team_id} 的邀请
<Button
Expand Down Expand Up @@ -301,11 +322,10 @@ const MainLayout: React.FC = () => {
拒绝
</Button>
</div>
</Menu.Item>
))}
</Menu>
}
trigger={["click"]}
),
})),
}}
trigger={['click']}
>
<Badge count={invitations.length} offset={[-25, -2]} size='small'>
<BellOutlined
Expand All @@ -330,7 +350,7 @@ const MainLayout: React.FC = () => {
/>
)}
<span>你好, {username}</span>
<Dropdown overlay={<Menu items={items} />} trigger={["click"]}>
<Dropdown menu={{ items }} trigger={["click"]}>
<a
onClick={(e) => e.preventDefault()}
style={{ marginLeft: "8px" }}
Expand All @@ -342,13 +362,7 @@ const MainLayout: React.FC = () => {
</Header>
)}
menuItemRender={(item, dom) => <Link to={item.path || "/"}>{dom}</Link>}
menuDataRender={() => [
{ path: "/", name: "🏠 首页", default: true },
{ path: "/staff-list", name: "📆 日程表" },
{ path: "/notelist-page", name: "📒 笔记备忘录" },
{ path: "/reservation-page", name: "🚪 会议室预定" },
{ path: "/team-page", name: "👨‍👩‍👧‍👦 团队管理" },
]}
menuDataRender={() => handleMenuDataRender(menuData)}
>
{contextHolder}
<Content style={{ padding: "0 24px 24px" }}>
Expand Down
Loading

0 comments on commit 98de8c3

Please sign in to comment.