UNPKG

@flatbiz/antd

Version:
557 lines (539 loc) 19.7 kB
/*! @flatjs/forge MIT @flatbiz/antd */ import { _ as _objectWithoutProperties, a as _slicedToArray, b as _objectSpread2 } from './_rollupPluginBabelHelpers-BspM60Sw.js'; import _CloseCircleFilled from '@ant-design/icons/es/icons/CloseCircleFilled.js'; import { useRef, useState, useEffect, useCallback, useMemo } from 'react'; import { Button, InputNumber, message, Spin } from 'antd'; import { openNewWindow, getUuid, loadScripts, loadStyles } from '@flatbiz/utils'; import { classNames } from '@dimjs/utils/class-names/class-names'; import { useMemoizedFn, useSize } from 'ahooks'; import { C as CardLayout } from './card-layout-P-Qb5pCT.js'; import { C as CodeRender } from './code-Dbu2QlhZ.js'; import { d as dialogModal } from './dialog-modal-CGMNBSgq.js'; import { jsx, jsxs } from 'react/jsx-runtime'; import { getCodeString } from 'rehype-rewrite'; import { isObject } from '@dimjs/lang/is-object'; import { isString } from '@dimjs/lang/is-string'; import { A as AlertWrapper } from './alert-DGRFbqqK.js'; import { E as EasyForm } from './form-ByFljQr9.js'; import { F as FormItemHidden } from './form-item-hidden-BtQvJEx7.js'; import { F as FormItemWrapper } from './form-item-wrapper-Ci7-5IKi.js'; import { G as Gap } from './gap-5v_ndf2v.js'; import { U as UploadMultiType } from './content-CXmBn9B0.js'; var MermaidViewBtn = function MermaidViewBtn() { var handeClick = function handeClick() { dialogModal.open({ title: 'Mermaid图形示例', content: function content() { return /*#__PURE__*/jsxs("div", { children: [/*#__PURE__*/jsx(CardLayout, { title: '示例', subTitle: '在Markdown中使用Mermaid图形', layoutType: "tight", children: /*#__PURE__*/jsx(CodeRender, { code: "```mermaid\nsequenceDiagram\n native->>haipay\u670D\u52A1\u7AEF:\u83B7\u53D6accessToken\n haipay\u670D\u52A1\u7AEF->>tc\u670D\u52A1\u7AEF: \u8C03\u7528tc\u7684\u63A5\u53E3\u83B7\u53D6accessToken\n tc\u670D\u52A1\u7AEF->>haipay\u670D\u52A1\u7AEF: \u8FD4\u56DEaccessToken\n haipay\u670D\u52A1\u7AEF-->>native: \u5C06accessToken\u8FD4\u56DEnative\n native->>tch5: \u5C06accessToken\u8FD4\u56DE\u7ED9tch5\uFF0C\u8FDB\u884C\u767B\u5F55" }) }), /*#__PURE__*/jsx(Button, { block: true, type: "primary", ghost: true, style: { marginTop: 20 }, onClick: function onClick() { openNewWindow('https://docs.min2k.com/zh/mermaid/syntax/pie.html'); }, children: "\u67E5\u770B\u66F4\u591A\u793A\u4F8B" })] }); }, okHidden: true, cancelHidden: true }); }; return /*#__PURE__*/jsx(Button, { type: "default", size: "small", style: { fontSize: 12 }, onClick: handeClick, children: "\u652F\u6301Mermaid\u56FE\u5F62" }, "imageUpload"); }; var _excluded$1 = ["children", "className"]; var PluginMermaidCode = function PluginMermaidCode(_ref) { var _ref$children = _ref.children, children = _ref$children === void 0 ? [] : _ref$children, className = _ref.className, props = _objectWithoutProperties(_ref, _excluded$1); var demoid = useRef(getUuid()); var _useState = useState(''), _useState2 = _slicedToArray(_useState, 2), svgContent = _useState2[0], setSvgContent = _useState2[1]; var renderAbortRef = useRef(false); var isMermaid = className && /^language-mermaid/.test(className.toLocaleLowerCase()); var code = children ? getCodeString(props.node.children) : children[0] || ''; useEffect(function () { if (isMermaid && code) { // 标记之前的渲染为已取消 renderAbortRef.current = true; // 重置取消标志 renderAbortRef.current = false; // 生成新的唯一 ID var currentId = getUuid(); demoid.current = currentId; // 创建一个临时的隐藏容器用于 Mermaid 渲染 var tempContainer = document.createElement('div'); tempContainer.style.position = 'absolute'; tempContainer.style.visibility = 'hidden'; tempContainer.style.width = '0'; tempContainer.style.height = '0'; tempContainer.style.top = '-9999px'; tempContainer.style.left = '-9999px'; tempContainer.id = currentId; document.body.appendChild(tempContainer); // 使用 requestAnimationFrame 确保在正确的时机渲染 var frameId = requestAnimationFrame(function () { if (renderAbortRef.current) { // 清理临时容器 try { if (tempContainer.parentNode) { document.body.removeChild(tempContainer); } } catch (_unused) { // 忽略清理错误 } return; } var mermaid = window['mermaid']; // 在临时容器中渲染 mermaid.render(currentId, code).then(function (_ref2) { var svg = _ref2.svg; // 检查是否已被取消 if (renderAbortRef.current) { return; } // 使用 React state 更新 SVG 内容,而不是直接操作 DOM setSvgContent(svg); }).catch(function (error) { if (!renderAbortRef.current) { console.log('Mermaid render error:', error); setSvgContent(''); } }).finally(function () { // 清理临时容器 try { if (tempContainer.parentNode) { document.body.removeChild(tempContainer); } } catch (_unused2) { // 忽略清理错误 } }); }); // 清理函数:取消正在进行的渲染 return function () { renderAbortRef.current = true; cancelAnimationFrame(frameId); // 清理临时容器 try { if (tempContainer.parentNode) { document.body.removeChild(tempContainer); } } catch (_unused3) { // 忽略清理错误 } }; } else { setSvgContent(''); } return undefined; }, [isMermaid, code]); if (isMermaid) { return /*#__PURE__*/jsx("code", { className: className, "data-name": "mermaid", dangerouslySetInnerHTML: { __html: svgContent } }); } return /*#__PURE__*/jsx("code", { className: className, children: children }); }; function getImageDimensions(url) { return new Promise(function (resolve, reject) { var img = new Image(); img.onload = function () { resolve({ width: img.naturalWidth, height: img.naturalHeight }); }; img.onerror = function () { reject(new Error('图片加载失败')); }; img.src = url; }); } var UploadImageContent = function UploadImageContent(props) { var onValuesChange = useCallback(function (changedFields) { return new Promise(function ($return, $error) { if (changedFields.imageUrls) { var imageUrls = changedFields.imageUrls; if (!(imageUrls !== null && imageUrls !== void 0 && imageUrls.length)) return $return(); var imageUrl = isString(imageUrls[0]) ? imageUrls[0] : imageUrls[0].url; getImageDimensions(imageUrl).then(function (_ref) { var width = _ref.width, height = _ref.height; props.form.setFieldsValue({ width: width, height: height }); }).catch(function () {}); } return $return(); }); }, [props.form]); var onUploadFile = useMemoizedFn(function (file) { return new Promise(function ($return, $error) { var _props$onUploadImage, formData, respData; formData = new FormData(); formData.append('file', file); return Promise.resolve((_props$onUploadImage = props.onUploadImage) === null || _props$onUploadImage === void 0 ? void 0 : _props$onUploadImage.call(props, file)).then(function ($await_1) { try { respData = $await_1; return $return(respData); } catch ($boundEx) { return $error($boundEx); } }, $error); }); }); return /*#__PURE__*/jsxs(EasyForm, { form: props.form, onValuesChange: onValuesChange, initialValues: { file: [{ url: 'https://file.40017.cn/jinfu/arbitrarily/umm4vdnm25z6kffm.png', name: 'xxx1.png' }, { url: 'https://file.40017.cn/jinfu/arbitrarily/umm4vdnm25z6kffm.png', name: 'xxx2.png' }] }, children: [/*#__PURE__*/jsx(FormItemHidden, { name: "height" }), /*#__PURE__*/jsx(FormItemWrapper, { name: "imageUrls", label: "\u56FE\u7247", rules: [{ required: true, message: '请选择或粘贴图片' }], noStyle: true, children: /*#__PURE__*/jsx(UploadMultiType, { onUploadFile: onUploadFile, maxCount: 1 }) }), /*#__PURE__*/jsx(Gap, { height: 12 }), /*#__PURE__*/jsx(FormItemWrapper, { name: "width", label: "\u5BBD\u5EA6", children: /*#__PURE__*/jsx(InputNumber, { placeholder: "\u8BF7\u8F93\u5165\u5BBD\u5EA6", style: { width: '200px' }, addonAfter: "px" }) }), /*#__PURE__*/jsx(AlertWrapper, { size: "small", description: "\u53EF\u4FEE\u6539\u5BBD\u5EA6\u8FDB\u884C\u7B49\u6BD4\u7F29\u653E\u663E\u793A" })] }); }; var UploadImageBtn = function UploadImageBtn(props) { var handleImageClick = function handleImageClick() { dialogModal.open({ title: '上传图片', content: function content(form) { return /*#__PURE__*/jsx(UploadImageContent, { form: form, onUploadImage: props.onUploadImage }); }, okText: '确定', onOk: function onOk(form) { return new Promise(function ($return, $error) { var values, imageUrls, imageItem, _props$onUploadResult, _props$onUploadResult2, name; return Promise.resolve(form.validateFields()).then(function ($await_2) { try { values = $await_2; try { imageUrls = values.imageUrls; if (!(imageUrls !== null && imageUrls !== void 0 && imageUrls.length)) return $return(); imageItem = imageUrls[0]; if (isObject(imageItem)) { (_props$onUploadResult = props.onUploadResult) === null || _props$onUploadResult === void 0 || _props$onUploadResult.call(props, { url: imageItem.url, name: imageItem.name, width: values.width }); } else { name = imageItem.split('/').pop(); (_props$onUploadResult2 = props.onUploadResult) === null || _props$onUploadResult2 === void 0 || _props$onUploadResult2.call(props, { url: imageItem, name: name, width: values.width }); } } catch (error) { message.error((error === null || error === void 0 ? void 0 : error['message']) || '上传图片失败'); return $return(Promise.reject()); } return $return(Promise.resolve()); } catch ($boundEx) { return $error($boundEx); } }, $error); }); }, onCancel: function onCancel() { console.log('onCancel'); } }); }; return /*#__PURE__*/jsx(Button, { type: "default", size: "small", style: { fontSize: 12 }, onClick: handleImageClick, children: "\u4E0A\u4F20\u56FE\u7247" }); }; var _excluded = ["value", "onChange", "onUploadImage", "readonly", "isDarkMode", "className", "style", "readonlyHeightAuto", "height", "hideBorder"]; var ReactMdEditor = function ReactMdEditor(props) { var value = props.value, onChange = props.onChange, onUploadImage = props.onUploadImage, readonly = props.readonly, isDarkMode = props.isDarkMode, className = props.className, style = props.style, readonlyHeightAuto = props.readonlyHeightAuto, height = props.height, hideBorder = props.hideBorder, restProps = _objectWithoutProperties(props, _excluded); var _useState = useState(value), _useState2 = _slicedToArray(_useState, 2), innerValue = _useState2[0], setInnerValue = _useState2[1]; var readonlyHeightAutoFt = readonlyHeightAuto !== null && readonlyHeightAuto !== void 0 ? readonlyHeightAuto : true; useEffect(function () { console.log('props.value', props.value); setInnerValue(props.value); }, [props.value]); // 编辑器实例引用(用于操作内容) var editorRef = useRef(null); // 隐藏的文件选择器ref useRef(null); var onInnerChange = function onInnerChange(value) { setInnerValue(value); onChange === null || onChange === void 0 || onChange(value); }; // 处理图片选择(核心:读取图片并插入) var onUploadResult = function onUploadResult(respData) { try { var imageMarkdown = "\n<img src=\"".concat(respData.url, "\" width=\"").concat(respData.width, "\" alt=\"").concat(respData.name, "\">\n"); // 将图片Markdown插入到编辑器光标位置 if (editorRef.current) { var _editorRef$current; var textarea = (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current['textarea']; var cursorPosition = textarea.selectionStart; var newContent = (innerValue === null || innerValue === void 0 ? void 0 : innerValue.slice(0, cursorPosition)) + imageMarkdown + (innerValue === null || innerValue === void 0 ? void 0 : innerValue.slice(cursorPosition)); onInnerChange(newContent); } } catch (error) { message.error((error === null || error === void 0 ? void 0 : error['message']) || '添加图片失败'); } }; var imageUploadCommand = onUploadImage ? { name: 'imageUpload', keyCommand: 'imageUpload', render: function render() { return /*#__PURE__*/jsx(UploadImageBtn, { onUploadResult: onUploadResult, onUploadImage: onUploadImage // type="default" // size="small" // style={{ fontSize: 12 }} // onClick={handleImageClick} }, "imageUpload"); } } : null; // 支持 mermaid 语法 var mermaidCommand = { name: 'mermaid', keyCommand: 'mermaid', render: function render() { return /*#__PURE__*/jsx(MermaidViewBtn // type="default" , {}, "mermaid"); } }; var MDEditor = window['@uiw/react-md-editor'].default; var commands = window['@uiw/react-md-editor'].commands; var extraCommands = []; if (imageUploadCommand && !readonly) { extraCommands = [imageUploadCommand, commands.divider, mermaidCommand, commands.divider, commands.fullscreen]; } else { extraCommands = [mermaidCommand, commands.divider, commands.fullscreen]; } var preview = readonly ? 'preview' : 'live'; var dataColorMode = isDarkMode ? 'dark' : 'light'; var commandsFt = useMemo(function () { if (readonly) return []; return undefined; }, [readonly]); var id = useMemo(function () { return getUuid(); }, []); var wmdeMarkdownSize = useSize(function () { return document.querySelector("#".concat(id, " .wmde-markdown")); }); var autoHeight = useMemo(function () { return readonly && readonlyHeightAutoFt; }, [readonly, readonlyHeightAutoFt]); var heightFt = useMemo(function () { if (autoHeight) { var _wmdeMarkdownSize$hei; return ((_wmdeMarkdownSize$hei = wmdeMarkdownSize === null || wmdeMarkdownSize === void 0 ? void 0 : wmdeMarkdownSize.height) !== null && _wmdeMarkdownSize$hei !== void 0 ? _wmdeMarkdownSize$hei : 0) + 25; } return height; }, [autoHeight, wmdeMarkdownSize === null || wmdeMarkdownSize === void 0 ? void 0 : wmdeMarkdownSize.height, height]); var visibleDragbarFt = useMemo(function () { if (autoHeight) return false; return restProps.visibleDragbar; }, [autoHeight, restProps.visibleDragbar]); return /*#__PURE__*/jsx("div", { style: style, className: classNames('v-markdown-editor', className), id: id, children: /*#__PURE__*/jsx(MDEditor, _objectSpread2(_objectSpread2({ highlightEnable: true }, restProps), {}, { style: { boxShadow: hideBorder ? 'none' : undefined }, height: heightFt, ref: editorRef, value: innerValue, preview: preview, onChange: onInnerChange, visibleDragbar: visibleDragbarFt, "data-color-mode": dataColorMode, extraCommands: extraCommands, commands: commandsFt, previewOptions: { components: { code: PluginMermaidCode } } })) }); }; /** * markdown编辑器 * ``` * 1. 使用@uiw/react-md-editor组件 * 2. 动态加载 mermaid.js 和 mdeditor.js 和 mdeditor.css * //oss.ly.com/newpay/cdn/react-md-editor/mermaid.11.12.0.min.js * //oss.ly.com/newpay/cdn/react-md-editor/4.0.11/mdeditor.min.js * //oss.ly.com/newpay/cdn/react-md-editor/4.0.11/mdeditor.min.css * ``` */ var MarkdownEditor = function MarkdownEditor(props) { var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), renderFlag = _useState2[0], setRenderFlag = _useState2[1]; var _useState3 = useState(null), _useState4 = _slicedToArray(_useState3, 2), errorMsg = _useState4[0], setErrorMsg = _useState4[1]; useEffect(function () { Promise.all([loadScripts({ scriptUrls: ['//oss.ly.com/newpay/cdn/react-md-editor/mermaid.11.12.0.min.js', '//oss.ly.com/newpay/cdn/react-md-editor/4.0.11/mdeditor.min.js'], checkFn: function checkFn() { var isLoaded = window['@uiw/react-md-editor'] && window['mermaid']; return isLoaded; }, intervalMs: 20 }), loadStyles({ styleUrls: ['//oss.ly.com/newpay/cdn/react-md-editor/4.0.11/mdeditor.min.css'] })]).then(function () { setRenderFlag(true); }).catch(function (error) { console.error(error); setRenderFlag(true); setErrorMsg((error === null || error === void 0 ? void 0 : error.message) || 'Markdown编辑器加载失败'); }); }, []); if (!renderFlag) { return /*#__PURE__*/jsx(Spin, { tip: /*#__PURE__*/jsx("div", { style: { marginTop: 10 }, children: "Markdown\u7F16\u8F91\u5668\u52A0\u8F7D\u4E2D" }), size: "small", children: /*#__PURE__*/jsx("div", { style: { padding: 50, background: 'rgba(0, 0, 0, 0.05)', borderRadius: 4 } }) }); } if (errorMsg) { return /*#__PURE__*/jsxs("div", { style: { textAlign: 'center', padding: 20, background: '#fff' }, children: [/*#__PURE__*/jsx(_CloseCircleFilled, { style: { color: '#f00', fontSize: 30 } }), /*#__PURE__*/jsx("div", { style: { color: '#f00', marginTop: 10, fontSize: 16 }, children: "Markdown\u7F16\u8F91\u5668\u52A0\u8F7D\u5931\u8D25" }), /*#__PURE__*/jsx("div", { style: { color: '#898989', marginTop: 5, fontSize: 12 }, children: errorMsg })] }); } return /*#__PURE__*/jsx(ReactMdEditor, _objectSpread2({}, props)); }; export { MarkdownEditor as M }; //# sourceMappingURL=content-DnpCk2LR.js.map