Commit 2f4c0ca3 authored by wuhao's avatar wuhao 🎯

asd

parent cb66f174
Pipeline #3464 passed with stages
in 4 minutes and 2 seconds
......@@ -7,12 +7,14 @@ export default {
"DEFAULT_HEAD_IMG":"./assets/images/avatars/avatar_21.jpg",
"DEFAULT_404_IMG":"./assets/illustrations/illustration_404.svg",
"DOWNLOAD_URL":"http://192.168.40.2",
"SOCKET_IO_URL":""
},
test:{
"REACT_APP_URL":"/vstp",
"DEFAULT_HEAD_IMG":"./assets/images/avatars/avatar_21.jpg",
"DEFAULT_404_IMG":"./assets/illustrations/illustration_404.svg",
"DOWNLOAD_URL":"http://192.168.40.2",
"SOCKET_IO_URL":"http://localhost:7001"
},
prod:{
"REACT_APP_URL":"http://tasks.nangaoyun.com"
......
import PropTypes from 'prop-types';
import { set, sub } from 'date-fns';
import { noCase } from 'change-case';
import { faker } from '@faker-js/faker';
import { useState } from 'react';
// @mui
import {
Box,
List,
Badge,
Button,
Avatar,
Tooltip,
Divider,
Popover,
Typography,
IconButton,
ListItemText,
ListSubheader,
ListItemAvatar,
ListItemButton,
} from '@mui/material';
// utils
import { fToNow } from '../../../utils/formatTime';
// components
import Iconify from '../../../components/iconify';
import Scrollbar from '../../../components/scrollbar';
// ----------------------------------------------------------------------
const NOTIFICATIONS = [
{
id: faker.datatype.uuid(),
title: 'Your order is placed',
description: 'waiting for shipping',
avatar: null,
type: 'order_placed',
createdAt: set(new Date(), { hours: 10, minutes: 30 }),
isUnRead: true,
},
{
id: faker.datatype.uuid(),
title: faker.name.fullName(),
description: 'answered to your comment on the Minimal',
avatar: './assets/images/avatars/avatar_2.jpg',
type: 'friend_interactive',
createdAt: sub(new Date(), { hours: 3, minutes: 30 }),
isUnRead: true,
},
{
id: faker.datatype.uuid(),
title: 'You have new message',
description: '5 unread messages',
avatar: null,
type: 'chat_message',
createdAt: sub(new Date(), { days: 1, hours: 3, minutes: 30 }),
isUnRead: false,
},
{
id: faker.datatype.uuid(),
title: 'You have new mail',
description: 'sent from Guido Padberg',
avatar: null,
type: 'mail',
createdAt: sub(new Date(), { days: 2, hours: 3, minutes: 30 }),
isUnRead: false,
},
{
id: faker.datatype.uuid(),
title: 'Delivery processing',
description: 'Your order is being shipped',
avatar: null,
type: 'order_shipped',
createdAt: sub(new Date(), { days: 3, hours: 3, minutes: 30 }),
isUnRead: false,
},
];
export default function NotificationsPopover() {
const [notifications, setNotifications] = useState(NOTIFICATIONS);
const totalUnRead = notifications.filter((item) => item.isUnRead === true).length;
const [open, setOpen] = useState(null);
const handleOpen = (event) => {
setOpen(event.currentTarget);
};
const handleClose = () => {
setOpen(null);
};
const handleMarkAllAsRead = () => {
setNotifications(
notifications.map((notification) => ({
...notification,
isUnRead: false,
}))
);
};
return (
<>
<IconButton color={open ? 'primary' : 'default'} onClick={handleOpen} sx={{ width: 40, height: 40 }}>
<Badge badgeContent={totalUnRead} color="error">
<Iconify icon="eva:bell-fill" />
</Badge>
</IconButton>
<Popover
open={Boolean(open)}
anchorEl={open}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
PaperProps={{
sx: {
mt: 1.5,
ml: 0.75,
width: 360,
},
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', py: 2, px: 2.5 }}>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle1">Notifications</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
You have {totalUnRead} unread messages
</Typography>
</Box>
{totalUnRead > 0 && (
<Tooltip title=" Mark all as read">
<IconButton color="primary" onClick={handleMarkAllAsRead}>
<Iconify icon="eva:done-all-fill" />
</IconButton>
</Tooltip>
)}
</Box>
<Divider sx={{ borderStyle: 'dashed' }} />
<Scrollbar sx={{ height: { xs: 340, sm: 'auto' } }}>
<List
disablePadding
subheader={
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
New
</ListSubheader>
}
>
{notifications.slice(0, 2).map((notification) => (
<NotificationItem key={notification.id} notification={notification} />
))}
</List>
<List
disablePadding
subheader={
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
Before that
</ListSubheader>
}
>
{notifications.slice(2, 5).map((notification) => (
<NotificationItem key={notification.id} notification={notification} />
))}
</List>
</Scrollbar>
<Divider sx={{ borderStyle: 'dashed' }} />
<Box sx={{ p: 1 }}>
<Button fullWidth disableRipple>
View All
</Button>
</Box>
</Popover>
</>
);
}
// ----------------------------------------------------------------------
NotificationItem.propTypes = {
notification: PropTypes.shape({
createdAt: PropTypes.instanceOf(Date),
id: PropTypes.string,
isUnRead: PropTypes.bool,
title: PropTypes.string,
description: PropTypes.string,
type: PropTypes.string,
avatar: PropTypes.any,
}),
};
function NotificationItem({ notification }) {
const { avatar, title } = renderContent(notification);
return (
<ListItemButton
sx={{
py: 1.5,
px: 2.5,
mt: '1px',
...(notification.isUnRead && {
bgcolor: 'action.selected',
}),
}}
>
<ListItemAvatar>
<Avatar sx={{ bgcolor: 'background.neutral' }}>{avatar}</Avatar>
</ListItemAvatar>
<ListItemText
primary={title}
secondary={
<Typography
variant="caption"
sx={{
mt: 0.5,
display: 'flex',
alignItems: 'center',
color: 'text.disabled',
}}
>
<Iconify icon="eva:clock-outline" sx={{ mr: 0.5, width: 16, height: 16 }} />
{fToNow(notification.createdAt)}
</Typography>
}
/>
</ListItemButton>
);
}
// ----------------------------------------------------------------------
function renderContent(notification) {
const title = (
<Typography variant="subtitle2">
{notification.title}
<Typography component="span" variant="body2" sx={{ color: 'text.secondary' }}>
&nbsp; {noCase(notification.description)}
</Typography>
</Typography>
);
if (notification.type === 'order_placed') {
return {
avatar: <img alt={notification.title} src="./assets/icons/ic_notification_package.svg" />,
title,
};
}
if (notification.type === 'order_shipped') {
return {
avatar: <img alt={notification.title} src="./assets/icons/ic_notification_shipping.svg" />,
title,
};
}
if (notification.type === 'mail') {
return {
avatar: <img alt={notification.title} src="./assets/icons/ic_notification_mail.svg" />,
title,
};
}
if (notification.type === 'chat_message') {
return {
avatar: <img alt={notification.title} src="./assets/icons/ic_notification_chat.svg" />,
title,
};
}
return {
avatar: notification.avatar ? <img alt={notification.title} src={notification.avatar} /> : null,
title,
};
}
import * as React from "react";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Box from "@mui/material/Box";
import { Container } from "@mui/material";
import { useLocation } from "@umijs/max";
import navConfig from "../nav/config";
export default function UserPage() {
const [value, setValue] = React.useState("one");
const handleChange = (event, newValue) => {
setValue(newValue);
};
const location = useLocation();
const data = React.useMemo(() => {
const currouteconfig = navConfig?.filter((it, i) => {
return it?.path === location?.pathname;
});
return currouteconfig[0]?.children ?? [];
}, [location.pathname]);
return (
<Container maxWidth="xl">
<Tabs
value={value}
onChange={handleChange}
textColor="secondary"
indicatorColor="secondary"
aria-label="secondary tabs example"
sx={{ height: 64,ml:"-16px" }}
>
{data?.map((it, i) => (
<Tab
value={it?.path+i}
label={it?.title}
sx={{ height: 64, margin: "0 6px" }}
key={i}
/>
))}
</Tabs>
</Container>
);
}
import PropTypes from "prop-types";
// @mui
import { styled } from "@mui/material/styles";
import {
AppBar,
Box,
IconButton,
Stack,
AppBar,
Toolbar,
IconButton,
useTheme,
} from "@mui/material";
import { styled } from "@mui/material/styles";
// utils
import { bgBlur } from "@/utils/cssStyles";
// components
import Iconify from "@/components/iconify";
import { useModel } from "@umijs/max";
import Searchbar from "./Searchbar";
import AccountPopover from "./AccountPopover";
import NotificationsPopover from "./NotificationsPopover";
import UserPage from './UserPage'
import Searchbar from "./Searchbar";
// ----------------------------------------------------------------------
......@@ -72,7 +70,6 @@ export default function Header({ onOpenNav }) {
</IconButton>
<Searchbar />
<UserPage></UserPage>
<Box sx={{ flexGrow: 1 }} />
<Stack
......@@ -83,7 +80,6 @@ export default function Header({ onOpenNav }) {
sm: 1,
}}
>
<NotificationsPopover />
<AccountPopover />
</Stack>
</StyledToolbar>
......
......@@ -18,11 +18,13 @@ const navConfig = [
title: "实训管理",
path: "/work/bustrain",
icon: icon("ic-shixun"),
key:"TEACH_TRAIN"
},
{
title: "作业批阅",
path: "/work/checkhomework",
icon: icon("ic_book"),
key:"TEACH_TASK"
},
{
title: "成绩单",
......@@ -43,28 +45,13 @@ const navConfig = [
title: "我的实训",
path: "/work/mybustrain",
icon: icon("ic_training"),
key:"STUDY_TRAIN"
},
{
title: "我的成绩",
path: "/work/myrecord",
icon: icon("ic_grades"),
info: (
<div
style={{
marginRight: 12,
backgroundColor: "#ff4800",
width: 20,
height: 20,
textAlign: "center",
lineHeight: "20px",
color: "#fff",
borderRadius: 12,
fontSize: 12,
}}
>
12
</div>
),
key:"STUDY_ACHIEVEMENT",
},
{
title: "学习记录",
......@@ -91,11 +78,13 @@ const navConfig = [
title: "教师管理",
path: "/work/teacher",
icon: icon("ic_user"),
key:"MANAGE_TEACHER"
},
{
title: "学生管理",
path: "/work/student",
icon: icon("ic_student"),
key:"MANAGE_STUDENT"
},
{
title: "班级管理",
......@@ -119,8 +108,6 @@ const navConfig = [
},
]
},
];
export default navConfig;
import { useLocation, useModel ,history} from "@umijs/max";
import { history, useLocation, useModel } from "@umijs/max";
import PropTypes from "prop-types";
import { useEffect } from "react";
import { useEffect, useState } from "react";
// @mui
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
import { Avatar, Box, Button, Drawer, Stack, Typography } from "@mui/material";
......@@ -11,6 +10,8 @@ import useResponsive from "@/hooks/useResponsive";
// components
import Logo from "@/components/logo";
import NavSection from "@/components/nav-section";
import { doFetch } from "@/utils/doFetch";
import { useRequest } from "ahooks";
import { Scrollbars } from "react-custom-scrollbars";
import navConfig from "./config";
......@@ -24,6 +25,8 @@ export default function Nav({ openNav, onCloseNav }) {
const isDesktop = useResponsive("up", "lg");
const [navConfigs, setNavConfigs] = useState(navConfig);
const {
initialState: { nav, currentUser },
setInitialState,
......@@ -37,11 +40,72 @@ export default function Nav({ openNav, onCloseNav }) {
}));
};
const { data } = useRequest(async () => {
let res = await doFetch({ url: "/system/message", params: {} });
let result = {};
res?.data?.dataList?.map((it, i) => {
result[it?.menu] = it?.count;
});
return result;
});
useEffect(() => {
if(!data){
return
}
setNavConfigs((s) => {
let news = navConfig?.map((it) => {
return {
...it,
children: it?.children?.map((item, i) => {
const info = data[item?.key ?? ""]
? {
info: (
<div
style={{
marginRight: 12,
backgroundColor: "#ff4800",
width: 20,
height: 20,
textAlign: "center",
lineHeight: "20px",
color: "#fff",
borderRadius: 12,
fontSize: 12,
}}
>
{data[item?.key]}
</div>
),
}
: {};
return {
...item,
...info,
};
}),
};
});
if (currentUser?.type == 1) {
return news?.filter((it, i) => {
return it?.title === "管理中心";
});
} else if (currentUser?.type == 2) {
return news?.filter((it, i) => {
return it?.title === "教学中心";
});
} else {
return news?.filter((it, i) => {
return it?.title === "学习中心";
});
}
});
}, [data, currentUser?.type]);
useEffect(() => {
if (openNav) {
onCloseNav();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname]);
const ifs = nav === 88;
......@@ -95,8 +159,8 @@ export default function Nav({ openNav, onCloseNav }) {
cursor: "pointer",
boxShadow: "0 0 6px #999",
}}
onClick={()=>{
history.push("/work/usercenter")
onClick={() => {
history.push("/work/usercenter");
}}
/>
{!ifs && (
......@@ -122,7 +186,7 @@ export default function Nav({ openNav, onCloseNav }) {
}}
hideTracksWhenNotNeeded
>
<NavSection collspan={ifs} data={navConfig} />
<NavSection collspan={ifs} data={navConfigs} />
</Scrollbars>
</Box>
<Box
......
......@@ -5,8 +5,10 @@ import Slide from "@mui/material/Slide";
import Snackbar from "@mui/material/Snackbar";
import * as Sentry from "@sentry/react";
import { Outlet, useModel } from "@umijs/max";
import React from "react";
import io from "socket.io-client";
import ThemeProvider from "./theme";
import React, { useState, useEffect } from 'react';
const socket = io(SOCKET_IO_URL, { query: { id: localStorage.getItem("ID") } });
// 自定义主题
// const { palette } = useTheme();
......@@ -72,6 +74,28 @@ function App() {
setInitialState,
} = useModel("@@initialState");
useEffect(() => {
socket.connect();
socket.on('message', (data) => {
console.log('====================================');
console.log(data);
console.log('====================================');
if(data?.wsMsgModel === ""){
}
})
return () => {
// 关闭 WebSocket 连接
socket.disconnect();
};
}, []);
return (
<ThemeProvider>
<CssBaseline />
......
......@@ -18,6 +18,7 @@ function Login() {
const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo();
if (userInfo) {
localStorage.setItem("ID",userInfo?.id);
await setInitialState((s) => {
return { ...s, currentUser: userInfo };
});
......
import { format, getTime, formatDistanceToNow } from 'date-fns';
// ----------------------------------------------------------------------
export function fDate(date, newFormat) {
const fm = newFormat || 'dd MMM yyyy';
return date ? format(new Date(date), fm) : '';
}
export function fDateTime(date, newFormat) {
const fm = newFormat || 'dd MMM yyyy p';
return date ? format(new Date(date), fm) : '';
}
export function fTimestamp(date) {
return date ? getTime(new Date(date)) : '';
}
export function fToNow(date) {
return date
? formatDistanceToNow(new Date(date), {
addSuffix: true,
})
: '';
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment