@cqsjjb/course-res-design 0.0.1 → 0.0.2

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.
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { CourseInfo } from './types';
3
+
4
+ /**
5
+ * CourseInfo 组件 Props
6
+ */
7
+ export interface CourseInfoProps {
8
+ /** 课程ID(如果提供,组件将内部获取课程信息) */
9
+ courseId?: string | number;
10
+ /** 课程信息(如果提供了 courseId 则此字段可选) */
11
+ courseInfo?: CourseInfo;
12
+ /** 样式 */
13
+ style?: React.CSSProperties;
14
+ }
15
+
16
+ /**
17
+ * 课程信息展示组件
18
+ *
19
+ * @description 用于展示课程的基本信息,包括课程名称、培训项目、课程标签、课程封面、课程介绍等。支持通过 courseId 内部获取数据或外部传入数据。
20
+ */
21
+ declare class CourseInfo extends React.Component<CourseInfoProps> {}
22
+
23
+ export default CourseInfo;
24
+
package/CourseInfo.js ADDED
@@ -0,0 +1,97 @@
1
+ import React from 'react';
2
+ import { Descriptions } from 'antd';
3
+ import './CourseInfo.less';
4
+ import { getLabelName } from './utils/tools';
5
+ import { getCourseInfo } from './api/courseLibrary';
6
+ export default class CourseInfo extends React.Component {
7
+ state = {
8
+ loading: false,
9
+ courseInfo: {}
10
+ };
11
+ componentDidMount() {
12
+ // 如果提供了 courseId,则内部获取数据
13
+ if (this.props.courseId) {
14
+ this.fetchCourseInfo();
15
+ }
16
+ }
17
+ componentDidUpdate(prevProps) {
18
+ // 如果 courseId 变化,重新获取数据
19
+ if (prevProps.courseId !== this.props.courseId && this.props.courseId) {
20
+ this.fetchCourseInfo();
21
+ return;
22
+ }
23
+ // 如果没有 courseId 且外部传入的 courseInfo 变化,清空内部 state
24
+ if (!this.props.courseId && prevProps.courseInfo !== this.props.courseInfo && this.state.courseInfo?.id) {
25
+ this.setState({
26
+ courseInfo: {}
27
+ });
28
+ }
29
+ }
30
+
31
+ /**
32
+ * 通过 courseId 获取课程信息
33
+ */
34
+ async fetchCourseInfo() {
35
+ const {
36
+ courseId
37
+ } = this.props;
38
+ if (!courseId) return;
39
+ this.setState({
40
+ loading: true
41
+ });
42
+ try {
43
+ const infoRes = await getCourseInfo({
44
+ id: courseId
45
+ });
46
+
47
+ // 处理响应数据(兼容不同的响应格式)
48
+ const courseInfo = infoRes?.data || infoRes || {};
49
+ this.setState({
50
+ courseInfo,
51
+ loading: false
52
+ });
53
+ } catch (error) {
54
+ console.error('获取课程信息失败:', error);
55
+ this.setState({
56
+ loading: false
57
+ });
58
+ }
59
+ }
60
+ render() {
61
+ const {
62
+ loading,
63
+ courseInfo: stateCourseInfo
64
+ } = this.state;
65
+ // 如果提供了 courseId,优先使用内部 state 的数据,否则使用 props
66
+ const hasCourseId = !!this.props.courseId;
67
+ const courseInfo = hasCourseId && stateCourseInfo?.id ? stateCourseInfo : this.props.courseInfo || {};
68
+ return /*#__PURE__*/React.createElement("div", {
69
+ style: this.props.style,
70
+ className: "course-info"
71
+ }, /*#__PURE__*/React.createElement(Descriptions, {
72
+ size: "middle",
73
+ title: "\u8BFE\u7A0B\u4FE1\u606F",
74
+ column: 3,
75
+ loading: loading
76
+ }, /*#__PURE__*/React.createElement(Descriptions.Item, {
77
+ label: "\u8BFE\u7A0B\u540D\u79F0"
78
+ }, courseInfo?.courseName || '--'), /*#__PURE__*/React.createElement(Descriptions.Item, {
79
+ label: "\u57F9\u8BAD\u9879\u76EE"
80
+ }, getLabelName(courseInfo?.courseTypeLabel) || '--'), /*#__PURE__*/React.createElement(Descriptions.Item, {
81
+ label: "\u8BFE\u7A0B\u6807\u7B7E"
82
+ }, getLabelName(courseInfo?.courseLabel) || '--'), /*#__PURE__*/React.createElement(Descriptions.Item, {
83
+ label: "\u8BFE\u7A0B\u5C01\u9762"
84
+ }, courseInfo?.courseCover ? /*#__PURE__*/React.createElement("img", {
85
+ src: courseInfo.courseCover,
86
+ alt: "\u8BFE\u7A0B\u5C01\u9762",
87
+ width: 84,
88
+ height: 50
89
+ }) : '--'), /*#__PURE__*/React.createElement(Descriptions.Item, {
90
+ label: "\u8BFE\u7A0B\u4ECB\u7ECD"
91
+ }, courseInfo?.courseDescription ? /*#__PURE__*/React.createElement("div", {
92
+ dangerouslySetInnerHTML: {
93
+ __html: courseInfo.courseDescription
94
+ }
95
+ }) : '--')));
96
+ }
97
+ }
@@ -0,0 +1,7 @@
1
+ .course-info {
2
+ background-color: #F7F7F7;
3
+ border-radius: 6px;
4
+ margin-bottom: 20px;
5
+ padding: 20px;
6
+ box-sizing: border-box;
7
+ }
package/Designable.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { default as React } from 'react';
2
-
1
+ import React from 'react';
3
2
 
