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 }) => 

, + 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 }} + > +
+ + + + + { + if (value && !/^[a-zA-Z][a-zA-Z0-9_]*$/.test(value)) { + callback('组件标识必须以字母开头,只能包含字母、数字和下划线'); + } + else { + callback(); + } + } + } + ]} + > + + + + + + + + + + + + + + + + + setDescription(content)} + /> + +
+
+ ); +}; + +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 {