@etsoo/materialui 1.2.74 → 1.2.76

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/index.d.ts CHANGED
@@ -7,6 +7,11 @@ export * from "./app/ISmartERPUser";
7
7
  export * from "./app/Labels";
8
8
  export * from "./app/ReactApp";
9
9
  export * from "./app/ServiceApp";
10
+ export * from "./messages/MessageUtils";
11
+ export * from "./messages/OperationMessageDto";
12
+ export * from "./messages/OperationMessageHandler";
13
+ export * from "./messages/RefreshHandler";
14
+ export * from "./messages/SignalRUser";
10
15
  export * from "./pages/CommonPage";
11
16
  export * from "./pages/CommonPageProps";
12
17
  export * from "./pages/DataGridPage";
package/lib/index.js CHANGED
@@ -7,6 +7,11 @@ export * from "./app/ISmartERPUser";
7
7
  export * from "./app/Labels";
8
8
  export * from "./app/ReactApp";
9
9
  export * from "./app/ServiceApp";
10
+ export * from "./messages/MessageUtils";
11
+ export * from "./messages/OperationMessageDto";
12
+ export * from "./messages/OperationMessageHandler";
13
+ export * from "./messages/RefreshHandler";
14
+ export * from "./messages/SignalRUser";
10
15
  export * from "./pages/CommonPage";
11
16
  export * from "./pages/CommonPageProps";
12
17
  export * from "./pages/DataGridPage";