4
3
  /**
5
4
  * 关闭回调数据
package/Designable.js ADDED
@@ -0,0 +1,74 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ const EVENT_OPEN_COURSE_RES_DESIGN = 'EVENT_OPEN_COURSE_RES_DESIGN';
4
+ const EVENT_CLOSE_COURSE_RES_DESIGN = 'EVENT_CLOSE_COURSE_RES_DESIGN';
5
+ export const Designable = ({
6
+ style,
7
+ host = window.location.origin,
8
+ id,
9
+ extraParams = {},
10
+ onClose,
11
+ src,
12
+ visible = true
13
+ }) => {
14
+ const iframeRef = useRef(null);
15
+
16
+ // 计算 iframe 的 src
17
+ const iframeSrc = src || `${host}/course-res-design/setting`;
18
+
19
+ // 处理 iframe 加载完成
20
+ const handleIframeLoad = () => {
21
+ // iframe 加载完成后发送 postMessage
22
+ if (iframeRef.current?.contentWindow) {
23
+ iframeRef.current.contentWindow.postMessage({
24
+ type: EVENT_OPEN_COURSE_RES_DESIGN,
25
+ id: id || '',
26
+ extraParams: extraParams || {}
27
+ }, new URL(iframeSrc).origin);
28
+ }
29
+ };
30
+
31
+ // 监听来自 iframe 的关闭消息
32
+ useEffect(() => {
33
+ const handleMessage = event => {
34
+ // 验证消息来源
35
+ const iframeOrigin = new URL(iframeSrc).origin;
36
+ if (event.origin !== iframeOrigin) {
37
+ return;
38
+ }
39
+ if (event.data && event.data.type === EVENT_CLOSE_COURSE_RES_DESIGN) {
40
+ const {
41
+ id: returnedId
42
+ } = event.data;
43
+ // 调用 onClose 回调
44
+ if (onClose) {
45
+ onClose({
46
+ id: returnedId || ''
47
+ });
48
+ }
49
+ }
50
+ };
51
+ window.addEventListener('message', handleMessage);
52
+ return () => {
53
+ window.removeEventListener('message', handleMessage);
54
+ };
55
+ }, [onClose, iframeSrc, id]);
56
+ if (!visible) {
57
+ return null;
58
+ }
59
+ const editorContent = /*#__PURE__*/React.createElement("div", {
60
+ style: style,
61
+ className: "course-res-designable-container"
62
+ }, /*#__PURE__*/React.createElement("iframe", {
63
+ ref: iframeRef,
64
+ src: iframeSrc,
65
+ frameBorder: "0",
66
+ className: "course-res-designable-iframe",
67
+ title: "\u8BFE\u7A0B\u8D44\u6E90\u8BBE\u8BA1\u5668",
68
+ onLoad: handleIframeLoad
69
+ }));
70
+
71
+ // 使用 createPortal 将组件渲染到 body
72
+ return /*#__PURE__*/createPortal(editorContent, document.body);
73
+ };
74
+ export default Designable;
package/Preview.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { default as React } from 'react';
2
- import { CourseInfo, ResourceLabel } from './types';
3
-
1
+ import React from 'react';
2
+ import { CourseInfo, ResourceLabel } from './types';
4
3
 
5
4
  /**
6
5
  * 试题选项
@@ -72,10 +71,14 @@ export interface CourseChapterNode {
72
71
  * Preview 组件 Props
73
72
  */
74
73
  export interface PreviewProps {
75
- /** 课程章节列表(树形结构) */
76
- courseChapterList: CourseChapterNode[];
77
- /** 课程信息 */
78
- courseInfo: CourseInfo;
74
+ /** 课程ID(如果提供,组件将内部获取课程数据) */
75
+ courseId?: string | number;
76
+ /** 课程章节列表(树形结构,如果提供了 courseId 则此字段可选) */
77
+ courseChapterList?: CourseChapterNode[];
78
+ /** 课程信息(如果提供了 courseId 则此字段可选) */
79
+ courseInfo?: CourseInfo;
80
+ /** 样式 */
81
+ style?: React.CSSProperties;
79
82
  /** 是否启用预览扫码功能 */
80
83
  enablePreview?: boolean;
81
84
  }