neo-cmp-cli 1.1.7 → 1.1.8
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/package.json +1 -1
- package/src/module/index.js +5 -0
- package/src/module/main.js +3 -3
- package/src/module/neoInitByCopy.js +4 -0
- package/src/template/antd-custom-cmp-template/.prettierrc.js +12 -0
- package/src/template/antd-custom-cmp-template/README.md +47 -0
- package/src/template/antd-custom-cmp-template/commitlint.config.js +59 -0
- package/src/template/antd-custom-cmp-template/neo.config.js +109 -0
- package/src/template/antd-custom-cmp-template/package.json +59 -0
- package/src/template/antd-custom-cmp-template/public/css/base.css +283 -0
- package/src/template/antd-custom-cmp-template/public/scripts/app/bluebird.js +6679 -0
- package/src/template/antd-custom-cmp-template/public/template.html +13 -0
- package/src/template/antd-custom-cmp-template/src/assets/css/common.scss +127 -0
- package/src/template/antd-custom-cmp-template/src/assets/css/mixin.scss +47 -0
- package/src/template/antd-custom-cmp-template/src/components/data-dashboard/README.md +39 -0
- package/src/template/antd-custom-cmp-template/src/components/data-dashboard/index.tsx +462 -0
- package/src/template/antd-custom-cmp-template/src/components/data-dashboard/model.ts +76 -0
- package/src/template/antd-custom-cmp-template/src/components/data-dashboard/style.scss +1409 -0
- package/src/template/antd-custom-cmp-template/src/components/info-card/index.tsx +72 -0
- package/src/template/antd-custom-cmp-template/src/components/info-card/model.ts +78 -0
- package/src/template/antd-custom-cmp-template/src/components/info-card/style.scss +105 -0
- package/src/template/antd-custom-cmp-template/tsconfig.json +68 -0
- package/src/template/echart-custom-cmp-template/.prettierrc.js +12 -0
- package/src/template/echart-custom-cmp-template/README.md +47 -0
- package/src/template/echart-custom-cmp-template/commitlint.config.js +59 -0
- package/src/template/echart-custom-cmp-template/neo.config.js +109 -0
- package/src/template/echart-custom-cmp-template/package.json +58 -0
- package/src/template/echart-custom-cmp-template/public/css/base.css +283 -0
- package/src/template/echart-custom-cmp-template/public/scripts/app/bluebird.js +6679 -0
- package/src/template/echart-custom-cmp-template/public/template.html +13 -0
- package/src/template/echart-custom-cmp-template/src/assets/css/common.scss +127 -0
- package/src/template/echart-custom-cmp-template/src/assets/css/mixin.scss +47 -0
- package/src/template/echart-custom-cmp-template/src/components/info-card/index.tsx +69 -0
- package/src/template/echart-custom-cmp-template/src/components/info-card/model.ts +78 -0
- package/src/template/echart-custom-cmp-template/src/components/info-card/style.scss +105 -0
- package/src/template/echart-custom-cmp-template/src/components/list-widget/README.md +2 -0
- package/src/template/echart-custom-cmp-template/src/components/list-widget/index.tsx +208 -0
- package/src/template/echart-custom-cmp-template/src/components/list-widget/model.ts +90 -0
- package/src/template/echart-custom-cmp-template/src/components/list-widget/style.scss +350 -0
- package/src/template/echart-custom-cmp-template/tsconfig.json +68 -0
- package/src/template/react-custom-cmp-template/package.json +1 -1
- package/src/template/react-ts-custom-cmp-template/package.json +1 -2
- package/src/template/react-ts-custom-cmp-template/src/components/info-card/index.tsx +1 -4
- package/src/template/vue2-custom-cmp-template/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<html lang="en">
|
|
2
|
+
<head>
|
|
3
|
+
<meta charset="UTF-8">
|
|
4
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
5
|
+
<meta name="format-detection" content="telephone=no"/>
|
|
6
|
+
<meta name="viewport" content="initial-scale=1.0,user-scalable=no,width=device-width,viewport-fit=cover">
|
|
7
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
8
|
+
<title>自定义组件预览页</title>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* 公共的自定义函数 */
|
|
2
|
+
|
|
3
|
+
@function px2vw($px, $screen-width: 750) {
|
|
4
|
+
@return ($px / $screen-width) * 100vw;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@function px2rem($px, $remRate: 100) {
|
|
8
|
+
@return ($px / $remRate) + rem;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@function px2vmin($px, $screen-width: 750) {
|
|
12
|
+
@return ($px / $screen-width) * 100vmin;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@mixin fillBox {
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* 头部细线 */
|
|
21
|
+
@mixin borderTop {
|
|
22
|
+
content: '';
|
|
23
|
+
position: absolute;
|
|
24
|
+
left: 0;
|
|
25
|
+
right: 0;
|
|
26
|
+
top: 0;
|
|
27
|
+
width: 100%;
|
|
28
|
+
height: 1px;
|
|
29
|
+
background: #ddd;
|
|
30
|
+
transform: scaleY(0.5);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* 底部细线 */
|
|
34
|
+
@mixin borderBtm {
|
|
35
|
+
content: '';
|
|
36
|
+
position: absolute;
|
|
37
|
+
left: 0;
|
|
38
|
+
right: 0;
|
|
39
|
+
bottom: 0;
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 1px;
|
|
42
|
+
background: #ddd;
|
|
43
|
+
transform: scaleY(0.5);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* 统一的内边距 */
|
|
47
|
+
@mixin unifiedPadding($value: 40) {
|
|
48
|
+
padding: 0 px2rem($value) 0 px2rem($value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* 统一的左内边距 */
|
|
52
|
+
@mixin unifiedLeftPadding($value: 40) {
|
|
53
|
+
padding-left: px2rem($value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* 统一的右内边距 */
|
|
57
|
+
@mixin unifiedRightPadding($value: 40) {
|
|
58
|
+
padding-right: px2rem($value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* 统一的底部边框样式 */
|
|
62
|
+
@mixin unifiedBottomBorder {
|
|
63
|
+
border-bottom: 1px solid #ddd;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* 统一的上边框样式 */
|
|
67
|
+
@mixin unifiedTopBorder {
|
|
68
|
+
border-top: 1px solid #ddd;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* 统一的Item高度 */
|
|
72
|
+
@mixin unifiedItemHeight {
|
|
73
|
+
line-height: px2rem(120);
|
|
74
|
+
height: px2rem(120);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* 设置行高样式 */
|
|
78
|
+
@mixin setItemHeight($value: 120) {
|
|
79
|
+
line-height: px2rem($value);
|
|
80
|
+
height: px2rem($value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* 统一的Item样式 */
|
|
84
|
+
@mixin unifiedItemStyle {
|
|
85
|
+
font-family: PingFangSC-Regular;
|
|
86
|
+
font-size: px2rem(28);
|
|
87
|
+
color: #828282;
|
|
88
|
+
letter-spacing: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* 统一的弹性盒子样式 */
|
|
92
|
+
@mixin unifiedFlexBoxStyle {
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-wrap: nowrap;
|
|
95
|
+
justify-content: center;
|
|
96
|
+
align-items: center;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* 统一的Title样式 */
|
|
100
|
+
@mixin unifiedTitleStyle {
|
|
101
|
+
font-family: PingFangSC-Regular;
|
|
102
|
+
font-size: px2rem(40);
|
|
103
|
+
color: #1e1e1e;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* 统一的内容样式 */
|
|
107
|
+
@mixin unifiedContentStyle {
|
|
108
|
+
font-family: PingFangSC-Regular;
|
|
109
|
+
font-size: px2rem(28);
|
|
110
|
+
color: #1e1e1e;
|
|
111
|
+
letter-spacing: 0;
|
|
112
|
+
text-align: right;
|
|
113
|
+
line-height: px2rem(28);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* 底部导航盒子样式 */
|
|
117
|
+
@mixin fixedBottomBox {
|
|
118
|
+
position: fixed;
|
|
119
|
+
left: 0;
|
|
120
|
+
bottom: 0;
|
|
121
|
+
width: 100%;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 常用的变量
|
|
125
|
+
$background-color: #fafafa;
|
|
126
|
+
$border-color: #f7f7f7;
|
|
127
|
+
$page-padding-top: px2rem(20);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// 通用mixin
|
|
2
|
+
$borderColor: #ddd;
|
|
3
|
+
|
|
4
|
+
// type 为top 或者 bottom
|
|
5
|
+
@mixin borderTopOrBtm($type) {
|
|
6
|
+
&::after {
|
|
7
|
+
content: '';
|
|
8
|
+
position: absolute;
|
|
9
|
+
left: 0;
|
|
10
|
+
right: 0;
|
|
11
|
+
#{$type}: 0;
|
|
12
|
+
width: 100%;
|
|
13
|
+
height: 1px;
|
|
14
|
+
background: $borderColor;
|
|
15
|
+
transform: scaleY(0.5);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// type为 right 或者 left
|
|
20
|
+
@mixin borderRtOrLt($type) {
|
|
21
|
+
&::after {
|
|
22
|
+
content: '';
|
|
23
|
+
position: absolute;
|
|
24
|
+
top: 0;
|
|
25
|
+
bottom: 0;
|
|
26
|
+
#{$type}: 0;
|
|
27
|
+
height: 100%;
|
|
28
|
+
width: 1px;
|
|
29
|
+
background: $borderColor;
|
|
30
|
+
transform: scaleX(0.5);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//超出1行显示...
|
|
35
|
+
@mixin ellipsis1 {
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
text-overflow: ellipsis;
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 超出多行显示...
|
|
42
|
+
@mixin ellipsis($num) {
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
display: -webkit-box;
|
|
45
|
+
-webkit-line-clamp: $num;
|
|
46
|
+
-webkit-box-orient: vertical;
|
|
47
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import './style.scss'; // 组件内容样式
|
|
3
|
+
|
|
4
|
+
interface InfoCardProps {
|
|
5
|
+
title: string;
|
|
6
|
+
backgroundImage: string;
|
|
7
|
+
imgCount: number;
|
|
8
|
+
commentCount: number;
|
|
9
|
+
data?: any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default class InfoCard extends React.PureComponent<InfoCardProps> {
|
|
13
|
+
constructor(props: InfoCardProps) {
|
|
14
|
+
super(props);
|
|
15
|
+
this.agreeDataFormat = this.agreeDataFormat.bind(this);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
agreeDataFormat(agreeData: number) {
|
|
19
|
+
if (agreeData && agreeData <= 9999) {
|
|
20
|
+
return agreeData;
|
|
21
|
+
}
|
|
22
|
+
if (agreeData && agreeData > 9999) {
|
|
23
|
+
return `${Math.floor(agreeData / 1000) / 10}w`;
|
|
24
|
+
}
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
render() {
|
|
29
|
+
const { title, backgroundImage, imgCount, commentCount } = this.props;
|
|
30
|
+
console.log('当前自定义组件:', this.props, this);
|
|
31
|
+
const curAmisData = this.props.data || {};
|
|
32
|
+
|
|
33
|
+
const userInfo = curAmisData.__NeoCurrentUser;
|
|
34
|
+
const systemInfo = curAmisData.__NeoSystemInfo || {};
|
|
35
|
+
|
|
36
|
+
const curBackgroundImage =
|
|
37
|
+
backgroundImage || 'https://neo-widgets.bj.bcebos.com/NeoCRM.jpg';
|
|
38
|
+
return (
|
|
39
|
+
<div className="info-card-container">
|
|
40
|
+
<div className="news-title">
|
|
41
|
+
{title ||
|
|
42
|
+
'营销服全场景智能CRM,帮助企业搭建数字化客户经营平台,实现业绩高质量增长。'}
|
|
43
|
+
{systemInfo.tenantName ? `【${systemInfo.tenantName}】` : ''}
|
|
44
|
+
</div>
|
|
45
|
+
<div className="item-imgbox">
|
|
46
|
+
{userInfo && userInfo.icon && (
|
|
47
|
+
<div className="user-info-box">
|
|
48
|
+
<img src={userInfo.icon} className="user-icon" />
|
|
49
|
+
<span className="user-name">{userInfo.name}</span>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
<div
|
|
53
|
+
className="news-img"
|
|
54
|
+
style={{ backgroundImage: `url(${curBackgroundImage})` }}
|
|
55
|
+
></div>
|
|
56
|
+
{imgCount > 0 && <div className="img-count">{imgCount}</div>}
|
|
57
|
+
</div>
|
|
58
|
+
<div className="news-info">
|
|
59
|
+
<div className="left media-mark">NeoCRM · 低代码平台</div>
|
|
60
|
+
{commentCount && commentCount > 0 && (
|
|
61
|
+
<div className="cmt-num right">
|
|
62
|
+
{this.agreeDataFormat(commentCount)}评
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 自定义组件对接编辑器的描述文件
|
|
3
|
+
*/
|
|
4
|
+
export class InfoCardModel {
|
|
5
|
+
/**
|
|
6
|
+
* cmpType 为自定义组件名称,用于标识组件的唯一性
|
|
7
|
+
* 在构建时根据当前组件目录名称自动生成
|
|
8
|
+
*/
|
|
9
|
+
// cmpType: string = 'info-card';
|
|
10
|
+
|
|
11
|
+
// 组件名称,用于设置在编辑器左侧组件面板中展示的名称
|
|
12
|
+
label: string = '信息卡片';
|
|
13
|
+
|
|
14
|
+
// 组件描述,用于设置在编辑器左侧组件面板中展示的描述
|
|
15
|
+
description: string = '信息展示卡片';
|
|
16
|
+
|
|
17
|
+
// 分类标签,用于设置在编辑器左侧组件面板哪个分类中展示(可设置多个分类标签)
|
|
18
|
+
tags: string[] = ['自定义组件'];
|
|
19
|
+
|
|
20
|
+
// 组件图标,用于设置在编辑器左侧组件面板中展示的图标
|
|
21
|
+
iconSrc: string = 'https://neo-widgets.bj.bcebos.com/custom-widget.svg';
|
|
22
|
+
// iconSrc = 'https://neo-widgets.bj.bcebos.com/favicon.png';
|
|
23
|
+
|
|
24
|
+
// 初次插入页面的默认属性数据
|
|
25
|
+
defaultComProps = {
|
|
26
|
+
title:
|
|
27
|
+
'营销服全场景智能CRM,帮助企业搭建数字化客户经营平台,实现业绩高质量增长。',
|
|
28
|
+
label: '信息卡片',
|
|
29
|
+
backgroundImage: 'https://neo-widgets.bj.bcebos.com/NeoCRM.jpg',
|
|
30
|
+
imgCount: 3,
|
|
31
|
+
commentCount: 2025,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// 设计器端预览时展示的默认数据
|
|
35
|
+
previewComProps = {
|
|
36
|
+
label: '信息卡片',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 组件面板配置,用于生成编辑器右侧属性配置面板内容
|
|
41
|
+
*/
|
|
42
|
+
propsSchema = [
|
|
43
|
+
{
|
|
44
|
+
type: 'textarea',
|
|
45
|
+
name: 'title',
|
|
46
|
+
label: '卡片title',
|
|
47
|
+
value:
|
|
48
|
+
'营销服全场景智能CRM,帮助企业搭建数字化客户经营平台,实现业绩高质量增长。',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'text',
|
|
52
|
+
name: 'backgroundImage',
|
|
53
|
+
label: '展示图片',
|
|
54
|
+
value: 'https://neo-widgets.bj.bcebos.com/NeoCRM.jpg',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'number',
|
|
58
|
+
name: 'imgCount',
|
|
59
|
+
label: '图片数量',
|
|
60
|
+
value: 3,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'number',
|
|
64
|
+
name: 'commentCount',
|
|
65
|
+
label: '评论数',
|
|
66
|
+
value: 2025,
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// 支持 函数式写法:propsSchemaCreator,com 为组件实例。优先级比 propsSchema 高
|
|
71
|
+
/*
|
|
72
|
+
propsSchemaCreator = (com: any) => {
|
|
73
|
+
return [];
|
|
74
|
+
};
|
|
75
|
+
*/
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default InfoCardModel;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--padding-bottom: 12px;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.info-card-container {
|
|
6
|
+
position: relative;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
|
|
9
|
+
/* border-bottom: 1px solid #ececec; */
|
|
10
|
+
margin: 6px 12px;
|
|
11
|
+
padding: 6px var(--padding-bottom);
|
|
12
|
+
background-color: #fff;
|
|
13
|
+
|
|
14
|
+
.news-title {
|
|
15
|
+
padding: 6px 0;
|
|
16
|
+
font-family: PingFangSC-Regular;
|
|
17
|
+
font-size: 16px;
|
|
18
|
+
line-height: 22px;
|
|
19
|
+
color: #5f5e5e;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.item-imgbox {
|
|
23
|
+
position: relative;
|
|
24
|
+
height: 395px;
|
|
25
|
+
background: #f0f0f0;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
text-align: center;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
|
|
31
|
+
.news-img {
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: 100%;
|
|
34
|
+
box-sizing: border-box;
|
|
35
|
+
background-size: contain;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.img-count {
|
|
39
|
+
position: absolute;
|
|
40
|
+
top: 0;
|
|
41
|
+
right: 0;
|
|
42
|
+
padding: 6px 8px;
|
|
43
|
+
color: #fff;
|
|
44
|
+
min-width: 60px;
|
|
45
|
+
text-align: center;
|
|
46
|
+
line-height: 1.2;
|
|
47
|
+
background: rgb(0 0 0 / 40%);
|
|
48
|
+
font-size: 25px;
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.user-info-box {
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: 10px;
|
|
56
|
+
left: 10px;
|
|
57
|
+
width: 100px;
|
|
58
|
+
min-height: 100px;
|
|
59
|
+
padding: 6px 8px;
|
|
60
|
+
color: #fff;
|
|
61
|
+
min-width: 60px;
|
|
62
|
+
text-align: center;
|
|
63
|
+
line-height: 1.2;
|
|
64
|
+
background: rgb(0 0 0 / 40%);
|
|
65
|
+
font-size: 25px;
|
|
66
|
+
box-sizing: border-box;
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
|
|
73
|
+
.user-icon {
|
|
74
|
+
width: 64px;
|
|
75
|
+
height: 64px;
|
|
76
|
+
border-radius: 50%;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.user-name {
|
|
80
|
+
font-size: 16px;
|
|
81
|
+
font-weight: 600;
|
|
82
|
+
margin-top: 10px;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.news-info {
|
|
88
|
+
font-family: PingFangSC-Light;
|
|
89
|
+
height: 28px;
|
|
90
|
+
box-sizing: border-box;
|
|
91
|
+
display: flex;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
align-items: center;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.media-mark,
|
|
97
|
+
.cmt-num {
|
|
98
|
+
display: inline-block;
|
|
99
|
+
height: 28px;
|
|
100
|
+
line-height: 28px;
|
|
101
|
+
font-family: PingFangSC-Light;
|
|
102
|
+
font-size: 18px;
|
|
103
|
+
color: #828282;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import './style.scss';
|
|
3
|
+
|
|
4
|
+
interface ListItem {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
image: string;
|
|
9
|
+
author: string;
|
|
10
|
+
createTime: string;
|
|
11
|
+
views: number;
|
|
12
|
+
likes: number;
|
|
13
|
+
comments: number;
|
|
14
|
+
tags: string[];
|
|
15
|
+
status: 'published' | 'draft' | 'archived';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ListWidgetProps {
|
|
19
|
+
title: string;
|
|
20
|
+
showImage: boolean;
|
|
21
|
+
showTags: boolean;
|
|
22
|
+
itemCount: number;
|
|
23
|
+
listType: 'default' | 'card' | 'simple';
|
|
24
|
+
data?: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 通过 Cursor 生成的一个自定义组件示例
|
|
29
|
+
* 组件名:ListWidget
|
|
30
|
+
* 组件描述:支持配置的列表展示组件,内置模拟数据
|
|
31
|
+
*/
|
|
32
|
+
export default class ListWidget extends React.PureComponent<ListWidgetProps> {
|
|
33
|
+
constructor(props: ListWidgetProps) {
|
|
34
|
+
super(props);
|
|
35
|
+
this.generateMockData = this.generateMockData.bind(this);
|
|
36
|
+
this.formatNumber = this.formatNumber.bind(this);
|
|
37
|
+
this.renderListItem = this.renderListItem.bind(this);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 生成模拟数据
|
|
41
|
+
generateMockData(count: number): ListItem[] {
|
|
42
|
+
const mockTitles = [
|
|
43
|
+
'营销服全场景智能CRM解决方案',
|
|
44
|
+
'企业数字化转型最佳实践',
|
|
45
|
+
'客户关系管理新趋势分析',
|
|
46
|
+
'数字化营销策略深度解析',
|
|
47
|
+
'智能客服系统优化方案',
|
|
48
|
+
'数据驱动的客户洞察',
|
|
49
|
+
'全渠道客户体验提升',
|
|
50
|
+
'AI赋能销售管理创新',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const mockDescriptions = [
|
|
54
|
+
'帮助企业搭建数字化客户经营平台,实现业绩高质量增长,提升客户满意度和忠诚度。',
|
|
55
|
+
'通过数据分析和智能算法,为企业提供精准的客户画像和个性化服务方案。',
|
|
56
|
+
'整合多渠道客户触点,打造无缝的客户体验,提升品牌价值和市场竞争力。',
|
|
57
|
+
'运用人工智能技术,优化销售流程,提高转化率,降低获客成本。',
|
|
58
|
+
'建立完善的客户数据体系,实现精准营销和个性化推荐,提升客户价值。',
|
|
59
|
+
'通过智能分析客户行为,预测客户需求,提前制定营销策略。',
|
|
60
|
+
'构建全生命周期客户管理体系,从获客到留存的全流程优化。',
|
|
61
|
+
'利用大数据和机器学习技术,实现客户价值的最大化挖掘。',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const mockAuthors = [
|
|
65
|
+
'张三',
|
|
66
|
+
'李四',
|
|
67
|
+
'王五',
|
|
68
|
+
'赵六',
|
|
69
|
+
'钱七',
|
|
70
|
+
'孙八',
|
|
71
|
+
'周九',
|
|
72
|
+
'吴十',
|
|
73
|
+
];
|
|
74
|
+
const mockTags = [
|
|
75
|
+
'CRM',
|
|
76
|
+
'营销',
|
|
77
|
+
'数字化',
|
|
78
|
+
'AI',
|
|
79
|
+
'数据分析',
|
|
80
|
+
'客户管理',
|
|
81
|
+
'智能客服',
|
|
82
|
+
'企业服务',
|
|
83
|
+
];
|
|
84
|
+
const mockImages = [
|
|
85
|
+
'https://picsum.photos/400/200?random=1',
|
|
86
|
+
'https://picsum.photos/400/200?random=2',
|
|
87
|
+
'https://picsum.photos/400/200?random=3',
|
|
88
|
+
'https://picsum.photos/400/200?random=4',
|
|
89
|
+
'https://picsum.photos/400/200?random=5',
|
|
90
|
+
'https://picsum.photos/400/200?random=6',
|
|
91
|
+
'https://picsum.photos/400/200?random=7',
|
|
92
|
+
'https://picsum.photos/400/200?random=8',
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
return Array.from({ length: count }, (_, index) => ({
|
|
96
|
+
id: `item-${index + 1}`,
|
|
97
|
+
title: mockTitles[index % mockTitles.length],
|
|
98
|
+
description: mockDescriptions[index % mockDescriptions.length],
|
|
99
|
+
image: mockImages[index % mockImages.length],
|
|
100
|
+
author: mockAuthors[index % mockAuthors.length],
|
|
101
|
+
createTime: new Date(
|
|
102
|
+
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000,
|
|
103
|
+
).toLocaleDateString(),
|
|
104
|
+
views: Math.floor(Math.random() * 1000) + 100,
|
|
105
|
+
likes: Math.floor(Math.random() * 500) + 50,
|
|
106
|
+
comments: Math.floor(Math.random() * 200) + 10,
|
|
107
|
+
tags: mockTags.slice(0, Math.floor(Math.random() * 3) + 1),
|
|
108
|
+
status: ['published', 'draft', 'archived'][
|
|
109
|
+
Math.floor(Math.random() * 3)
|
|
110
|
+
] as 'published' | 'draft' | 'archived',
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 格式化数字显示
|
|
115
|
+
formatNumber(num: number): string {
|
|
116
|
+
if (num < 1000) return num.toString();
|
|
117
|
+
if (num < 10000) return `${(num / 1000).toFixed(1)}k`;
|
|
118
|
+
return `${(num / 10000).toFixed(1)}w`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 获取状态标签颜色
|
|
122
|
+
getStatusColor(status: string): string {
|
|
123
|
+
const statusMap: { [key: string]: string } = {
|
|
124
|
+
published: 'green',
|
|
125
|
+
draft: 'orange',
|
|
126
|
+
archived: 'gray',
|
|
127
|
+
};
|
|
128
|
+
return statusMap[status] || 'default';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 获取状态文本
|
|
132
|
+
getStatusText(status: string): string {
|
|
133
|
+
const statusMap: { [key: string]: string } = {
|
|
134
|
+
published: '已发布',
|
|
135
|
+
draft: '草稿',
|
|
136
|
+
archived: '已归档',
|
|
137
|
+
};
|
|
138
|
+
return statusMap[status] || '未知';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 渲染列表项内容
|
|
142
|
+
renderListItem(item: ListItem) {
|
|
143
|
+
const { showImage, showTags } = this.props;
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div key={item.id} className="list-item">
|
|
147
|
+
{showImage && (
|
|
148
|
+
<div className="item-image">
|
|
149
|
+
<img src={item.image} alt={item.title} />
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
<div className="item-content">
|
|
153
|
+
<div className="item-header">
|
|
154
|
+
<h3 className="item-title">{item.title}</h3>
|
|
155
|
+
<span
|
|
156
|
+
className={`item-status status-${this.getStatusColor(
|
|
157
|
+
item.status,
|
|
158
|
+
)}`}
|
|
159
|
+
>
|
|
160
|
+
{this.getStatusText(item.status)}
|
|
161
|
+
</span>
|
|
162
|
+
</div>
|
|
163
|
+
<div className="item-description">{item.description}</div>
|
|
164
|
+
<div className="item-meta">
|
|
165
|
+
<div className="item-meta-info">
|
|
166
|
+
<span className="meta-item">作者:{item.author}</span>
|
|
167
|
+
<span className="meta-separator">·</span>
|
|
168
|
+
<span className="meta-item">发布时间:{item.createTime}</span>
|
|
169
|
+
</div>
|
|
170
|
+
{showTags && item.tags.length > 0 && (
|
|
171
|
+
<div className="item-tags">
|
|
172
|
+
{item.tags.map((tag, index) => (
|
|
173
|
+
<span key={index} className="tag">
|
|
174
|
+
{tag}
|
|
175
|
+
</span>
|
|
176
|
+
))}
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
render() {
|
|
186
|
+
const { title, itemCount, listType, data } = this.props;
|
|
187
|
+
|
|
188
|
+
const curAmisData = data || {};
|
|
189
|
+
const systemInfo = curAmisData.__NeoSystemInfo || {};
|
|
190
|
+
|
|
191
|
+
const mockData = this.generateMockData(itemCount);
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<div className="list-widget-container">
|
|
195
|
+
<div className="list-header">
|
|
196
|
+
<h2 className="list-title">
|
|
197
|
+
{title || '内容列表'}
|
|
198
|
+
{systemInfo.tenantName ? `【${systemInfo.tenantName}】` : ''}
|
|
199
|
+
</h2>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div className={`list-content list-${listType}`}>
|
|
203
|
+
{mockData.map((item) => this.renderListItem(item))}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|