first commit
This commit is contained in:
@@ -0,0 +1,445 @@
|
||||
import React from "react";
|
||||
import {Card, Col, Row, theme, Radio, Table, Tag, Space, List, Avatar} from "antd";
|
||||
import ReactECharts from "echarts-for-react";
|
||||
import {ArrowDownOutlined, ArrowUpOutlined, LikeOutlined, TeamOutlined} from "@ant-design/icons";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const {useToken} = theme;
|
||||
|
||||
interface DataType {
|
||||
key: string;
|
||||
name: string;
|
||||
age: number;
|
||||
address: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const Index: React.FC = () => {
|
||||
const {token} = useToken();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const optionsBar = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
show: false,
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
|
||||
},
|
||||
yAxis: {
|
||||
show: false,
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [120, 88, 116, 60, 70],
|
||||
type: 'bar',
|
||||
itemStyle: {
|
||||
color: token.colorPrimary
|
||||
},
|
||||
barWidth: 10,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const optionsLine = {
|
||||
xAxis: {
|
||||
show: false,
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
},
|
||||
yAxis: {
|
||||
show: false,
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [1, 2, 3, 2, 3, 2, 1],
|
||||
type: 'line',
|
||||
itemStyle: {
|
||||
color: token.colorPrimary
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const options = {
|
||||
title: {
|
||||
text: t('dashboard.analysis.annualSales'),
|
||||
textStyle: {
|
||||
color: token.colorText,
|
||||
fontSize: token.fontSizeLG,
|
||||
fontWeight: token.fontWeightStrong
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: token.colorPrimaryBg,
|
||||
color: token.colorPrimary
|
||||
}
|
||||
},
|
||||
borderWidth: 0,
|
||||
backgroundColor: token.colorPrimaryBg,
|
||||
textStyle: {
|
||||
color: token.colorText
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: [t('dashboard.analysis.grossProfit'), t('dashboard.analysis.netProfit'), t('dashboard.analysis.totalExpense')],
|
||||
textStyle: {
|
||||
color: token.colorText
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [
|
||||
t('dashboard.analysis.january'),
|
||||
t('dashboard.analysis.february'),
|
||||
t('dashboard.analysis.march'),
|
||||
t('dashboard.analysis.april'),
|
||||
t('dashboard.analysis.may'),
|
||||
t('dashboard.analysis.june'),
|
||||
t('dashboard.analysis.july'),
|
||||
t('dashboard.analysis.august'),
|
||||
t('dashboard.analysis.september'),
|
||||
t('dashboard.analysis.october'),
|
||||
t('dashboard.analysis.november'),
|
||||
t('dashboard.analysis.december')
|
||||
]
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
// 使用深浅的间隔色
|
||||
color: token.colorBorder
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: t('dashboard.analysis.grossProfit'),
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: token.colorPrimaryBorder
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
itemStyle: {
|
||||
color: token.colorPrimary
|
||||
},
|
||||
data: [30,36,42,33,21,26,29,35,42,32,28,26]
|
||||
},
|
||||
{
|
||||
name: t('dashboard.analysis.netProfit'),
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: token.colorSuccessBorder
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
itemStyle: {
|
||||
color: token.colorSuccess
|
||||
},
|
||||
data: [32,16,18,30,15,19,22,17,24,19,30,31]
|
||||
},
|
||||
{
|
||||
name: t('dashboard.analysis.totalExpense'),
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: token.colorWarningBorder
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
itemStyle: {
|
||||
color: token.colorWarning
|
||||
},
|
||||
data: [36,24,36,36,39,56,24,23,21,12,16,19]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const optionsPie = {
|
||||
title: {
|
||||
text: t('dashboard.analysis.accessFrom'),
|
||||
textStyle: {
|
||||
color: token.colorText,
|
||||
fontSize: token.fontSizeLG,
|
||||
fontWeight: token.fontWeightStrong
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
bottom: '0%',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
color: token.colorText
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('dashboard.analysis.accessFrom'),
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: token.borderRadius,
|
||||
borderColor: token.colorBorder,
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 40,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 1048, name: t('dashboard.analysis.searchEngine') },
|
||||
{ value: 735, name: t('dashboard.analysis.direct') },
|
||||
{ value: 580, name: t('dashboard.analysis.email') },
|
||||
{ value: 484, name: t('dashboard.analysis.unionAds') },
|
||||
{ value: 300, name: t('dashboard.analysis.videoAds') }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const data: DataType[] = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer'],
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['loser'],
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sydney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher'],
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['loser'],
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sydney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher'],
|
||||
},
|
||||
];
|
||||
|
||||
const msgData = [
|
||||
{
|
||||
title: 'Ant Design Title 1',
|
||||
},
|
||||
{
|
||||
title: 'Ant Design Title 2',
|
||||
},
|
||||
{
|
||||
title: 'Ant Design Title 3',
|
||||
},
|
||||
{
|
||||
title: 'Ant Design Title 4',
|
||||
},
|
||||
{
|
||||
title: 'Ant Design Title 5',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row gutter={[20, 20]}>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<div>{t('dashboard.analysis.totalRevenue')}</div>
|
||||
<div className={"flex items-center justify-between pt-4 pb-2"}>
|
||||
<div className={"text-4xl flex-0"}>¥3,415.00</div>
|
||||
<ReactECharts style={{width: 120, height: 80}} option={optionsBar} />
|
||||
</div>
|
||||
<div>{t('dashboard.since.lastWeek')} <span style={{color: token.colorError}}><ArrowUpOutlined />11.28%</span></div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<div>{t('dashboard.analysis.totalExpenses')}</div>
|
||||
<div className={"flex items-center justify-between pt-4 pb-2"}>
|
||||
<div className={"text-4xl flex-0"}>¥8,425.00</div>
|
||||
<ReactECharts style={{width: 120, height: 80}} option={optionsLine} />
|
||||
</div>
|
||||
<div>{t('dashboard.since.lastWeek')} <span style={{color: token.colorSuccess}}><ArrowDownOutlined />15.33%</span></div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<div>{t('dashboard.analysis.visitors')}</div>
|
||||
<div className={"flex items-center justify-between pt-4 pb-2"}>
|
||||
<div className={"text-4xl flex-0"}>
|
||||
1,128
|
||||
</div>
|
||||
<div className={"text-4xl rounded-full flex items-center justify-center"} style={{height: 80, width: 80, background: token.colorPrimaryBg}}>
|
||||
<TeamOutlined style={{ color: token.colorPrimary }}/>
|
||||
</div>
|
||||
</div>
|
||||
<div>{t('dashboard.since.lastWeek')} <span style={{color: token.colorError}}><ArrowUpOutlined />32.60%</span></div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<div>{t('dashboard.analysis.likes')}</div>
|
||||
<div className={"flex items-center justify-between pt-4 pb-2"}>
|
||||
<div className={"text-4xl flex-0"}>
|
||||
668
|
||||
</div>
|
||||
<div className={"text-4xl rounded-full flex items-center justify-center"} style={{height: 80, width: 80, background: token.colorPrimaryBg}}>
|
||||
<LikeOutlined style={{ color: token.colorPrimary }}/>
|
||||
</div>
|
||||
</div>
|
||||
<div>{t('dashboard.since.lastWeek')} <span style={{color: token.colorSuccess}}><ArrowDownOutlined />9.60%</span></div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xl={18} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<ReactECharts style={{width: '100%', height: 460}} option={options} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xl={6} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<ReactECharts style={{width: '100%', height: 460}} option={optionsPie} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xl={12} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<div className={"flex items-center justify-between mb-5"}>
|
||||
<div style={{ fontSize: token.fontSizeLG, fontWeight: token.fontWeightStrong }}>{t('dashboard.analysis.salesRanking')}</div>
|
||||
<Radio.Group
|
||||
options={[
|
||||
{ label: t('dashboard.analysis.month'), value: 'month'},
|
||||
{ label: t('dashboard.analysis.year'), value: 'year'},
|
||||
{ label: t('dashboard.analysis.day'), value: 'day'},
|
||||
]}
|
||||
defaultValue={'day'}
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
title: t('dashboard.analysis.article'),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text) => <a>{text}</a>,
|
||||
},
|
||||
{
|
||||
title: t('dashboard.analysis.age'),
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
},
|
||||
{
|
||||
title: t('dashboard.analysis.address'),
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
},
|
||||
{
|
||||
title: t('dashboard.analysis.tags'),
|
||||
key: 'tags',
|
||||
dataIndex: 'tags',
|
||||
render: (_, { tags }) => (
|
||||
<>
|
||||
{tags.map((tag) => {
|
||||
let color = tag.length > 5 ? 'geekblue' : 'green';
|
||||
if (tag === 'loser') {
|
||||
color = 'volcano';
|
||||
}
|
||||
return (
|
||||
<Tag color={color} key={tag}>
|
||||
{tag.toUpperCase()}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('dashboard.analysis.action'),
|
||||
key: 'action',
|
||||
render: (_, record) => (
|
||||
<Space size="middle">
|
||||
<a>{t('dashboard.analysis.invite')} {record.name}</a>
|
||||
<a>{t('dashboard.analysis.delete')}</a>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]}
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
scroll={{x: 800}}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xl={12} xs={24}>
|
||||
<Card variant={"borderless"}>
|
||||
<div className={"mb-5"} style={{ fontSize: token.fontSizeLG, fontWeight: token.fontWeightStrong }}>{t('dashboard.analysis.userReviews')}</div>
|
||||
<List
|
||||
dataSource={msgData}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar src={`https://xsgames.co/randomusers/avatar.php?g=pixel&key=${index}`} />
|
||||
}
|
||||
title={<a href="https://ant.design">{item.title}</a>}
|
||||
description={t('dashboard.analysis.reviewDescription')}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Index
|
||||
@@ -0,0 +1,139 @@
|
||||
import {Card, Row, Col, Space, Divider, List, Avatar, theme} from "antd";
|
||||
import {ArrowDownOutlined, ArrowUpOutlined} from "@ant-design/icons";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const {useToken} = theme
|
||||
|
||||
const Monitor = () => {
|
||||
const {token} = useToken();
|
||||
const { t } = useTranslation();
|
||||
const imageUrl = import.meta.env.VITE_BASE_URL + '/static';
|
||||
const data = [
|
||||
{
|
||||
title: '3号楼2层走廊灯不亮,需检查线路并更换灯泡。',
|
||||
description: '3号楼2层走廊灯故障,经检查为灯泡损坏,需更换LED节能灯泡(型号:E27-9W),并排查线路是否老化。预计完成时间:6月20日前。',
|
||||
},
|
||||
{
|
||||
title: '安排维保单位对小区所有电梯进行月度维护,确保运行安全。',
|
||||
description: '联系XX维保公司(电话:123-456789)于6月25日对小区12部电梯进行月度维护,重点检查曳引机、门锁装置及应急报警功能,完成后提交保养报告。',
|
||||
},
|
||||
{
|
||||
title: '5号楼前绿化带杂草丛生,需安排园艺工人本周内清理。',
|
||||
description: '5号楼前绿化带因雨季杂草生长迅速,需安排园艺组(责任人:张师傅)本周内清理,并喷洒除草剂。同步检查灌溉系统是否漏水。',
|
||||
},
|
||||
{
|
||||
title: '8号楼302室反映厨房水管漏水,需上门检修并更换配件。',
|
||||
description: '8号楼302室业主反映厨房下水管接口渗水,已临时关闭水阀。需工程部带PVC管件(Φ50mm)及密封胶上门维修,完成后拍照反馈业主。',
|
||||
},
|
||||
{
|
||||
title: '检查各楼栋灭火器压力及有效期,记录并更换不合格器材。',
|
||||
description: "按消防规定,6月需全面检查56处灭火器压力表(绿色达标)、软管龟裂情况,并更换2号楼过期灭火器(编号:2023-015至018)。",
|
||||
},
|
||||
{
|
||||
title: '安排保洁人员彻底清扫B1层车库,清理杂物和积水。',
|
||||
description: "B1层车库地面油渍、垃圾较多,安排保洁组6月22日集中清扫,重点清理排水沟杂物,并检查照明设施是否正常。",
|
||||
},
|
||||
{
|
||||
title: '联系技术员升级小区门禁系统,测试各单元刷卡功能。',
|
||||
description: "新门禁系统(版本V2.3)需于6月28日前完成升级,联系供应商调试人脸识别功能,并同步更新业主卡数据。测试期3天。",
|
||||
},
|
||||
{
|
||||
title: '统计未缴费业主名单,发送短信及书面催缴通知。',
|
||||
description: "截至6月,尚有15户未缴纳Q2物业费(名单见附件),本周内发送短信提醒,对逾期户上门送达《催缴通知书》(模板编号:WY-2024-06)。",
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<Row gutter={[20, 20]}>
|
||||
<Col xxl={18} lg={16} xs={24}>
|
||||
<Card variant={"borderless"} style={{marginBottom: 20}}>
|
||||
<Row gutter={[20, 20]}>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<div className={"flex items-center"}>
|
||||
<div className={"size-14 rounded-full p-3"} style={{background: token.colorPrimaryBg }}>
|
||||
<img src={imageUrl + '/帮办代办.png'} alt="1"/>
|
||||
</div>
|
||||
<div className={"ml-5"}>
|
||||
<div style={{color: token.colorTextDescription}}>{t('dashboard.monitor.helpService')}</div>
|
||||
<div className={"text-[30px]"}>30 / 255</div>
|
||||
<div>{t('dashboard.completion')} <span style={{color: token.colorError}}>11.60%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<div className={"flex items-center"}>
|
||||
<div className={"size-14 rounded-full p-3"} style={{background: token.colorPrimaryBg }}>
|
||||
<img src={imageUrl + '/购房.png'} alt="1"/>
|
||||
</div>
|
||||
<div className={"ml-5"}>
|
||||
<div style={{color: token.colorTextDescription}}>{t('dashboard.monitor.propertyFee')}</div>
|
||||
<div className={"text-[30px]"}>19,308 ¥</div>
|
||||
<div>{t('dashboard.vs.lastWeek')} <span style={{color: token.colorError}}><ArrowUpOutlined />19.20%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<div className={"flex items-center"}>
|
||||
<div className={"size-14 rounded-full p-3"} style={{background: token.colorPrimaryBg }}>
|
||||
<img src={imageUrl + '/购房.png'} alt="1"/>
|
||||
</div>
|
||||
<div className={"ml-5"}>
|
||||
<div style={{color: token.colorTextDescription}}>{t('dashboard.monitor.alarmEvents')}</div>
|
||||
<div className={"text-[30px]"}>30</div>
|
||||
<div>{t('dashboard.vs.lastWeek')} <span style={{color: token.colorSuccess}}><ArrowDownOutlined />32.60%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xxl={6} lg={12} xs={24}>
|
||||
<div className={"flex items-center"}>
|
||||
<div className={"size-14 rounded-full p-3"} style={{background: token.colorPrimaryBg }}>
|
||||
<img src={imageUrl + '/购房.png'} alt="1"/>
|
||||
</div>
|
||||
<div className={"ml-5"}>
|
||||
<div style={{color: token.colorTextDescription}}>{t('dashboard.monitor.otherEvents')}</div>
|
||||
<div className={"text-[30px]"}>255件</div>
|
||||
<div>{t('dashboard.vs.lastWeek')} <span style={{color: token.colorError}}><ArrowUpOutlined />16.50%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
<Card variant={"borderless"}>
|
||||
<div style={{ marginBottom: 20, fontSize: token.fontSizeLG, fontWeight: token.fontWeightStrong }}>
|
||||
{t('dashboard.monitor.todoList')} <Divider type="vertical" /> <span style={{color: token.colorTextDescription}}>{t('dashboard.monitor.myFocus')}</span>
|
||||
</div>
|
||||
<Space className={"mb-5"} wrap>
|
||||
<div className={"rounded-xl pl-3 pr-3"} style={{ color: token.colorPrimary, background: token.colorPrimaryBg }}>{t('dashboard.monitor.all')} 15</div>
|
||||
<div className={"rounded-xl pl-3 pr-3"} style={{ color: token.colorText, background: token.colorBorderSecondary }}>{t('dashboard.monitor.propertyOrder')} 8</div>
|
||||
<div className={"rounded-xl pl-3 pr-3"} style={{ color: token.colorText, background: token.colorBorderSecondary }}>{t('dashboard.monitor.repairOrder')} 2</div>
|
||||
<div className={"rounded-xl pl-3 pr-3"} style={{ color: token.colorText, background: token.colorBorderSecondary }}>{t('dashboard.monitor.fireSafety')} 3</div>
|
||||
<div className={"rounded-xl pl-3 pr-3"} style={{ color: token.colorText, background: token.colorBorderSecondary }}>{t('dashboard.monitor.paidService')} 5</div>
|
||||
<div className={"rounded-xl pl-3 pr-3"} style={{ color: token.colorText, background: token.colorBorderSecondary }}>{t('dashboard.monitor.publicService')} 7</div>
|
||||
</Space>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={data}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${index}`} />}
|
||||
title={<a>{item.title}</a>}
|
||||
description={item.description}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xxl={6} lg={8} xs={24}>
|
||||
<Card variant={"borderless"} style={{ marginBottom: 20 }}>
|
||||
<img src={imageUrl + '/group65.png'} alt="1"/>
|
||||
</Card>
|
||||
<Card variant={"borderless"} styles={{body: {padding: 0, borderRadius: token.borderRadius, overflow: 'hidden'}}}>
|
||||
<img src={imageUrl + '/group57.png'} alt="1"/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default Monitor;
|
||||
@@ -0,0 +1,293 @@
|
||||
import {Card, Avatar, List, Badge, Tag, Divider, Typography, Space, Empty} from 'antd';
|
||||
import {
|
||||
ProjectOutlined,
|
||||
TeamOutlined,
|
||||
BellOutlined,
|
||||
ClockCircleOutlined,
|
||||
MailOutlined,
|
||||
PhoneOutlined,
|
||||
EnvironmentOutlined,
|
||||
LinkOutlined,
|
||||
SmileOutlined,
|
||||
AppstoreOutlined,
|
||||
ShopOutlined,
|
||||
FileTextOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useAuthStore from "@/stores/user";
|
||||
import type {ReactNode} from "react";
|
||||
const { Meta } = Card;
|
||||
const { Text, Title } = Typography;
|
||||
|
||||
interface AppType {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: ReactNode;
|
||||
badge?: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const PersonalCenter = () => {
|
||||
const { t } = useTranslation();
|
||||
// 用户信息数据
|
||||
const userInfo = useAuthStore(state => state.userinfo);
|
||||
if (! userInfo ) return <></>;
|
||||
|
||||
// 标签页列表数据
|
||||
const tabList = [
|
||||
{
|
||||
key: "projects",
|
||||
label: <><ProjectOutlined className={'mr-2'}/> {t('dashboard.workplace.myProjects')} </>,
|
||||
},
|
||||
{
|
||||
key: "teams",
|
||||
label: <><TeamOutlined className={'mr-2'}/> {t('dashboard.workplace.myTeams')} </>,
|
||||
},
|
||||
{
|
||||
key: "activities",
|
||||
label: <><ClockCircleOutlined className={'mr-2'}/> {t('dashboard.workplace.latestActivities')} </>,
|
||||
},
|
||||
{
|
||||
key: "notifications",
|
||||
label: (
|
||||
<Badge count={1} offset={[10, 0]}>
|
||||
<span>
|
||||
<BellOutlined className={'mr-2'} />
|
||||
{t('dashboard.workplace.notifications')}
|
||||
</span>
|
||||
</Badge>
|
||||
)
|
||||
},
|
||||
]
|
||||
// 我的项目数据
|
||||
const projects = [
|
||||
{
|
||||
id: 1,
|
||||
name: t('dashboard.workplace.project1.name'),
|
||||
description: t('dashboard.workplace.project1.desc'),
|
||||
status: t('dashboard.workplace.status.inProgress'),
|
||||
progress: 65,
|
||||
members: 5,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: t('dashboard.workplace.project2.name'),
|
||||
description: t('dashboard.workplace.project2.desc'),
|
||||
status: t('dashboard.workplace.status.completed'),
|
||||
progress: 100,
|
||||
members: 3,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: t('dashboard.workplace.project3.name'),
|
||||
description: t('dashboard.workplace.project3.desc'),
|
||||
status: t('dashboard.workplace.status.planning'),
|
||||
progress: 10,
|
||||
members: 2,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: t('dashboard.workplace.project4.name'),
|
||||
description: t('dashboard.workplace.project4.desc'),
|
||||
status: '2023-06-10',
|
||||
progress: 10,
|
||||
members: 2,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: t('dashboard.workplace.project5.name'),
|
||||
description: t('dashboard.workplace.project5.desc'),
|
||||
status: '2023-06-08',
|
||||
progress: 15,
|
||||
members: 5,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: t('dashboard.workplace.project6.name'),
|
||||
description: t('dashboard.workplace.project6.desc'),
|
||||
status: '2023-06-05',
|
||||
progress: 60,
|
||||
members: 3,
|
||||
},
|
||||
];
|
||||
// APP 应用列表
|
||||
const apps: AppType[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: t('dashboard.workplace.appStore'),
|
||||
icon: <AppstoreOutlined />,
|
||||
description: t('dashboard.workplace.appStore.desc'),
|
||||
badge: t('dashboard.workplace.badge.new'),
|
||||
color: '#1890ff',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: t('dashboard.workplace.mallSystem'),
|
||||
icon: <ShopOutlined />,
|
||||
description: t('dashboard.workplace.mallSystem.desc'),
|
||||
color: '#52c41a',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: t('dashboard.workplace.collaboration'),
|
||||
icon: <TeamOutlined />,
|
||||
description: t('dashboard.workplace.collaboration.desc'),
|
||||
color: '#faad14',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: t('dashboard.workplace.docCenter'),
|
||||
icon: <FileTextOutlined />,
|
||||
description: t('dashboard.workplace.docCenter.desc'),
|
||||
color: '#13c2c2',
|
||||
}
|
||||
];
|
||||
// 获取当前时间问候语
|
||||
const getGreeting = () => {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 12) return t('dashboard.workplace.greeting.morning');
|
||||
if (hour < 18) return t('dashboard.workplace.greeting.afternoon');
|
||||
return t('dashboard.workplace.greeting.evening');
|
||||
};
|
||||
// APP 应用卡片
|
||||
const AppCard = (props: {app: AppType}) => (
|
||||
<Card
|
||||
hoverable
|
||||
variant={'borderless'}
|
||||
cover={
|
||||
<div
|
||||
className="flex items-center justify-center h-32"
|
||||
style={{ backgroundColor: `${props.app.color}20` }} // 20表示透明度
|
||||
>
|
||||
<Avatar
|
||||
icon={props.app.icon}
|
||||
size={64}
|
||||
style={{
|
||||
backgroundColor: props.app.color,
|
||||
color: '#fff',
|
||||
fontSize: '28px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Meta
|
||||
title={<span className="font-semibold">{props.app.name}</span>}
|
||||
description={props.app.description}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
{/* 左侧布局显示内容 */}
|
||||
<div className="lg:col-span-3 space-y-6">
|
||||
<Card variant={'borderless'} styles={{body: {padding: 0}}} className={'overflow-hidden'}>
|
||||
<div className="bg-gradient-to-r from-blue-500 to-purple-600 flex items-center p-8">
|
||||
<Space size="large" className="w-full">
|
||||
<Avatar
|
||||
size={64}
|
||||
src={userInfo.avatar_url}
|
||||
icon={<SmileOutlined />}
|
||||
className="border-2 border-white shadow"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<Title level={3} className="!text-white !mb-1">
|
||||
{getGreeting()},{userInfo.nickname},{t('dashboard.workplace.welcome')}
|
||||
</Title>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<Text className="text-white/80 text-lg">
|
||||
{t('dashboard.workplace.today')} {new Date().toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'long'
|
||||
})}
|
||||
</Text>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||
{apps.map((app) => (
|
||||
<div key={app.id}>
|
||||
{ app.badge ?
|
||||
<Badge.Ribbon text={app.badge} color={app.color}>
|
||||
<AppCard app={app}></AppCard>
|
||||
</Badge.Ribbon>
|
||||
:
|
||||
<AppCard app={app} ></AppCard>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Card variant={'borderless'} defaultActiveTabKey="projects" tabList={tabList}>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={projects}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar icon={<ProjectOutlined />} />}
|
||||
title={<a href="#">{item.name}</a>}
|
||||
description={item.description}
|
||||
/>
|
||||
<div className="flex flex-col items-end">
|
||||
<Tag color={item.status === t('dashboard.workplace.status.completed') ? 'success' : item.status === t('dashboard.workplace.status.inProgress') ? 'processing' : 'default'}>
|
||||
{item.status}
|
||||
</Tag>
|
||||
<div className="mt-2">
|
||||
<span className="text-gray-500 mr-2">{t('dashboard.workplace.progress')}: {item.progress}%</span>
|
||||
<span className="text-gray-500">{t('dashboard.workplace.members')}: {item.members}{t('dashboard.workplace.people')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 右侧布局显示内容 */}
|
||||
<div className="lg:col-span-1">
|
||||
<Card variant={'borderless'} className="mb-5">
|
||||
<div className="flex flex-col items-center">
|
||||
<Avatar size={100} src={userInfo.avatar_url} className="mb-4" />
|
||||
<h2 className="text-xl font-bold mb-2">{userInfo.nickname}</h2>
|
||||
<Tag color="blue" className="mb-4">
|
||||
{userInfo.dept_name}
|
||||
</Tag>
|
||||
<p className="text-gray-700 mb-2 text-center">hello</p>
|
||||
<Divider className="my-3 mb-4" />
|
||||
<div className="w-full space-y-3">
|
||||
<div className="flex items-center text-gray-600">
|
||||
<MailOutlined className="mr-2" />
|
||||
<span>{userInfo.email}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-gray-600">
|
||||
<PhoneOutlined className="mr-2" />
|
||||
<span>{userInfo.mobile}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-gray-600">
|
||||
<EnvironmentOutlined className="mr-2" />
|
||||
<span>{t('dashboard.workplace.location')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-gray-600">
|
||||
<LinkOutlined className="mr-2" />
|
||||
<a target="_blank" rel="noopener noreferrer" className="text-blue-500">
|
||||
123
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card variant={'borderless'} title={t('dashboard.workplace.recentVisits')}>
|
||||
<Empty/>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonalCenter;
|
||||
Reference in New Issue
Block a user