@lxzy/code-snippet 0.0.8 → 0.0.9
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/.opencode/rules/ts.md +11 -0
- package/README.md +40 -12
- package/package.json +2 -2
- package/src/App.tsx +1 -1
- package/src/layouts/index.tsx +2 -1
- package/src/main.tsx +1 -0
- package/src/pages/activityCenter/activitySystemManagement/add.tsx +2 -13
- package/src/pages/activityCenter/activitySystemManagement/addPages/components/ChannelLinkModal.tsx +3 -14
- package/src/pages/activityCenter/activitySystemManagement/addPages/components/CustomLink.tsx +116 -11
- package/src/pages/activityCenter/activitySystemManagement/addPages/components/CustomPage.tsx +2 -2
- package/src/pages/activityCenter/activitySystemManagement/addPages/components/SystemPage.tsx +1 -1
- package/src/pages/activityCenter/activitySystemManagement/addPages/index.tsx +1 -1
- package/src/pages/activityCenter/activitySystemManagement/index.tsx +6 -2
- package/mock/.gitkeep +0 -0
- package/src/ly/chart/index.less +0 -101
- package/src/ly/chart/index.tsx +0 -272
- package/src/ly/chart/test.js +0 -43
package/README.md
CHANGED
|
@@ -1,15 +1,43 @@
|
|
|
1
|
-
# umi project
|
|
2
|
-
|
|
3
|
-
## Getting Started
|
|
4
|
-
|
|
5
|
-
Install dependencies,
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
$ yarn
|
|
9
1
|
```
|
|
2
|
+
{
|
|
3
|
+
// Place your 全局 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
|
4
|
+
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
|
5
|
+
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
|
6
|
+
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
|
7
|
+
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
|
8
|
+
// Placeholders with the same ids are connected.
|
|
9
|
+
"快速打印": {
|
|
10
|
+
"scope": "javascript,javascriptreact,typescript,typescriptreact",
|
|
11
|
+
"prefix": "clg",
|
|
12
|
+
"body": ["console.log($1);"],
|
|
13
|
+
"description": "快速打印"
|
|
14
|
+
},
|
|
15
|
+
"react组件模版": {
|
|
16
|
+
"scope": "javascript, javascriptreact, typescript, typescriptreact",
|
|
17
|
+
"prefix": "react",
|
|
18
|
+
"body": [
|
|
19
|
+
"import React, {useState} from 'react'",
|
|
20
|
+
"",
|
|
21
|
+
"export default function ${TM_FILENAME_BASE/(.*)/${1:/capitalize}/}() {",
|
|
22
|
+
" return (",
|
|
23
|
+
" <div>",
|
|
24
|
+
" $1",
|
|
25
|
+
" </div>",
|
|
26
|
+
" )",
|
|
27
|
+
"}",
|
|
28
|
+
],
|
|
29
|
+
"description": "react组件模版"
|
|
30
|
+
},
|
|
31
|
+
"formily模版": {
|
|
32
|
+
"scope": "javascript, javascriptreact, typescript, typescriptreact",
|
|
33
|
+
"prefix": "formily",
|
|
34
|
+
"body": [
|
|
35
|
+
"import { createForm, onFieldInputValueChange } from '@formily/core';",
|
|
36
|
+
"import { Form, FormItem, FormGrid } from '@formily/next';",
|
|
37
|
+
"import { Field } from '@formily/react';"
|
|
38
|
+
],
|
|
39
|
+
"description": "formily模版"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
10
42
|
|
|
11
|
-
Start the dev server,
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
$ yarn start
|
|
15
43
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lxzy/code-snippet",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"files": [
|
|
5
5
|
"*",
|
|
6
6
|
"!dist",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"wangeditor": "^4.7.11"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@types/node": "^22.1.0",
|
|
65
|
+
"@types/node": "^22.1.0",
|
|
66
66
|
"@types/react": "^17.0.0",
|
|
67
67
|
"@types/react-dom": "^17.0.0",
|
|
68
68
|
"@vitejs/plugin-react": "^6.0.0",
|
package/src/App.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import ActivitySystemManagement from '@/pages/activityCenter/activitySystemManag
|
|
|
5
5
|
import ActivitySystemManagementAddPages from '@/pages/activityCenter/activitySystemManagement/addPages';
|
|
6
6
|
import Page404 from '@/pages/404';
|
|
7
7
|
|
|
8
|
-
const App
|
|
8
|
+
const App = () => {
|
|
9
9
|
return (
|
|
10
10
|
<BrowserRouter>
|
|
11
11
|
<BasicLayout>
|
package/src/layouts/index.tsx
CHANGED
|
@@ -13,7 +13,8 @@ const menuDataRender = (menuList: any[]) => {
|
|
|
13
13
|
});
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
const BasicLayout
|
|
16
|
+
const BasicLayout = (props) => {
|
|
17
|
+
const { children } = props;
|
|
17
18
|
const navigate = useNavigate();
|
|
18
19
|
|
|
19
20
|
return (
|
package/src/main.tsx
CHANGED
|
@@ -29,19 +29,8 @@ const strategyTagOptions = ['常态化', '拉新', '促活', '促销', '品牌
|
|
|
29
29
|
const ipTagOptions = ['品牌A', '品牌B', '品牌C', '品牌D'];
|
|
30
30
|
const industryTagOptions = ['制造业', '轻工业', '重工业', '服务业', '农业'];
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
visible
|
|
34
|
-
editingItem: any;
|
|
35
|
-
onClose: () => void;
|
|
36
|
-
onSuccess: () => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const AddDrawer: React.FC<AddDrawerProps> = ({
|
|
40
|
-
visible,
|
|
41
|
-
editingItem,
|
|
42
|
-
onClose,
|
|
43
|
-
onSuccess,
|
|
44
|
-
}) => {
|
|
32
|
+
const AddDrawer = (props) => {
|
|
33
|
+
const { visible, editingItem, onClose, onSuccess } = props;
|
|
45
34
|
const formRef = useRef<any>();
|
|
46
35
|
|
|
47
36
|
const handleSubmit = async (values) => {
|
package/src/pages/activityCenter/activitySystemManagement/addPages/components/ChannelLinkModal.tsx
CHANGED
|
@@ -5,23 +5,12 @@ import ProTable from '@ant-design/pro-table';
|
|
|
5
5
|
import { ModalForm, ProFormText } from '@ant-design/pro-form';
|
|
6
6
|
import axios from 'axios';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
visible
|
|
10
|
-
pageName: string;
|
|
11
|
-
pageId: number;
|
|
12
|
-
onClose: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const ChannelLinkModal: React.FC<ChannelLinkModalProps> = ({
|
|
16
|
-
visible,
|
|
17
|
-
pageName,
|
|
18
|
-
pageId,
|
|
19
|
-
onClose,
|
|
20
|
-
}) => {
|
|
8
|
+
const ChannelLinkModal = (props) => {
|
|
9
|
+
const { visible, pageName, pageId, onClose } = props;
|
|
21
10
|
const actionRef = useRef<any>();
|
|
22
11
|
const [addVisible, setAddVisible] = useState(false);
|
|
23
12
|
const [editVisible, setEditVisible] = useState(false);
|
|
24
|
-
const [editingItem, setEditingItem] = useState
|
|
13
|
+
const [editingItem, setEditingItem] = useState(null);
|
|
25
14
|
|
|
26
15
|
const handleAdd = async (values) => {
|
|
27
16
|
await axios.post('/api/activity/channelLink/add', {
|
package/src/pages/activityCenter/activitySystemManagement/addPages/components/CustomLink.tsx
CHANGED
|
@@ -1,39 +1,144 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Button } from 'antd';
|
|
1
|
+
import React, { useRef, useState } from 'react';
|
|
2
|
+
import { Button, Modal, Radio, Input, message, Space } from 'antd';
|
|
3
|
+
import { PlusOutlined } from '@ant-design/icons';
|
|
3
4
|
import ProTable from '@ant-design/pro-table';
|
|
5
|
+
import { ModalForm } from '@ant-design/pro-form';
|
|
4
6
|
import axios from 'axios';
|
|
5
7
|
|
|
6
|
-
const CustomLink
|
|
8
|
+
const CustomLink = () => {
|
|
9
|
+
const actionRef = useRef<any>();
|
|
10
|
+
const [addVisible, setAddVisible] = useState(false);
|
|
11
|
+
const [editVisible, setEditVisible] = useState(false);
|
|
12
|
+
const [editingItem, setEditingItem] = useState(null);
|
|
13
|
+
|
|
14
|
+
const handleAdd = async (values) => {
|
|
15
|
+
await axios.post('/api/activity/customLink/add', values);
|
|
16
|
+
message.success('新增成功');
|
|
17
|
+
setAddVisible(false);
|
|
18
|
+
actionRef.current?.reload();
|
|
19
|
+
return true;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const handleEdit = async (values) => {
|
|
23
|
+
await axios.post('/api/activity/customLink/update', {
|
|
24
|
+
...values,
|
|
25
|
+
id: editingItem?.id,
|
|
26
|
+
});
|
|
27
|
+
message.success('编辑成功');
|
|
28
|
+
setEditVisible(false);
|
|
29
|
+
setEditingItem(null);
|
|
30
|
+
actionRef.current?.reload();
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleDelete = async (record) => {
|
|
35
|
+
Modal.confirm({
|
|
36
|
+
title: '确认删除',
|
|
37
|
+
content: '确定要删除这条自定义链接吗?',
|
|
38
|
+
onOk: async () => {
|
|
39
|
+
await axios.post('/api/activity/customLink/delete', { id: record.id });
|
|
40
|
+
message.success('删除成功');
|
|
41
|
+
actionRef.current?.reload();
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getTypeName = (type: number) => {
|
|
47
|
+
return type === 1 ? '小程序' : 'H5链接';
|
|
48
|
+
};
|
|
49
|
+
|
|
7
50
|
return (
|
|
8
51
|
<div>
|
|
9
|
-
<ProTable
|
|
52
|
+
<ProTable<any>
|
|
10
53
|
headerTitle={false}
|
|
11
54
|
toolBarRender={() => [
|
|
12
|
-
<Button
|
|
55
|
+
<Button
|
|
56
|
+
key="add"
|
|
57
|
+
type="primary"
|
|
58
|
+
icon={<PlusOutlined />}
|
|
59
|
+
onClick={() => setAddVisible(true)}
|
|
60
|
+
>
|
|
61
|
+
新建自定义链接
|
|
62
|
+
</Button>,
|
|
13
63
|
]}
|
|
64
|
+
actionRef={actionRef}
|
|
14
65
|
rowKey="id"
|
|
15
66
|
request={async (params) => {
|
|
16
67
|
const { data: result } = await axios.get('/api/activity/customLinkList', { params });
|
|
17
68
|
return { data: result.data.list, total: result.data.total, success: result.success };
|
|
18
69
|
}}
|
|
19
70
|
columns={[
|
|
20
|
-
{ title: '跳转类型', dataIndex: 'type' },
|
|
71
|
+
{ title: '跳转类型', dataIndex: 'type', render: (_, record) => getTypeName(record.type) },
|
|
21
72
|
{ title: '页面路径', dataIndex: 'path' },
|
|
22
73
|
{ title: '最近更新时间', dataIndex: 'updateTime' },
|
|
23
74
|
{
|
|
24
75
|
title: '操作',
|
|
25
76
|
valueType: 'option',
|
|
26
|
-
render: () => (
|
|
27
|
-
|
|
28
|
-
<Button
|
|
29
|
-
|
|
30
|
-
|
|
77
|
+
render: (_, record) => (
|
|
78
|
+
<Space>
|
|
79
|
+
<Button
|
|
80
|
+
type="link"
|
|
81
|
+
size="small"
|
|
82
|
+
onClick={() => {
|
|
83
|
+
setEditingItem(record);
|
|
84
|
+
setEditVisible(true);
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
编辑
|
|
88
|
+
</Button>
|
|
89
|
+
<Button type="link" danger size="small" onClick={() => handleDelete(record)}>
|
|
90
|
+
删除
|
|
91
|
+
</Button>
|
|
92
|
+
</Space>
|
|
31
93
|
),
|
|
32
94
|
},
|
|
33
95
|
]}
|
|
34
96
|
pagination={false}
|
|
35
97
|
options={false}
|
|
98
|
+
search={false}
|
|
36
99
|
/>
|
|
100
|
+
|
|
101
|
+
<ModalForm
|
|
102
|
+
title="新建自定义链接"
|
|
103
|
+
visible={addVisible}
|
|
104
|
+
onVisibleChange={setAddVisible}
|
|
105
|
+
onFinish={handleAdd}
|
|
106
|
+
modalProps={{ destroyOnClose: true }}
|
|
107
|
+
>
|
|
108
|
+
<div style={{ marginBottom: 24 }}>
|
|
109
|
+
<div style={{ marginBottom: 8, fontWeight: 500 }}><span style={{ color: '#ff4d4f' }}>*</span> 跳转类型</div>
|
|
110
|
+
<Radio.Group name="type" defaultValue={1}>
|
|
111
|
+
<Radio value={1}>小程序</Radio>
|
|
112
|
+
<Radio value={2}>H5链接</Radio>
|
|
113
|
+
</Radio.Group>
|
|
114
|
+
</div>
|
|
115
|
+
<div style={{ marginBottom: 24 }}>
|
|
116
|
+
<div style={{ marginBottom: 8, fontWeight: 500 }}><span style={{ color: '#ff4d4f' }}>*</span> 页面路径</div>
|
|
117
|
+
<Input name="path" placeholder="请输入页面路径" />
|
|
118
|
+
</div>
|
|
119
|
+
</ModalForm>
|
|
120
|
+
|
|
121
|
+
<ModalForm
|
|
122
|
+
title="编辑自定义链接"
|
|
123
|
+
visible={editVisible}
|
|
124
|
+
onVisibleChange={setEditVisible}
|
|
125
|
+
onFinish={handleEdit}
|
|
126
|
+
initialValues={editingItem ? { type: editingItem.type, path: editingItem.path } : {}}
|
|
127
|
+
key={editingItem?.id}
|
|
128
|
+
modalProps={{ destroyOnClose: true }}
|
|
129
|
+
>
|
|
130
|
+
<div style={{ marginBottom: 24 }}>
|
|
131
|
+
<div style={{ marginBottom: 8, fontWeight: 500 }}><span style={{ color: '#ff4d4f' }}>*</span> 跳转类型</div>
|
|
132
|
+
<Radio.Group name="type">
|
|
133
|
+
<Radio value={1}>小程序</Radio>
|
|
134
|
+
<Radio value={2}>H5链接</Radio>
|
|
135
|
+
</Radio.Group>
|
|
136
|
+
</div>
|
|
137
|
+
<div style={{ marginBottom: 24 }}>
|
|
138
|
+
<div style={{ marginBottom: 8, fontWeight: 500 }}><span style={{ color: '#ff4d4f' }}>*</span> 页面路径</div>
|
|
139
|
+
<Input name="path" placeholder="请输入页面路径" />
|
|
140
|
+
</div>
|
|
141
|
+
</ModalForm>
|
|
37
142
|
</div>
|
|
38
143
|
);
|
|
39
144
|
};
|
package/src/pages/activityCenter/activitySystemManagement/addPages/components/CustomPage.tsx
CHANGED
|
@@ -4,9 +4,9 @@ import ProTable from '@ant-design/pro-table';
|
|
|
4
4
|
import axios from 'axios';
|
|
5
5
|
import ChannelLinkModal from './ChannelLinkModal';
|
|
6
6
|
|
|
7
|
-
const CustomPage
|
|
7
|
+
const CustomPage = () => {
|
|
8
8
|
const [channelLinkVisible, setChannelLinkVisible] = useState(false);
|
|
9
|
-
const [currentPage, setCurrentPage] = useState
|
|
9
|
+
const [currentPage, setCurrentPage] = useState(null);
|
|
10
10
|
|
|
11
11
|
return (
|
|
12
12
|
<div>
|
|
@@ -4,7 +4,7 @@ import SystemPage from './components/SystemPage';
|
|
|
4
4
|
import CustomPage from './components/CustomPage';
|
|
5
5
|
import CustomLink from './components/CustomLink';
|
|
6
6
|
|
|
7
|
-
const AddPages
|
|
7
|
+
const AddPages = () => {
|
|
8
8
|
const [activeTab, setActiveTab] = useState('system');
|
|
9
9
|
|
|
10
10
|
const tabContent = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useRef, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { Button, DatePicker, message, Modal, Select, Space } from 'antd';
|
|
3
3
|
import ProTable, { ActionType } from '@ant-design/pro-table';
|
|
4
4
|
import { useNavigate } from 'react-router-dom';
|
|
@@ -158,7 +158,11 @@ const ActivitySystemManagement = () => {
|
|
|
158
158
|
<Button
|
|
159
159
|
type="link"
|
|
160
160
|
size="small"
|
|
161
|
-
onClick={() =>
|
|
161
|
+
onClick={() =>
|
|
162
|
+
navigate(
|
|
163
|
+
'/activityCenter/activitySystemManagement/addPages',
|
|
164
|
+
)
|
|
165
|
+
}
|
|
162
166
|
>
|
|
163
167
|
添加页面
|
|
164
168
|
</Button>
|
package/mock/.gitkeep
DELETED
|
File without changes
|
package/src/ly/chart/index.less
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
.page {
|
|
2
|
-
min-height: 100vh;
|
|
3
|
-
background: linear-gradient(135deg, #f5f7fa 0%, #e4ecf7 100%);
|
|
4
|
-
padding: 32px 24px;
|
|
5
|
-
box-sizing: border-box;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.header {
|
|
9
|
-
text-align: center;
|
|
10
|
-
margin-bottom: 24px;
|
|
11
|
-
|
|
12
|
-
h1 {
|
|
13
|
-
font-size: 28px;
|
|
14
|
-
font-weight: 700;
|
|
15
|
-
color: #1a1a2e;
|
|
16
|
-
margin: 0 0 8px;
|
|
17
|
-
letter-spacing: 2px;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
p {
|
|
21
|
-
font-size: 14px;
|
|
22
|
-
color: #888;
|
|
23
|
-
margin: 0;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.toolbar {
|
|
28
|
-
max-width: 1400px;
|
|
29
|
-
margin: 0 auto 16px;
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
justify-content: space-between;
|
|
33
|
-
flex-wrap: wrap;
|
|
34
|
-
gap: 12px;
|
|
35
|
-
background: #fff;
|
|
36
|
-
border-radius: 12px;
|
|
37
|
-
padding: 14px 20px;
|
|
38
|
-
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.toolbarLeft {
|
|
42
|
-
display: flex;
|
|
43
|
-
align-items: center;
|
|
44
|
-
gap: 8px;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.toolbarRight {
|
|
48
|
-
display: flex;
|
|
49
|
-
align-items: center;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.label {
|
|
53
|
-
font-size: 14px;
|
|
54
|
-
color: #555;
|
|
55
|
-
font-weight: 500;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.info {
|
|
59
|
-
font-size: 13px;
|
|
60
|
-
color: #666;
|
|
61
|
-
background: #f0f5ff;
|
|
62
|
-
padding: 5px 14px;
|
|
63
|
-
border-radius: 6px;
|
|
64
|
-
border: 1px solid #d6e4ff;
|
|
65
|
-
|
|
66
|
-
strong {
|
|
67
|
-
color: #1677ff;
|
|
68
|
-
font-size: 15px;
|
|
69
|
-
margin: 0 2px;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.chartWrapper {
|
|
74
|
-
max-width: 1400px;
|
|
75
|
-
margin: 0 auto;
|
|
76
|
-
background: #fff;
|
|
77
|
-
border-radius: 16px;
|
|
78
|
-
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
|
|
79
|
-
padding: 24px 16px 16px;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.chart {
|
|
83
|
-
width: 100%;
|
|
84
|
-
height: 520px;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.tips {
|
|
88
|
-
max-width: 1400px;
|
|
89
|
-
margin: 16px auto 0;
|
|
90
|
-
text-align: center;
|
|
91
|
-
|
|
92
|
-
span {
|
|
93
|
-
display: inline-block;
|
|
94
|
-
background: #fffbe6;
|
|
95
|
-
color: #ad6800;
|
|
96
|
-
font-size: 13px;
|
|
97
|
-
padding: 8px 20px;
|
|
98
|
-
border-radius: 8px;
|
|
99
|
-
border: 1px solid #ffe58f;
|
|
100
|
-
}
|
|
101
|
-
}
|
package/src/ly/chart/index.tsx
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState, useCallback } from "react";
|
|
2
|
-
import * as echarts from "echarts";
|
|
3
|
-
import { DatePicker } from "antd";
|
|
4
|
-
import dayjs, { Dayjs } from "dayjs";
|
|
5
|
-
import s from "./index.less";
|
|
6
|
-
|
|
7
|
-
const { RangePicker } = DatePicker;
|
|
8
|
-
|
|
9
|
-
// 根据小时生成模拟随机数(带日间波动规律)
|
|
10
|
-
function generateData(startDate: Dayjs, endDate: Dayjs) {
|
|
11
|
-
const xData: string[] = [];
|
|
12
|
-
const temperatureData: number[] = [];
|
|
13
|
-
const humidityData: number[] = [];
|
|
14
|
-
const powerData: number[] = [];
|
|
15
|
-
|
|
16
|
-
const totalDays = endDate.diff(startDate, "day") + 1;
|
|
17
|
-
|
|
18
|
-
for (let d = 0; d < totalDays; d++) {
|
|
19
|
-
const currentDay = startDate.add(d, "day");
|
|
20
|
-
const dateStr = currentDay.format("YYYY-MM-DD");
|
|
21
|
-
|
|
22
|
-
for (let h = 0; h < 24; h++) {
|
|
23
|
-
const hour = h.toString().padStart(2, "0");
|
|
24
|
-
xData.push(`${dateStr} ${hour}:00`);
|
|
25
|
-
|
|
26
|
-
// 温度:夜间低(14-18°C),日间高(24-32°C)
|
|
27
|
-
const tempBase = h >= 8 && h <= 18 ? 24 : 16;
|
|
28
|
-
const tempRange = h >= 8 && h <= 18 ? 8 : 4;
|
|
29
|
-
temperatureData.push(
|
|
30
|
-
+(tempBase + Math.random() * tempRange).toFixed(1)
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
// 湿度:与温度大致相反,夜间高(65-80%),日间低(40-58%)
|
|
34
|
-
const humBase = h >= 8 && h <= 18 ? 40 : 65;
|
|
35
|
-
const humRange = h >= 8 && h <= 18 ? 18 : 15;
|
|
36
|
-
humidityData.push(
|
|
37
|
-
+(humBase + Math.random() * humRange).toFixed(1)
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
// 用电量:工作时间高(300-500kWh),夜间低(80-160kWh)
|
|
41
|
-
const powBase = h >= 8 && h <= 20 ? 300 : 80;
|
|
42
|
-
const powRange = h >= 8 && h <= 20 ? 200 : 80;
|
|
43
|
-
powerData.push(
|
|
44
|
-
+(powBase + Math.random() * powRange).toFixed(0)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return { xData, temperatureData, humidityData, powerData };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// 默认时间:当前时间前三天
|
|
53
|
-
const defaultRange: [Dayjs, Dayjs] = [
|
|
54
|
-
dayjs().subtract(2, "day").startOf("day"),
|
|
55
|
-
dayjs().startOf("day"),
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
export default function ChartPage() {
|
|
59
|
-
const chartRef = useRef<HTMLDivElement>(null);
|
|
60
|
-
const chartInstance = useRef<echarts.ECharts | null>(null);
|
|
61
|
-
const [dateRange, setDateRange] = useState<[Dayjs, Dayjs]>(defaultRange);
|
|
62
|
-
|
|
63
|
-
// 构建 ECharts 配置
|
|
64
|
-
const buildOption = useCallback(
|
|
65
|
-
(data: ReturnType<typeof generateData>): echarts.EChartsOption => {
|
|
66
|
-
const totalPoints = data.xData.length;
|
|
67
|
-
// 默认展示约 24 个数据点(1天)的宽度
|
|
68
|
-
const visiblePercent = Math.min(100, (24 / totalPoints) * 100);
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
title: {
|
|
72
|
-
text: `${dateRange[0].format("MM-DD")} 至 ${dateRange[1].format("MM-DD")} 环境监测统计图`,
|
|
73
|
-
subtext: `共 ${totalPoints} 个数据点 · 温度 / 湿度 / 用电量(每小时采样)`,
|
|
74
|
-
left: "center",
|
|
75
|
-
textStyle: { color: "#333", fontSize: 20, fontWeight: "bold" },
|
|
76
|
-
subtextStyle: { color: "#999", fontSize: 13 },
|
|
77
|
-
top: 16,
|
|
78
|
-
},
|
|
79
|
-
tooltip: {
|
|
80
|
-
trigger: "axis",
|
|
81
|
-
backgroundColor: "rgba(255,255,255,0.96)",
|
|
82
|
-
borderColor: "#eee",
|
|
83
|
-
textStyle: { color: "#333" },
|
|
84
|
-
axisPointer: { type: "cross", crossStyle: { color: "#999" } },
|
|
85
|
-
},
|
|
86
|
-
legend: {
|
|
87
|
-
data: ["温度 (°C)", "湿度 (%)", "用电量 (kWh)"],
|
|
88
|
-
top: 80,
|
|
89
|
-
itemGap: 32,
|
|
90
|
-
textStyle: { fontSize: 13 },
|
|
91
|
-
},
|
|
92
|
-
grid: {
|
|
93
|
-
left: 60,
|
|
94
|
-
right: 60,
|
|
95
|
-
top: 130,
|
|
96
|
-
bottom: 120,
|
|
97
|
-
},
|
|
98
|
-
xAxis: {
|
|
99
|
-
type: "category",
|
|
100
|
-
data: data.xData,
|
|
101
|
-
axisLabel: {
|
|
102
|
-
rotate: 45,
|
|
103
|
-
fontSize: 11,
|
|
104
|
-
interval: Math.max(0, Math.floor(totalPoints / 24) - 1),
|
|
105
|
-
formatter: (value: string) => {
|
|
106
|
-
const parts = value.split(" ");
|
|
107
|
-
return `${parts[0].slice(5)}\n${parts[1]}`;
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
axisTick: { alignWithLabel: true },
|
|
111
|
-
boundaryGap: false,
|
|
112
|
-
splitLine: { show: true, lineStyle: { type: "dashed", color: "#eee" } },
|
|
113
|
-
},
|
|
114
|
-
yAxis: [
|
|
115
|
-
{
|
|
116
|
-
type: "value",
|
|
117
|
-
name: "温度(°C) / 湿度(%)",
|
|
118
|
-
position: "left",
|
|
119
|
-
axisLabel: { formatter: "{value}" },
|
|
120
|
-
splitLine: { lineStyle: { type: "dashed", color: "#eee" } },
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
type: "value",
|
|
124
|
-
name: "用电量(kWh)",
|
|
125
|
-
position: "right",
|
|
126
|
-
axisLabel: { formatter: "{value}" },
|
|
127
|
-
splitLine: { show: false },
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
dataZoom: [
|
|
131
|
-
{
|
|
132
|
-
type: "slider",
|
|
133
|
-
xAxisIndex: 0,
|
|
134
|
-
start: 0,
|
|
135
|
-
end: visiblePercent,
|
|
136
|
-
bottom: 20,
|
|
137
|
-
height: 30,
|
|
138
|
-
borderColor: "#ddd",
|
|
139
|
-
fillerColor: "rgba(64,136,255,0.15)",
|
|
140
|
-
handleStyle: { color: "#4088ff" },
|
|
141
|
-
textStyle: { fontSize: 11 },
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
type: "inside",
|
|
145
|
-
xAxisIndex: 0,
|
|
146
|
-
start: 0,
|
|
147
|
-
end: visiblePercent,
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
series: [
|
|
151
|
-
{
|
|
152
|
-
name: "温度 (°C)",
|
|
153
|
-
type: "line",
|
|
154
|
-
data: data.temperatureData,
|
|
155
|
-
smooth: true,
|
|
156
|
-
symbol: "circle",
|
|
157
|
-
symbolSize: 6,
|
|
158
|
-
lineStyle: { width: 2.5, color: "#ff6b6b" },
|
|
159
|
-
itemStyle: { color: "#ff6b6b" },
|
|
160
|
-
areaStyle: {
|
|
161
|
-
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
162
|
-
{ offset: 0, color: "rgba(255,107,107,0.25)" },
|
|
163
|
-
{ offset: 1, color: "rgba(255,107,107,0.02)" },
|
|
164
|
-
]),
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
name: "湿度 (%)",
|
|
169
|
-
type: "line",
|
|
170
|
-
data: data.humidityData,
|
|
171
|
-
smooth: true,
|
|
172
|
-
symbol: "circle",
|
|
173
|
-
symbolSize: 6,
|
|
174
|
-
lineStyle: { width: 2.5, color: "#4ecdc4" },
|
|
175
|
-
itemStyle: { color: "#4ecdc4" },
|
|
176
|
-
areaStyle: {
|
|
177
|
-
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
178
|
-
{ offset: 0, color: "rgba(78,205,196,0.25)" },
|
|
179
|
-
{ offset: 1, color: "rgba(78,205,196,0.02)" },
|
|
180
|
-
]),
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
name: "用电量 (kWh)",
|
|
185
|
-
type: "line",
|
|
186
|
-
yAxisIndex: 1,
|
|
187
|
-
data: data.powerData,
|
|
188
|
-
smooth: true,
|
|
189
|
-
symbol: "circle",
|
|
190
|
-
symbolSize: 6,
|
|
191
|
-
lineStyle: { width: 2.5, color: "#4088ff" },
|
|
192
|
-
itemStyle: { color: "#4088ff" },
|
|
193
|
-
areaStyle: {
|
|
194
|
-
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
195
|
-
{ offset: 0, color: "rgba(64,136,255,0.25)" },
|
|
196
|
-
{ offset: 1, color: "rgba(64,136,255,0.02)" },
|
|
197
|
-
]),
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
],
|
|
201
|
-
};
|
|
202
|
-
},
|
|
203
|
-
[dateRange]
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
// 初始化图表
|
|
207
|
-
useEffect(() => {
|
|
208
|
-
if (!chartRef.current) return;
|
|
209
|
-
chartInstance.current = echarts.init(chartRef.current);
|
|
210
|
-
|
|
211
|
-
const handleResize = () => chartInstance.current?.resize();
|
|
212
|
-
window.addEventListener("resize", handleResize);
|
|
213
|
-
|
|
214
|
-
return () => {
|
|
215
|
-
window.removeEventListener("resize", handleResize);
|
|
216
|
-
chartInstance.current?.dispose();
|
|
217
|
-
};
|
|
218
|
-
}, []);
|
|
219
|
-
|
|
220
|
-
// 时间区间变化时更新图表
|
|
221
|
-
useEffect(() => {
|
|
222
|
-
if (!chartInstance.current) return;
|
|
223
|
-
const data = generateData(dateRange[0], dateRange[1]);
|
|
224
|
-
chartInstance.current.setOption(buildOption(data), true);
|
|
225
|
-
}, [dateRange, buildOption]);
|
|
226
|
-
|
|
227
|
-
const onRangeChange = (dates: [Dayjs | null, Dayjs | null] | null) => {
|
|
228
|
-
if (dates && dates[0] && dates[1]) {
|
|
229
|
-
setDateRange([dates[0].startOf("day"), dates[1].startOf("day")]);
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
return (
|
|
234
|
-
<div className={s.page}>
|
|
235
|
-
<div className={s.header}>
|
|
236
|
-
<h1>数据统计看板</h1>
|
|
237
|
-
<p>环境监测数据 · 每小时采样一次 · 拖动下方滑块或鼠标滚轮可浏览全部数据</p>
|
|
238
|
-
</div>
|
|
239
|
-
<div className={s.toolbar}>
|
|
240
|
-
<div className={s.toolbarLeft}>
|
|
241
|
-
<span className={s.label}>时间区间:</span>
|
|
242
|
-
<RangePicker
|
|
243
|
-
value={dateRange}
|
|
244
|
-
onChange={onRangeChange}
|
|
245
|
-
allowClear={false}
|
|
246
|
-
format="YYYY-MM-DD"
|
|
247
|
-
disabledDate={(current) => current && current > dayjs().endOf("day")}
|
|
248
|
-
presets={[
|
|
249
|
-
{ label: "近 1 天", value: [dayjs().subtract(0, "day").startOf("day"), dayjs().startOf("day")] },
|
|
250
|
-
{ label: "近 3 天", value: [dayjs().subtract(2, "day").startOf("day"), dayjs().startOf("day")] },
|
|
251
|
-
{ label: "近 7 天", value: [dayjs().subtract(6, "day").startOf("day"), dayjs().startOf("day")] },
|
|
252
|
-
{ label: "近 15 天", value: [dayjs().subtract(14, "day").startOf("day"), dayjs().startOf("day")] },
|
|
253
|
-
{ label: "近 30 天", value: [dayjs().subtract(29, "day").startOf("day"), dayjs().startOf("day")] },
|
|
254
|
-
]}
|
|
255
|
-
/>
|
|
256
|
-
</div>
|
|
257
|
-
<div className={s.toolbarRight}>
|
|
258
|
-
<span className={s.info}>
|
|
259
|
-
共 <strong>{dateRange[1].diff(dateRange[0], "day") + 1}</strong> 天 /
|
|
260
|
-
<strong>{(dateRange[1].diff(dateRange[0], "day") + 1) * 24}</strong> 个数据点
|
|
261
|
-
</span>
|
|
262
|
-
</div>
|
|
263
|
-
</div>
|
|
264
|
-
<div className={s.chartWrapper}>
|
|
265
|
-
<div ref={chartRef} className={s.chart} />
|
|
266
|
-
</div>
|
|
267
|
-
<div className={s.tips}>
|
|
268
|
-
<span>💡 提示:拖动图表下方滑块可左右浏览数据,也可在图表区域内使用鼠标滚轮缩放</span>
|
|
269
|
-
</div>
|
|
270
|
-
</div>
|
|
271
|
-
);
|
|
272
|
-
}
|
package/src/ly/chart/test.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const conditionGroups = [
|
|
2
|
-
{
|
|
3
|
-
"name": "振动传感器条件组",
|
|
4
|
-
"description": "加速度/速度/位移/频率阈值",
|
|
5
|
-
"logic": "or",
|
|
6
|
-
"alarm_level": "prompt",
|
|
7
|
-
"action": "notify",
|
|
8
|
-
"conditions": [
|
|
9
|
-
{
|
|
10
|
-
"field_name": "Acceleration 加速度",
|
|
11
|
-
"field_key": "/VibrationSensor/Acceleration",
|
|
12
|
-
"field_type": "float",
|
|
13
|
-
"operator": ">",
|
|
14
|
-
"value": data?.Acceleration,
|
|
15
|
-
"unit": ""
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"field_name": "Vibration speed 振动速度",
|
|
19
|
-
"field_key": "/VibrationSensor/VibrationSpeed",
|
|
20
|
-
"field_type": "float",
|
|
21
|
-
"operator": ">",
|
|
22
|
-
"value": data?.VibrationSpeed,
|
|
23
|
-
"unit": ""
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"field_name": "Vibration displacement 振动位移",
|
|
27
|
-
"field_key": "/VibrationSensor/VibrationDisplacement",
|
|
28
|
-
"field_type": "float",
|
|
29
|
-
"operator": ">",
|
|
30
|
-
"value": data?.VibrationDisplacement,
|
|
31
|
-
"unit": ""
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"field_name": "Vibration frequency 振动频率",
|
|
35
|
-
"field_key": "/VibrationSensor/VibrationFrequency",
|
|
36
|
-
"field_type": "float",
|
|
37
|
-
"operator": ">",
|
|
38
|
-
"value": data?.VibrationFrequency,
|
|
39
|
-
"unit": ""
|
|
40
|
-
}
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
]
|