@plasosdk/plaso-electron-sdk 1.3.16-beta.0 → 1.3.17-beta.0
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/.prettierrc +8 -0
- package/README.md +18 -0
- package/js/macro.js +12 -2
- package/js/main.js +1 -1
- package/js/render.d.ts +98 -0
- package/js/render.js +454 -380
- package/jsconfig.json +10 -0
- package/package.json +1 -1
package/.prettierrc
ADDED
package/README.md
CHANGED
|
@@ -200,6 +200,7 @@ interface CreateClassWindowPamras {
|
|
|
200
200
|
onGetExtFileNameFn?: (info: any[], ...args: any[]) => Promise<string>;
|
|
201
201
|
onGetPreParseFileNameFn?: (info: any[], option: { suffix: string }) => Promise<string>;
|
|
202
202
|
onReportIssuesFn?: (Issues: any) => void;
|
|
203
|
+
browserOptions?: BrowserOptionsType;
|
|
203
204
|
}
|
|
204
205
|
```
|
|
205
206
|
|
|
@@ -245,6 +246,8 @@ interface ILiveClassOptions {
|
|
|
245
246
|
residentCamera?: boolean;
|
|
246
247
|
/** 是否启用日志上传。默认启用,进入和退出课堂时自动将日志上传至伯索服务器。 */
|
|
247
248
|
enableUploadLog?: boolean;
|
|
249
|
+
/** 是否启用课堂内置浏览器工具 */
|
|
250
|
+
enableBrowser?: boolean;
|
|
248
251
|
}
|
|
249
252
|
|
|
250
253
|
interface UserInfo {
|
|
@@ -274,6 +277,7 @@ interface UserInfo {
|
|
|
274
277
|
| supportSaveBoard | 否 | boolean | 是否启用保存板书,启用后工具箱中显示`保存当页板书`按钮,需要配合云盘使用。默认不启用。 |
|
|
275
278
|
| residentCamera | 否 | boolean | 是否启用常驻摄像头:只对学生或游客生效。启用后学生进入课堂后摄像头常驻开启,没有开启摄像头权限时也会开启。默认不启用。 |
|
|
276
279
|
| enableUploadLog | 否 | boolean | 是否启用日志自动上传。默认启用,退出课堂时 SDK 会自动将日志文件压缩并上传至伯索日志服务器,便于问题排查。传入 `false` 可关闭。 |
|
|
280
|
+
| enableBrowser | 否 | boolean | 是否启用课堂内置浏览器工具。启用后课堂工具栏将显示浏览器入口,需配合 `browserOptions` 使用以对接外部收藏数据。默认不启用。 |
|
|
277
281
|
|
|
278
282
|
<a id="query"></a>
|
|
279
283
|
**query属性说明**
|
|
@@ -520,6 +524,20 @@ interface Issues {
|
|
|
520
524
|
type onReportIssuesFn = (info: any) => void;
|
|
521
525
|
```
|
|
522
526
|
|
|
527
|
+
##### browserOptions
|
|
528
|
+
|
|
529
|
+
课堂内置浏览器相关配置,用于对接外部的书签功能。需要同时在 `classOptions` 中传入 `enableBrowser: true` 才能启用内置浏览器。
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
type BrowserOptionsType = {
|
|
533
|
+
/** 获取自定义分类列表的回调。返回分类数组,每个分类包含名称和该分类下的书签列表 */
|
|
534
|
+
onFetchCategoriesFn?: () => Promise<
|
|
535
|
+
| { success: true; data: { name: string; bookmarks: { title: string; url: string; icon: string }[] }[] }
|
|
536
|
+
| { success: false; message?: string }
|
|
537
|
+
>;
|
|
538
|
+
};
|
|
539
|
+
```
|
|
540
|
+
|
|
523
541
|
#### API参考
|
|
524
542
|
|
|
525
543
|
##### PlasoElectronSdk.initLogConfig
|
package/js/macro.js
CHANGED
|
@@ -37,6 +37,14 @@ const CLASS_WINDOW_MESG_TYPE = {
|
|
|
37
37
|
GET_PRE_PARSE_FILE_NAME: 'getPreParseFileName',
|
|
38
38
|
/** 上报问题 */
|
|
39
39
|
REPORT_ISSUES: 'reportIssues',
|
|
40
|
+
/** 浏览器-获取收藏列表 */
|
|
41
|
+
FETCH_FAVORITES: 'fetchFavorites',
|
|
42
|
+
/** 浏览器-获取分类列表 */
|
|
43
|
+
FETCH_CATEGORIES: 'fetchCategories',
|
|
44
|
+
/** 浏览器-添加收藏 */
|
|
45
|
+
ADD_FAVORITE: 'addFavorite',
|
|
46
|
+
/** 浏览器-删除收藏 */
|
|
47
|
+
REMOVE_FAVORITE: 'removeFavorite',
|
|
40
48
|
|
|
41
49
|
// >>>>>>> 发送的消息 >>>>>>>
|
|
42
50
|
/** html ready */
|
|
@@ -49,10 +57,12 @@ const CLASS_WINDOW_MESG_TYPE = {
|
|
|
49
57
|
INSERT_BOARD: 'insert_board',
|
|
50
58
|
/** 插入文件 */
|
|
51
59
|
INSERT_OBJECT: 'insertObject',
|
|
52
|
-
/** 响应getExtFileName */
|
|
53
60
|
RESP_GET_EXT_FILE_NAME: 'respGetExtFileName',
|
|
54
|
-
/** 响应getPreParseFileName */
|
|
55
61
|
RESP_GET_PRE_PARSE_FILE_NAME: 'respGetPreParseFileName',
|
|
62
|
+
RESP_FETCH_FAVORITES: 'respFetchFavorites',
|
|
63
|
+
RESP_FETCH_CATEGORIES: 'respFetchCategories',
|
|
64
|
+
RESP_ADD_FAVORITE: 'respAddFavorite',
|
|
65
|
+
RESP_REMOVE_FAVORITE: 'respRemoveFavorite',
|
|
56
66
|
};
|
|
57
67
|
|
|
58
68
|
const LEVEL = {
|
package/js/main.js
CHANGED
package/js/render.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export interface ClassOptionsType {
|
|
2
|
+
/** 是否开启课堂窗口debug模式 */
|
|
3
|
+
debug?: boolean;
|
|
4
|
+
/** 进课堂的必备query */
|
|
5
|
+
query: string;
|
|
6
|
+
/** 格式参考:1.53.901 */
|
|
7
|
+
version?: string;
|
|
8
|
+
env?: string;
|
|
9
|
+
appType?: string;
|
|
10
|
+
classType?: string;
|
|
11
|
+
topic?: string;
|
|
12
|
+
loginName?: string;
|
|
13
|
+
memoryConfigKey?: string;
|
|
14
|
+
/** 是否启用浏览器工具 */
|
|
15
|
+
enableBrowser?: boolean;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface FileParams {
|
|
20
|
+
/** 备课文件本地地址 */
|
|
21
|
+
filePath: string[];
|
|
22
|
+
fileType: 'png' | 'pb' | string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ClassWindowType {
|
|
26
|
+
classOptions: ClassOptionsType;
|
|
27
|
+
/** 自定义electron的窗口参数 */
|
|
28
|
+
electronWinOptions?: object;
|
|
29
|
+
/** 课堂窗口打开渲染成功后的回调 */
|
|
30
|
+
onClassWindowReadyFn?: (winId: number) => void;
|
|
31
|
+
/** 课堂窗口关闭后的回调 */
|
|
32
|
+
onClassWindowLeaveFn?: (winId: number) => void;
|
|
33
|
+
/** 课堂结束后的回调 */
|
|
34
|
+
onClassFinishedFn?: (meetingId: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* 保存板书,具体的保存逻辑由外部实现。
|
|
37
|
+
* 取消保存板书时,callback 传 false,否则传 true
|
|
38
|
+
*/
|
|
39
|
+
onSaveBoardFn?: (params: { fileInfo: FileParams[]; fileName?: string }, callback: (result: boolean) => void) => void;
|
|
40
|
+
/** 通知外部用户打开自己的资料中心 */
|
|
41
|
+
onOpenResourceCenterFn?: () => void;
|
|
42
|
+
/** 通过 insertObject 插入的文件时,从 info 中获取文件可访问地址 */
|
|
43
|
+
onGetExtFileNameFn?: (info: any, option: any) => Promise<string>;
|
|
44
|
+
/** 类似 onGetExtFileNameFn,获取的是预解析文件地址 */
|
|
45
|
+
onGetPreParseFileNameFn?: (info: any, option: { suffix: string }) => Promise<string>;
|
|
46
|
+
/** 上报问题,具体的上报逻辑由外部实现 */
|
|
47
|
+
onReportIssuesFn?: (info: { time: string; id: string; name: string; bugDescription: string }) => void;
|
|
48
|
+
/** 浏览器工具相关配置 */
|
|
49
|
+
browserOptions?: BrowserOptionsType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type BrowserOptionsType = {
|
|
53
|
+
/** 获取自定义分类 */
|
|
54
|
+
onFetchCategoriesFn?: () => Promise<
|
|
55
|
+
| { success: true; data: { name: string; bookmarks: { title: string; url: string; icon: string }[] }[] }
|
|
56
|
+
| { success: false; message?: string }
|
|
57
|
+
>;
|
|
58
|
+
// 出于鉴权方面考量,收藏功能暂不对外
|
|
59
|
+
/** @deprecated 获取"我的收藏"列表 */
|
|
60
|
+
onFetchFavoritesFn?: () => Promise<
|
|
61
|
+
{ success: true; data: { id: number; title: string; url: string; icon: string }[] } | { success: false; message?: string }
|
|
62
|
+
>;
|
|
63
|
+
/** @deprecated 添加收藏 */
|
|
64
|
+
onAddFavoriteFn?: (value: {
|
|
65
|
+
url: string;
|
|
66
|
+
title: string;
|
|
67
|
+
}) => Promise<
|
|
68
|
+
{ success: true; data: { id: number; title: string; url: string; icon: string } } | { success: false; message?: string }
|
|
69
|
+
>;
|
|
70
|
+
/** @deprecated 删除收藏 */
|
|
71
|
+
onRemoveFavoriteFn?: (ids: number[]) => Promise<{ success: boolean; message?: string }>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export interface FileDataObj {
|
|
75
|
+
/** 插入文件的格式,内容参考 FILE_TYPE */
|
|
76
|
+
type: number;
|
|
77
|
+
/** 文件名称,传入后会显示在文件窗口标题栏上,建议带上文件后缀名 */
|
|
78
|
+
title?: string;
|
|
79
|
+
/** 公开访问权限的文件全地址,建议 https 协议全地址 */
|
|
80
|
+
url?: string;
|
|
81
|
+
/** 具体的文件信息,除备课外,内容都由用户自己定义 */
|
|
82
|
+
info?: any[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type BuildReplyingCallback = <TArgs extends any[] = any[], TResult = any>(config: {
|
|
86
|
+
requestMsgType: string;
|
|
87
|
+
responseMsgType?: string;
|
|
88
|
+
/** 返回 false 时跳过后续流程 */
|
|
89
|
+
beforeCallback?: (...args: TArgs) => boolean | void;
|
|
90
|
+
/** 不传时跳过后续流程 */
|
|
91
|
+
callback?: (...args: TArgs) => TResult | Promise<TResult>;
|
|
92
|
+
/** 返回 false 时跳过后续流程 */
|
|
93
|
+
afterCallback?: (result: TResult) => boolean | void;
|
|
94
|
+
}) => {
|
|
95
|
+
requestMsgType: string;
|
|
96
|
+
responseMsgType: string;
|
|
97
|
+
handler: (event: any, requestId: string, ...args: TArgs) => Promise<void>;
|
|
98
|
+
};
|
package/js/render.js
CHANGED
|
@@ -1,65 +1,23 @@
|
|
|
1
|
-
const LogFormatter = require('../scripts/logger');
|
|
2
1
|
const path = require('path');
|
|
3
|
-
const {
|
|
2
|
+
const { ERROR_CODE, LESSON_TYPE, CLASS_WINDOW_MESG_TYPE, RENDER_TO_MAIN_MESG_TYPE, FILE_TYPE } = require('./macro');
|
|
3
|
+
const getConfig = require('../scripts/getConfig');
|
|
4
|
+
const LogFormatter = require('../scripts/logger');
|
|
5
|
+
const { getElectronRemote, parseUrlParams, getDisplayMatching } = require('./util');
|
|
6
|
+
|
|
4
7
|
const remote = getElectronRemote();
|
|
5
|
-
const
|
|
8
|
+
const isMac = getConfig().platform === 'darwin';
|
|
6
9
|
|
|
10
|
+
// logger
|
|
11
|
+
const defaultLoggerPath = remote ? path.join(remote.app.getPath('userData'), '/P403FileTemp/') : '';
|
|
7
12
|
const logger = new LogFormatter(defaultLoggerPath);
|
|
8
13
|
logger.init();
|
|
9
|
-
const getConfig = require('../scripts/getConfig');
|
|
10
|
-
|
|
11
|
-
const { platform } = getConfig();
|
|
12
|
-
|
|
13
|
-
const isMac = platform === 'darwin';
|
|
14
|
-
|
|
15
|
-
const { ERROR_CODE, LESSON_TYPE, CLASS_WINDOW_MESG_TYPE, RENDER_TO_MAIN_MESG_TYPE, FILE_TYPE } = require('./macro');
|
|
16
14
|
|
|
17
15
|
let currentWinId = null;
|
|
18
|
-
|
|
19
16
|
let currentWebContentsId = null;
|
|
20
17
|
|
|
21
18
|
/**
|
|
22
|
-
* @
|
|
23
|
-
* @
|
|
24
|
-
* @property {string} query - 进课堂的必备query
|
|
25
|
-
* @property {string} [version] - 格式参考:1.53.901
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @typedef {Object} FileParams
|
|
30
|
-
* @property {string[]} filePath - 备课文件本地地址
|
|
31
|
-
* @property {'png' | 'pb' | string} fileType
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @typedef {Object} classWindowType
|
|
36
|
-
* @property {classOptionsType} classOptions 进课堂参数对象
|
|
37
|
-
* @property {Object} [electronWinOptions] 自定义electron的窗口参数
|
|
38
|
-
* @property {(winId: number)=>void} [onClassWindowReadyFn] 课堂窗口打开渲染成功后的回调
|
|
39
|
-
* @property {(winId: number)=>void} [onClassWindowLeaveFn] 课堂窗口关闭后的回调
|
|
40
|
-
* @property {(meetingId: string)=>void} [onClassFinishedFn] 课堂结束后的回调
|
|
41
|
-
* @property {(
|
|
42
|
-
* params: {
|
|
43
|
-
* fileInfo: FileParams[],
|
|
44
|
-
* fileName?: string
|
|
45
|
-
* },
|
|
46
|
-
* callback: (result: boolean) => void
|
|
47
|
-
* ) => void} [onSaveBoardFn] 保存板书,具体的保存逻辑由外部实现,取消保存板书时,callback传false, 不然传true
|
|
48
|
-
* @property {()=>void} [onOpenResourceCenterFn] 通知外部用户打开自己的资料中心,资料中心的具体ui和逻辑由外部用户自己实现
|
|
49
|
-
* @property {(info: any, option: any)=>Promise<string>} [onGetExtFileNameFn] 通过insertObject插入的文件传入 参数 info 时,怎么从info中获取文件的可访问地址的逻辑在用户那,所以需要函数从外部用户获取外部用户传入的文件地址
|
|
50
|
-
* @property {(info: any, option: { suffix: string })=>Promise<string>} [onGetPreParseFileNameFn] 类似onGetExtFileNameFn,只是获取的文件地址是预解析文件地址,通过suffix得到预解析文件路径
|
|
51
|
-
* @property {(info: {
|
|
52
|
-
* time: string;
|
|
53
|
-
* id: string;
|
|
54
|
-
* name: string;
|
|
55
|
-
* bugDescription: string;
|
|
56
|
-
* })=>void} [onReportIssuesFn] 上报问题,具体的上报逻辑由外部实现
|
|
57
|
-
*
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @param {classWindowType} classWindowProps
|
|
62
|
-
* @returns
|
|
19
|
+
* @param {import('./render').ClassWindowType} classWindowProps
|
|
20
|
+
* @returns {number}
|
|
63
21
|
*/
|
|
64
22
|
function createClassWindow(classWindowProps) {
|
|
65
23
|
const {
|
|
@@ -75,345 +33,375 @@ function createClassWindow(classWindowProps) {
|
|
|
75
33
|
onReportIssuesFn,
|
|
76
34
|
} = classWindowProps;
|
|
77
35
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
36
|
+
const onFetchFavoritesFn = classWindowProps.browserOptions?.onFetchFavoritesFn;
|
|
37
|
+
const onFetchCategoriesFn = classWindowProps.browserOptions?.onFetchCategoriesFn;
|
|
38
|
+
const onAddFavoriteFn = classWindowProps.browserOptions?.onAddFavoriteFn;
|
|
39
|
+
const onRemoveFavoriteFn = classWindowProps.browserOptions?.onRemoveFavoriteFn;
|
|
40
|
+
|
|
41
|
+
logger.info('课堂窗口创建信息', {
|
|
42
|
+
version: getVersion(),
|
|
43
|
+
classOptions: classOptions,
|
|
44
|
+
electronWinOptions: electronWinOptions,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/* ----------------------------------- 守卫 ----------------------------------- */
|
|
83
48
|
if (currentWinId) {
|
|
84
49
|
logger.warn('课堂窗口同时仅能存在一个');
|
|
85
50
|
return ERROR_CODE.CLASS_WINODW_GREATER_THAN_ONE;
|
|
86
51
|
}
|
|
87
52
|
|
|
88
53
|
const isElectron = !!process?.versions?.['electron'];
|
|
89
|
-
if (isElectron) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const displayInfo = getDisplayMatching();
|
|
54
|
+
if (!isElectron) {
|
|
55
|
+
logger.error('非 Electron 环境');
|
|
56
|
+
return ERROR_CODE.NO_ELECTRON_ENVIRONMENT;
|
|
57
|
+
}
|
|
94
58
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
59
|
+
if (!remote) {
|
|
60
|
+
logger.error('获取 remote 失败');
|
|
61
|
+
return ERROR_CODE.GET_REMOTE_FAIL;
|
|
62
|
+
}
|
|
98
63
|
|
|
99
|
-
|
|
64
|
+
const displayInfo = getDisplayMatching();
|
|
65
|
+
if (!displayInfo) {
|
|
66
|
+
logger.error('获取 displayInfo 失败');
|
|
67
|
+
return ERROR_CODE.GET_DISPLAY_INFO_FAIL;
|
|
68
|
+
}
|
|
100
69
|
|
|
101
|
-
|
|
70
|
+
/* ---------------------------- 拼凑 globalAppInfo ---------------------------- */
|
|
71
|
+
const openerId = remote.getCurrentWebContents().id;
|
|
72
|
+
const queryObj = classOptions.query ? parseUrlParams(classOptions.query) : {};
|
|
73
|
+
const autoMaximized = true;
|
|
74
|
+
const env = classOptions.env ? classOptions.env.toLowerCase() : 'www';
|
|
75
|
+
|
|
76
|
+
const getHost = () => {
|
|
77
|
+
let rhost, dhost;
|
|
78
|
+
switch (env) {
|
|
79
|
+
case 'local':
|
|
80
|
+
rhost = 'http://localhost:4399/';
|
|
81
|
+
dhost = 'https://dev.plaso.cn/';
|
|
82
|
+
break;
|
|
83
|
+
case 'dev':
|
|
84
|
+
rhost = `https://${env}.plaso.cn/static/yxtelectronsdk/`;
|
|
85
|
+
dhost = `https://${env}.plaso.cn/`;
|
|
86
|
+
break;
|
|
87
|
+
case 'test':
|
|
88
|
+
case 'itest':
|
|
89
|
+
case 'ftest':
|
|
90
|
+
rhost = `https://${env}.plaso.cn/static/yxtsdk/`;
|
|
91
|
+
dhost = `https://${env}.plaso.cn/`;
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
rhost = `https://wwwr.plaso.cn/static/sdk/styleupime/${classOptions.version ?? '1.60.139'}/`;
|
|
95
|
+
dhost = 'https://www.plaso.cn/';
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
return { rhost, dhost };
|
|
99
|
+
};
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
const globalAppInfo = {
|
|
102
|
+
...classOptions,
|
|
103
|
+
...queryObj,
|
|
104
|
+
...getHost(),
|
|
105
|
+
query: classOptions.query,
|
|
106
|
+
appType: classOptions.appType,
|
|
107
|
+
markString: env,
|
|
108
|
+
env,
|
|
109
|
+
classType: classOptions.classType,
|
|
110
|
+
onClose: 'close', // 让课堂里退出时标识状态,详情见commFunction.js
|
|
111
|
+
electronSdkLoggerPath: logger.loggerPath ?? '',
|
|
112
|
+
electronSdkLoggerName: logger.logFileName ?? '',
|
|
113
|
+
flameshotPath: isMac
|
|
114
|
+
? path.join(__dirname, '../lib/flameshot.app/Contents/MacOS/flameshot')
|
|
115
|
+
: path.join(__dirname, '../lib/flameshot/flameshot.exe'),
|
|
116
|
+
PlasoALDPath: isMac //
|
|
117
|
+
? path.join(__dirname, '../lib/PlasoALD')
|
|
118
|
+
: '',
|
|
119
|
+
clientType: 'electron',
|
|
120
|
+
isElectronSdk: true,
|
|
121
|
+
openerId,
|
|
122
|
+
memoryConfigKey: classOptions.memoryConfigKey ?? queryObj.loginName ?? classOptions.loginName ?? '',
|
|
123
|
+
maximized: autoMaximized,
|
|
124
|
+
fullScreenable: queryObj.userType === 'listener', // 课堂最大化按钮是否是切换全屏模式
|
|
125
|
+
supportOnReportIssues: !!onReportIssuesFn,
|
|
126
|
+
supportOnFetchFavorites: !!onFetchFavoritesFn,
|
|
127
|
+
supportOnFetchCategories: !!onFetchCategoriesFn,
|
|
128
|
+
supportOnAddFavorite: !!onAddFavoriteFn,
|
|
129
|
+
supportOnRemoveFavorite: !!onRemoveFavoriteFn,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
if (globalAppInfo.userType === 'monitor') {
|
|
133
|
+
globalAppInfo.monitor = true;
|
|
134
|
+
}
|
|
109
135
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
136
|
+
/* ------------------------------ electron 窗口配置 ----------------------------- */
|
|
137
|
+
const buildElectronWinOptions = () => {
|
|
138
|
+
const buildBounds = () => {
|
|
139
|
+
const bounds = remote.getCurrentWindow().getBounds();
|
|
140
|
+
const screenWidth = displayInfo.size.width;
|
|
141
|
+
const screenWorkAreaWidth = displayInfo.workAreaSize.width;
|
|
142
|
+
const screenHeight = displayInfo.size.height;
|
|
143
|
+
const screenWorkAreaHeight = displayInfo.workAreaSize.height;
|
|
144
|
+
|
|
145
|
+
const changeBoundsDate = (ischangeWidth, baseWidthOrHeight) => {
|
|
146
|
+
const newWidthOrHeight = baseWidthOrHeight - 10;
|
|
147
|
+
if (ischangeWidth) {
|
|
148
|
+
bounds.height = Math.round((bounds.height * newWidthOrHeight) / bounds.width);
|
|
149
|
+
bounds.width = newWidthOrHeight;
|
|
122
150
|
} else {
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
bounds.width = Math.round((bounds.width * newWidthOrHeight) / bounds.height);
|
|
152
|
+
bounds.height = newWidthOrHeight;
|
|
125
153
|
}
|
|
154
|
+
};
|
|
155
|
+
// 课堂窗口创建时的宽或高和 屏幕宽高或屏幕工作区宽高 一致时,此时透明窗口会失效,需要调整进课堂时的窗口宽高
|
|
156
|
+
if (bounds.width === screenWidth || bounds.width === screenWorkAreaWidth) {
|
|
157
|
+
changeBoundsDate(true, Math.min(screenWidth, screenWorkAreaWidth));
|
|
158
|
+
}
|
|
159
|
+
if (bounds.height === screenHeight || bounds.height === screenWorkAreaHeight) {
|
|
160
|
+
changeBoundsDate(false, Math.min(screenHeight, screenWorkAreaHeight));
|
|
161
|
+
}
|
|
162
|
+
return bounds;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const defaultElectronWinOptions = {
|
|
166
|
+
frame: false,
|
|
167
|
+
/**
|
|
168
|
+
* electron 12.0.18之后,resizable设为true才能全屏和最大化,
|
|
169
|
+
* 而课堂窗口不允许通过electron自身的缩放行为来改变大小,因此进入课堂后会将resizable设为false
|
|
170
|
+
*/
|
|
171
|
+
resizable: true,
|
|
172
|
+
/** 设置为true,mac上setFullScreen(true)才生效,setSimpleFullScreen(true)不依赖此参数 */
|
|
173
|
+
fullscreenable: true,
|
|
174
|
+
...buildBounds(),
|
|
175
|
+
webPreferences: {
|
|
176
|
+
nodeIntegration: true,
|
|
177
|
+
enableRemoteModule: true,
|
|
178
|
+
contextIsolation: false,
|
|
179
|
+
nodeIntegrationInWorker: true,
|
|
180
|
+
webviewTag: true,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if (isMac) {
|
|
185
|
+
// 在Mac下要显式设置fullscreen为false,防止客户端全屏时进入课堂,课堂窗口也默认全屏,
|
|
186
|
+
// 导致部分按钮失效,以及独立窗口打开异常
|
|
187
|
+
defaultElectronWinOptions.fullscreen = false;
|
|
188
|
+
}
|
|
126
189
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
PlasoALD = path.join(__dirname, '../lib/PlasoALD');
|
|
133
|
-
}
|
|
190
|
+
// 学生无需使用透明窗口(透明窗口问题多),减少影响
|
|
191
|
+
if (queryObj.userType !== 'listener') {
|
|
192
|
+
defaultElectronWinOptions.transparent = true;
|
|
193
|
+
defaultElectronWinOptions.backgroundColor = '#00ffffff';
|
|
194
|
+
}
|
|
134
195
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
appType: classOptions.appType,
|
|
142
|
-
markString: env,
|
|
143
|
-
env,
|
|
144
|
-
classType: classOptions.classType,
|
|
145
|
-
// 让课堂里退出时标识状态,详情见commFunction.js
|
|
146
|
-
onClose: 'close',
|
|
147
|
-
electronSdkLoggerPath: logger.loggerPath ?? '',
|
|
148
|
-
electronSdkLoggerName: logger.logFileName ?? '',
|
|
149
|
-
flameshotPath: flameshotPath ?? '',
|
|
150
|
-
PlasoALDPath: PlasoALD ?? '',
|
|
151
|
-
clientType: 'electron',
|
|
152
|
-
isElectronSdk: true,
|
|
153
|
-
openerId: webContents.id,
|
|
154
|
-
memoryConfigKey: classOptions.memoryConfigKey ?? classInfo.loginName ?? classOptions.loginName ?? '',
|
|
155
|
-
maximized: autoMaximized,
|
|
156
|
-
fullScreenable,
|
|
157
|
-
supportOnReportIssues: !!onReportIssuesFn,
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
if (classOptionsObj.userType === 'monitor') {
|
|
161
|
-
classOptionsObj.monitor = true;
|
|
162
|
-
}
|
|
196
|
+
if (queryObj.topic) {
|
|
197
|
+
defaultElectronWinOptions.title = queryObj.topic + '';
|
|
198
|
+
}
|
|
199
|
+
if (classOptions.topic && classOptions.classType === LESSON_TYPE.PREPARE_LESSONS) {
|
|
200
|
+
defaultElectronWinOptions.title = classOptions.topic + '';
|
|
201
|
+
}
|
|
163
202
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const screenHeight = displayInfo.size.height;
|
|
170
|
-
const screenWorkAreaHeight = displayInfo.workAreaSize.height;
|
|
171
|
-
|
|
172
|
-
const changeBoundsDate = (ischangeWidth, baseWidthOrHeight) => {
|
|
173
|
-
const newWidthOrHeight = baseWidthOrHeight - 10;
|
|
174
|
-
if (ischangeWidth) {
|
|
175
|
-
bounds.height = Math.round((bounds.height * newWidthOrHeight) / bounds.width);
|
|
176
|
-
bounds.width = newWidthOrHeight;
|
|
177
|
-
} else {
|
|
178
|
-
bounds.width = Math.round((bounds.width * newWidthOrHeight) / bounds.height);
|
|
179
|
-
bounds.height = newWidthOrHeight;
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
// 课堂窗口创建时的宽或高和 屏幕宽高或屏幕工作区宽高 一致时,此时透明窗口会失效,需要调整进课堂时的窗口宽高
|
|
183
|
-
if (bounds.width === screenWidth || bounds.width === screenWorkAreaWidth) {
|
|
184
|
-
changeBoundsDate(true, Math.min(screenWidth, screenWorkAreaWidth));
|
|
185
|
-
}
|
|
186
|
-
if (bounds.height === screenHeight || bounds.height === screenWorkAreaHeight) {
|
|
187
|
-
changeBoundsDate(false, Math.min(screenHeight, screenWorkAreaHeight));
|
|
188
|
-
}
|
|
203
|
+
return {
|
|
204
|
+
...defaultElectronWinOptions,
|
|
205
|
+
...(electronWinOptions || {}),
|
|
206
|
+
};
|
|
207
|
+
};
|
|
189
208
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const _electeonWinOptions = electronWinOptions
|
|
222
|
-
? {
|
|
223
|
-
...defaultElecteonWinOptions,
|
|
224
|
-
...electronWinOptions,
|
|
225
|
-
}
|
|
226
|
-
: defaultElecteonWinOptions;
|
|
227
|
-
|
|
228
|
-
/**---------------------------------------------------- 创建课堂/备课窗口-----------------------------------*/
|
|
229
|
-
|
|
230
|
-
const onClassWindowReady = (event) => {
|
|
231
|
-
const classWindow = remote.BrowserWindow.fromId(currentWinId);
|
|
232
|
-
classWindow.moveTop();
|
|
233
|
-
classWindow.focus();
|
|
234
|
-
logger.info(`课堂窗口ready,id:${currentWinId}`);
|
|
235
|
-
if (onClassWindowReadyFn && currentWinId) onClassWindowReadyFn(currentWinId);
|
|
236
|
-
};
|
|
237
|
-
const onClassFinished = (event, value) => {
|
|
238
|
-
const meetingId = value?.meetingId;
|
|
239
|
-
logger.info(`课堂已结束,id:${currentWinId}, meetingId is ${meetingId}`);
|
|
240
|
-
if (onClassFinishedFn && currentWinId) onClassFinishedFn(meetingId);
|
|
241
|
-
};
|
|
242
|
-
const onHtmlReady = (event, value) => {
|
|
243
|
-
currentWebContentsId = value?.webContentId;
|
|
244
|
-
logger.info(`课堂窗口 html ready,web contents id:${currentWebContentsId}`);
|
|
245
|
-
|
|
246
|
-
if (currentWebContentsId) {
|
|
247
|
-
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.INIT_GLOBAL_APPINFO, classOptionsObj);
|
|
209
|
+
/* -------------------------------- 回调 bridge ------------------------------- */
|
|
210
|
+
const { ipcRenderer } = window.require('electron');
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 适用范围
|
|
214
|
+
* 用于创建带回复的回调,要求
|
|
215
|
+
* ipc-request payload 格式为 (requestId, ...args)
|
|
216
|
+
* ipc-response payload 格式为 (requestId, returnValue)
|
|
217
|
+
*
|
|
218
|
+
* 整条链路
|
|
219
|
+
* [课堂窗口] --ipc-request--> [Handler] --> [classWindowProps.onXxxFn] --> [Handler] --ipc-response--> [课堂窗口]
|
|
220
|
+
* 本函数用来构建这个桥接用的 handler
|
|
221
|
+
*
|
|
222
|
+
* 内部流程
|
|
223
|
+
* - step 1 执行 beforeCallback,如果返回 false 则中止
|
|
224
|
+
* - step 2 如果 callback 不存在则中止
|
|
225
|
+
* - step 3 执行 callback
|
|
226
|
+
* - step 4 执行 afterCallback,如果返回 false 则中止
|
|
227
|
+
* - step 5 则把 callback 返回值发给课堂窗口
|
|
228
|
+
*/
|
|
229
|
+
/** @type {import('./render').BuildReplyingCallback} */
|
|
230
|
+
const buildReplyingCallback = ({ requestMsgType, responseMsgType, callback, beforeCallback, afterCallback }) => {
|
|
231
|
+
return {
|
|
232
|
+
requestMsgType,
|
|
233
|
+
responseMsgType,
|
|
234
|
+
handler: async (event, requestId, ...args) => {
|
|
235
|
+
const info = `${requestMsgType} ${responseMsgType} ${requestId}`;
|
|
236
|
+
try {
|
|
237
|
+
logger.info(`[callback bridge] start ${info}`);
|
|
238
|
+
if (!currentWebContentsId || !responseMsgType || !requestId) {
|
|
239
|
+
return;
|
|
248
240
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const cb = (value) => {
|
|
253
|
-
if (currentWebContentsId) {
|
|
254
|
-
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.RESP_SAVE_BOARD, value);
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
if (onSaveBoardFn && value) onSaveBoardFn(value, cb);
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const onOpenResourceCenter = (event) => {
|
|
261
|
-
if (onOpenResourceCenterFn) onOpenResourceCenterFn();
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const onGetExtFileName = async (event, requestId, ...args) => {
|
|
265
|
-
if (onGetExtFileNameFn && args.length > 0) {
|
|
266
|
-
const url = await onGetExtFileNameFn(...args);
|
|
267
|
-
if (url) {
|
|
268
|
-
if (currentWebContentsId) {
|
|
269
|
-
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.RESP_GET_EXT_FILE_NAME, requestId, url);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
241
|
+
if (beforeCallback?.(...args) === false) {
|
|
242
|
+
logger.info(`[callback bridge] beforeCallback guard, ${info}`);
|
|
243
|
+
return;
|
|
272
244
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (onGetPreParseFileNameFn && args.length > 0) {
|
|
277
|
-
const url = await onGetPreParseFileNameFn(...args);
|
|
278
|
-
if (url) {
|
|
279
|
-
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.RESP_GET_PRE_PARSE_FILE_NAME, requestId, url);
|
|
280
|
-
}
|
|
245
|
+
if (!callback) {
|
|
246
|
+
logger.info(`[callback bridge] callback is null, skip, ${info}`);
|
|
247
|
+
return;
|
|
281
248
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
slashes: true,
|
|
304
|
-
})
|
|
305
|
-
: path.join(__dirname, '../index.html')) + `?openerId=${webContents.id}`;
|
|
306
|
-
|
|
307
|
-
appProxy.createWindow(
|
|
308
|
-
{
|
|
309
|
-
path: htmlPath,
|
|
310
|
-
debug: classOptions?.debug ?? false,
|
|
311
|
-
maximize: autoMaximized,
|
|
312
|
-
windowOptions: _electeonWinOptions,
|
|
313
|
-
},
|
|
314
|
-
async (id) => {
|
|
315
|
-
currentWinId = id;
|
|
316
|
-
|
|
317
|
-
appProxy.addWinEventListener(id, 'closed', () => {
|
|
318
|
-
logger.info(`课堂窗口关闭,id:${currentWinId}`);
|
|
319
|
-
|
|
320
|
-
if (onClassWindowLeaveFn) onClassWindowLeaveFn(currentWinId);
|
|
321
|
-
currentWinId = null;
|
|
322
|
-
currentWebContentsId = null;
|
|
323
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.LIVE_WINDOW_READY, onClassWindowReady);
|
|
324
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.ON_CLASS_FINISHED, onClassFinished);
|
|
325
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.HTML_READY, onHtmlReady);
|
|
326
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.SAVE_BOARD, onSaveBoard);
|
|
327
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.OPEN_RESOURCE_CENTER, onOpenResourceCenter);
|
|
328
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.GET_EXT_FILE_NAME, onGetExtFileName);
|
|
329
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.GET_PRE_PARSE_FILE_NAME, onGetPreParseFileName);
|
|
330
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.REPORT_ISSUES, onReportIssues);
|
|
331
|
-
});
|
|
332
|
-
},
|
|
333
|
-
);
|
|
334
|
-
return 0;
|
|
335
|
-
} else {
|
|
336
|
-
logger.error('获取remote、displayInfo 失败');
|
|
337
|
-
if (!remote) return ERROR_CODE.GET_REMOTE_FAIL;
|
|
338
|
-
if (!displayInfo) return ERROR_CODE.GET_DISPLAY_INFO_FAIL;
|
|
339
|
-
}
|
|
340
|
-
} catch (error) {
|
|
341
|
-
logger.error(`异常错误,${JSON.stringify(error)}`);
|
|
342
|
-
return ERROR_CODE.COMMOM_ERROR;
|
|
249
|
+
const result = await callback(...args);
|
|
250
|
+
if (afterCallback?.(result) === false) {
|
|
251
|
+
logger.info(`[callback bridge] afterCallback guard, ${info}`);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
ipcRenderer.sendTo(currentWebContentsId, responseMsgType, requestId, result);
|
|
255
|
+
logger.info(`[callback bridge] end ${info}`);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
logger.error(`[callback bridge] error ${info}`, error);
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const onClassWindowReady = (event) => {
|
|
264
|
+
const classWindow = remote.BrowserWindow.fromId(currentWinId);
|
|
265
|
+
classWindow.moveTop();
|
|
266
|
+
classWindow.focus();
|
|
267
|
+
logger.info(`课堂窗口ready,id:${currentWinId}`);
|
|
268
|
+
if (onClassWindowReadyFn && currentWinId) {
|
|
269
|
+
onClassWindowReadyFn(currentWinId);
|
|
343
270
|
}
|
|
344
|
-
}
|
|
345
|
-
logger.error('非Electron环境');
|
|
346
|
-
return ERROR_CODE.NO_ELECTRON_ENVIRONMENT;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
271
|
+
};
|
|
349
272
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
...liveClassWindowProps,
|
|
357
|
-
classOptions: {
|
|
358
|
-
...liveClassWindowProps.classOptions,
|
|
359
|
-
appType: 'liveclassSDK',
|
|
360
|
-
classType: LESSON_TYPE.LIVECLASS,
|
|
361
|
-
},
|
|
273
|
+
const onClassFinished = (event, value) => {
|
|
274
|
+
const meetingId = value?.meetingId;
|
|
275
|
+
logger.info(`课堂已结束,id:${currentWinId}, meetingId is ${meetingId}`);
|
|
276
|
+
if (onClassFinishedFn && currentWinId) {
|
|
277
|
+
onClassFinishedFn(meetingId);
|
|
278
|
+
}
|
|
362
279
|
};
|
|
363
|
-
return createClassWindow(classWindowProps);
|
|
364
|
-
}
|
|
365
280
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
...prepareClassWindowProps,
|
|
373
|
-
classOptions: {
|
|
374
|
-
...prepareClassWindowProps.classOptions,
|
|
375
|
-
appType: 'prepareLessonSdk',
|
|
376
|
-
meetingType: LESSON_TYPE.PREPARE_LESSONS,
|
|
377
|
-
classType: LESSON_TYPE.PREPARE_LESSONS,
|
|
378
|
-
userType: 'speaker',
|
|
379
|
-
topic: prepareClassWindowProps.classOptions.topic ?? '备课课堂',
|
|
380
|
-
},
|
|
281
|
+
const onHtmlReady = (event, value) => {
|
|
282
|
+
currentWebContentsId = value?.webContentId;
|
|
283
|
+
logger.info(`课堂窗口 html ready,web contents id:${currentWebContentsId}`);
|
|
284
|
+
if (currentWebContentsId) {
|
|
285
|
+
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.INIT_GLOBAL_APPINFO, globalAppInfo);
|
|
286
|
+
}
|
|
381
287
|
};
|
|
382
|
-
return createClassWindow(classWindowProps);
|
|
383
|
-
}
|
|
384
288
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
289
|
+
const onSaveBoard = (event, value) => {
|
|
290
|
+
logger.info(`保存板书:${JSON.stringify(value)}`);
|
|
291
|
+
if (onSaveBoardFn && value) {
|
|
292
|
+
onSaveBoardFn(value, (value) => {
|
|
293
|
+
if (currentWebContentsId) {
|
|
294
|
+
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.RESP_SAVE_BOARD, value);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const onOpenResourceCenter = (event) => {
|
|
301
|
+
onOpenResourceCenterFn?.();
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const onReportIssues = (event, info) => {
|
|
305
|
+
onReportIssuesFn?.(info);
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const onGetExtFileName = buildReplyingCallback({
|
|
309
|
+
requestMsgType: CLASS_WINDOW_MESG_TYPE.GET_EXT_FILE_NAME,
|
|
310
|
+
beforeCallback: (...args) => args.length > 0,
|
|
311
|
+
callback: onGetExtFileNameFn,
|
|
312
|
+
afterCallback: (result) => !!result,
|
|
313
|
+
responseMsgType: CLASS_WINDOW_MESG_TYPE.RESP_GET_EXT_FILE_NAME,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const onGetPreParseFileName = buildReplyingCallback({
|
|
317
|
+
requestMsgType: CLASS_WINDOW_MESG_TYPE.GET_PRE_PARSE_FILE_NAME,
|
|
318
|
+
beforeCallback: (...args) => args.length > 0,
|
|
319
|
+
callback: onGetPreParseFileNameFn,
|
|
320
|
+
afterCallback: (result) => !!result,
|
|
321
|
+
responseMsgType: CLASS_WINDOW_MESG_TYPE.RESP_GET_PRE_PARSE_FILE_NAME,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const onFetchFavorites = buildReplyingCallback({
|
|
325
|
+
requestMsgType: CLASS_WINDOW_MESG_TYPE.FETCH_FAVORITES,
|
|
326
|
+
responseMsgType: CLASS_WINDOW_MESG_TYPE.RESP_FETCH_FAVORITES,
|
|
327
|
+
callback: onFetchFavoritesFn,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const onFetchCategories = buildReplyingCallback({
|
|
331
|
+
requestMsgType: CLASS_WINDOW_MESG_TYPE.FETCH_CATEGORIES,
|
|
332
|
+
responseMsgType: CLASS_WINDOW_MESG_TYPE.RESP_FETCH_CATEGORIES,
|
|
333
|
+
callback: onFetchCategoriesFn,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const onAddFavorite = buildReplyingCallback({
|
|
337
|
+
requestMsgType: CLASS_WINDOW_MESG_TYPE.ADD_FAVORITE,
|
|
338
|
+
responseMsgType: CLASS_WINDOW_MESG_TYPE.RESP_ADD_FAVORITE,
|
|
339
|
+
callback: onAddFavoriteFn,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const onRemoveFavorite = buildReplyingCallback({
|
|
343
|
+
requestMsgType: CLASS_WINDOW_MESG_TYPE.REMOVE_FAVORITE,
|
|
344
|
+
responseMsgType: CLASS_WINDOW_MESG_TYPE.RESP_REMOVE_FAVORITE,
|
|
345
|
+
callback: onRemoveFavoriteFn,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const callbacks = {
|
|
349
|
+
[CLASS_WINDOW_MESG_TYPE.LIVE_WINDOW_READY]: onClassWindowReady,
|
|
350
|
+
[CLASS_WINDOW_MESG_TYPE.ON_CLASS_FINISHED]: onClassFinished,
|
|
351
|
+
[CLASS_WINDOW_MESG_TYPE.HTML_READY]: onHtmlReady,
|
|
352
|
+
[CLASS_WINDOW_MESG_TYPE.SAVE_BOARD]: onSaveBoard,
|
|
353
|
+
[CLASS_WINDOW_MESG_TYPE.OPEN_RESOURCE_CENTER]: onOpenResourceCenter,
|
|
354
|
+
[CLASS_WINDOW_MESG_TYPE.REPORT_ISSUES]: onReportIssues,
|
|
355
|
+
[onGetExtFileName.requestMsgType]: onGetExtFileName.handler,
|
|
356
|
+
[onGetPreParseFileName.requestMsgType]: onGetPreParseFileName.handler,
|
|
357
|
+
[onFetchFavorites.requestMsgType]: onFetchFavorites.handler,
|
|
358
|
+
[onFetchCategories.requestMsgType]: onFetchCategories.handler,
|
|
359
|
+
[onAddFavorite.requestMsgType]: onAddFavorite.handler,
|
|
360
|
+
[onRemoveFavorite.requestMsgType]: onRemoveFavorite.handler,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
Object.entries(callbacks).forEach(([type, callback]) => {
|
|
364
|
+
ipcRenderer.on(type, callback);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
/* ---------------------------------- 窗口创建 ---------------------------------- */
|
|
368
|
+
const appProxy = require('@plasosdk/winproxy');
|
|
369
|
+
|
|
370
|
+
const indexHtmlPath = isMac
|
|
371
|
+
? require('url').format({
|
|
372
|
+
pathname: path.join(__dirname, '../index.html'),
|
|
373
|
+
protocol: 'file:',
|
|
374
|
+
slashes: true,
|
|
375
|
+
})
|
|
376
|
+
: path.join(__dirname, '../index.html');
|
|
377
|
+
|
|
378
|
+
appProxy.createWindow(
|
|
379
|
+
{
|
|
380
|
+
path: indexHtmlPath + `?openerId=${openerId}`,
|
|
381
|
+
debug: classOptions?.debug ?? false,
|
|
382
|
+
maximize: autoMaximized,
|
|
383
|
+
windowOptions: buildElectronWinOptions(),
|
|
384
|
+
},
|
|
385
|
+
onWindowCreated,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
async function onWindowCreated(id) {
|
|
389
|
+
currentWinId = id;
|
|
390
|
+
appProxy.addWinEventListener(id, 'closed', onWindowClosed);
|
|
403
391
|
}
|
|
404
|
-
}
|
|
405
392
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
logger.error(`updateProhibitedWords error is: ${JSON.stringify(error)}}`);
|
|
393
|
+
/* ---------------------------------- 窗口关闭 ---------------------------------- */
|
|
394
|
+
function onWindowClosed() {
|
|
395
|
+
logger.info(`课堂窗口关闭,id:${currentWinId}`);
|
|
396
|
+
onClassWindowLeaveFn?.(currentWinId);
|
|
397
|
+
currentWinId = null;
|
|
398
|
+
currentWebContentsId = null;
|
|
399
|
+
Object.entries(callbacks).forEach(([type, callback]) => {
|
|
400
|
+
ipcRenderer.removeListener(type, callback);
|
|
401
|
+
});
|
|
416
402
|
}
|
|
403
|
+
|
|
404
|
+
return 0;
|
|
417
405
|
}
|
|
418
406
|
|
|
419
407
|
function getVersion() {
|
|
@@ -426,23 +414,109 @@ function getVersion() {
|
|
|
426
414
|
}
|
|
427
415
|
}
|
|
428
416
|
|
|
429
|
-
/**
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
417
|
+
/** Plaso Electron SDK */
|
|
418
|
+
module.exports = {
|
|
419
|
+
/**
|
|
420
|
+
* 课堂
|
|
421
|
+
* @param {import('./render').ClassWindowType} liveClassWindowProps
|
|
422
|
+
* @returns {number}
|
|
423
|
+
*/
|
|
424
|
+
createLiveClassWindow(liveClassWindowProps) {
|
|
425
|
+
try {
|
|
426
|
+
return createClassWindow({
|
|
427
|
+
...liveClassWindowProps,
|
|
428
|
+
classOptions: {
|
|
429
|
+
...liveClassWindowProps.classOptions,
|
|
430
|
+
appType: 'liveclassSDK',
|
|
431
|
+
classType: LESSON_TYPE.LIVECLASS,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
} catch (error) {
|
|
435
|
+
logger.error(`异常错误,${JSON.stringify(error)}`);
|
|
436
|
+
return ERROR_CODE.COMMOM_ERROR;
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* 备课
|
|
442
|
+
* @param {import('./render').ClassWindowType} prepareClassWindowProps
|
|
443
|
+
* @returns {number}
|
|
444
|
+
*/
|
|
445
|
+
createPrepareClassWindow(prepareClassWindowProps) {
|
|
446
|
+
try {
|
|
447
|
+
return createClassWindow({
|
|
448
|
+
...prepareClassWindowProps,
|
|
449
|
+
classOptions: {
|
|
450
|
+
...prepareClassWindowProps.classOptions,
|
|
451
|
+
appType: 'prepareLessonSdk',
|
|
452
|
+
meetingType: LESSON_TYPE.PREPARE_LESSONS,
|
|
453
|
+
classType: LESSON_TYPE.PREPARE_LESSONS,
|
|
454
|
+
userType: 'speaker',
|
|
455
|
+
topic: prepareClassWindowProps.classOptions.topic ?? '备课课堂',
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
} catch (error) {
|
|
459
|
+
logger.error(`异常错误,${JSON.stringify(error)}`);
|
|
460
|
+
return ERROR_CODE.COMMOM_ERROR;
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* 获取 sdk 版本
|
|
466
|
+
*/
|
|
467
|
+
getVersion,
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
*初始化日志文件路径
|
|
471
|
+
* @param {string} logFilePath 日志文件路径
|
|
472
|
+
*/
|
|
473
|
+
initLogConfig(logFilePath) {
|
|
474
|
+
if (logFilePath) {
|
|
475
|
+
logger.initLoggerFilePath(logFilePath);
|
|
476
|
+
require('electron')
|
|
477
|
+
.ipcRenderer //
|
|
478
|
+
.send(RENDER_TO_MAIN_MESG_TYPE.PLASO_INIT_LOG_PATH, logFilePath);
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* 插入的资源文件类型定义
|
|
484
|
+
*/
|
|
485
|
+
FILE_TYPE,
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* 插入外部云盘里的文件,文件需要遵循特定的数据结构
|
|
489
|
+
* @param {import('./render').FileDataObj} fileData 文件数据
|
|
490
|
+
*/
|
|
491
|
+
insertObject(fileData) {
|
|
492
|
+
try {
|
|
493
|
+
if (currentWebContentsId) {
|
|
494
|
+
window
|
|
495
|
+
.require('electron')
|
|
496
|
+
.ipcRenderer //
|
|
497
|
+
.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.INSERT_OBJECT, fileData);
|
|
498
|
+
}
|
|
499
|
+
} catch (error) {
|
|
500
|
+
logger.error(`insertObject error is: ${JSON.stringify(error)}}`);
|
|
501
|
+
return ERROR_CODE.COMMOM_ERROR;
|
|
502
|
+
}
|
|
503
|
+
},
|
|
437
504
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
505
|
+
/**
|
|
506
|
+
* 更新严禁词列表
|
|
507
|
+
* @param {string[]} words 严禁词数组
|
|
508
|
+
*/
|
|
509
|
+
updateProhibitedWords(words) {
|
|
510
|
+
try {
|
|
511
|
+
if (currentWebContentsId) {
|
|
512
|
+
window
|
|
513
|
+
.require('electron')
|
|
514
|
+
.ipcRenderer //
|
|
515
|
+
.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.UPDATE_SENSITIVE_WORDS, words || []);
|
|
516
|
+
}
|
|
517
|
+
} catch (error) {
|
|
518
|
+
logger.error(`updateProhibitedWords error is: ${JSON.stringify(error)}}`);
|
|
519
|
+
return ERROR_CODE.COMMOM_ERROR;
|
|
520
|
+
}
|
|
521
|
+
},
|
|
446
522
|
};
|
|
447
|
-
|
|
448
|
-
module.exports = PlasoElectronSdk;
|
package/jsconfig.json
ADDED