@@ -0,0 +1,53 @@
1
+ /// <reference types="node" />
2
+ import EventEmitter from "events";
3
+ import { SignalRUser } from "./SignalRUser";
4
+ import { OperationMessageDto } from "./OperationMessageDto";
5
+ import { OperationMessageHandler } from "./OperationMessageHandler";
6
+ /**
7
+ * Message utilities
8
+ * 消息工具
9
+ */
10
+ export declare namespace MessageUtils {
11
+ /**
12
+ * Event emitter
13
+ */
14
+ const eventEmitter: EventEmitter;
15
+ /**
16
+ * Emit operation message
17
+ * 发出操作信息
18
+ * @param seed Refresh seed
19
+ */
20
+ function emitOperationMessage(user: SignalRUser | undefined, isSelf: boolean, message: OperationMessageDto): void;
21
+ /**
22
+ * Emit refresh
23
+ * 发出刷新
24
+ * @param user SignalR user
25
+ * @param isSelf Is current user self
26
+ * @param message Message
27
+ */
28
+ function emitRefresh(): void;
29
+ /**
30
+ * Add operation event listener
31
+ * 添加操作事件监控器
32
+ * @param handler Handler
33
+ */
34
+ function onOperationMessage(handler: OperationMessageHandler): void;
35
+ /**
36
+ * Add refresh event listener
37
+ * 添加事件事件监控器
38
+ * @param handler Handler
39
+ */
40
+ function onRefresh(handler: OperationMessageHandler): void;
41
+ /**
42
+ * Remove operation event listener
43
+ * 移除操作事件监控器
44
+ * @param handler Handler
45
+ */
46
+ function offOperationMessage(handler: OperationMessageHandler): void;
47
+ /**
48
+ * Remove refresh event listener
49
+ * 移除刷新事件监控器
50
+ * @param handler Handler
51
+ */
52
+ function offRefresh(handler: OperationMessageHandler): void;
53
+ }
@@ -0,0 +1,70 @@
1
+ import EventEmitter from "events";
2
+ /**
3
+ * Message utilities
4
+ * 消息工具
5
+ */
6
+ export var MessageUtils;
7
+ (function (MessageUtils) {
8
+ /**
9
+ * Event emitter
10
+ */
11
+ MessageUtils.eventEmitter = new EventEmitter();
12
+ const OperationMessageName = "onOperationMessage";
13
+ const RefreshName = "onRefresh";
14
+ /**
15
+ * Emit operation message
16
+ * 发出操作信息
17
+ * @param seed Refresh seed
18
+ */
19
+ function emitOperationMessage(user, isSelf, message) {
20
+ MessageUtils.eventEmitter.emit(OperationMessageName, user, isSelf, message);
21
+ }
22
+ MessageUtils.emitOperationMessage = emitOperationMessage;
23
+ /**
24
+ * Emit refresh
25
+ * 发出刷新
26
+ * @param user SignalR user
27
+ * @param isSelf Is current user self
28
+ * @param message Message
29
+ */
30
+ function emitRefresh() {
31
+ MessageUtils.eventEmitter.emit(RefreshName);
32
+ }
33
+ MessageUtils.emitRefresh = emitRefresh;
34
+ /**
35
+ * Add operation event listener
36
+ * 添加操作事件监控器
37
+ * @param handler Handler
38
+ */
39
+ function onOperationMessage(handler) {
40
+ MessageUtils.eventEmitter.on(OperationMessageName, handler);
41
+ }
42
+ MessageUtils.onOperationMessage = onOperationMessage;
43
+ /**
44
+ * Add refresh event listener
45
+ * 添加事件事件监控器
46
+ * @param handler Handler
47
+ */
48
+ function onRefresh(handler) {
49
+ MessageUtils.eventEmitter.on(RefreshName, handler);
50
+ }
51
+ MessageUtils.onRefresh = onRefresh;
52
+ /**
53
+ * Remove operation event listener
54
+ * 移除操作事件监控器
55
+ * @param handler Handler
56
+ */
57
+ function offOperationMessage(handler) {
58
+ MessageUtils.eventEmitter.off(OperationMessageName, handler);
59
+ }
60
+ MessageUtils.offOperationMessage = offOperationMessage;
61
+ /**
62
+ * Remove refresh event listener
63
+ * 移除刷新事件监控器
64
+ * @param handler Handler
65
+ */
66
+ function offRefresh(handler) {
67
+ MessageUtils.eventEmitter.off(RefreshName, handler);
68
+ }
69
+ MessageUtils.offRefresh = offRefresh;
70
+ })(MessageUtils || (MessageUtils = {}));
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Operation message data
3
+ * 操作信息数据
4
+ */
5
+ export type OperationMessageDto = {
6
+ /**
7
+ * Operation type
8
+ * 操作类型
9
+ */
10
+ operationType: string;
11
+ /**
12
+ * Target object id
13
+ * 目标对象编号
14
+ */
15
+ id?: number;
16
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { OperationMessageDto } from "./OperationMessageDto";
2
+ import { SignalRUser } from "./SignalRUser";
3
+ /**
4
+ * Operation message handler type
5
+ * 操作信息处理程序类型
6
+ */
7
+ export type OperationMessageHandler = (
8
+ /**
9
+ * Signal user data
10
+ */
11
+ user: SignalRUser | undefined,
12
+ /**
13
+ * Is current self user
14
+ */
15
+ isSelf: boolean,
16
+ /**
17
+ * Message
18
+ */
19
+ message: OperationMessageDto) => void | boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Refresh handler type
3
+ * 刷新处理程序类型
4
+ */
5
+ export type RefreshHandler = () => void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { UserRole } from "@etsoo/appscript";
2
+ /**
3
+ * SignalR user
4
+ */
5
+ export type SignalRUser = {
6
+ /**
7
+ * Id
8
+ * 编号
9
+ */
10
+ id: number;
11
+ /**
12
+ * Name
13
+ * 姓名
14
+ */
15
+ name: string;
16
+ /**
17
+ * Avatar
18
+ * 头像
19
+ */
20
+ avatar?: string;
21
+ /**
22
+ * Device id
23
+ * 设备编号
24
+ */
25
+ deviceId: number | number[];
26
+ /**
27
+ * Role
28
+ * 角色
29
+ */
30
+ role?: UserRole;
31
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,6 @@
1
1
  import React, { FormEventHandler } from "react";
2
2
  import { CommonPageProps } from "./CommonPageProps";
3
+ import { OperationMessageHandler } from "../messages/OperationMessageHandler";
3
4
  /**
4
5
  * Add / Edit page props
5
6
  */
@@ -29,6 +30,10 @@ export interface EditPageProps extends Omit<CommonPageProps, "onSubmit"> {
29
30
  * Top part
30
31
  */
31
32
  topPart?: React.ReactNode;
33
+ /**
34
+ * Operation message handler
35
+ */
36
+ operationMessageHandler?: OperationMessageHandler;
32
37
  }
33
38
  /**
34
39
  * Add / Edit page
@@ -6,15 +6,24 @@ import SaveIcon from "@mui/icons-material/Save";
6
6
  import DeleteIcon from "@mui/icons-material/Delete";
7
7
  import { BackButton } from "../BackButton";
8
8
  import { Labels } from "../app/Labels";
9
+ import { MessageUtils } from "../messages/MessageUtils";
9
10
  /**
10
11
  * Add / Edit page
11
12
  * @param props Props
12
13
  */
13
14
  export function EditPage(props) {
14
15
  // Destruct
15
- const { children, isEditing, onDelete, onSubmit, paddings = MUGlobal.pagePaddings, scrollContainer = CommonPageScrollContainer, supportBack = true, submitDisabled = false, topPart, ...rest } = props;
16
+ const { children, isEditing, onDelete, onSubmit, paddings = MUGlobal.pagePaddings, scrollContainer = CommonPageScrollContainer, supportBack = true, submitDisabled = false, topPart, operationMessageHandler, ...rest } = props;
16
17
  // Labels
17
18
  const labels = Labels.CommonPage;
19
+ React.useEffect(() => {
20
+ if (operationMessageHandler == null)
21
+ return;
22
+ MessageUtils.onOperationMessage(operationMessageHandler);
23
+ return () => {
24
+ MessageUtils.offOperationMessage(operationMessageHandler);
25
+ };
26
+ }, []);
18
27
  return (React.createElement(CommonPage, { paddings: paddings, scrollContainer: scrollContainer, ...rest },
19
28
  topPart,
20
29
  React.createElement("form", { onSubmit: onSubmit },
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { MUGlobal } from "../MUGlobal";
3
3
  import { ResponsibleContainer } from "../ResponsibleContainer";
4
4
  import { CommonPage } from "./CommonPage";
5
+ import { MessageUtils } from "../messages/MessageUtils";
5
6
  /**
6
7
  * Fixed height list page
7
8
  * @param props Props
@@ -10,12 +11,20 @@ import { CommonPage } from "./CommonPage";
10
11
  export function ResponsivePage(props) {
11
12
  var _a;
12
13
  // Destruct
13
- const { pageProps = {}, ...rest } = props;
14
+ const { pageProps = {}, operationMessageHandler, ...rest } = props;
14
15
  (_a = pageProps.paddings) !== null && _a !== void 0 ? _a : (pageProps.paddings = MUGlobal.pagePaddings);
15
16
  const { paddings, fabColumnDirection, ...pageRest } = pageProps;
16
17
  // State
17
18
  const [scrollContainer, setScrollContainer] = React.useState();
18
19
  const [direction, setDirection] = React.useState(fabColumnDirection);
20
+ React.useEffect(() => {
21
+ if (operationMessageHandler == null)
22
+ return;
23
+ MessageUtils.onOperationMessage(operationMessageHandler);
24
+ return () => {
25
+ MessageUtils.offOperationMessage(operationMessageHandler);
26
+ };
27
+ }, []);
19
28
  // Layout
20
29
  return (React.createElement(CommonPage, { ...pageRest, paddings: {}, scrollContainer: scrollContainer, fabColumnDirection: direction },
21
30
  React.createElement(ResponsibleContainer, { paddings: paddings, containerBoxSx: (paddings, hasField, _dataGrid) => {
@@ -4,6 +4,7 @@ import { DataTypes, IdDefaultType } from "@etsoo/shared";
4
4
  import { ListChildComponentProps } from "react-window";
5
5
  import { ScrollerListExInnerItemRendererProps, ScrollerListExItemSize } from "../ScrollerListEx";
6
6
  import { DataGridPageProps } from "./DataGridPageProps";
7
+ import { OperationMessageHandler } from "../messages/OperationMessageHandler";
7
8
  /**
8
9
  * Response page props
9
10
  */
@@ -43,4 +44,8 @@ export type ResponsePageProps<T extends object, F extends DataTypes.BasicTemplat
43
44
  * Quick action for double click or click under mobile
44
45
  */
45
46
  quickAction?: (data: T) => void;
47
+ /**
48
+ * Operation message handler
49
+ */
50
+ operationMessageHandler?: OperationMessageHandler;
46
51
  };
@@ -3,6 +3,7 @@ import { DataTypes } from "@etsoo/shared";
3
3
  import { GridProps } from "@mui/material";
4
4
  import React from "react";
5
5
  import { CommonPageProps } from "./CommonPageProps";
6
+ import { OperationMessageHandler } from "../messages/OperationMessageHandler";
6
7
  /**
7
8
  * View page grid item properties
8
9
  */
@@ -88,9 +89,9 @@ export interface ViewPageProps<T extends DataTypes.StringRecord> extends Omit<Co
88
89
  */
89
90
  gridRef?: React.Ref<HTMLDivElement>;
90
91
  /**
91
- * Refresh seed
92
+ * Operation message handler
92
93
  */
93
- refreshSeed?: number;
94
+ operationMessageHandler?: OperationMessageHandler | string[];
94
95
  }
95
96
  /**
96
97
  * View page
@@ -8,6 +8,7 @@ import { GridDataFormat } from "../GridDataFormat";
8
8
  import { MUGlobal } from "../MUGlobal";
9
9
  import { PullToRefreshUI } from "../PullToRefreshUI";
10
10
  import { CommonPage } from "./CommonPage";
11
+ import { MessageUtils } from "../messages/MessageUtils";
11
12
  /**
12
13
  * View page grid item
13
14
  * @param props Props
@@ -101,7 +102,7 @@ function getItemField(field, data) {
101
102
  */
102
103
  export function ViewPage(props) {
103
104
  // Destruct
104
- const { actions, children, fields, loadData, paddings = MUGlobal.pagePaddings, spacing = MUGlobal.half(MUGlobal.pagePaddings), supportRefresh = true, fabColumnDirection = true, fabTop = true, supportBack = true, pullToRefresh = true, gridRef, refreshSeed = 0, ...rest } = props;
105
+ const { actions, children, fields, loadData, paddings = MUGlobal.pagePaddings, spacing = MUGlobal.half(MUGlobal.pagePaddings), supportRefresh = true, fabColumnDirection = true, fabTop = true, supportBack = true, pullToRefresh = true, gridRef, operationMessageHandler, ...rest } = props;
105
106
  // Data
106
107
  const [data, setData] = React.useState();
107
108
  // Labels
@@ -116,11 +117,29 @@ export function ViewPage(props) {
116
117
  setData(result);
117
118
  }, [loadData]);
118
119
  React.useEffect(() => {
119
- // Only refresh after the first data load
120
- if (refreshSeed === 0 || data == null)
121
- return;
122
- refresh();
123
- }, [refreshSeed]);
120
+ const handler = (user, isSelf, message) => {
121
+ if (operationMessageHandler == null)
122
+ return;
123
+ if (Array.isArray(operationMessageHandler)) {
124
+ if (!operationMessageHandler.includes(message.operationType))
125
+ return;
126
+ }
127
+ else {
128
+ if (operationMessageHandler(user, isSelf, message) === false)
129
+ return;
130
+ }
131
+ refresh();
132
+ };
133
+ MessageUtils.onOperationMessage(handler);
134
+ const refreshHandler = async () => {
135
+ await refresh();
136
+ };
137
+ MessageUtils.onRefresh(refreshHandler);
138
+ return () => {
139
+ MessageUtils.offOperationMessage(handler);
140
+ MessageUtils.offRefresh(refreshHandler);
141
+ };
142
+ }, []);
124
143
  return (React.createElement(CommonPage, { paddings: paddings, onRefresh: supportRefresh ? refresh : undefined, onUpdate: supportRefresh ? undefined : refresh, ...rest, scrollContainer: globalThis, fabColumnDirection: fabColumnDirection, fabTop: fabTop, supportBack: supportBack }, data == null ? (React.createElement(LinearProgress, null)) : (React.createElement(React.Fragment, null,
125
144
  React.createElement(Grid, { container: true, justifyContent: "left", spacing: spacing, className: "ET-ViewPage", ref: gridRef, sx: {
126
145
  ".MuiTypography-subtitle2": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.2.74",
3
+ "version": "1.2.76",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,20 +50,20 @@
50
50
  "@emotion/css": "^11.11.2",
51
51
  "@emotion/react": "^11.11.1",
52
52
  "@emotion/styled": "^11.11.0",
53
- "@etsoo/appscript": "^1.4.28",
53
+ "@etsoo/appscript": "^1.4.29",
54
54
  "@etsoo/notificationbase": "^1.1.25",
55
- "@etsoo/react": "^1.6.99",
55
+ "@etsoo/react": "^1.7.0",
56
56
  "@etsoo/shared": "^1.2.8",
57
- "@mui/icons-material": "^5.14.1",
58
- "@mui/material": "^5.14.2",
59
- "@mui/x-data-grid": "^6.10.1",
57
+ "@mui/icons-material": "^5.14.3",
58
+ "@mui/material": "^5.14.4",
59
+ "@mui/x-data-grid": "^6.11.0",
60
60
  "@types/pica": "^9.0.1",
61
61
  "@types/pulltorefreshjs": "^0.1.5",
62
- "@types/react": "^18.2.16",
62
+ "@types/react": "^18.2.20",
63
63
  "@types/react-avatar-editor": "^13.0.0",
64
64
  "@types/react-dom": "^18.2.7",
65
65
  "@types/react-input-mask": "^3.0.2",
66
- "chart.js": "^4.3.1",
66
+ "chart.js": "^4.3.3",
67
67
  "chartjs-plugin-datalabels": "^2.2.0",
68
68
  "pica": "^9.0.1",
69
69
  "pulltorefreshjs": "^0.1.22",
@@ -75,20 +75,20 @@
75
75
  "react-imask": "6.6.3"
76
76
  },
77
77
  "devDependencies": {
78
- "@babel/cli": "^7.22.9",
79
- "@babel/core": "^7.22.9",
80
- "@babel/plugin-transform-runtime": "^7.22.9",
81
- "@babel/preset-env": "^7.22.9",
78
+ "@babel/cli": "^7.22.10",
79
+ "@babel/core": "^7.22.10",
80
+ "@babel/plugin-transform-runtime": "^7.22.10",
81
+ "@babel/preset-env": "^7.22.10",
82
82
  "@babel/preset-react": "^7.22.5",
83
83
  "@babel/preset-typescript": "^7.22.5",
84
- "@babel/runtime-corejs3": "^7.22.6",
84
+ "@babel/runtime-corejs3": "^7.22.10",
85
85
  "@testing-library/jest-dom": "^5.17.0",
86
86
  "@testing-library/react": "^14.0.0",
87
87
  "@types/jest": "^29.5.3",
88
- "@typescript-eslint/eslint-plugin": "^6.2.0",
89
- "@typescript-eslint/parser": "^6.2.0",
90
- "jest": "^29.6.1",
91
- "jest-environment-jsdom": "^29.6.1",
88
+ "@typescript-eslint/eslint-plugin": "^6.3.0",
89
+ "@typescript-eslint/parser": "^6.3.0",
90
+ "jest": "^29.6.2",
91
+ "jest-environment-jsdom": "^29.6.2",
92
92
  "typescript": "^5.1.6"
93
93
  }
94
94
  }
package/src/index.ts CHANGED
@@ -8,6 +8,12 @@ export * from "./app/Labels";
8
8
  export * from "./app/ReactApp";
9
9
  export * from "./app/ServiceApp";
10
10
 
11
+ export * from "./messages/MessageUtils";
12
+ export * from "./messages/OperationMessageDto";
13
+ export * from "./messages/OperationMessageHandler";
14
+ export * from "./messages/RefreshHandler";
15
+ export * from "./messages/SignalRUser";
16
+
11
17
  export * from "./pages/CommonPage";
12
18
  export * from "./pages/CommonPageProps";
13
19
  export * from "./pages/DataGridPage";
@@ -0,0 +1,78 @@
1
+ import EventEmitter from "events";
2
+ import { SignalRUser } from "./SignalRUser";
3
+ import { OperationMessageDto } from "./OperationMessageDto";
4
+ import { OperationMessageHandler } from "./OperationMessageHandler";
5
+
6
+ /**
7
+ * Message utilities
8
+ * 消息工具
9
+ */
10
+ export namespace MessageUtils {
11
+ /**
12
+ * Event emitter
13
+ */
14
+ export const eventEmitter = new EventEmitter();
15
+
16
+ const OperationMessageName = "onOperationMessage";
17
+ const RefreshName = "onRefresh";
18
+
19
+ /**
20
+ * Emit operation message
21
+ * 发出操作信息
22
+ * @param seed Refresh seed
23
+ */
24
+ export function emitOperationMessage(
25
+ user: SignalRUser | undefined,
26
+ isSelf: boolean,
27
+ message: OperationMessageDto
28
+ ) {
29
+ eventEmitter.emit(OperationMessageName, user, isSelf, message);
30
+ }
31
+
32
+ /**
33
+ * Emit refresh
34
+ * 发出刷新
35
+ * @param user SignalR user
36
+ * @param isSelf Is current user self
37
+ * @param message Message
38
+ */
39
+ export function emitRefresh() {
40
+ eventEmitter.emit(RefreshName);
41
+ }
42
+
43
+ /**
44
+ * Add operation event listener
45
+ * 添加操作事件监控器
46
+ * @param handler Handler
47
+ */
48
+ export function onOperationMessage(handler: OperationMessageHandler) {
49
+ eventEmitter.on(OperationMessageName, handler);
50
+ }
51
+
52
+ /**
53
+ * Add refresh event listener
54
+ * 添加事件事件监控器
55
+ * @param handler Handler
56
+ */
57
+ export function onRefresh(handler: OperationMessageHandler) {
58
+ eventEmitter.on(RefreshName, handler);
59
+ }
60
+
61
+ /**
62
+ * Remove operation event listener
63
+ * 移除操作事件监控器
64
+ * @param handler Handler
65
+ */
66
+ export function offOperationMessage(handler: OperationMessageHandler) {
67
+ eventEmitter.off(OperationMessageName, handler);
68
+ }
69
+
70
+ /**
71
+ * Remove refresh event listener
72
+ * 移除刷新事件监控器
73
+ * @param handler Handler
74
+ */
75
+ export function offRefresh(handler: OperationMessageHandler) {
76
+ eventEmitter.off(RefreshName, handler);
77
+ }
78
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Operation message data
3
+ * 操作信息数据
4
+ */
5
+ export type OperationMessageDto = {
6
+ /**
7
+ * Operation type
8
+ * 操作类型
9
+ */
10
+ operationType: string;
11
+
12
+ /**
13
+ * Target object id
14
+ * 目标对象编号
15
+ */
16
+ id?: number;
17
+ };
@@ -0,0 +1,23 @@
1
+ import { OperationMessageDto } from "./OperationMessageDto";
2
+ import { SignalRUser } from "./SignalRUser";
3
+
4
+ /**
5
+ * Operation message handler type
6
+ * 操作信息处理程序类型
7
+ */
8
+ export type OperationMessageHandler = (
9
+ /**
10
+ * Signal user data
11
+ */
12
+ user: SignalRUser | undefined,
13
+
14
+ /**
15
+ * Is current self user
16
+ */
17
+ isSelf: boolean,
18
+
19
+ /**
20
+ * Message
21
+ */
22
+ message: OperationMessageDto
23
+ ) => void | boolean;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Refresh handler type
3
+ * 刷新处理程序类型
4
+ */
5
+ export type RefreshHandler = () => void;
@@ -0,0 +1,36 @@
1
+ import { UserRole } from "@etsoo/appscript";
2
+
3
+ /**
4
+ * SignalR user
5
+ */
6
+ export type SignalRUser = {
7
+ /**
8
+ * Id
9
+ * 编号
10
+ */
11
+ id: number;
12
+
13
+ /**
14
+ * Name
15
+ * 姓名
16
+ */
17
+ name: string;
18
+
19
+ /**
20
+ * Avatar
21
+ * 头像
22
+ */
23
+ avatar?: string;
24
+
25
+ /**
26
+ * Device id
27
+ * 设备编号
28
+ */
29
+ deviceId: number | number[];
30
+
31
+ /**
32
+ * Role
33
+ * 角色
34
+ */
35
+ role?: UserRole;
36
+ };
@@ -7,6 +7,8 @@ import SaveIcon from "@mui/icons-material/Save";
7
7
  import DeleteIcon from "@mui/icons-material/Delete";
8
8
  import { BackButton } from "../BackButton";
9
9
  import { Labels } from "../app/Labels";
10
+ import { MessageUtils } from "../messages/MessageUtils";
11
+ import { OperationMessageHandler } from "../messages/OperationMessageHandler";
10
12
 
11
13
  /**
12
14
  * Add / Edit page props
@@ -42,6 +44,11 @@ export interface EditPageProps extends Omit<CommonPageProps, "onSubmit"> {
42
44
  * Top part
43
45
  */
44
46
  topPart?: React.ReactNode;
47
+
48
+ /**
49
+ * Operation message handler
50
+ */
51
+ operationMessageHandler?: OperationMessageHandler;
45
52
  }
46
53
 
47
54
  /**
@@ -60,12 +67,23 @@ export function EditPage(props: EditPageProps) {
60
67
  supportBack = true,
61
68
  submitDisabled = false,
62
69
  topPart,
70
+ operationMessageHandler,
63
71
  ...rest
64
72
  } = props;
65
73
 
66
74
  // Labels
67
75
  const labels = Labels.CommonPage;
68
76
 
77
+ React.useEffect(() => {
78
+ if (operationMessageHandler == null) return;
79
+
80
+ MessageUtils.onOperationMessage(operationMessageHandler);
81
+
82
+ return () => {
83
+ MessageUtils.offOperationMessage(operationMessageHandler);
84
+ };
85
+ }, []);
86
+
69
87
  return (
70
88
  <CommonPage paddings={paddings} scrollContainer={scrollContainer} {...rest}>
71
89
  {topPart}
@@ -4,6 +4,7 @@ import { MUGlobal } from "../MUGlobal";
4
4
  import { ResponsibleContainer } from "../ResponsibleContainer";
5
5
  import { CommonPage } from "./CommonPage";
6
6
  import { ResponsePageProps } from "./ResponsivePageProps";
7
+ import { MessageUtils } from "../messages/MessageUtils";
7
8
 
8
9
  /**
9
10
  * Fixed height list page
@@ -16,7 +17,7 @@ export function ResponsivePage<
16
17
  D extends DataTypes.Keys<T> = IdDefaultType<T>
17
18
  >(props: ResponsePageProps<T, F, D>) {
18
19
  // Destruct
19
- const { pageProps = {}, ...rest } = props;
20
+ const { pageProps = {}, operationMessageHandler, ...rest } = props;
20
21
 
21
22
  pageProps.paddings ??= MUGlobal.pagePaddings;
22
23
  const { paddings, fabColumnDirection, ...pageRest } = pageProps;
@@ -25,6 +26,16 @@ export function ResponsivePage<
25
26
  const [scrollContainer, setScrollContainer] = React.useState<HTMLElement>();
26
27
  const [direction, setDirection] = React.useState(fabColumnDirection);
27
28
 
29
+ React.useEffect(() => {
30
+ if (operationMessageHandler == null) return;
31
+
32
+ MessageUtils.onOperationMessage(operationMessageHandler);
33
+
34
+ return () => {
35
+ MessageUtils.offOperationMessage(operationMessageHandler);
36
+ };
37
+ }, []);
38
+
28
39
  // Layout
29
40
  return (
30
41
  <CommonPage
@@ -6,6 +6,7 @@ import {
6
6
  ScrollerListExItemSize
7
7
  } from "../ScrollerListEx";
8
8
  import { DataGridPageProps } from "./DataGridPageProps";
9
+ import { OperationMessageHandler } from "../messages/OperationMessageHandler";
9
10
 
10
11
  /**
11
12
  * Response page props
@@ -62,4 +63,9 @@ export type ResponsePageProps<
62
63
  * Quick action for double click or click under mobile
63
64
  */
64
65
  quickAction?: (data: T) => void;
66
+
67
+ /**
68
+ * Operation message handler
69
+ */
70
+ operationMessageHandler?: OperationMessageHandler;
65
71
  };
@@ -19,6 +19,9 @@ import { MUGlobal } from "../MUGlobal";
19
19
  import { PullToRefreshUI } from "../PullToRefreshUI";
20
20
  import { CommonPage } from "./CommonPage";
21
21
  import { CommonPageProps } from "./CommonPageProps";
22
+ import { OperationMessageHandler } from "../messages/OperationMessageHandler";
23
+ import { MessageUtils } from "../messages/MessageUtils";
24
+ import { RefreshHandler } from "../messages/RefreshHandler";
22
25
 
23
26
  /**
24
27
  * View page grid item properties
@@ -160,9 +163,9 @@ export interface ViewPageProps<T extends DataTypes.StringRecord>
160
163
  gridRef?: React.Ref<HTMLDivElement>;
161
164
 
162
165
  /**
163
- * Refresh seed
166
+ * Operation message handler
164
167
  */
165
- refreshSeed?: number;
168
+ operationMessageHandler?: OperationMessageHandler | string[];
166
169
  }
167
170
 
168
171
  function formatItemData(fieldData: unknown): string | undefined {
@@ -262,7 +265,7 @@ export function ViewPage<T extends DataTypes.StringRecord>(
262
265
  supportBack = true,
263
266
  pullToRefresh = true,
264
267
  gridRef,
265
- refreshSeed = 0,
268
+ operationMessageHandler,
266
269
  ...rest
267
270
  } = props;
268
271
 
@@ -283,10 +286,27 @@ export function ViewPage<T extends DataTypes.StringRecord>(
283
286
  }, [loadData]);
284
287
 
285
288
  React.useEffect(() => {
286
- // Only refresh after the first data load
287
- if (refreshSeed === 0 || data == null) return;
288
- refresh();
289
- }, [refreshSeed]);
289
+ const handler: OperationMessageHandler = (user, isSelf, message) => {
290
+ if (operationMessageHandler == null) return;
291
+ if (Array.isArray(operationMessageHandler)) {
292
+ if (!operationMessageHandler.includes(message.operationType)) return;
293
+ } else {
294
+ if (operationMessageHandler(user, isSelf, message) === false) return;
295
+ }
296
+ refresh();
297
+ };
298
+ MessageUtils.onOperationMessage(handler);
299
+
300
+ const refreshHandler: RefreshHandler = async () => {
301
+ await refresh();
302
+ };
303
+ MessageUtils.onRefresh(refreshHandler);
304
+
305
+ return () => {
306
+ MessageUtils.offOperationMessage(handler);
307
+ MessageUtils.offRefresh(refreshHandler);
308
+ };
309
+ }, []);
290
310
 
291
311
  return (
292
312
  <CommonPage