import React, { useMemo, useEffect } from 'react'; import { Controlled } from 'react-codemirror2'; import { useState } from 'react'; import { Button, message, Popover, Modal, Dropdown, Menu, Tree, Upload, Spin } from 'antd'; import { saveAs } from 'file-saver'; import styles from './index.less'; import { isDev, useGetRect } from 'editorUtils/tool'; import { EyeOutlined, CodeOutlined, DeleteOutlined, SketchOutlined, DownOutlined, MoreOutlined } from '@ant-design/icons'; import { useHotkeys } from 'react-hotkeys-hook'; import { connect } from 'dva'; import { StateWithHistory } from 'redux-undo'; import PageForm from './PageForm'; import VersionList from './components/VersionList'; require('codemirror/mode/xml/xml'); require('codemirror/mode/javascript/javascript'); const serverUrl = isDev ? 'http://localhost:3000' : 'http://localhost:3000'; const IdeEditor = ({ location, dispatch, ideEditor }) => { const { htmlContent, isNewPage, __pageConfig, theme, pageId, pageConfig, previewUrl, assetsData, isLoading, historyData } = ideEditor; let siteInfo = localStorage.getItem(`SITE-INFO${SITE_VERSION}`) console.log(historyData, '@@@') const [isUpdate, setUpdate] = useState(false); const [cursor, setCursor] = useState({ line: 1, ch: 1 }); const [data, setData] = useState<{ data: string }>({ data: htmlContent }); const [doc, setDoc] = useState() const [spin, setSpin] = useState(false); useEffect(() => { setSpin(isLoading) }, [isLoading]) const handleChange = ( _editor: CodeMirror.Editor, _data: CodeMirror.EditorChange, value: string, ) => { // setData({ data: value }); dispatch({ type: 'ideEditor/changeHtmlContent', payload: { htmlContent: value, id: pageId, client:theme === 'wap' ? 'wap' : 'pc', } }) }; const onMouseLeaveEvent = () => { dispatch({ type: 'ideEditor/mouseLeaveEventPreview', payload: { id: pageId, client:theme === 'wap' ? 'wap' : 'pc', } }) } const fetchPage = (_htmlContent) => { dispatch({ type: 'ideEditor/updatePreview', payload: { id: pageId, client:theme === 'wap' ? 'wap' : 'pc', html: _htmlContent || htmlContent } }) }; useEffect(function(){ let timer = setInterval(function() { dispatch({ type: 'ideEditor/mouseLeaveEventPreview', payload: { id: pageId, client:theme === 'wap' ? 'wap' : 'pc', } }) }, 10000); return () => clearInterval(timer) },[]) const downLoadHtml = () => { var file = new File([data.data], `${Date.now()}.html`, { type: 'text/html;charset=utf-8' }); saveAs(file); }; const onCursorChange = (_editor: CodeMirror.Editor, data1: CodeMirror.Position) => { const { line, ch } = data1; setCursor({ line, ch }); setDoc(_editor) }; useHotkeys( 'ctrl+s', event => { fetchPage(); event.preventDefault(); }, [data], ); const editHotKey = useMemo(() => { return (editor: CodeMirror.Editor, event: KeyboardEvent) => { if (event.ctrlKey && event.key === 's') { fetchPage(editor.getValue()); event.preventDefault(); } }; }, [fetchPage]); const CodeMirrorRender = useMemo(() => { return ( ); }, [cursor, htmlContent, editHotKey]); const rect = useGetRect(); const height = useMemo(() => { let res = rect.height - 42 - 1; //-1防止差值产生滚动条 return res < 694 ? 694 : res; }, [rect.height]); const phoneHeight = useMemo(() => { //let res = rect.height - 42 - 30 - 1; //30是其上边距 //return res < 694 ? 694 : res; return 694; //大屏幕过长,维持高度,需要变高另外处理 }, []); const iframeHeight = useMemo(() => { return phoneHeight - 30 - 12 - 12; //上边距30 上下padding 12 }, [phoneHeight]); const onSave = (values, flag) => { dispatch({ type: 'ideEditor/updateState', payload: { pageConfig: values } }) if (flag) { dispatch({ type: 'ideEditor/updateState', payload: { __pageConfig: values } }) } } const delPage = () => { if (!isNewPage) { Modal.confirm({ width: 600, title: '删除', content: '是否要删除?', cancelText: '取消', okText: '确认', onOk: () => { dispatch({ type: 'ideEditor/delPage', payload: { type: theme === 'wap' ? 'wap' : 'pc', id: pageId } }); }, onCancel() { console.log('Cancel'); }, }) } else { return false; } } const savePage = () => { dispatch({ type: 'ideEditor/savePage', payload: { client: theme === 'wap' ? 'wap' : 'pc', content: htmlContent, html_param: JSON.stringify({ pageConfig: pageConfig }), id: pageId, param: { online_consult: pageConfig?.online_consult, sub_status: pageConfig?.sub_status }, sub_status: pageConfig?.sub_status, type: 3 } }); } const uploadFile = (file:any) => { if (file.file.status === 'done') { dispatch({ type: 'ideEditor/getAssets', payload:{ client:theme === 'wap' ? 'wap' : 'pc', page_id: pageId } }) setSpin(false) message.success('上传成功', 3); }else if(file.file.status === 'error'){ setSpin(false) message.error(file.file.response.message, 3); } } const beforeUpload = (file) => { setSpin(true) const isLessthan = file.size / 1024 / 1024 < 5 if (!isLessthan) { setSpin(false) message.error(`图片大小不能超过${5}MB!`) } return isLessthan } const historyDetail = (historyId:string) => { dispatch({ type: 'ideEditor/getPageHistoryDetail', payload:{ id: historyId, pageId: pageId, client:theme === 'wap' ? 'wap' : 'pc' } }) } const menu = ( 上传文件 上传ZIP ); const checkSource = (selectedKeys, e) => { let node = e.node; if(!node.children && doc){ let str = doc.getLine(cursor.line).substr(cursor.ch, doc.getLine(cursor.line).length) let content = ''; if(node.type == 30){// js代码 content = ``; }else if(node.type == 40){ // css代码 content = ``; }else if(node.type == 50){ // css代码 content = ``; }else{ content = node.path ?? ''; } doc.replaceRange(`${content}${str}`, cursor, {line:cursor.line, ch: doc.getLine(cursor.line).length}) } } return (
} className={styles.headerButton} >静态资源
} defaultExpandAll={true} onSelect={checkSource} treeData={assetsData} height={height} />
| 在线代码编辑器
{theme === 'wap' && } } trigger="click">
{CodeMirrorRender}
{theme === 'wap' &&
}
); } export default connect((state: StateWithHistory) => { return { ideEditor: state.ideEditor }; })(IdeEditor);