Skip to content

Commit a0099c1

Browse files
committed
[add] Activity components & pages
1 parent 7e0ec30 commit a0099c1

40 files changed

Lines changed: 1527 additions & 125 deletions

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ jobs:
3030

3131
- name: Lark notification
3232
uses: foxundermoon/feishu-action@v2
33+
if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}
3334
with:
3435
url: ${{ secrets.LARK_CHATBOT_HOOK_URL }}
3536
msg_type: post

components/Git/Card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
44
import { FC, useContext } from 'react';
55
import { Badge, Button, Card, CardProps, Col, Row } from 'react-bootstrap';
66

7-
import { I18nContext } from '../../models/Translation';
7+
import { I18nContext } from '../../models/Base/Translation';
88
import { GitLogo } from './Logo';
99

1010
export interface GitCardProps

components/Layout/CardPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ComponentClass, FC } from 'react';
22
import { Col, Pagination, Row } from 'react-bootstrap';
33

4-
import { SearchPageMeta } from '../../models/System';
4+
import { SearchPageMeta } from '../../models/Base/System';
55

66
export interface CardPageProps extends SearchPageMeta {
77
Card: ComponentClass<any> | FC<any>;

components/Layout/NotFoundCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { observer } from 'mobx-react';
22
import { ErrorProps } from 'next/error';
33
import { FC, useContext } from 'react';
44

5-
import { I18nContext } from '../../models/Translation';
5+
import { I18nContext } from '../../models/Base/Translation';
66

77
export const NotFoundCard: FC<ErrorProps> = observer(({ title }) => {
88
const { currentLanguage } = useContext(I18nContext);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.sectionTitle::before {
2+
display: block;
3+
margin-top: -88px;
4+
width: 100%;
5+
height: 88px;
6+
content: '';
7+
}

components/Layout/SectionTitle.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { FC, HTMLAttributes } from 'react';
2+
import { Badge } from 'react-bootstrap';
3+
4+
import { i18n } from '../../models/Base/Translation';
5+
import styles from './SectionTitle.module.less';
6+
7+
export interface SectionTitleProps extends HTMLAttributes<HTMLHeadingElement> {
8+
count?: number;
9+
}
10+
11+
export const SectionTitle: FC<SectionTitleProps> = ({
12+
className = '',
13+
title = i18n.t('unclassified'),
14+
count = 0,
15+
...props
16+
}) => (
17+
<h2
18+
id={title}
19+
className={`position-relative border-bottom border-2 border-info lh-lg mb-4 ${styles.sectionTitle} ${className}`}
20+
{...props}
21+
>
22+
<a className="text-decoration-none text-dark" href={`/department/${title}`}>
23+
{title}
24+
</a>
25+
<Badge
26+
className="position-absolute translate-middle top-4 mt-2 ms-2 fw-normal fs-6 rounded-pill"
27+
bg="info"
28+
>
29+
{count}
30+
</Badge>
31+
</h2>
32+
);

components/Map/ChinaMap.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { OpenReactMap, OpenReactMapProps, TileLayer } from 'open-react-map';
2+
import { FC } from 'react';
3+
4+
const ChinaMap: FC<OpenReactMapProps> = props => (
5+
<OpenReactMap
6+
{...props}
7+
renderTileLayer={() => <TileLayer vendor="GaoDe" />}
8+
/>
9+
);
10+
export default ChinaMap;

components/Map/ListMap.tsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { Icon } from 'idea-react';
2+
import { LatLngTuple } from 'leaflet';
3+
import { computed, observable } from 'mobx';
4+
import { observer } from 'mobx-react';
5+
import { ImagePreview } from 'mobx-restful-table';
6+
import { MarkerMeta, OpenReactMapProps } from 'open-react-map';
7+
import { Component } from 'react';
8+
import { Button, ListGroup } from 'react-bootstrap';
9+
import { buildURLData } from 'web-utility';
10+
11+
import systemStore from '../../models/Base/System';
12+
import ChinaMap from './ChinaMap';
13+
14+
export interface ImageMarker extends MarkerMeta {
15+
title: string;
16+
summary?: string;
17+
image?: string;
18+
}
19+
20+
export interface ListMapProps extends OpenReactMapProps {
21+
markers: ImageMarker[];
22+
}
23+
24+
@observer
25+
export default class ListMap extends Component<ListMapProps> {
26+
@observable
27+
accessor drawerOpen = false;
28+
29+
@computed
30+
get drawerWidth() {
31+
return this.drawerOpen
32+
? systemStore.screenNarrow
33+
? 'w-100'
34+
: 'w-25'
35+
: 'w-auto';
36+
}
37+
38+
@observable
39+
accessor currentMarker: ImageMarker | undefined;
40+
41+
componentDidMount() {
42+
this.drawerOpen = !systemStore.screenNarrow;
43+
}
44+
45+
selectOne = (marker: ImageMarker) => () => {
46+
this.currentMarker = {
47+
tooltip: [marker.title, marker.summary].filter(Boolean).join(' - '),
48+
...marker,
49+
};
50+
if (systemStore.screenNarrow) this.drawerOpen = false;
51+
};
52+
53+
makeGaoDeLink({ position, title }: ImageMarker) {
54+
const [latitude, longitude] = position as LatLngTuple;
55+
56+
return `https://uri.amap.com/marker?${buildURLData({
57+
position: [longitude, latitude],
58+
name: title,
59+
src: 'KaiYuanShe',
60+
coordinate: 'gaode',
61+
callnative: 1,
62+
})}`;
63+
}
64+
65+
renderItem = (marker: ImageMarker) => (
66+
<ListGroup.Item
67+
key={marker.title}
68+
className="d-flex gap-2 justify-content-between cursor-pointer"
69+
onClick={this.selectOne(marker)}
70+
>
71+
<ImagePreview className="w-25" loading="lazy" src={marker.image} fluid />
72+
<div>
73+
<h3 className="fs-5">{marker.title}</h3>
74+
<p>{marker.summary}</p>
75+
</div>
76+
<a href={this.makeGaoDeLink(marker)} target="_blank" rel="noreferrer">
77+
<Icon name="geo-alt-fill" className="fs-3" />
78+
</a>
79+
</ListGroup.Item>
80+
);
81+
82+
render() {
83+
const { className = '', style, markers, ...props } = this.props,
84+
{ drawerOpen, drawerWidth, currentMarker } = this;
85+
86+
return (
87+
<div className={`position-relative ${className}`} style={style}>
88+
<ChinaMap
89+
className="h-100"
90+
{...props}
91+
markers={currentMarker && [currentMarker]}
92+
/>
93+
<div
94+
className={`position-absolute end-0 top-0 h-100 overflow-y-auto d-flex align-items-start ${drawerWidth}`}
95+
style={{ zIndex: 1000 }}
96+
>
97+
<Button
98+
className="position-sticky top-0"
99+
variant={drawerOpen ? 'secondary' : 'primary'}
100+
onClick={() => (this.drawerOpen = !drawerOpen)}
101+
>
102+
<Icon name="layout-text-sidebar" />
103+
</Button>
104+
105+
<ListGroup hidden={!drawerOpen}>
106+
{markers.map(this.renderItem)}
107+
</ListGroup>
108+
</div>
109+
</div>
110+
);
111+
}
112+
}

components/Member/Card.module.less

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
.member {
2+
flex: none;
3+
content-visibility: auto;
4+
contain-intrinsic-height: 10rem;
5+
6+
.avatar {
7+
transition: all 0.3s;
8+
width: 6rem;
9+
height: 6rem;
10+
object-fit: contain;
11+
}
12+
.btn_tamaya {
13+
width: 6rem;
14+
height: 1rem;
15+
line-height: 1rem;
16+
&::before,
17+
&::after {
18+
position: absolute;
19+
left: 0;
20+
transition: transform 0.5s;
21+
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
22+
width: 6rem;
23+
height: 0.5rem;
24+
overflow: hidden;
25+
content: attr(data-text);
26+
color: #333;
27+
text-overflow: ellipsis;
28+
white-space: nowrap;
29+
}
30+
&::before {
31+
top: 0;
32+
}
33+
&::after {
34+
bottom: 0;
35+
line-height: 0;
36+
}
37+
& > i {
38+
transform: scale3d(0.2, 0.2, 1);
39+
opacity: 0;
40+
transition:
41+
transform 0.5s,
42+
opacity 0.5s;
43+
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
44+
}
45+
}
46+
&:hover {
47+
.avatar {
48+
transform: scale(1.05) translate3d(-1px, -5px, -4px);
49+
}
50+
.btn_tamaya {
51+
&::before {
52+
transform: translate3d(0, -120%, 0);
53+
}
54+
&::after {
55+
transform: translate3d(0, 120%, 0);
56+
}
57+
& > i {
58+
transform: scale3d(1, 1, 1);
59+
opacity: 1;
60+
}
61+
}
62+
}
63+
}

components/Member/Card.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { TableCellValue } from 'mobx-lark';
2+
import { observer } from 'mobx-react';
3+
import { FC, useContext } from 'react';
4+
5+
import { I18nContext } from '../../models/Base/Translation';
6+
import { LarkImage } from '../Base/LarkImage';
7+
import styles from './Card.module.less';
8+
9+
export interface MemberCardProps extends Partial<Record<'name' | 'nickname', string>> {
10+
avatar?: TableCellValue;
11+
}
12+
13+
export const MemberCard: FC<MemberCardProps> = observer(({ name, nickname, avatar }) => {
14+
const { t } = useContext(I18nContext);
15+
16+
return (
17+
<div
18+
className={`d-inline-block position-relative w-auto text-center ${styles.member}`}
19+
title={name || nickname || t('member_x')}
20+
>
21+
<LarkImage
22+
className={`d-inlink-block overflow-hidden rounded-circle position-relative ${styles.avatar}`}
23+
src={avatar}
24+
/>
25+
<h3 className="h4 my-3">
26+
<a className="fs-6 stretched-link" href={`/member/${name}`} rel="noreferrer">
27+
<span
28+
className={`d-inline-block position-relative text-truncate ${styles.btn_tamaya}`}
29+
data-text={name || nickname || t('member_x')}
30+
>
31+
<i className="d-inline-block fst-normal overflow-hidden">
32+
{nickname || name || t('unpublished')}
33+
</i>
34+
</span>
35+
</a>
36+
</h3>
37+
</div>
38+
);
39+
});

0 commit comments

Comments
 (0)