diff --git a/src/components/EditorSection/index.tsx b/src/components/EditorSection/index.tsx
index a465c7b..9c00b94 100644
--- a/src/components/EditorSection/index.tsx
+++ b/src/components/EditorSection/index.tsx
@@ -6,6 +6,7 @@ import { isSSR } from '@/utils/is';
interface EditorSectionProps {
initialContent?: string;
+ onChange?: (content: string) => void;
}
// 创建一个 Viewer 组件用于服务端渲染
@@ -33,9 +34,9 @@ const EditorViewer: React.FC<{ content: string }> = ({ content }) => {
return
;
};
-export default function EditorSection({ initialContent }: EditorSectionProps) {
+export default function EditorSection({ initialContent, onChange }: EditorSectionProps) {
const [isClient, setIsClient] = useState(false);
- const editorRef = useRef(null);
+ const editorRef = useRef(null);
useEffect(() => {
if (!isSSR) {
@@ -63,7 +64,18 @@ export default function EditorSection({ initialContent }: EditorSectionProps) {
return (
-
+ {
+ // 获取编辑器实例并读取内容
+ const editorInstance = editorRef.current?.getInstance?.();
+ if (editorInstance && onChange) {
+ const content = editorInstance.getMarkdown();
+ onChange(content);
+ }
+ }}
+ />
);
}
\ No newline at end of file
diff --git a/src/pages/componentDevelopment/componentList/index.tsx b/src/pages/componentDevelopment/componentList/index.tsx
index 3a0c904..663b459 100644
--- a/src/pages/componentDevelopment/componentList/index.tsx
+++ b/src/pages/componentDevelopment/componentList/index.tsx
@@ -592,7 +592,7 @@ const GlobalVarContainer = () => {
{/*数据列表*/}
-
+ {selectedItem !== '组件审核' && (
{
onChange={handlePageChange}
/>
-
+ )}
diff --git a/src/pages/componentMarket/compDetails.tsx b/src/pages/componentMarket/compDetails.tsx
index 8bffb58..e89527e 100644
--- a/src/pages/componentMarket/compDetails.tsx
+++ b/src/pages/componentMarket/compDetails.tsx
@@ -3,12 +3,16 @@ import styles from './style/compDetails.module.less';
import { Space, Divider, Button, Typography, Card, Image, Rate, Grid } from '@arco-design/web-react';
import dayjs from 'dayjs';
import { getComponentMarket } from '@/api/componentMarket';
+import ReactMarkdown from 'react-markdown';
+import remarkGfm from 'remark-gfm';
+import CopyComponentModal from './copyComponentModal';
const { Row, Col } = Grid;
const CompDetails = ({ compInfo }) => {
const [componentList, setComponentList] = useState([]);
const [currentCompInfo, setCurrentCompInfo] = useState(compInfo);
+ const [copyModalVisible, setCopyModalVisible] = useState(false);
// 获取组件市场数据
const fetchComponentData = async () => {
@@ -24,160 +28,6 @@ const CompDetails = ({ compInfo }) => {
if (res?.code === 200 && res?.data) {
setComponentList(res.data.list || []);
- setComponentList([
- {
- 'baseConfigJson': [],
- 'codeLanguage': 'Python',
- 'collaboratorId': 0,
- 'componentBaseId': '1993166956502499330',
- 'componentClassify': '设备数采与控制交互组件',
- 'componentStatus': 'DEPLOYED',
- 'componentVersion': 1,
- 'createBy': '1992851580222222338',
- 'createTime': 1764228733000,
- 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}',
- 'deployType': 'Python',
- 'desc': '',
- 'dockerImageId': '',
- 'dockerImageName': '',
- 'extraSystemId': '',
- 'filesName': '',
- 'id': '1993945941293449218',
- 'identifier': 'admin_add_num_maket1',
- 'isDeleted': 0,
- 'localProjectPath': '/000000/admin_add_num_maket1/master',
- 'logoUrl': '',
- 'name': '两数之和2',
- 'operatesJson': [],
- 'permission': null,
- 'projectId': 'add_num_maket1',
- 'publicStatus': 1,
- 'publishTime': null,
- 'reviewOpinion': '',
- 'size': '106.30 KB',
- 'star': 0,
- 'status': 1,
- 'tags': [],
- 'tenantId': '000000',
- 'updateBy': '1992851580222222338',
- 'updateTime': 1764232122000,
- 'version': 'master'
- },
- {
- 'baseConfigJson': [],
- 'codeLanguage': 'Python',
- 'collaboratorId': 0,
- 'componentBaseId': '1993166956502499330',
- 'componentClassify': '设备数采与控制交互组件',
- 'componentStatus': 'DEPLOYED',
- 'componentVersion': 1,
- 'createBy': '1992851580222222338',
- 'createTime': 1764228733000,
- 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}',
- 'deployType': 'Python',
- 'desc': '',
- 'dockerImageId': '',
- 'dockerImageName': '',
- 'extraSystemId': '',
- 'filesName': '',
- 'id': '1993945941293449218',
- 'identifier': 'admin_add_num_maket1',
- 'isDeleted': 0,
- 'localProjectPath': '/000000/admin_add_num_maket1/master',
- 'logoUrl': '',
- 'name': '两数之和3',
- 'operatesJson': [],
- 'permission': null,
- 'projectId': 'add_num_maket1',
- 'publicStatus': 1,
- 'publishTime': null,
- 'reviewOpinion': '',
- 'size': '106.30 KB',
- 'star': 0,
- 'status': 1,
- 'tags': [],
- 'tenantId': '000000',
- 'updateBy': '1992851580222222338',
- 'updateTime': 1764232122000,
- 'version': 'master'
- },
- {
- 'baseConfigJson': [],
- 'codeLanguage': 'Python',
- 'collaboratorId': 0,
- 'componentBaseId': '1993166956502499330',
- 'componentClassify': '设备数采与控制交互组件',
- 'componentStatus': 'DEPLOYED',
- 'componentVersion': 1,
- 'createBy': '1992851580222222338',
- 'createTime': 1764228733000,
- 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}',
- 'deployType': 'Python',
- 'desc': '',
- 'dockerImageId': '',
- 'dockerImageName': '',
- 'extraSystemId': '',
- 'filesName': '',
- 'id': '1993945941293449218',
- 'identifier': 'admin_add_num_maket1',
- 'isDeleted': 0,
- 'localProjectPath': '/000000/admin_add_num_maket1/master',
- 'logoUrl': '',
- 'name': '两数之和4',
- 'operatesJson': [],
- 'permission': null,
- 'projectId': 'add_num_maket1',
- 'publicStatus': 1,
- 'publishTime': null,
- 'reviewOpinion': '',
- 'size': '106.30 KB',
- 'star': 0,
- 'status': 1,
- 'tags': [],
- 'tenantId': '000000',
- 'updateBy': '1992851580222222338',
- 'updateTime': 1764232122000,
- 'version': 'master'
- },
- {
- 'baseConfigJson': [],
- 'codeLanguage': 'Python',
- 'collaboratorId': 0,
- 'componentBaseId': '1993166956502499330',
- 'componentClassify': '设备数采与控制交互组件',
- 'componentStatus': 'DEPLOYED',
- 'componentVersion': 1,
- 'createBy': '1992851580222222338',
- 'createTime': 1764228733000,
- 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}',
- 'deployType': 'Python',
- 'desc': '',
- 'dockerImageId': '',
- 'dockerImageName': '',
- 'extraSystemId': '',
- 'filesName': '',
- 'id': '1993945941293449218',
- 'identifier': 'admin_add_num_maket1',
- 'isDeleted': 0,
- 'localProjectPath': '/000000/admin_add_num_maket1/master',
- 'logoUrl': '',
- 'name': '两数之和5',
- 'operatesJson': [],
- 'permission': null,
- 'projectId': 'add_num_maket1',
- 'publicStatus': 1,
- 'publishTime': null,
- 'reviewOpinion': '',
- 'size': '106.30 KB',
- 'star': 0,
- 'status': 1,
- 'tags': [],
- 'tenantId': '000000',
- 'updateBy': '1992851580222222338',
- 'updateTime': 1764232122000,
- 'version': 'master'
- }
- ]);
}
else {
setComponentList([]);
@@ -192,6 +42,11 @@ const CompDetails = ({ compInfo }) => {
fetchComponentData();
}, []);
+ // 处理复制组件
+ const handleCopyComponent = () => {
+ setCopyModalVisible(true);
+ };
+
// 渲染组件外壳
const renderCompHousing = () => {
@@ -278,11 +133,68 @@ const CompDetails = ({ compInfo }) => {
{currentCompInfo.description ? currentCompInfo.description : ' - '}
- 这里是一坨md语法
+
+
,
+ h2: ({ node, ...props }) => ,
+ h3: ({ node, ...props }) => ,
+ p: ({ node, ...props }) => ,
+ code: ({ node, inline, ...props }: any) =>
+ inline
+ ?
+ : ,
+ pre: ({ node, ...props }) => ,
+ ul: ({ node, ...props }) => ,
+ ol: ({ node, ...props }) =>
,
+ li: ({ node, ...props }) => ,
+ blockquote: ({ node, ...props }) => ,
+ a: ({ node, ...props }) => ,
+ table: ({ node, ...props }) => ,
+ th: ({ node, ...props }) => | ,
+ td: ({ node, ...props }) => |
+ }}
+ >
+ {currentCompInfo.readme || currentCompInfo.desc || '暂无详细说明'}
+
+
-
+
@@ -328,12 +240,22 @@ const CompDetails = ({ compInfo }) => {
{item.componentClassify || '-'}
-
{item.deployType || '-'}
+
{item.codeLanguage || '-'}
))}
+
+ {/* 复制组件弹窗 */}
+ setCopyModalVisible(false)}
+ onSuccess={() => {
+ setCopyModalVisible(false);
+ }}
+ />
>
);
};
diff --git a/src/pages/componentMarket/copyComponentModal.tsx b/src/pages/componentMarket/copyComponentModal.tsx
new file mode 100644
index 0000000..0645423
--- /dev/null
+++ b/src/pages/componentMarket/copyComponentModal.tsx
@@ -0,0 +1,271 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Modal,
+ Form,
+ Input,
+ Select,
+ Space,
+ Button,
+ Message
+} from '@arco-design/web-react';
+import { getComponentClassify } from '@/api/componentClassify';
+import { copyDesign } from '@/api/componentMarket';
+import { copyAll } from '@/api/componentBase';
+import EditorSection from '@/components/EditorSection';
+
+const FormItem = Form.Item;
+const Option = Select.Option;
+
+interface CopyComponentModalProps {
+ visible: boolean;
+ componentInfo: any;
+ onCancel: () => void;
+ onSuccess?: () => void;
+}
+
+const CopyComponentModal: React.FC = ({
+ visible,
+ componentInfo,
+ onCancel,
+ onSuccess
+ }) => {
+ const [form] = Form.useForm();
+ const [classifyList, setClassifyList] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [description, setDescription] = useState('');
+
+ // 组件语言选项
+ const codeLanguageOptions = [
+ { label: 'Java:8', value: 'Java' },
+ { label: 'Python:3.10.12', value: 'Python' }
+ ];
+
+ // 组件类型选项
+ const componentTypeOptions = [
+ { label: '普通组件', value: 'normal' },
+ { label: '监听组件', value: 'loop' }
+ ];
+
+ // 获取组件分类列表
+ const getComponentClassifyList = async () => {
+ try {
+ const res: any = await getComponentClassify('component');
+ if (res.code === 200) {
+ const data = res.data.map((item) => ({
+ label: item.classifyName,
+ value: item.id
+ }));
+ setClassifyList(data);
+ }
+ } catch (error) {
+ console.error('获取组件分类失败:', error);
+ }
+ };
+
+ useEffect(() => {
+ getComponentClassifyList();
+ }, []);
+
+ // 当弹窗打开时,设置表单初始值
+ useEffect(() => {
+ if (visible && componentInfo) {
+ form.setFieldsValue({
+ name: componentInfo.name || '',
+ projectId: '', // 复制时项目标识需要重新填写
+ componentClassify: componentInfo.componentClassify || '',
+ codeLanguage: componentInfo.codeLanguage || '',
+ type: componentInfo.type || 'normal'
+ });
+ setDescription(componentInfo.desc || '');
+ }
+ }, [visible, componentInfo, form]);
+
+ // 仅复制设计
+ const handleCopyDesign = async () => {
+ try {
+ await form.validate();
+ const formData = form.getFields();
+
+ setLoading(true);
+ const params = {
+ id: componentInfo.id,
+ name: formData.name,
+ projectId: formData.projectId,
+ componentClassify: formData.componentClassify,
+ codeLanguage: formData.codeLanguage,
+ type: formData.type,
+ desc: description
+ };
+
+ const res: any = await copyDesign(params);
+
+ if (res.code === 200) {
+ Message.success('仅复制设计成功');
+ onSuccess?.();
+ handleClose();
+ }
+ else {
+ Message.error(res.message || '复制失败');
+ }
+ } catch (error) {
+ console.error('复制失败:', error);
+ Message.error('复制失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 复制设计和代码
+ const handleCopyAll = async () => {
+ try {
+ await form.validate();
+ const formData = form.getFields();
+
+ setLoading(true);
+ const params = {
+ id: componentInfo.id,
+ name: formData.name,
+ projectId: formData.projectId,
+ componentClassify: formData.componentClassify,
+ codeLanguage: formData.codeLanguage,
+ type: formData.type,
+ desc: description
+ };
+
+ const res: any = await copyAll(params);
+
+ if (res.code === 200) {
+ Message.success('复制设计和代码成功');
+ onSuccess?.();
+ handleClose();
+ }
+ else {
+ Message.error(res.message || '复制失败');
+ }
+ } catch (error) {
+ console.error('复制失败:', error);
+ Message.error('复制失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleClose = () => {
+ form.resetFields();
+ onCancel();
+ };
+
+ return (
+
+
+
+
+
+ }
+ style={{ width: '70%', maxWidth: 900 }}
+ >
+
+
+ );
+};
+
+export default CopyComponentModal;
diff --git a/src/pages/componentMarket/style/compDetails.module.less b/src/pages/componentMarket/style/compDetails.module.less
index cb1c466..1e1adfa 100644
--- a/src/pages/componentMarket/style/compDetails.module.less
+++ b/src/pages/componentMarket/style/compDetails.module.less
@@ -11,9 +11,6 @@
width: 345px;
height: 100%;
margin-right: 68px;
- padding: 20px 25px;
- border-radius: 8px;
- box-shadow: 2px 2px 20px 0 rgba(0, 0, 0, .25);
.comp-housing {
width: 95%;
@@ -55,8 +52,15 @@
.comp-info {
flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ height: 100%;
.header {
+ flex-shrink: 0;
+ margin-bottom: 20px;
+
.title {
font-size: 22px;
font-weight: 700;
@@ -68,6 +72,8 @@
}
.extra {
+ flex-shrink: 0;
+
.extra-font {
font-size: 18px;
font-weight: 700;
@@ -75,8 +81,133 @@
}
.description {
- max-height: 15%;
+ flex-shrink: 0;
+ max-height: 80px;
+ overflow-y: auto;
+ margin-bottom: 16px;
+ }
+
+ .markdown-content {
+ flex: 1;
overflow-y: auto;
+ padding: 16px;
+ background-color: #fafafa;
+ border-radius: 4px;
+ min-height: 200px;
+
+ // Markdown 样式优化
+ :global {
+ // 标题样式
+ h1, h2, h3, h4, h5, h6 {
+ margin-top: 24px;
+ margin-bottom: 16px;
+ font-weight: 600;
+ line-height: 1.25;
+ }
+
+ h1 {
+ padding-bottom: 0.3em;
+ border-bottom: 1px solid #eaecef;
+ }
+
+ // 段落样式
+ p {
+ margin-bottom: 16px;
+ line-height: 1.6;
+ }
+
+ // 代码块样式
+ code {
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
+ }
+
+ pre {
+ margin: 16px 0;
+ overflow: auto;
+ }
+
+ pre code {
+ background: transparent !important;
+ padding: 0 !important;
+ }
+
+ // 列表样式
+ ul, ol {
+ padding-left: 2em;
+ margin-bottom: 16px;
+ }
+
+ li {
+ margin-bottom: 0.25em;
+ }
+
+ // 引用样式
+ blockquote {
+ padding: 0 1em;
+ color: #6a737d;
+ border-left: 0.25em solid #dfe2e5;
+ margin: 16px 0;
+ }
+
+ // 链接样式
+ a {
+ color: #165DFF;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ // 表格样式
+ table {
+ border-spacing: 0;
+ border-collapse: collapse;
+ margin: 16px 0;
+ width: 100%;
+ }
+
+ table tr {
+ background-color: #fff;
+ border-top: 1px solid #c6cbd1;
+ }
+
+ table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+ }
+
+ table th,
+ table td {
+ padding: 6px 13px;
+ border: 1px solid #dfe2e5;
+ }
+
+ table th {
+ font-weight: 600;
+ background-color: #f6f6f6;
+ }
+
+ // 水平分割线
+ hr {
+ height: 0.25em;
+ padding: 0;
+ margin: 24px 0;
+ background-color: #e1e4e8;
+ border: 0;
+ }
+
+ // 图片样式
+ img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 4px;
+ }
+ }
+ }
+
+ .handel-box {
+ flex-shrink: 0;
+ padding: 16px 0;
}
.params {