@titaui/pc 1.7.21 → 1.7.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/button/text-btn/index.css +1 -0
- package/lib/components/button/text-btn/index.js +10 -12
- package/lib/components/cascader/index.css +38 -11
- package/lib/components/cascader/index.js +10 -5
- package/lib/components/cascader/time.js +31 -0
- package/lib/components/common-share/index.css +223 -0
- package/lib/components/common-share/index.js +104 -0
- package/lib/components/common-share/publishMblog.js +155 -0
- package/lib/components/common-share/request-apis.js +56 -0
- package/lib/components/common-share/types.js +5 -0
- package/lib/components/follower-visitor/index.css +8 -3
- package/lib/components/form/form-fields/group/index.js +2 -1
- package/lib/components/menus/components/menu-tree/tree-node/index.css +9 -6
- package/lib/components/okr-detail/components/okr-tree/tree-node/e-node/e-project.js +4 -2
- package/lib/components/okr-detail/components/okr-tree/tree-node/e-node/e-task-level2.js +2 -2
- package/lib/components/okr-detail/components/okr-tree/tree-node/e-node/e-task.js +2 -2
- package/lib/components/scrollbar/index.js +13 -3
- package/lib/components/select-tags/index.css +28 -18
- package/lib/components/select-tags/index.js +2 -2
- package/lib/components/weekly-report/board.js +43 -0
- package/lib/components/weekly-report/card.js +235 -0
- package/lib/components/weekly-report/common-painter/darwAvatar.js +157 -0
- package/lib/components/weekly-report/common-painter/drawName.js +59 -0
- package/lib/components/weekly-report/common-painter/openDataPainter.js +95 -0
- package/lib/components/weekly-report/common-painter/setSize.js +19 -0
- package/lib/components/weekly-report/common-painter/utils.js +85 -0
- package/lib/components/weekly-report/img/body.png +0 -0
- package/lib/components/weekly-report/img/bofang.png +0 -0
- package/lib/components/weekly-report/img/bottom.png +0 -0
- package/lib/components/weekly-report/img/communication.png +0 -0
- package/lib/components/weekly-report/img/down.png +0 -0
- package/lib/components/weekly-report/img/finish.png +0 -0
- package/lib/components/weekly-report/img/laba.png +0 -0
- package/lib/components/weekly-report/img/praise.png +0 -0
- package/lib/components/weekly-report/img/quote.png +0 -0
- package/lib/components/weekly-report/img/title.png +0 -0
- package/lib/components/weekly-report/img/triangle-pop.png +0 -0
- package/lib/components/weekly-report/img/up.png +0 -0
- package/lib/components/weekly-report/index.css +332 -0
- package/lib/components/weekly-report/index.js +134 -0
- package/lib/components/weekly-report/statistics.js +47 -0
- package/lib/index.js +8 -0
- package/lib/pages/aha2021/index.css +0 -1
- package/lib/pages/new-okr-list/header/filter.js +51 -2
- package/lib/pages/new-okr-list/index.js +44 -5
- package/lib/utils/helpers.js +8 -1
- package/package.json +1 -1
- package/src/components/button/text-btn/index.scss +1 -0
- package/src/components/button/text-btn/index.tsx +13 -13
- package/src/components/button/types.ts +1 -0
- package/src/components/cascader/index.scss +50 -27
- package/src/components/cascader/index.tsx +18 -16
- package/src/components/cascader/time.js +13 -0
- package/src/components/common-share/index.scss +199 -0
- package/src/components/common-share/index.tsx +91 -0
- package/src/components/common-share/publishMblog.tsx +79 -0
- package/src/components/common-share/request-apis.js +37 -0
- package/src/components/common-share/types.ts +4 -0
- package/src/components/follower-visitor/index.scss +9 -4
- package/src/components/form/form-fields/group/index.tsx +3 -3
- package/src/components/menus/components/menu-tree/tree-node/index.scss +2 -1
- package/src/components/okr-detail/components/okr-tree/tree-node/e-node/e-project.js +1 -1
- package/src/components/okr-detail/components/okr-tree/tree-node/e-node/e-task-level2.js +2 -2
- package/src/components/okr-detail/components/okr-tree/tree-node/e-node/e-task.js +2 -2
- package/src/components/okr-share/index.tsx +4 -17
- package/src/components/scrollbar/index.tsx +2 -1
- package/src/components/select-tags/index.scss +9 -1
- package/src/components/select-tags/index.tsx +8 -12
- package/src/components/weekly-report/board.tsx +25 -0
- package/src/components/weekly-report/card.tsx +125 -0
- package/src/components/weekly-report/common-painter/darwAvatar.ts +81 -0
- package/src/components/weekly-report/common-painter/drawName.ts +19 -0
- package/src/components/weekly-report/common-painter/openDataPainter.ts +47 -0
- package/src/components/weekly-report/common-painter/setSize.ts +12 -0
- package/src/components/weekly-report/common-painter/utils.ts +83 -0
- package/src/components/weekly-report/img/body.png +0 -0
- package/src/components/weekly-report/img/bofang.png +0 -0
- package/src/components/weekly-report/img/bottom.png +0 -0
- package/src/components/weekly-report/img/communication.png +0 -0
- package/src/components/weekly-report/img/down.png +0 -0
- package/src/components/weekly-report/img/finish.png +0 -0
- package/src/components/weekly-report/img/laba.png +0 -0
- package/src/components/weekly-report/img/praise.png +0 -0
- package/src/components/weekly-report/img/quote.png +0 -0
- package/src/components/weekly-report/img/title.png +0 -0
- package/src/components/weekly-report/img/triangle-pop.png +0 -0
- package/src/components/weekly-report/img/up.png +0 -0
- package/src/components/weekly-report/index.scss +299 -0
- package/src/components/weekly-report/index.tsx +51 -0
- package/src/components/weekly-report/statistics.tsx +28 -0
- package/src/index.d.ts +2 -1
- package/src/index.js +2 -1
- package/src/pages/aha2021/index.scss +0 -1
- package/src/pages/new-okr-list/header/filter.tsx +35 -4
- package/src/pages/new-okr-list/index.tsx +33 -5
- package/src/utils/helpers.ts +4 -1
|
@@ -11,6 +11,7 @@ interface IProps {
|
|
|
11
11
|
require?: boolean;
|
|
12
12
|
width?: number;
|
|
13
13
|
children?: ReactElement;
|
|
14
|
+
className?: any;
|
|
14
15
|
direction?: "horizontal" | "vertical";
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -18,15 +19,14 @@ const FieldGroup = (props: IProps, ref) => {
|
|
|
18
19
|
const {
|
|
19
20
|
label = "",
|
|
20
21
|
direction = "vertical",
|
|
21
|
-
|
|
22
|
-
// values,
|
|
22
|
+
className,
|
|
23
23
|
children,
|
|
24
24
|
} = props;
|
|
25
25
|
|
|
26
26
|
useImperativeHandle(ref, () => ({}));
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
|
-
<div className={classNames(precls, `${precls}--${direction}`)}>
|
|
29
|
+
<div className={classNames(precls,className, `${precls}--${direction}`)}>
|
|
30
30
|
<div className={`${precls}__label`}>{label}</div>
|
|
31
31
|
<div className={`${precls}__content`}>
|
|
32
32
|
{React.Children.map(children, (child) => {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
&-indent {
|
|
20
20
|
display: inline-block;
|
|
21
21
|
width: 16px;
|
|
22
|
+
min-width: 16px;
|
|
22
23
|
}
|
|
23
24
|
&-title {
|
|
24
25
|
font-size: 14px;
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
font-size: 14px;
|
|
37
38
|
line-height: 22px;
|
|
38
39
|
color: #141c28;
|
|
39
|
-
font-weight:
|
|
40
|
+
font-weight: 600;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -70,7 +70,7 @@ export default props => {
|
|
|
70
70
|
|
|
71
71
|
<span className={classNames(`${preCls}__e-project-node-actions-user`)}>
|
|
72
72
|
{/* <TextAvatar name={user.name} id={user.userId} width={76} editable onEditUser={onEditUserHandler} /> */}
|
|
73
|
-
<Avatar.Text name={data
|
|
73
|
+
<Avatar.Text name={data?.principalUser?.name} id={data?.principalUser?.userId} width={64} extraCls={isGray&&`${preCls}__e-project-node-gray`} />
|
|
74
74
|
</span>
|
|
75
75
|
|
|
76
76
|
<span className={classNames(`${preCls}__e-project-node-actions-date`)}>
|
|
@@ -218,8 +218,8 @@ export default (props) => {
|
|
|
218
218
|
className={classNames(`${preCls}__e-task-level2-node-actions-user`)}
|
|
219
219
|
>
|
|
220
220
|
<Avatar.Text
|
|
221
|
-
name={user
|
|
222
|
-
id={user
|
|
221
|
+
name={user?.name}
|
|
222
|
+
id={user?.userId}
|
|
223
223
|
width={64}
|
|
224
224
|
editable={hasAuth}
|
|
225
225
|
onEditUser={onEditUserHandler}
|
|
@@ -219,8 +219,8 @@ export default (props) => {
|
|
|
219
219
|
<span className={classNames(`${preCls}__e-task-node-actions`)}>
|
|
220
220
|
<span className={classNames(`${preCls}__e-task-node-actions-user`)}>
|
|
221
221
|
<Avatar.Text
|
|
222
|
-
name={user
|
|
223
|
-
id={user
|
|
222
|
+
name={user?.name}
|
|
223
|
+
id={user?.userId}
|
|
224
224
|
width={64}
|
|
225
225
|
editable={hasEditAuth}
|
|
226
226
|
onEditUser={onEditUserHandler}
|
|
@@ -8,7 +8,7 @@ import './index.scss';
|
|
|
8
8
|
interface Props {
|
|
9
9
|
closeModal: Function;
|
|
10
10
|
getModalRef: Function;
|
|
11
|
-
data:
|
|
11
|
+
data: any;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const ShareContent = function({ closeModal, getModalRef, data }: Props) {
|
|
@@ -41,23 +41,10 @@ const ShareContent = function({ closeModal, getModalRef, data }: Props) {
|
|
|
41
41
|
</Queue>
|
|
42
42
|
);
|
|
43
43
|
};
|
|
44
|
-
|
|
45
|
-
okrName: string;
|
|
46
|
-
okrId: number;
|
|
47
|
-
krs: string[];
|
|
48
|
-
user: {
|
|
49
|
-
name: string;
|
|
50
|
-
userId: number;
|
|
51
|
-
departmentName: string;
|
|
52
|
-
departmentId: number;
|
|
53
|
-
avatar: {
|
|
54
|
-
color: string;
|
|
55
|
-
src?: string;
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
}
|
|
44
|
+
|
|
59
45
|
interface ShareProps {
|
|
60
|
-
|
|
46
|
+
shareComponent
|
|
47
|
+
data: any;
|
|
61
48
|
visible:boolean;
|
|
62
49
|
onClose: () => void
|
|
63
50
|
}
|
|
@@ -11,13 +11,14 @@ interface Props {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export default (props: Props) => {
|
|
14
|
-
const { children, style } = props;
|
|
14
|
+
const { children, style,...restProps } = props;
|
|
15
15
|
|
|
16
16
|
// @ts-ignore
|
|
17
17
|
return <SimpleBar
|
|
18
18
|
className={classNames(preCls)}
|
|
19
19
|
timeout={800}
|
|
20
20
|
style={style}
|
|
21
|
+
{...restProps}
|
|
21
22
|
>
|
|
22
23
|
{children}
|
|
23
24
|
</SimpleBar>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
.titaui-select {
|
|
1
|
+
.titaui-select-tags {
|
|
2
|
+
display: flex;
|
|
3
|
+
|
|
2
4
|
.rc-select-single:not(.rc-select-customize-input) .rc-select-selector {
|
|
3
5
|
border: 1px solid #DFE3EA;
|
|
4
6
|
}
|
|
@@ -18,6 +20,10 @@
|
|
|
18
20
|
display: none;
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
.rc-select-multiple .rc-select-selector .rc-select-selection-overflow {
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
.rc-select-single:not(.rc-select-customize-input) .rc-select-selector .rc-select-selection-search-input {
|
|
22
28
|
display: none;
|
|
23
29
|
}
|
|
@@ -46,6 +52,8 @@
|
|
|
46
52
|
height: unset;
|
|
47
53
|
padding: 7px 12px 0;
|
|
48
54
|
border: 1px solid #DFE3EA;
|
|
55
|
+
max-height: 116px;
|
|
56
|
+
overflow: auto;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
.rc-select-multiple .rc-select-selector .rc-select-selection-overflow-item {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { FC, useState
|
|
1
|
+
import React, { FC, useState } from "react";
|
|
2
2
|
import Select, { Option } from "rc-select";
|
|
3
3
|
import classNames from "classnames";
|
|
4
4
|
import "rc-select/assets/index.less";
|
|
@@ -11,7 +11,7 @@ interface DataProps {
|
|
|
11
11
|
|
|
12
12
|
interface Props {
|
|
13
13
|
data: DataProps[];
|
|
14
|
-
name?:any;
|
|
14
|
+
name?: any;
|
|
15
15
|
selected: any;
|
|
16
16
|
onChange: Function;
|
|
17
17
|
mode?: any | "single" | "multiple";
|
|
@@ -19,10 +19,10 @@ interface Props {
|
|
|
19
19
|
placeholder?: string;
|
|
20
20
|
className?: any;
|
|
21
21
|
noBorder?: boolean;
|
|
22
|
-
suffix?:string;
|
|
22
|
+
suffix?: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const preCls = "titaui-select";
|
|
25
|
+
const preCls = "titaui-select-tags";
|
|
26
26
|
|
|
27
27
|
const SelectTags: FC<Props> = ({
|
|
28
28
|
selected = [],
|
|
@@ -31,26 +31,23 @@ const SelectTags: FC<Props> = ({
|
|
|
31
31
|
style = { width: "200px" },
|
|
32
32
|
mode = "single",
|
|
33
33
|
className,
|
|
34
|
-
placeholder=
|
|
34
|
+
placeholder = "请选择",
|
|
35
35
|
name,
|
|
36
36
|
noBorder,
|
|
37
37
|
...restProps
|
|
38
38
|
}) => {
|
|
39
|
-
|
|
40
39
|
const [value, setValue] = useState(selected);
|
|
41
40
|
|
|
42
41
|
const onhandleChange = (value, options) => {
|
|
43
|
-
onChange && onChange(value, options,name);
|
|
42
|
+
onChange && onChange(value, options, name);
|
|
44
43
|
setValue(value);
|
|
45
44
|
};
|
|
46
45
|
|
|
47
46
|
return (
|
|
48
47
|
<div className={preCls}>
|
|
49
48
|
<Select
|
|
50
|
-
className={classNames(className,{[`${preCls}-noborder`]: noBorder}
|
|
51
|
-
dropdownClassName={classNames(
|
|
52
|
-
`titaui-dropDown`,
|
|
53
|
-
{
|
|
49
|
+
className={classNames(className, { [`${preCls}-noborder`]: noBorder })}
|
|
50
|
+
dropdownClassName={classNames(`titaui-dropDown`, {
|
|
54
51
|
[`${preCls}-hasSelectItem`]: mode === "multiple",
|
|
55
52
|
})}
|
|
56
53
|
value={value}
|
|
@@ -75,7 +72,6 @@ const SelectTags: FC<Props> = ({
|
|
|
75
72
|
})}
|
|
76
73
|
</Select>
|
|
77
74
|
</div>
|
|
78
|
-
|
|
79
75
|
);
|
|
80
76
|
};
|
|
81
77
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React, { ReactElement } from 'react'
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
percent: number
|
|
5
|
+
}
|
|
6
|
+
const preCls = 'weekly-report-card-board'
|
|
7
|
+
|
|
8
|
+
function Board({percent}: Props): ReactElement {
|
|
9
|
+
return (
|
|
10
|
+
<div className={preCls}>
|
|
11
|
+
<div className={preCls+'__done'}>
|
|
12
|
+
<div className={preCls+'__undone'}style={{transform:`rotate(${percent/100 * 180}deg)`}}/>
|
|
13
|
+
<div className={preCls+'__space'} style={{transform:'rotate(45deg)'}}/>
|
|
14
|
+
<div className={preCls+'__space'} />
|
|
15
|
+
<div className={preCls+'__space'} style={{transform:'rotate(-45deg)'}}/>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div className={preCls+'__center'}>
|
|
19
|
+
<div>{percent}%</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default Board
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React, { forwardRef, ReactElement, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
2
|
+
import html2Canvas from 'html2canvas'
|
|
3
|
+
import Board from './board';
|
|
4
|
+
import { getLoginUserInfo } from '../../utils/helpers';
|
|
5
|
+
import { drawAvatar } from './common-painter/darwAvatar';
|
|
6
|
+
import drawName from './common-painter/drawName';
|
|
7
|
+
import Statistics from './statistics';
|
|
8
|
+
import {isWx} from '../../utils/bs-global'
|
|
9
|
+
import './index.scss';
|
|
10
|
+
import { rpost } from '../../utils/request';
|
|
11
|
+
|
|
12
|
+
const preCls = 'weekly-report-card';
|
|
13
|
+
|
|
14
|
+
interface Props {}
|
|
15
|
+
const getImageBase64ByUrl = toUserId => {
|
|
16
|
+
return rpost('v1')(`okr/share/getBase64Str`, { toUserId })
|
|
17
|
+
.then(getBase64Str => {
|
|
18
|
+
return getBase64Str.Data;
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
function Card({}: Props,ref): ReactElement {
|
|
22
|
+
const [avatarSrc, setAvatarSrc] = useState<string>();
|
|
23
|
+
const [nameSrc,setNameSrc] = useState<string>()
|
|
24
|
+
const loginUserInfo = getLoginUserInfo();
|
|
25
|
+
const cardRef = useRef()
|
|
26
|
+
useImperativeHandle(ref,()=>({
|
|
27
|
+
getImage: async function(){
|
|
28
|
+
return new Promise(res => {
|
|
29
|
+
let shareContent = cardRef.current, //需要截图的包裹的(原生的)DOM 对象
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
width = 375, //获取dom 宽度
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
height = 710, //获取dom 高度
|
|
34
|
+
canvas = document.createElement("canvas"), //创建一个canvas节点
|
|
35
|
+
scale = 2; //定义任意放大倍数 支持小数
|
|
36
|
+
canvas.width = width * scale; //定义canvas 宽度 * 缩放
|
|
37
|
+
canvas.height = height * scale; //定义canvas高度 *缩放
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
canvas.style.width = width + "px";
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
canvas.style.height = height + "px";
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
let opts = {
|
|
44
|
+
backgroundColor: null, // 背景透明
|
|
45
|
+
scale: scale, // 添加的scale 参数
|
|
46
|
+
canvas: canvas, // 自定义 canvas
|
|
47
|
+
logging: false, // 日志开关,便于查看html2canvas的内部执行流程
|
|
48
|
+
width: width, // dom 原始宽度
|
|
49
|
+
height: height,
|
|
50
|
+
useCORS: true // 【重要】开启跨域配置
|
|
51
|
+
}
|
|
52
|
+
html2Canvas(cardRef.current as any, opts).then(canvas => {
|
|
53
|
+
let imgUrl = canvas.toDataURL('image/png'); //得到图片的base64编码数据
|
|
54
|
+
res(imgUrl)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
}),[])
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (isWx()) {
|
|
62
|
+
window['WWOpenData'] && window['WWOpenData'].enableCanvasSharing && window['WWOpenData'].enableCanvasSharing()
|
|
63
|
+
}
|
|
64
|
+
if (loginUserInfo.UserAvatar.HasAvatar) {
|
|
65
|
+
getImageBase64ByUrl(loginUserInfo.Id).then((data) =>
|
|
66
|
+
drawAvatar(
|
|
67
|
+
{
|
|
68
|
+
name: loginUserInfo.Name,
|
|
69
|
+
userId: loginUserInfo.Id,
|
|
70
|
+
color: loginUserInfo.UserAvatar.Color,
|
|
71
|
+
},
|
|
72
|
+
'data:image/jpeg;base64,' + data.obj,
|
|
73
|
+
),
|
|
74
|
+
).then(setAvatarSrc);
|
|
75
|
+
}else {
|
|
76
|
+
drawAvatar(
|
|
77
|
+
{
|
|
78
|
+
name: loginUserInfo.Name,
|
|
79
|
+
userId: loginUserInfo.Id,
|
|
80
|
+
color: loginUserInfo.UserAvatar.Color,
|
|
81
|
+
},
|
|
82
|
+
'',
|
|
83
|
+
).then(setAvatarSrc);
|
|
84
|
+
}
|
|
85
|
+
drawName(loginUserInfo.Name,loginUserInfo.Id).then(setNameSrc)
|
|
86
|
+
return () => {
|
|
87
|
+
if (isWx()) {
|
|
88
|
+
window['WWOpenData'] && window['WWOpenData'].disableCanvasSharing && window['WWOpenData'].disableCanvasSharing()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div className={preCls + '-content'} ref={cardRef}>
|
|
95
|
+
<img className={preCls + '__title'} src={require('./img/title.png')} />
|
|
96
|
+
<div className={preCls + '__date-range'}>6月7日-6月11日</div>
|
|
97
|
+
<div className={preCls + '__shadow'} />
|
|
98
|
+
<img className={preCls + '__laba'} src={require('./img/laba.png')}/>
|
|
99
|
+
<img className={preCls + '__bofang'} src={require('./img/bofang.png')}/>
|
|
100
|
+
<div className={preCls + '__body'}>
|
|
101
|
+
<div className={preCls + '__avatar'}>
|
|
102
|
+
<img src={avatarSrc} />
|
|
103
|
+
</div>
|
|
104
|
+
<img className={preCls + '__userName'} src={nameSrc}/>
|
|
105
|
+
<img className={preCls + '__quote'} src={require('./img/quote.png')} alt="" />
|
|
106
|
+
<div className={preCls + '__text'}>
|
|
107
|
+
坚持每周反馈才是真正的优秀真正的优秀
|
|
108
|
+
</div>
|
|
109
|
+
<Board percent={80} />
|
|
110
|
+
<div className={preCls + '__comparison'}>
|
|
111
|
+
OKR 完成度
|
|
112
|
+
<img className={preCls + '__direction'} src={require('./img/up.png')} alt="" />
|
|
113
|
+
<span style={{color:'#4DC591'}}>20%</span>
|
|
114
|
+
</div>
|
|
115
|
+
<div className={preCls + '__colleague'}>
|
|
116
|
+
恭喜超越了 <span>80%</span> 的同事
|
|
117
|
+
</div>
|
|
118
|
+
<Statistics/>
|
|
119
|
+
</div>
|
|
120
|
+
<img className={preCls + '__bottom'} src={require('./img/bottom.png')}/>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default forwardRef(Card);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import openDataPainter from "./openDataPainter";
|
|
2
|
+
import setSize from "./setSize";
|
|
3
|
+
import { drawImage } from "./utils";
|
|
4
|
+
|
|
5
|
+
interface avatarParms {
|
|
6
|
+
url?: string;
|
|
7
|
+
color?: string;
|
|
8
|
+
name: string;
|
|
9
|
+
id: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
maxNameWidth?: number;
|
|
15
|
+
}
|
|
16
|
+
export async function drawAvatar(shareData, avatar: string) {
|
|
17
|
+
if (window['WWOpenData'] && window['WWOpenData'].initCanvas) {
|
|
18
|
+
window['WWOpenData'].initCanvas()
|
|
19
|
+
}
|
|
20
|
+
const canvas = document.createElement('canvas');
|
|
21
|
+
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
22
|
+
setSize(canvas, ctx, 72, 72);
|
|
23
|
+
ctx.beginPath();
|
|
24
|
+
await draw(ctx, {
|
|
25
|
+
width: 72,
|
|
26
|
+
height: 72,
|
|
27
|
+
x: 0,
|
|
28
|
+
y: 0,
|
|
29
|
+
url: avatar,
|
|
30
|
+
name: shareData.name,
|
|
31
|
+
id: shareData.userId,
|
|
32
|
+
color: shareData.color
|
|
33
|
+
});
|
|
34
|
+
const dataUrl = canvas.toDataURL();
|
|
35
|
+
|
|
36
|
+
return dataUrl;
|
|
37
|
+
}
|
|
38
|
+
async function draw (ctx: CanvasRenderingContext2D, params: avatarParms):Promise<number> {
|
|
39
|
+
ctx.save();
|
|
40
|
+
ctx.beginPath();
|
|
41
|
+
ctx.arc(
|
|
42
|
+
params.width / 2 + params.x,
|
|
43
|
+
params.height / 2 + params.y,
|
|
44
|
+
params.width / 2,
|
|
45
|
+
0,
|
|
46
|
+
Math.PI * 2,
|
|
47
|
+
false,
|
|
48
|
+
);
|
|
49
|
+
ctx.clip();
|
|
50
|
+
ctx.closePath();
|
|
51
|
+
params.url ? await drawImageAvatar(ctx, params) : await drawTextAvatar(ctx, params);
|
|
52
|
+
ctx.restore();
|
|
53
|
+
return params.y + params.height;
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
function drawImageAvatar(ctx: CanvasRenderingContext2D, avatar: avatarParms) {
|
|
57
|
+
return drawImage(ctx,avatar.url as string,avatar.x,avatar.y,avatar.width,avatar.height)
|
|
58
|
+
}
|
|
59
|
+
function drawTextAvatar(ctx: CanvasRenderingContext2D, avatar: avatarParms) {
|
|
60
|
+
console.log('text')
|
|
61
|
+
const maxNameWidth = avatar.maxNameWidth || 28;
|
|
62
|
+
return new Promise<void>(async resolve => {
|
|
63
|
+
ctx.fillStyle = avatar.color as string;
|
|
64
|
+
ctx.fill();
|
|
65
|
+
|
|
66
|
+
ctx.closePath();
|
|
67
|
+
ctx.fillStyle = 'white';
|
|
68
|
+
ctx.font = '14px arial';
|
|
69
|
+
ctx.textBaseline = 'bottom';
|
|
70
|
+
let namePainter = await openDataPainter(ctx,{id:avatar.id,name:avatar.name,type:'userName'},22,maxNameWidth);
|
|
71
|
+
const { width, height } = namePainter;
|
|
72
|
+
|
|
73
|
+
namePainter.draw(
|
|
74
|
+
ctx,
|
|
75
|
+
avatar.x + avatar.width / 2 - width / 2,
|
|
76
|
+
avatar.y + avatar.height / 2 - height / 2,
|
|
77
|
+
);
|
|
78
|
+
ctx.closePath();
|
|
79
|
+
resolve();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import openDataPainter from "./openDataPainter";
|
|
2
|
+
import setSize from "./setSize";
|
|
3
|
+
|
|
4
|
+
export default async function(name,id){
|
|
5
|
+
const canvas = document.createElement('canvas');
|
|
6
|
+
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
7
|
+
setSize(canvas, ctx, 295, 24);
|
|
8
|
+
ctx.beginPath();
|
|
9
|
+
const lineHeight = 24;
|
|
10
|
+
ctx.font = `bold 16px/${lineHeight}px arial,sans-serif`;
|
|
11
|
+
ctx.fillStyle = '#141C28';
|
|
12
|
+
ctx.textBaseline='middle';
|
|
13
|
+
const painter = await openDataPainter(ctx, {name,id,type:'userName'},24,295);
|
|
14
|
+
let x = (295 - (painter.width)) / 2
|
|
15
|
+
painter.draw(ctx,x,0)
|
|
16
|
+
const dataUrl = canvas.toDataURL();
|
|
17
|
+
|
|
18
|
+
return dataUrl;
|
|
19
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getOpenId } from "&/components/common/openData";
|
|
2
|
+
import { isWx } from "&/components/helper";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
interface openDataParam {
|
|
6
|
+
id: number; name: string; type: 'userName' | 'departmentName'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default async function (
|
|
10
|
+
ctx: CanvasRenderingContext2D,
|
|
11
|
+
openDataParams: openDataParam,
|
|
12
|
+
lineHeight: number,
|
|
13
|
+
maxWidth: number = Infinity
|
|
14
|
+
) {
|
|
15
|
+
let name = openDataParams.name
|
|
16
|
+
if (isWx()) {
|
|
17
|
+
name = await getOpenData(openDataParams)
|
|
18
|
+
}
|
|
19
|
+
const { width } = ctx.measureText(name);
|
|
20
|
+
let actualWidth = Math.min(width, maxWidth)
|
|
21
|
+
return {
|
|
22
|
+
width: actualWidth,
|
|
23
|
+
height: lineHeight,
|
|
24
|
+
draw: function (ctx: CanvasRenderingContext2D, x: number, y: number) {
|
|
25
|
+
ctx.save()
|
|
26
|
+
ctx.textBaseline = 'middle';
|
|
27
|
+
ctx.beginPath();
|
|
28
|
+
ctx.rect(x, y, actualWidth, lineHeight);
|
|
29
|
+
ctx.clip();
|
|
30
|
+
ctx.fillText(name, x, y + lineHeight / 2);
|
|
31
|
+
ctx.closePath()
|
|
32
|
+
ctx.restore()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const getOpenData = ({ id, name, type }: openDataParam) => {
|
|
37
|
+
const openId = getOpenId(id, type)
|
|
38
|
+
if (!window['WWOpenData'] || !openId) return Promise.resolve(name)
|
|
39
|
+
return new Promise<string>((resolve, reject) => {
|
|
40
|
+
window['WWOpenData'].prefetch({ items: [{ id: openId, type }] }, (err, data) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
return reject(err)
|
|
43
|
+
}
|
|
44
|
+
resolve(data.items[0].data)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default function (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, width, height) {
|
|
2
|
+
const devicePixelRatio = window.devicePixelRatio || 1;
|
|
3
|
+
const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
4
|
+
//要将 canvas 的宽高设置成容器宽高的 2 倍
|
|
5
|
+
canvas.width = width * devicePixelRatio;
|
|
6
|
+
canvas.height = height * devicePixelRatio;
|
|
7
|
+
canvas.style.width = width + 'px';
|
|
8
|
+
canvas.style.height = height + 'px';
|
|
9
|
+
//然后将画布缩放,将图像放大两倍画到画布上
|
|
10
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
11
|
+
ctx.putImageData(data, 0, 0);
|
|
12
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export function drawImage(
|
|
2
|
+
ctx: CanvasRenderingContext2D,
|
|
3
|
+
url: string,
|
|
4
|
+
x: number,
|
|
5
|
+
y: number,
|
|
6
|
+
width: number,
|
|
7
|
+
height: number,
|
|
8
|
+
) {
|
|
9
|
+
return new Promise<void>(res => {
|
|
10
|
+
const img = new Image();
|
|
11
|
+
img.src = url;
|
|
12
|
+
img.onload = function () {
|
|
13
|
+
ctx.drawImage(img, x, y, width, height);
|
|
14
|
+
res();
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function drawTextMiddle(
|
|
19
|
+
ctx: CanvasRenderingContext2D,
|
|
20
|
+
text: string,
|
|
21
|
+
x: number,
|
|
22
|
+
y: number,
|
|
23
|
+
lineHeight: number,
|
|
24
|
+
maxWidth?: number,
|
|
25
|
+
textAlign?: 'left' | 'center'
|
|
26
|
+
) {
|
|
27
|
+
ctx.textBaseline = 'middle';
|
|
28
|
+
if (!maxWidth) {
|
|
29
|
+
|
|
30
|
+
ctx.fillText(text, x, y + lineHeight / 2);
|
|
31
|
+
return y + lineHeight;
|
|
32
|
+
}
|
|
33
|
+
const lines = breakLinesForCanvas(ctx, text, maxWidth);
|
|
34
|
+
lines.forEach((l) => {
|
|
35
|
+
const { width } = ctx.measureText(l)
|
|
36
|
+
let left = x;
|
|
37
|
+
if (textAlign == 'center' && maxWidth) {
|
|
38
|
+
console.log(maxWidth, width)
|
|
39
|
+
left = (maxWidth - width) / 2 + x
|
|
40
|
+
}
|
|
41
|
+
ctx.fillText(l, left, y + lineHeight / 2);
|
|
42
|
+
|
|
43
|
+
y = y + lineHeight;
|
|
44
|
+
})
|
|
45
|
+
return y
|
|
46
|
+
}
|
|
47
|
+
function findBreakPoint(text, width, context) {
|
|
48
|
+
var min = 0;
|
|
49
|
+
var max = text.length - 1;
|
|
50
|
+
|
|
51
|
+
while (min <= max) {
|
|
52
|
+
var middle = Math.floor((min + max) / 2);
|
|
53
|
+
var middleWidth = context.measureText(text.substr(0, middle)).width;
|
|
54
|
+
var oneCharWiderThanMiddleWidth = context.measureText(text.substr(0, middle + 1)).width;
|
|
55
|
+
if (middleWidth <= width && oneCharWiderThanMiddleWidth > width) {
|
|
56
|
+
return middle;
|
|
57
|
+
}
|
|
58
|
+
if (middleWidth < width) {
|
|
59
|
+
min = middle + 1;
|
|
60
|
+
} else {
|
|
61
|
+
max = middle - 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return -1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function breakLinesForCanvas(ctx: CanvasRenderingContext2D, text: string, width: number) {
|
|
69
|
+
var result: string[] = [];
|
|
70
|
+
var breakPoint = 0;
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
while ((breakPoint = findBreakPoint(text, width, ctx)) !== -1) {
|
|
74
|
+
result.push(text.substr(0, breakPoint));
|
|
75
|
+
text = text.substr(breakPoint);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (text) {
|
|
79
|
+
result.push(text);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|