对article upset页面样式优化
This commit is contained in:
parent
0007146658
commit
37d87844f7
|
|
@ -104,7 +104,7 @@ export async function createSession(params, context) {
|
|||
origin: 'wechat',
|
||||
type: 'image',
|
||||
tag1: 'image',
|
||||
objectId: await generateNewIdAsync(),
|
||||
objectId: await generateNewIdAsync(), // 这个域用来标识唯一性
|
||||
sort: 1000,
|
||||
uploadState: 'success',
|
||||
extra1: data.MediaId,
|
||||
|
|
@ -129,7 +129,7 @@ export async function createSession(params, context) {
|
|||
origin: 'wechat',
|
||||
type: 'video',
|
||||
tag1: 'video',
|
||||
objectId: await generateNewIdAsync(),
|
||||
objectId: await generateNewIdAsync(), // 这个域用来标识唯一性
|
||||
sort: 1000,
|
||||
uploadState: 'success',
|
||||
extra1: data.MediaId,
|
||||
|
|
@ -151,7 +151,7 @@ export async function createSession(params, context) {
|
|||
origin: 'wechat',
|
||||
type: 'audio',
|
||||
tag1: 'audio',
|
||||
objectId: await generateNewIdAsync(),
|
||||
objectId: await generateNewIdAsync(), // 这个域用来标识唯一性
|
||||
sort: 1000,
|
||||
uploadState: 'success',
|
||||
extra1: data.MediaId,
|
||||
|
|
|
|||
|
|
@ -2325,8 +2325,8 @@ export async function refreshToken(params, context) {
|
|||
// 只有server模式去刷新token
|
||||
// 'development' | 'production' | 'staging'
|
||||
const intervals = {
|
||||
development: 7200 * 1000,
|
||||
staging: 600 * 1000,
|
||||
development: 7200 * 1000, // 2小时
|
||||
staging: 600 * 1000, // 十分钟
|
||||
production: 600 * 1000, // 十分钟
|
||||
};
|
||||
const application = context.getApplication();
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ export async function createWechatQrCode(options, context) {
|
|||
permanent,
|
||||
url,
|
||||
expired: false,
|
||||
expiresAt: Date.now() + 2592000 * 1000,
|
||||
expiresAt: Date.now() + 2592000 * 1000, // wecharQrCode里的过期时间都放到最大,由上层关联对象来主动过期(by Xc, 20230131)
|
||||
props,
|
||||
};
|
||||
// 直接创建
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const checkers: (import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "address", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "application", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "applicationPassport", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "token", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "user", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "mobile", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "message", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "parasite", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const checkers: (import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "address", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "application", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "applicationPassport", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "token", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "user", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "mobile", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "message", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Checker<import("../oak-app-domain").EntityDict, "parasite", import("..").RuntimeCxt<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default checkers;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "article", false, {
|
||||
tocWidth: number;
|
||||
tocClosed: boolean;
|
||||
tocFixed: boolean;
|
||||
tocPosition: "left" | "right" | "none";
|
||||
tocPosition: "none" | "left" | "right";
|
||||
highlightBgColor: string;
|
||||
headerTop: number;
|
||||
className: string;
|
||||
scrollId: string;
|
||||
tocWidth: number | "auto" | undefined;
|
||||
tocHeight: number | "auto" | undefined;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -19,14 +19,15 @@ export default OakComponent({
|
|||
};
|
||||
},
|
||||
properties: {
|
||||
tocWidth: 228,
|
||||
tocClosed: false,
|
||||
tocFixed: true,
|
||||
tocPosition: 'none',
|
||||
highlightBgColor: 'none',
|
||||
headerTop: 0,
|
||||
headerTop: 0, //页面中吸顶部分高度
|
||||
className: '',
|
||||
scrollId: '', // 滚动条所在容器id,不传默认body
|
||||
tocWidth: undefined,
|
||||
tocHeight: undefined,
|
||||
},
|
||||
lifetimes: {},
|
||||
methods: {}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { WebComponentProps } from 'oak-frontend-base';
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
export default function Render(props: WebComponentProps<EntityDict, 'article', false, {
|
||||
title?: string;
|
||||
name?: string;
|
||||
content?: string;
|
||||
tocPosition: 'none' | 'left' | 'right';
|
||||
highlightBgColor: string;
|
||||
|
|
@ -10,6 +10,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'article', f
|
|||
className?: string;
|
||||
tocFixed: boolean;
|
||||
tocClosed: boolean;
|
||||
tocWidth: 228;
|
||||
scrollId?: string;
|
||||
tocWidth?: number | 'auto';
|
||||
tocHeight?: number | 'auto';
|
||||
}, {}>): React.JSX.Element;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Col, Row } from 'antd';
|
||||
import { Editor } from "@wangeditor/editor-for-react";
|
||||
import { SlateNode } from "@wangeditor/editor";
|
||||
import classNames from 'classnames';
|
||||
import { TocView } from '../toc/tocView';
|
||||
import Style from './web.module.less';
|
||||
import Styles from './web.module.less';
|
||||
export default function Render(props) {
|
||||
const { className, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed = false, tocWidth = 228, scrollId } = props.data;
|
||||
const { className, name, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed = false, scrollId, tocWidth, tocHeight } = props.data;
|
||||
const editorConfig = {
|
||||
readOnly: true,
|
||||
autoFocus: true,
|
||||
|
|
@ -34,21 +33,23 @@ export default function Render(props) {
|
|||
setShowToc(true);
|
||||
}
|
||||
}, [tocPosition]);
|
||||
return (<div className={classNames(Style.container, className)}>
|
||||
<Row gutter={[16, 0]}>
|
||||
{tocPosition === 'left' ? (<Col flex={`${tocWidth}px`}>
|
||||
<TocView toc={toc} showToc={showToc} tocPosition='left' setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId}/>
|
||||
</Col>) : null}
|
||||
useEffect(() => {
|
||||
if (name) {
|
||||
window.document.title = name;
|
||||
}
|
||||
}, [name]);
|
||||
return (<div className={classNames(Styles.container, className)}>
|
||||
<div className={Styles.contentContainer}>
|
||||
{tocPosition === "left" ? (<TocView toc={toc} showToc={showToc} tocPosition="left" setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId} tocWidth={tocWidth} tocHeight={tocHeight}/>) : null}
|
||||
|
||||
<Col flex="auto">
|
||||
<div className={Style.content}>
|
||||
<div className={Style.editorContainer}>
|
||||
<div className={Styles.content}>
|
||||
<div className={Styles.editorContainer}>
|
||||
<div style={{ width: "100%" }}>
|
||||
<Editor defaultConfig={editorConfig} value={html} mode="default" style={{
|
||||
width: '100%'
|
||||
}} onCreated={setEditor} onChange={(editor) => {
|
||||
setHtml(editor.getHtml());
|
||||
const headers = editor.getElemsByTypePrefix('header');
|
||||
const headers = editor.getElemsByTypePrefix("header");
|
||||
const tocItems = headers.map((header) => {
|
||||
const text = SlateNode.string(header);
|
||||
const { id, type } = header;
|
||||
|
|
@ -63,10 +64,7 @@ export default function Render(props) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
{tocPosition === 'right' ? (<Col flex={`${tocWidth}px`}>
|
||||
<TocView toc={toc} showToc={showToc} tocPosition='right' setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId}/>
|
||||
</Col>) : null}
|
||||
</Row>
|
||||
{tocPosition === "right" ? (<TocView toc={toc} showToc={showToc} tocPosition="right" setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId} tocWidth={tocWidth} tocHeight={tocHeight}/>) : null}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
padding: 16px;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
|
|
@ -14,7 +20,7 @@
|
|||
max-width: 794px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--oak-bg-color-container);
|
||||
background: #fff;
|
||||
padding: 20px 50px 50px 50px;
|
||||
box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, keyof import("../../../oak-app-domain").EntityDict, false, {
|
||||
tocClosed: boolean;
|
||||
tocFixed: boolean;
|
||||
tocPosition: "left" | "right" | "none";
|
||||
tocPosition: "none" | "left" | "right";
|
||||
highlightBgColor: string;
|
||||
headerTop: number;
|
||||
className: string;
|
||||
scrollId: string;
|
||||
tocWidth: number | "auto" | undefined;
|
||||
tocHeight: number | "auto" | undefined;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ export default OakComponent({
|
|||
data: {
|
||||
content: '',
|
||||
title: '',
|
||||
author: '',
|
||||
},
|
||||
lifetimes: {
|
||||
async attached() {
|
||||
|
|
@ -18,7 +17,6 @@ export default OakComponent({
|
|||
this.setState({
|
||||
content: article?.content,
|
||||
title: article?.title,
|
||||
author: article?.author,
|
||||
});
|
||||
},
|
||||
detached() {
|
||||
|
|
@ -30,9 +28,11 @@ export default OakComponent({
|
|||
tocFixed: true,
|
||||
tocPosition: 'none',
|
||||
highlightBgColor: 'none',
|
||||
headerTop: 0,
|
||||
headerTop: 0, //页面中吸顶部分高度
|
||||
className: '',
|
||||
scrollId: '', // 滚动条所在容器id,不传默认body
|
||||
tocWidth: undefined,
|
||||
tocHeight: undefined,
|
||||
},
|
||||
methods: {},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,4 +10,6 @@ export default function Render(props: WebComponentProps<EntityDict, 'article', f
|
|||
tocFixed: boolean;
|
||||
tocClosed: boolean;
|
||||
scrollId?: string;
|
||||
tocWidth?: number | 'auto';
|
||||
tocHeight?: number | 'auto';
|
||||
}, {}>): React.JSX.Element;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Editor } from '@wangeditor/editor-for-react';
|
||||
import { SlateNode } from '@wangeditor/editor';
|
||||
import { Col, Row } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { TocView } from '../toc/tocView';
|
||||
import Styles from './web.module.less';
|
||||
export default function Render(props) {
|
||||
const { className, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed = false, scrollId } = props.data;
|
||||
const { className, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed = false, scrollId, tocWidth, tocHeight } = props.data;
|
||||
const editorConfig = {
|
||||
readOnly: true,
|
||||
autoFocus: true,
|
||||
|
|
@ -35,20 +34,17 @@ export default function Render(props) {
|
|||
}
|
||||
}, [tocPosition]);
|
||||
return (<div className={classNames(Styles.container, className)}>
|
||||
<Row gutter={[16, 0]}>
|
||||
{tocPosition === 'left' && (<Col flex="228px">
|
||||
<TocView toc={toc} showToc={showToc} tocPosition='left' setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId}/>
|
||||
</Col>)}
|
||||
<div className={Styles.contentContainer}>
|
||||
{tocPosition === "left" && (<TocView toc={toc} showToc={showToc} tocPosition="left" setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId} tocWidth={tocWidth} tocHeight={tocHeight}/>)}
|
||||
|
||||
<Col flex="auto">
|
||||
<div className={Styles.content}>
|
||||
<div className={Styles.editorContainer}>
|
||||
<div style={{ width: "100%" }}>
|
||||
<Editor defaultConfig={editorConfig} value={html} mode="default" style={{
|
||||
width: '100%'
|
||||
width: "100%",
|
||||
}} onCreated={setEditor} onChange={(editor) => {
|
||||
setHtml(editor.getHtml());
|
||||
const headers = editor.getElemsByTypePrefix('header');
|
||||
const headers = editor.getElemsByTypePrefix("header");
|
||||
const tocItems = headers.map((header) => {
|
||||
const text = SlateNode.string(header);
|
||||
const { id, type } = header;
|
||||
|
|
@ -63,10 +59,7 @@ export default function Render(props) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
{tocPosition === 'right' && (<Col flex="228px">
|
||||
<TocView toc={toc} showToc={showToc} tocPosition='right' setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId}/>
|
||||
</Col>)}
|
||||
</Row>
|
||||
{tocPosition === "right" && (<TocView toc={toc} showToc={showToc} tocPosition="right" setShowToc={setShowToc} highlightBgColor={highlightBgColor} headerTop={headerTop} fixed={tocFixed} closed={tocClosed} scrollId={scrollId} tocWidth={tocWidth} tocHeight={tocHeight}/>)}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
.container {
|
||||
// min-height: 100vh;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -14,5 +14,6 @@ export declare function TocView(props: {
|
|||
fixed?: boolean;
|
||||
scrollId?: string;
|
||||
closed?: boolean;
|
||||
tocWidth?: number;
|
||||
tocWidth?: number | 'auto';
|
||||
tocHeight?: number | 'auto';
|
||||
}): import("react").JSX.Element;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { CaretDownOutlined, CloseOutlined, MenuOutlined } from "@ant-design/icon
|
|||
import classNames from "classnames";
|
||||
import Style from './tocView.module.less';
|
||||
export function TocView(props) {
|
||||
const { toc, showToc, tocPosition, setShowToc, highlightBgColor, headerTop = 0, scrollId, fixed = false, closed = false, tocWidth = 228 } = props;
|
||||
const { toc, showToc, tocPosition, setShowToc, highlightBgColor, headerTop = 0, scrollId, fixed = false, closed = false, tocWidth, tocHeight } = props;
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty('--highlight-bg-color', highlightBgColor);
|
||||
}, [highlightBgColor]);
|
||||
|
|
@ -84,7 +84,7 @@ export function TocView(props) {
|
|||
};
|
||||
return (<div className={classNames(Style.tocContainer, {
|
||||
[Style.fixed]: fixed
|
||||
})}>
|
||||
})} style={Object.assign({}, tocWidth ? { width: tocWidth } : {}, tocHeight ? { height: tocHeight } : {})}>
|
||||
{showToc ? (<>
|
||||
<div className={Style.catalogTitle}>
|
||||
<div style={{ color: '#A5A5A5' }}>大纲</div>
|
||||
|
|
|
|||
|
|
@ -88,9 +88,10 @@ export default function Render(props) {
|
|||
}}/>
|
||||
</div> : <>
|
||||
<Button type="text" icon={<EditOutlined />} size="small" onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setName(ele.name);
|
||||
setNameEditing(ele.id);
|
||||
}} style={{ marginRight: 4 }}/>
|
||||
e.stopPropagation();
|
||||
}}/>
|
||||
<div className={Styles.name}>
|
||||
<div style={{ marginLeft: 4, overflow: 'hidden', width: '150px', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{ele?.name}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
height: 50px;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import { EntityDict } from '../../../oak-app-domain';
|
|||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "article", false, {
|
||||
articleMenuId: string;
|
||||
changeIsEdit: () => void;
|
||||
tocPosition: "left" | "right" | "none";
|
||||
tocPosition: "none" | "left" | "right";
|
||||
highlightBgColor: string;
|
||||
onArticlePreview: (content?: string, title?: string) => void;
|
||||
origin: string;
|
||||
scrollId: string;
|
||||
height: number | "auto";
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -25,11 +25,12 @@ export default OakComponent({
|
|||
properties: {
|
||||
articleMenuId: '',
|
||||
changeIsEdit: () => undefined,
|
||||
tocPosition: 'none',
|
||||
highlightBgColor: 'none',
|
||||
onArticlePreview: (content, title) => undefined,
|
||||
origin: 'qiniu',
|
||||
tocPosition: 'none', //目录显示位置,none为不显示目录
|
||||
highlightBgColor: 'none', //点击目录时标题高亮背景色,none为不显示高亮背景色
|
||||
onArticlePreview: (content, title) => undefined, //预览文章
|
||||
origin: 'qiniu', // 默认为七牛云
|
||||
scrollId: '', // 滚动条所在容器id,不传默认页面编辑器容器id
|
||||
height: 600,
|
||||
},
|
||||
listeners: {
|
||||
'editor,content'(prev, next) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ export default function Render(props: WebComponentProps<EntityDict, 'article', f
|
|||
name: string;
|
||||
editor: any;
|
||||
content?: string;
|
||||
html?: string;
|
||||
origin?: string;
|
||||
contentTip: boolean;
|
||||
articleMenuId: string;
|
||||
|
|
@ -15,6 +14,9 @@ export default function Render(props: WebComponentProps<EntityDict, 'article', f
|
|||
tocPosition: 'none' | 'left' | 'right';
|
||||
highlightBgColor: string;
|
||||
scrollId?: string;
|
||||
tocWidth?: number | 'auto';
|
||||
tocHeight?: number | 'auto';
|
||||
height?: number | 'auto';
|
||||
}, {
|
||||
setHtml: (content: string) => void;
|
||||
setEditor: (editor: any) => void;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Alert, Button, Row, Col, Space, Input, Tooltip, } from "antd";
|
||||
import { Alert, Button, Space, Input, } from "antd";
|
||||
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
|
||||
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
|
||||
import { SlateNode } from "@wangeditor/editor";
|
||||
|
|
@ -7,7 +7,8 @@ import { generateNewId } from "oak-domain/lib/utils/uuid";
|
|||
import classNames from "classnames";
|
||||
import Prompt from "../../../components/common/prompt";
|
||||
import Style from "./web.module.less";
|
||||
import { CloseOutlined, EyeOutlined, MenuOutlined, CaretDownOutlined } from "@ant-design/icons";
|
||||
import { TocView } from '../toc/tocView';
|
||||
import { EyeOutlined, } from "@ant-design/icons";
|
||||
// 工具栏配置
|
||||
const toolbarConfig = {
|
||||
excludeKeys: ["fullScreen"],
|
||||
|
|
@ -27,105 +28,14 @@ function customCheckImageFn(src, alt, url) {
|
|||
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
|
||||
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
|
||||
}
|
||||
function TocView(props) {
|
||||
const { toc, showToc, tocPosition, setShowToc, highlightBgColor, scrollId } = props;
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty('--highlight-bg-color', highlightBgColor);
|
||||
}, [highlightBgColor]);
|
||||
const generateTocList = (items, currentLevel = 1, parentId = null) => {
|
||||
//递归生成嵌套列表
|
||||
const result = [];
|
||||
let lastId = parentId;
|
||||
while (items.length > 0) {
|
||||
const item = items[0];
|
||||
//有无子级标题,有则显示展开图标
|
||||
const hasChildren = items.length > 1 && items[1].level > item.level;
|
||||
if (item.level > currentLevel) {
|
||||
// 递归生成子列表
|
||||
const sublist = generateTocList(items, item.level, item.id);
|
||||
result.push(<li key={item.id} id={`ul-${lastId}`}>
|
||||
<ul style={{ listStyleType: 'none', paddingInlineStart: '6px' }}>
|
||||
{sublist}
|
||||
</ul>
|
||||
</li>);
|
||||
}
|
||||
else if (item.level < currentLevel) {
|
||||
// 结束当前层级
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// 添加当前层级的 <li>
|
||||
result.push(<li className={Style.listItem} key={item.id} id={`li-${item.id}`} style={{ paddingLeft: `${(item.level - 1) * 6}px`, }}>
|
||||
<CaretDownOutlined id={`icon-${item.id}`} className={Style.icon} style={{ visibility: hasChildren && item.level < 5 ? 'visible' : 'hidden', color: '#A5A5A5' }} onClick={() => {
|
||||
const iconElem = document.getElementById(`icon-${item.id}`);
|
||||
const ulElem = document.getElementById(`ul-${item.id}`);
|
||||
const isFolded = iconElem?.className.includes('iconFold') || ulElem?.className.includes('fold');
|
||||
if (isFolded) {
|
||||
iconElem?.classList.remove(Style.iconFold);
|
||||
ulElem?.classList.remove(Style.fold);
|
||||
}
|
||||
else {
|
||||
iconElem?.classList.add(Style.iconFold);
|
||||
ulElem?.classList.add(Style.fold);
|
||||
}
|
||||
}}/>
|
||||
<div style={{ fontSize: '1em', fontWeight: item.level === 1 ? 'bold' : 'normal' }} className={Style.tocItem} onClick={(event) => {
|
||||
// editor.scrollToElem(item.id);
|
||||
//编辑器滚动到对应元素
|
||||
const elem = document.getElementById(item.id);
|
||||
const elemTop = elem?.getBoundingClientRect().top;
|
||||
const scrollContainer = document.getElementById(scrollId || 'article-upsert-editorContainer');
|
||||
const containerTop = scrollContainer?.getBoundingClientRect().top;
|
||||
scrollContainer?.scrollBy({
|
||||
top: elemTop - containerTop,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
//添加背景色
|
||||
elem?.classList.add(Style.highlight);
|
||||
//移除背景色类名
|
||||
setTimeout(function () {
|
||||
elem?.classList.remove(Style.highlight);
|
||||
}, 1000);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}}>
|
||||
{item.text}
|
||||
</div>
|
||||
</li>);
|
||||
items.shift(); // 移除已处理的项
|
||||
lastId = item.id;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return (<div className={classNames(Style.tocContainer, {
|
||||
// [Style.fixed]: fixed
|
||||
})}>
|
||||
{showToc ? (<>
|
||||
<div className={Style.catalogTitle}>
|
||||
<div style={{ color: '#A5A5A5' }}>大纲</div>
|
||||
<CloseOutlined style={{ color: '#A5A5A5' }} onClick={() => setShowToc(false)}/>
|
||||
</div>
|
||||
|
||||
{(toc && toc.length > 0) ? (<ul style={{ listStyleType: 'none', paddingInlineStart: '0px' }}>{generateTocList([...toc])}</ul>) : (<div style={{ display: 'flex', alignItems: 'center', color: '#B1B1B1', height: '200px' }}>
|
||||
<div>
|
||||
对文档内容应用“标题”样式,即可生成大纲
|
||||
</div>
|
||||
</div>)}
|
||||
</>) : (<div className={classNames(Style.tocButton, { [Style.tocButtonRight]: tocPosition === 'right' })}>
|
||||
<Tooltip title="显示大纲" placement={tocPosition === 'right' ? 'left' : 'right'}>
|
||||
<Button size="small" icon={<MenuOutlined />} onClick={() => setShowToc(true)}/>
|
||||
</Tooltip>
|
||||
</div>)}
|
||||
</div>);
|
||||
}
|
||||
export default function Render(props) {
|
||||
const { methods, data } = props;
|
||||
const { t, setEditor, check, uploadFile, update, setHtml, gotoPreview, clearContentTip, } = methods;
|
||||
const { id, content, editor, origin = 'qiniu', oakFullpath, html, tocPosition = 'none', highlightBgColor = 'none', scrollId } = data;
|
||||
const { oakFullpath, id, content, editor, origin = 'qiniu', tocPosition = 'none', highlightBgColor = 'none', scrollId, tocWidth, tocHeight, height = 600 } = data;
|
||||
const [articleId, setArticleId] = useState('');
|
||||
const [toc, setToc] = useState([]);
|
||||
const [showToc, setShowToc] = useState(false);
|
||||
const containerId = scrollId || 'article-upsert-editorContainer';
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
setArticleId(id);
|
||||
|
|
@ -137,28 +47,25 @@ export default function Render(props) {
|
|||
}
|
||||
}, [tocPosition]);
|
||||
return (<div className={Style.container}>
|
||||
<Prompt when={!id || data.oakDirty} message={'您确认离开页面吗?'}/>
|
||||
<div style={{ width: 'calc(100% - 16px)', }}>
|
||||
<Prompt when={!id || data.oakDirty} message={"您确认离开页面吗?"}/>
|
||||
<div style={{ width: "calc(100% - 16px)" }}>
|
||||
<Toolbar editor={editor} defaultConfig={toolbarConfig} mode="default"/>
|
||||
</div>
|
||||
<Row gutter={[16, 0]}>
|
||||
{tocPosition === 'left' ? (<Col flex="228px">
|
||||
<TocView toc={toc} showToc={showToc} tocPosition='left' setShowToc={setShowToc} highlightBgColor={highlightBgColor} scrollId={scrollId}/>
|
||||
</Col>) : null}
|
||||
<div className={Style.contentContainer}>
|
||||
{tocPosition === "left" ? (<TocView toc={toc} showToc={showToc} tocPosition="left" setShowToc={setShowToc} highlightBgColor={highlightBgColor} scrollId={containerId} tocWidth={tocWidth} tocHeight={tocHeight || height}/>) : null}
|
||||
|
||||
<Col flex="auto">
|
||||
<div className={Style.content}>
|
||||
<div id="article-upsert-editorContainer" className={classNames(Style.editorContainer, {
|
||||
[Style.editorExternalContainer]: !!scrollId
|
||||
<div className={Style.content} style={{ maxWidth: `calc(100% - ${tocWidth || 228}px)` }}>
|
||||
<div id={containerId} className={classNames(Style.editorContainer, {
|
||||
[Style.editorExternalContainer]: !!scrollId,
|
||||
})}>
|
||||
{data.contentTip && (<Alert type="info" message={t('tips.content')} closable onClose={() => clearContentTip()}/>)}
|
||||
{data.contentTip && (<Alert type="info" message={t("tips.content")} closable onClose={() => clearContentTip()}/>)}
|
||||
<div className={Style.titleContainer}>
|
||||
<Input onChange={(e) => update({ name: e.target.value })} value={data.name} placeholder={'请输入文章标题'} size="large" maxLength={32} suffix={`${[...(data.name || '')].length}/32`} className={Style.titleInput}/>
|
||||
<Input onChange={(e) => update({ name: e.target.value })} value={data.name} placeholder={"请输入文章标题"} size="large" maxLength={32} suffix={`${(data.name || "").length}/32`} className={Style.titleInput}/>
|
||||
</div>
|
||||
<div className={Style.editorContent}>
|
||||
<Editor defaultConfig={{
|
||||
autoFocus: true,
|
||||
placeholder: '请输入文章内容...',
|
||||
placeholder: "请输入文章内容...",
|
||||
MENU_CONF: {
|
||||
checkImage: customCheckImageFn,
|
||||
uploadImage: {
|
||||
|
|
@ -167,21 +74,21 @@ export default function Render(props) {
|
|||
// TS 语法
|
||||
// file 即选中的文件
|
||||
const { name, size, type } = file;
|
||||
const extension = name.substring(name.lastIndexOf('.') + 1);
|
||||
const filename = name.substring(0, name.lastIndexOf('.'));
|
||||
const extension = name.substring(name.lastIndexOf(".") + 1);
|
||||
const filename = name.substring(0, name.lastIndexOf("."));
|
||||
const extraFile = {
|
||||
entity: 'article',
|
||||
entity: "article",
|
||||
entityId: articleId,
|
||||
origin: origin,
|
||||
type: 'image',
|
||||
tag1: 'source',
|
||||
type: "image",
|
||||
tag1: "source",
|
||||
objectId: generateNewId(),
|
||||
filename,
|
||||
size,
|
||||
extension,
|
||||
bucket: '',
|
||||
bucket: "",
|
||||
id: generateNewId(),
|
||||
fileType: type
|
||||
fileType: type,
|
||||
};
|
||||
try {
|
||||
// 自己实现上传,并得到图片 url alt href
|
||||
|
|
@ -198,27 +105,28 @@ export default function Render(props) {
|
|||
// TS 语法
|
||||
// file 即选中的文件
|
||||
const { name, size, type } = file;
|
||||
const extension = name.substring(name.lastIndexOf('.') + 1);
|
||||
const filename = name.substring(0, name.lastIndexOf('.'));
|
||||
const extension = name.substring(name.lastIndexOf(".") + 1);
|
||||
const filename = name.substring(0, name.lastIndexOf("."));
|
||||
const extraFile = {
|
||||
entity: 'article',
|
||||
entity: "article",
|
||||
entityId: articleId,
|
||||
origin: origin,
|
||||
type: 'video',
|
||||
tag1: 'source',
|
||||
type: "video",
|
||||
tag1: "source",
|
||||
objectId: generateNewId(),
|
||||
filename,
|
||||
size,
|
||||
extension,
|
||||
bucket: '',
|
||||
bucket: "",
|
||||
id: generateNewId(),
|
||||
fileType: type
|
||||
fileType: type,
|
||||
};
|
||||
try {
|
||||
// 自己实现上传,并得到图片 url alt href
|
||||
const url = await uploadFile(extraFile, file);
|
||||
// 最后插入图片
|
||||
insertFn(url, url + '?vframe/jpg/offset/0');
|
||||
insertFn(url, url +
|
||||
"?vframe/jpg/offset/0");
|
||||
}
|
||||
catch (err) { }
|
||||
},
|
||||
|
|
@ -226,7 +134,7 @@ export default function Render(props) {
|
|||
},
|
||||
}} onCreated={setEditor} onChange={(editor) => {
|
||||
setHtml(editor.getHtml());
|
||||
const headers = editor.getElemsByTypePrefix('header');
|
||||
const headers = editor.getElemsByTypePrefix("header");
|
||||
const tocItems = headers.map((header) => {
|
||||
const text = SlateNode.string(header);
|
||||
const { id, type } = header;
|
||||
|
|
@ -241,11 +149,8 @@ export default function Render(props) {
|
|||
minHeight: 440,
|
||||
}} mode="default"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={Style.footer}>
|
||||
<Row align="middle">
|
||||
<Col flex="none">
|
||||
<Space>
|
||||
<Button disabled={!data.oakDirty ||
|
||||
data.oakExecuting} type="primary" onClick={() => {
|
||||
|
|
@ -255,19 +160,13 @@ export default function Render(props) {
|
|||
</Button>
|
||||
<Button onClick={() => {
|
||||
gotoPreview(content, data.name);
|
||||
}}>
|
||||
<EyeOutlined />
|
||||
}} icon={<EyeOutlined />}>
|
||||
预览
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
{tocPosition === 'right' ? (<Col flex="228px">
|
||||
<TocView toc={toc} showToc={showToc} tocPosition='right' setShowToc={setShowToc} highlightBgColor={highlightBgColor} scrollId={scrollId}/>
|
||||
</Col>) : null}
|
||||
</Row>
|
||||
{tocPosition === "right" ? (<TocView toc={toc} showToc={showToc} tocPosition="right" setShowToc={setShowToc} highlightBgColor={highlightBgColor} scrollId={containerId} tocWidth={tocWidth} tocHeight={tocHeight}/>) : null}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,21 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
.editorContainer {
|
||||
width: 100%;
|
||||
margin: 30px auto 30px auto;
|
||||
|
|
@ -45,10 +56,8 @@
|
|||
}
|
||||
|
||||
.footer {
|
||||
// position: absolute;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.contentNumber {
|
||||
|
|
@ -60,7 +69,7 @@
|
|||
}
|
||||
|
||||
.tocContainer {
|
||||
padding-top: 28px;
|
||||
// padding-top: 28px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
|
|
@ -101,7 +110,7 @@
|
|||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding: 6px 6px 10px 6px;
|
||||
padding: 34px 6px 10px 6px;
|
||||
box-sizing: border-box;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export default function Render(props) {
|
|||
}
|
||||
}, [editArticleId]);
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [name, setName] = useState('');
|
||||
const [nameEditing, setNameEditing] = useState(false);
|
||||
const [showSub, setShowSub] = useState(false);
|
||||
const [newBreadcrumbItems, setNewBreadcrumbItems] = useState([]);
|
||||
|
|
@ -51,9 +50,7 @@ export default function Render(props) {
|
|||
oakPath={`$articleMenu-parent-${row.id}`} onGrandChildEditArticleChange={onChildEditArticleChange} show={show} getBreadcrumbItems={getBreadcrumbItemsByParent} breadcrumbItems={newBreadcrumbItems} drawerOpen={drawerOpen} changeDrawerOpen={changeDrawerOpen} selectedArticleId={selectedArticleId} openArray={openArray ? openArray : undefined} getTopInfo={getTopInfo} articleId={articleId} currentArticle={currentArticle} setCurrentArticle={setCurrentArticle}/>);
|
||||
if (!row.parentId && articleMenuId) {
|
||||
return (<>
|
||||
<div>
|
||||
{Sub}
|
||||
</div>
|
||||
{contextHolder}
|
||||
</>);
|
||||
}
|
||||
|
|
@ -145,39 +142,23 @@ export default function Render(props) {
|
|||
return (<>
|
||||
<div className={Styles.container}>
|
||||
<div className={Styles.ne}>
|
||||
{
|
||||
// nameEditing ? <div className={Styles.name}>
|
||||
// <Input
|
||||
// autoFocus
|
||||
// value={ name || row?.name}
|
||||
// onChange={(evt) => setName(evt.target.value)}
|
||||
// onPressEnter={async () => {
|
||||
// if (name && name !== row?.name) {
|
||||
// await onUpdateName(name);
|
||||
// }
|
||||
// setNameEditing(false);
|
||||
// }}
|
||||
// onBlur={async () => {
|
||||
// if (name && name !== row?.name) {
|
||||
// await onUpdateName(name);
|
||||
// }
|
||||
// setNameEditing(false);
|
||||
// }}
|
||||
// />
|
||||
// </div> :
|
||||
<>
|
||||
<Button type="text" icon={<EditOutlined />} size="small" onClick={() => {
|
||||
setNameEditing(true);
|
||||
const modalInstance = modal.confirm({
|
||||
title: '编辑分类',
|
||||
cancelText: '取消',
|
||||
okText: '提交',
|
||||
title: "编辑分类",
|
||||
cancelText: "取消",
|
||||
okText: "提交",
|
||||
content: (<div>
|
||||
<Form.Item label="分类名称">
|
||||
<Input ref={menuNameRef} defaultValue={row.name} onChange={(val) => update({ name: val.target.value })}/>
|
||||
<Input ref={menuNameRef} defaultValue={row.name} onChange={(val) => update({
|
||||
name: val.target
|
||||
.value,
|
||||
})}/>
|
||||
</Form.Item>
|
||||
<Form.Item label="LOGO" help={<div>
|
||||
<span>请上传LOGO高清图片,</span>
|
||||
<span>
|
||||
请上传LOGO高清图片,
|
||||
</span>
|
||||
<span>
|
||||
108*108像素,仅支持PNG、JPG格式,大小不超过300KB。
|
||||
</span>
|
||||
|
|
@ -197,11 +178,12 @@ export default function Render(props) {
|
|||
// });
|
||||
// }
|
||||
// }
|
||||
footer: <Space>
|
||||
footer: (<Space>
|
||||
<ExtraFileCommit entity={oakEntity} oakPath={oakFullpath} afterCommit={() => {
|
||||
modalInstance.destroy();
|
||||
}} beforeCommit={() => {
|
||||
if (menuNameRef.current.input.value) {
|
||||
if (menuNameRef.current
|
||||
.input.value) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
|
@ -211,22 +193,31 @@ export default function Render(props) {
|
|||
<Button onClick={() => modalInstance.destroy()}>
|
||||
取消
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>),
|
||||
});
|
||||
}} style={{ marginRight: 4 }}/>
|
||||
}}/>
|
||||
<div className={Styles.name}>
|
||||
{logo ? (<Image height={26} width={26} src={logo} preview={false}/>) : null}
|
||||
<div style={{ marginLeft: 4, overflow: 'hidden', width: '100px', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{row?.name}</div>
|
||||
<div style={{
|
||||
marginLeft: 4,
|
||||
overflow: "hidden",
|
||||
width: "100px",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}>
|
||||
{row?.name}
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
<Divider type="vertical" style={{ height: '100%', marginTop: 4, marginBottom: 4 }}/>
|
||||
</div>
|
||||
<Divider type="vertical" style={{
|
||||
height: "100%",
|
||||
marginTop: 4,
|
||||
marginBottom: 4,
|
||||
}}/>
|
||||
<div className={Styles.control}>
|
||||
{!row.parentId && <Button type="text" onClick={() => {
|
||||
{!row.parentId && (<Button type="text" size="small" onClick={() => {
|
||||
gotoDoc(row?.id);
|
||||
}} icon={<EyeOutlined />}>
|
||||
</Button>}
|
||||
|
||||
}} icon={<EyeOutlined />}></Button>)}
|
||||
|
||||
<Dropdown menu={{ items }} placement="bottomRight" arrow>
|
||||
<Button type="text" icon={<PlusOutlined />} size="small"/>
|
||||
|
|
@ -234,23 +225,21 @@ export default function Render(props) {
|
|||
<Button type="text" icon={<MinusOutlined />} size="small" onClick={() => {
|
||||
if (!allowRemove) {
|
||||
modal.error({
|
||||
title: '无法删除',
|
||||
content: hasSubArticles ? '请先删除目录下的文章' : '请先删除目录下的子目录',
|
||||
okText: '确认'
|
||||
title: "无法删除",
|
||||
content: hasSubArticles
|
||||
? "请先删除目录下的文章"
|
||||
: "请先删除目录下的子目录",
|
||||
okText: "确认",
|
||||
});
|
||||
}
|
||||
else {
|
||||
onRemove();
|
||||
}
|
||||
}}/>
|
||||
{(hasSubArticles || hasSubMenus) ? (showSub ?
|
||||
<Button type="text" icon={<UpOutlined />} size="small" onClick={() => setShowSub(false)}/> :
|
||||
<Button type="text" icon={<DownOutlined />} size="small" onClick={() => setShowSub(true)}/>) : <div className={Styles.ph}/>}
|
||||
{hasSubArticles || hasSubMenus ? (showSub ? (<Button type="text" icon={<UpOutlined />} size="small" onClick={() => setShowSub(false)}/>) : (<Button type="text" icon={<DownOutlined />} size="small" onClick={() => setShowSub(true)}/>)) : (<div className={Styles.ph}/>)}
|
||||
</div>
|
||||
</div>
|
||||
{showSub && (<div className={Styles.sub}>
|
||||
{Sub}
|
||||
</div>)}
|
||||
{showSub ? Sub : null}
|
||||
{contextHolder}
|
||||
</>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
height: 50px;
|
||||
justify-content: space-between;
|
||||
|
||||
.ne {
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: 20px;
|
||||
margin-left: 10px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
|
|
@ -43,6 +43,8 @@
|
|||
}
|
||||
}
|
||||
.sub {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
|
@ -78,7 +80,6 @@
|
|||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
// padding: 10px;
|
||||
|
||||
.ph {
|
||||
width: 24px;
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps<
|
|||
show: "edit" | "doc" | "preview";
|
||||
articleMenuId: string;
|
||||
articleId: string;
|
||||
tocPosition: "left" | "right" | "none";
|
||||
tocPosition: "none" | "left" | "right";
|
||||
highlightBgColor: string;
|
||||
onMenuView: () => void;
|
||||
onMenuViewById: (articleMenuId: string) => void;
|
||||
onArticleView: (oakId: string) => void;
|
||||
onArticleView: (articleId: string) => void;
|
||||
onArticlePreview: (content?: string, title?: string) => void;
|
||||
onArticleEdit: (oakId: string) => void;
|
||||
setCopyArticleUrl: (id: string) => string;
|
||||
onArticleEdit: (articleId: string) => void;
|
||||
setCopyArticleUrl: (articleId: string) => string;
|
||||
origin: string;
|
||||
scrollId: string;
|
||||
}>) => React.ReactElement;
|
||||
|
|
|
|||
|
|
@ -14,18 +14,18 @@ export default OakComponent({
|
|||
properties: {
|
||||
entity: '',
|
||||
entityId: '',
|
||||
show: 'edit',
|
||||
articleMenuId: '',
|
||||
articleId: '',
|
||||
tocPosition: 'none',
|
||||
highlightBgColor: 'none',
|
||||
onMenuView: () => undefined,
|
||||
onMenuViewById: (articleMenuId) => undefined,
|
||||
onArticleView: (oakId) => undefined,
|
||||
onArticlePreview: (content, title) => undefined,
|
||||
onArticleEdit: (oakId) => undefined,
|
||||
setCopyArticleUrl: (id) => '',
|
||||
origin: 'qiniu',
|
||||
show: 'edit', // edit为编辑,doc为查看,preview为预览
|
||||
articleMenuId: '', // 菜单id
|
||||
articleId: '', //文章id
|
||||
tocPosition: 'none', //文章目录显示位置,none为不显示目录
|
||||
highlightBgColor: 'none', //点击文章目录时标题高亮背景色,none为不显示高亮背景色
|
||||
onMenuView: () => undefined, //查看全部菜单
|
||||
onMenuViewById: (articleMenuId) => undefined, //查看指定id菜单
|
||||
onArticleView: (articleId) => undefined, //查看文章
|
||||
onArticlePreview: (content, title) => undefined, //预览文章
|
||||
onArticleEdit: (articleId) => undefined, //编辑文章
|
||||
setCopyArticleUrl: (articleId) => '',
|
||||
origin: 'qiniu', // cos origin默认七牛云
|
||||
scrollId: '', // 滚动条所在容器id,不传默认页面编辑器容器id
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
overflow-x: hidden;
|
||||
|
||||
.menu {
|
||||
min-width: 320px;
|
||||
width: 320px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
|
@ -17,13 +17,14 @@
|
|||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-width: 320px;
|
||||
padding: 8px 10px;
|
||||
width: 320px;
|
||||
padding: 0px 10px;
|
||||
position: sticky;
|
||||
z-index: 99;
|
||||
background: #ffffff;
|
||||
top: 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
height: 50px;
|
||||
|
||||
.menuTitle {
|
||||
font-size: 16px;
|
||||
|
|
@ -129,7 +130,6 @@
|
|||
padding: 20px 15px;
|
||||
min-width: 320px;
|
||||
height: 100%;
|
||||
// max-height: 800px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { ReactComponentProps } from 'oak-frontend-base';
|
|||
import { ECode } from '../../../types/ErrorPage';
|
||||
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, false, {
|
||||
code: ECode;
|
||||
title?: string | undefined;
|
||||
desc?: string | undefined;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
}>) => React.ReactElement;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/// <reference types="react" />
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
||||
|
|
@ -9,31 +8,14 @@ type AfterCommit = (() => void) | undefined;
|
|||
type BeforeCommit = (() => boolean | undefined | Promise<boolean | undefined>) | undefined;
|
||||
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
|
||||
entity: keyof ED2;
|
||||
action?: string | undefined;
|
||||
action?: string;
|
||||
size?: ButtonProps['size'] | AmButtonProps['size'];
|
||||
block?: boolean | undefined;
|
||||
block?: boolean;
|
||||
type?: ButtonProps['type'] | AmButtonProps['type'];
|
||||
executeText?: string | undefined;
|
||||
buttonProps?: (ButtonProps & {
|
||||
color?: "default" | "success" | "warning" | "primary" | "danger" | undefined;
|
||||
fill?: "none" | "solid" | "outline" | undefined;
|
||||
size?: "small" | "middle" | "large" | "mini" | undefined;
|
||||
block?: boolean | undefined;
|
||||
loading?: boolean | "auto" | undefined;
|
||||
loadingText?: string | undefined;
|
||||
loadingIcon?: import("react").ReactNode;
|
||||
disabled?: boolean | undefined;
|
||||
onClick?: ((event: import("react").MouseEvent<HTMLButtonElement, MouseEvent>) => unknown) | undefined;
|
||||
type?: "button" | "submit" | "reset" | undefined;
|
||||
shape?: "default" | "rounded" | "rectangular" | undefined;
|
||||
children?: import("react").ReactNode;
|
||||
} & Pick<import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "id" | "onMouseDown" | "onMouseUp" | "onTouchEnd" | "onTouchStart"> & {
|
||||
className?: string | undefined;
|
||||
style?: (import("react").CSSProperties & Partial<Record<"--text-color" | "--background-color" | "--border-radius" | "--border-width" | "--border-style" | "--border-color", string>>) | undefined;
|
||||
tabIndex?: number | undefined;
|
||||
} & import("react").AriaAttributes) | undefined;
|
||||
executeText?: string;
|
||||
buttonProps?: ButtonProps & AmButtonProps;
|
||||
afterCommit?: AfterCommit;
|
||||
beforeCommit?: BeforeCommit;
|
||||
messageProps?: boolean | MessageProps | undefined;
|
||||
messageProps?: MessageProps | boolean;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ export default OakComponent({
|
|||
data: {
|
||||
isModalOpen: false,
|
||||
isModalOpen1: false,
|
||||
renderImgs: [],
|
||||
renderImgs: [], // 读取的原文图片,在modal使用
|
||||
methodsType: '',
|
||||
bridgeUrl: '',
|
||||
bridgeUrl: '', // 通过桥接方式获得的url
|
||||
selectedId: -1,
|
||||
},
|
||||
properties: {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends key
|
|||
tag2: string;
|
||||
entity: keyof ED2;
|
||||
entityId: string;
|
||||
style?: string | undefined;
|
||||
style?: string;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,6 @@ declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends key
|
|||
entityId: string;
|
||||
theme: Theme;
|
||||
children?: React.ReactNode;
|
||||
style?: string | undefined;
|
||||
style?: string;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ export default OakComponent({
|
|||
},
|
||||
properties: {
|
||||
disabled: '',
|
||||
url: '',
|
||||
callback: undefined,
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
setLoginMode: (value) => undefined,
|
||||
digit: 4, //验证码位数
|
||||
},
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@ export default OakComponent({
|
|||
allowPassword: false,
|
||||
allowWechatMp: false,
|
||||
setLoginModeMp(value) { this.setLoginMode(value); },
|
||||
smsDigit: 4,
|
||||
smsDigit: 4, //短信验证码位数
|
||||
emailDigit: 4, //邮箱验证码位数
|
||||
},
|
||||
properties: {
|
||||
onlyCaptcha: false,
|
||||
onlyPassword: false,
|
||||
disabled: '',
|
||||
redirectUri: '',
|
||||
url: '',
|
||||
redirectUri: '', // 微信登录后的redirectUri,要指向wechatUser/login去处理
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
},
|
||||
formData({ features, props }) {
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ export default OakComponent({
|
|||
},
|
||||
properties: {
|
||||
disabled: '',
|
||||
redirectUri: '',
|
||||
url: '',
|
||||
callback: undefined,
|
||||
redirectUri: '', // 微信登录后的redirectUri,要指向wechatUser/login去处理
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
allowSms: false,
|
||||
allowEmail: false,
|
||||
allowWechatMp: false,
|
||||
allowWechatMp: false, //小程序切换授权登录
|
||||
setLoginMode: (value) => undefined,
|
||||
},
|
||||
lifetimes: {},
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ export default OakComponent({
|
|||
},
|
||||
properties: {
|
||||
disabled: '',
|
||||
url: '',
|
||||
callback: undefined,
|
||||
allowPassword: false,
|
||||
allowWechatMp: false,
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
allowPassword: false, //小程序切换密码登录
|
||||
allowWechatMp: false, //小程序切换授权登录
|
||||
setLoginMode: (value) => undefined,
|
||||
digit: 4 //验证码位数,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { EntityDict } from '../../../oak-app-domain';
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "userEntityGrant", false, {
|
||||
picker: ((props: {
|
||||
disabled?: boolean | undefined;
|
||||
disabled?: boolean;
|
||||
entity: keyof EntityDict;
|
||||
entityFilter: object;
|
||||
relationIds: string[];
|
||||
|
|
@ -9,8 +9,8 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps<
|
|||
ruleOnRow: EntityDict['userEntityGrant']['OpSchema']['ruleOnRow'];
|
||||
onPickRelations: (ids: string[]) => void;
|
||||
onPickRows: (ids: string[]) => void;
|
||||
pickedRowIds?: string[] | undefined;
|
||||
pickedRelationIds?: string[] | undefined;
|
||||
pickedRowIds?: string[];
|
||||
pickedRelationIds?: string[];
|
||||
oakPath: string;
|
||||
}) => React.ReactElement) | undefined;
|
||||
hideInfo: boolean;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { ReactComponentProps } from 'oak-frontend-base';
|
|||
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
|
||||
entity: keyof ED2;
|
||||
entityId: string;
|
||||
redirectToAfterConfirm: ED2["userEntityGrant"]["Schema"]["redirectTo"];
|
||||
redirectToAfterConfirm: ED2['userEntityGrant']['Schema']['redirectTo'];
|
||||
qrCodeType: string;
|
||||
showTitle: true;
|
||||
showBack: false;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends key
|
|||
entity: keyof ED2;
|
||||
entityId: string;
|
||||
relations: EntityDict['relation']['OpSchema'][];
|
||||
passwordRequire?: boolean | undefined;
|
||||
allowUpdateName?: boolean | undefined;
|
||||
allowUpdateNickname?: boolean | undefined;
|
||||
passwordRequire?: boolean;
|
||||
allowUpdateName?: boolean;
|
||||
allowUpdateNickname?: boolean;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
|||
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
|
||||
entity: keyof ED2;
|
||||
entityId: string;
|
||||
allowUpdateName?: boolean | undefined;
|
||||
allowUpdateNickname?: boolean | undefined;
|
||||
allowUpdateName?: boolean;
|
||||
allowUpdateNickname?: boolean;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { ReactComponentProps } from 'oak-frontend-base';
|
|||
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
|
||||
entity: keyof ED2;
|
||||
entityId: string;
|
||||
redirectToAfterConfirm: ED2["userEntityGrant"]["Schema"]["redirectTo"];
|
||||
redirectToAfterConfirm: ED2['userEntityGrant']['Schema']['redirectTo'];
|
||||
qrCodeType: QrCodeType;
|
||||
type: EntityDict['userEntityGrant']['Schema']['type'];
|
||||
relations: EntityDict['relation']['OpSchema'][];
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
|||
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
|
||||
entity: keyof ED2;
|
||||
entityId: string;
|
||||
redirectToAfterConfirm: ED2["userEntityGrant"]["Schema"]["redirectTo"];
|
||||
redirectToAfterConfirm: ED2['userEntityGrant']['Schema']['redirectTo'];
|
||||
claimUrl: string;
|
||||
qrCodeType: string;
|
||||
passwordRequire?: boolean | undefined;
|
||||
allowUpdateName?: boolean | undefined;
|
||||
allowUpdateNickname?: boolean | undefined;
|
||||
passwordRequire?: boolean;
|
||||
allowUpdateName?: boolean;
|
||||
allowUpdateNickname?: boolean;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export const desc = {
|
|||
origin: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["qiniu", "wechat", "ctyun", "aliyun", "tencent", "unknown"]
|
||||
enumeration: ["qiniu", "wechat", "ctyun", "aliyun", "tencent", "local", "unknown"]
|
||||
},
|
||||
type: {
|
||||
notNull: true,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export const style = {
|
|||
ctyun: '#ff0000',
|
||||
aliyun: '#1677ff',
|
||||
tencent: '#0052d9',
|
||||
local: '#A9A9A9',
|
||||
unknown: '#A9A9A9',
|
||||
},
|
||||
type: {
|
||||
|
|
|
|||
|
|
@ -1 +1,47 @@
|
|||
{ "name": "文件", "attr": { "origin": "源", "type": "类型", "bucket": "桶", "objectId": "对象编号", "tag1": "标签一", "tag2": "标签二", "filename": "文件名", "md5": "md5", "entity": "关联对象", "entityId": "关联对象id", "extra1": "额外信息", "extra2": "非结构化额外信息", "extension": "后缀名", "size": "文件大小", "sort": "排序", "fileType": "文件类型", "isBridge": "是否桥接访问", "uploadState": "上传状态", "uploadMeta": "上传需要的metadata", "application": "来源应用" }, "v": { "origin": { "qiniu": "七牛云", "ctyun": "天翼云", "wechat": "微信", "aliyun": "阿里云", "tencent": "腾讯云", "unknown": "未知" }, "type": { "image": "图像", "video": "视频", "audio": "音频", "file": "文件" }, "uploadState": { "success": "上传成功", "failed": "上传失败", "uploading": "上传中" } } }
|
||||
{
|
||||
"name": "文件",
|
||||
"attr": {
|
||||
"origin": "源",
|
||||
"type": "类型",
|
||||
"bucket": "桶",
|
||||
"objectId": "对象编号",
|
||||
"tag1": "标签一",
|
||||
"tag2": "标签二",
|
||||
"filename": "文件名",
|
||||
"md5": "md5",
|
||||
"entity": "关联对象",
|
||||
"entityId": "关联对象id",
|
||||
"extra1": "额外信息",
|
||||
"extra2": "非结构化额外信息",
|
||||
"extension": "后缀名",
|
||||
"size": "文件大小",
|
||||
"sort": "排序",
|
||||
"fileType": "文件类型",
|
||||
"isBridge": "是否桥接访问",
|
||||
"uploadState": "上传状态",
|
||||
"uploadMeta": "上传需要的metadata",
|
||||
"application": "来源应用"
|
||||
},
|
||||
"v": {
|
||||
"origin": {
|
||||
"qiniu": "七牛云",
|
||||
"ctyun": "天翼云",
|
||||
"wechat": "微信",
|
||||
"aliyun": "阿里云",
|
||||
"tencent": "腾讯云",
|
||||
"local": "本地",
|
||||
"unknown": "未知"
|
||||
},
|
||||
"type": {
|
||||
"image": "图像",
|
||||
"video": "视频",
|
||||
"audio": "音频",
|
||||
"file": "文件"
|
||||
},
|
||||
"uploadState": {
|
||||
"success": "上传成功",
|
||||
"failed": "上传失败",
|
||||
"uploading": "上传中"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ async function sendNotification(notification, context) {
|
|||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId,
|
||||
data: data,
|
||||
openId: data1.openId,
|
||||
openId: data1.openId, // 在notification创建时就赋值了
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ async function createSession(params, context) {
|
|||
origin: 'wechat',
|
||||
type: 'image',
|
||||
tag1: 'image',
|
||||
objectId: await (0, uuid_1.generateNewIdAsync)(),
|
||||
objectId: await (0, uuid_1.generateNewIdAsync)(), // 这个域用来标识唯一性
|
||||
sort: 1000,
|
||||
uploadState: 'success',
|
||||
extra1: data.MediaId,
|
||||
|
|
@ -132,7 +132,7 @@ async function createSession(params, context) {
|
|||
origin: 'wechat',
|
||||
type: 'video',
|
||||
tag1: 'video',
|
||||
objectId: await (0, uuid_1.generateNewIdAsync)(),
|
||||
objectId: await (0, uuid_1.generateNewIdAsync)(), // 这个域用来标识唯一性
|
||||
sort: 1000,
|
||||
uploadState: 'success',
|
||||
extra1: data.MediaId,
|
||||
|
|
@ -154,7 +154,7 @@ async function createSession(params, context) {
|
|||
origin: 'wechat',
|
||||
type: 'audio',
|
||||
tag1: 'audio',
|
||||
objectId: await (0, uuid_1.generateNewIdAsync)(),
|
||||
objectId: await (0, uuid_1.generateNewIdAsync)(), // 这个域用来标识唯一性
|
||||
sort: 1000,
|
||||
uploadState: 'success',
|
||||
extra1: data.MediaId,
|
||||
|
|
|
|||
|
|
@ -2346,8 +2346,8 @@ async function refreshToken(params, context) {
|
|||
// 只有server模式去刷新token
|
||||
// 'development' | 'production' | 'staging'
|
||||
const intervals = {
|
||||
development: 7200 * 1000,
|
||||
staging: 600 * 1000,
|
||||
development: 7200 * 1000, // 2小时
|
||||
staging: 600 * 1000, // 十分钟
|
||||
production: 600 * 1000, // 十分钟
|
||||
};
|
||||
const application = context.getApplication();
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ async function createWechatQrCode(options, context) {
|
|||
permanent,
|
||||
url,
|
||||
expired: false,
|
||||
expiresAt: Date.now() + 2592000 * 1000,
|
||||
expiresAt: Date.now() + 2592000 * 1000, // wecharQrCode里的过期时间都放到最大,由上层关联对象来主动过期(by Xc, 20230131)
|
||||
props,
|
||||
};
|
||||
// 直接创建
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ exports.desc = {
|
|||
origin: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["qiniu", "wechat", "ctyun", "aliyun", "tencent", "unknown"]
|
||||
enumeration: ["qiniu", "wechat", "ctyun", "aliyun", "tencent", "local", "unknown"]
|
||||
},
|
||||
type: {
|
||||
notNull: true,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ exports.style = {
|
|||
ctyun: '#ff0000',
|
||||
aliyun: '#1677ff',
|
||||
tencent: '#0052d9',
|
||||
local: '#A9A9A9',
|
||||
unknown: '#A9A9A9',
|
||||
},
|
||||
type: {
|
||||
|
|
|
|||
|
|
@ -1 +1,47 @@
|
|||
{ "name": "文件", "attr": { "origin": "源", "type": "类型", "bucket": "桶", "objectId": "对象编号", "tag1": "标签一", "tag2": "标签二", "filename": "文件名", "md5": "md5", "entity": "关联对象", "entityId": "关联对象id", "extra1": "额外信息", "extra2": "非结构化额外信息", "extension": "后缀名", "size": "文件大小", "sort": "排序", "fileType": "文件类型", "isBridge": "是否桥接访问", "uploadState": "上传状态", "uploadMeta": "上传需要的metadata", "application": "来源应用" }, "v": { "origin": { "qiniu": "七牛云", "ctyun": "天翼云", "wechat": "微信", "aliyun": "阿里云", "tencent": "腾讯云", "unknown": "未知" }, "type": { "image": "图像", "video": "视频", "audio": "音频", "file": "文件" }, "uploadState": { "success": "上传成功", "failed": "上传失败", "uploading": "上传中" } } }
|
||||
{
|
||||
"name": "文件",
|
||||
"attr": {
|
||||
"origin": "源",
|
||||
"type": "类型",
|
||||
"bucket": "桶",
|
||||
"objectId": "对象编号",
|
||||
"tag1": "标签一",
|
||||
"tag2": "标签二",
|
||||
"filename": "文件名",
|
||||
"md5": "md5",
|
||||
"entity": "关联对象",
|
||||
"entityId": "关联对象id",
|
||||
"extra1": "额外信息",
|
||||
"extra2": "非结构化额外信息",
|
||||
"extension": "后缀名",
|
||||
"size": "文件大小",
|
||||
"sort": "排序",
|
||||
"fileType": "文件类型",
|
||||
"isBridge": "是否桥接访问",
|
||||
"uploadState": "上传状态",
|
||||
"uploadMeta": "上传需要的metadata",
|
||||
"application": "来源应用"
|
||||
},
|
||||
"v": {
|
||||
"origin": {
|
||||
"qiniu": "七牛云",
|
||||
"ctyun": "天翼云",
|
||||
"wechat": "微信",
|
||||
"aliyun": "阿里云",
|
||||
"tencent": "腾讯云",
|
||||
"local": "本地",
|
||||
"unknown": "未知"
|
||||
},
|
||||
"type": {
|
||||
"image": "图像",
|
||||
"video": "视频",
|
||||
"audio": "音频",
|
||||
"file": "文件"
|
||||
},
|
||||
"uploadState": {
|
||||
"success": "上传成功",
|
||||
"failed": "上传失败",
|
||||
"uploading": "上传中"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ async function sendNotification(notification, context) {
|
|||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId,
|
||||
data: data,
|
||||
openId: data1.openId,
|
||||
openId: data1.openId, // 在notification创建时就赋值了
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ export default OakComponent({
|
|||
};
|
||||
},
|
||||
properties: {
|
||||
tocWidth: 228,
|
||||
tocClosed: false,
|
||||
tocFixed: true,
|
||||
tocPosition: 'none' as 'none' | 'left' | 'right',
|
||||
|
|
@ -28,6 +27,8 @@ export default OakComponent({
|
|||
headerTop: 0, //页面中吸顶部分高度
|
||||
className: '',
|
||||
scrollId: '', // 滚动条所在容器id,不传默认body
|
||||
tocWidth: undefined as number | 'auto' | undefined,
|
||||
tocHeight: undefined as number | 'auto' | undefined,
|
||||
},
|
||||
lifetimes: {
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
padding: 16px;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
|
|
@ -14,7 +20,7 @@
|
|||
max-width: 794px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--oak-bg-color-container);
|
||||
background: #fff;
|
||||
padding: 20px 50px 50px 50px;
|
||||
box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import { WebComponentProps } from 'oak-frontend-base';
|
|||
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
import { TocItem, TocView } from '../toc/tocView';
|
||||
import Style from './web.module.less';
|
||||
import Styles from './web.module.less';
|
||||
|
||||
export default function Render(
|
||||
props: WebComponentProps<
|
||||
|
|
@ -16,7 +16,7 @@ export default function Render(
|
|||
'article',
|
||||
false,
|
||||
{
|
||||
title?: string;
|
||||
name?: string;
|
||||
content?: string;
|
||||
tocPosition: 'none' | 'left' | 'right';
|
||||
highlightBgColor: string;
|
||||
|
|
@ -24,13 +24,14 @@ export default function Render(
|
|||
className?: string;
|
||||
tocFixed: boolean;
|
||||
tocClosed: boolean;
|
||||
tocWidth: 228,
|
||||
scrollId?: string;
|
||||
tocWidth?: number | 'auto';
|
||||
tocHeight?: number | 'auto';
|
||||
},
|
||||
{}
|
||||
>
|
||||
) {
|
||||
const { className, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed= false, tocWidth = 228, scrollId } = props.data;
|
||||
const { className, name, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed= false, scrollId, tocWidth, tocHeight } = props.data;
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
readOnly: true,
|
||||
autoFocus: true,
|
||||
|
|
@ -63,28 +64,33 @@ export default function Render(
|
|||
}
|
||||
}, [tocPosition])
|
||||
|
||||
useEffect(() => {
|
||||
if (name) {
|
||||
window.document.title = name;
|
||||
}
|
||||
}, [name]);
|
||||
|
||||
return (
|
||||
<div className={classNames(Style.container, className)}>
|
||||
<Row gutter={[16, 0]}>
|
||||
{tocPosition === 'left' ? (
|
||||
<Col flex={`${tocWidth}px`}>
|
||||
<div className={classNames(Styles.container, className)}>
|
||||
<div className={Styles.contentContainer}>
|
||||
{tocPosition === "left" ? (
|
||||
<TocView
|
||||
toc={toc}
|
||||
showToc={showToc}
|
||||
tocPosition='left'
|
||||
tocPosition="left"
|
||||
setShowToc={setShowToc}
|
||||
highlightBgColor={highlightBgColor}
|
||||
headerTop={headerTop}
|
||||
fixed={tocFixed}
|
||||
closed={tocClosed}
|
||||
scrollId={scrollId}
|
||||
tocWidth={tocWidth}
|
||||
tocHeight={tocHeight}
|
||||
/>
|
||||
</Col>
|
||||
) : null}
|
||||
|
||||
<Col flex="auto">
|
||||
<div className={Style.content}>
|
||||
<div className={Style.editorContainer}>
|
||||
<div className={Styles.content}>
|
||||
<div className={Styles.editorContainer}>
|
||||
<div style={{ width: "100%" }}>
|
||||
<Editor
|
||||
defaultConfig={editorConfig}
|
||||
|
|
@ -96,39 +102,44 @@ export default function Render(
|
|||
onCreated={setEditor}
|
||||
onChange={(editor) => {
|
||||
setHtml(editor.getHtml());
|
||||
const headers = editor.getElemsByTypePrefix('header');
|
||||
const tocItems = headers.map((header: any) => {
|
||||
const text = SlateNode.string(header)
|
||||
const { id, type } = header
|
||||
const headers =
|
||||
editor.getElemsByTypePrefix("header");
|
||||
const tocItems = headers.map(
|
||||
(header: any) => {
|
||||
const text =
|
||||
SlateNode.string(header);
|
||||
const { id, type } = header;
|
||||
return {
|
||||
text,
|
||||
level: parseInt(type.substring(6)),
|
||||
level: parseInt(
|
||||
type.substring(6)
|
||||
),
|
||||
id,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
setToc([...tocItems]);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
{tocPosition === 'right' ? (
|
||||
<Col flex={`${tocWidth}px`}>
|
||||
{tocPosition === "right" ? (
|
||||
<TocView
|
||||
toc={toc}
|
||||
showToc={showToc}
|
||||
tocPosition='right'
|
||||
tocPosition="right"
|
||||
setShowToc={setShowToc}
|
||||
highlightBgColor={highlightBgColor}
|
||||
headerTop={headerTop}
|
||||
fixed={tocFixed}
|
||||
closed={tocClosed}
|
||||
scrollId={scrollId}
|
||||
tocWidth={tocWidth}
|
||||
tocHeight={tocHeight}
|
||||
/>
|
||||
</Col>
|
||||
) : null}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ export default OakComponent({
|
|||
data: {
|
||||
content: '',
|
||||
title: '',
|
||||
author: '',
|
||||
},
|
||||
lifetimes: {
|
||||
async attached() {
|
||||
|
|
@ -20,7 +19,6 @@ export default OakComponent({
|
|||
this.setState({
|
||||
content: article?.content,
|
||||
title: article?.title,
|
||||
author: article?.author,
|
||||
});
|
||||
},
|
||||
detached() {
|
||||
|
|
@ -35,6 +33,8 @@ export default OakComponent({
|
|||
headerTop: 0, //页面中吸顶部分高度
|
||||
className: '',
|
||||
scrollId: '', // 滚动条所在容器id,不传默认body
|
||||
tocWidth: undefined as number | 'auto' | undefined,
|
||||
tocHeight: undefined as number | 'auto' | undefined,
|
||||
},
|
||||
methods: {},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
.container {
|
||||
// min-height: 100vh;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -22,11 +22,13 @@ export default function Render(
|
|||
tocFixed: boolean;
|
||||
tocClosed: boolean;
|
||||
scrollId?: string;
|
||||
tocWidth?: number | 'auto';
|
||||
tocHeight?: number | 'auto';
|
||||
},
|
||||
{}
|
||||
>
|
||||
) {
|
||||
const { className, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed= false, scrollId } = props.data;
|
||||
const { className, content, tocPosition = 'none', tocFixed, highlightBgColor = 'none', headerTop = 0, tocClosed= false, scrollId, tocWidth, tocHeight } = props.data;
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
readOnly: true,
|
||||
autoFocus: true,
|
||||
|
|
@ -60,24 +62,23 @@ export default function Render(
|
|||
|
||||
return (
|
||||
<div className={classNames(Styles.container, className)}>
|
||||
<Row gutter={[16, 0]}>
|
||||
{tocPosition === 'left' && (
|
||||
<Col flex="228px">
|
||||
<div className={Styles.contentContainer}>
|
||||
{tocPosition === "left" && (
|
||||
<TocView
|
||||
toc={toc}
|
||||
showToc={showToc}
|
||||
tocPosition='left'
|
||||
tocPosition="left"
|
||||
setShowToc={setShowToc}
|
||||
highlightBgColor={highlightBgColor}
|
||||
headerTop={headerTop}
|
||||
fixed={tocFixed}
|
||||
closed={tocClosed}
|
||||
scrollId={scrollId}
|
||||
tocWidth={tocWidth}
|
||||
tocHeight={tocHeight}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<Col flex="auto">
|
||||
<div className={Styles.content}>
|
||||
<div className={Styles.editorContainer}>
|
||||
<div style={{ width: "100%" }}>
|
||||
|
|
@ -86,44 +87,49 @@ export default function Render(
|
|||
value={html}
|
||||
mode="default"
|
||||
style={{
|
||||
width: '100%'
|
||||
width: "100%",
|
||||
}}
|
||||
onCreated={setEditor}
|
||||
onChange={(editor) => {
|
||||
setHtml(editor.getHtml());
|
||||
const headers = editor.getElemsByTypePrefix('header');
|
||||
const tocItems = headers.map((header: any) => {
|
||||
const text = SlateNode.string(header)
|
||||
const { id, type } = header
|
||||
const headers =
|
||||
editor.getElemsByTypePrefix("header");
|
||||
const tocItems = headers.map(
|
||||
(header: any) => {
|
||||
const text =
|
||||
SlateNode.string(header);
|
||||
const { id, type } = header;
|
||||
return {
|
||||
text,
|
||||
level: parseInt(type.substring(6)),
|
||||
level: parseInt(
|
||||
type.substring(6)
|
||||
),
|
||||
id,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
setToc([...tocItems]);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
{tocPosition === 'right' && (
|
||||
<Col flex="228px">
|
||||
{tocPosition === "right" && (
|
||||
<TocView
|
||||
toc={toc}
|
||||
showToc={showToc}
|
||||
tocPosition='right'
|
||||
tocPosition="right"
|
||||
setShowToc={setShowToc}
|
||||
highlightBgColor={highlightBgColor}
|
||||
headerTop={headerTop}
|
||||
fixed={tocFixed}
|
||||
closed={tocClosed}
|
||||
scrollId={scrollId}
|
||||
tocWidth={tocWidth}
|
||||
tocHeight={tocHeight}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@ export function TocView(
|
|||
fixed?: boolean;
|
||||
scrollId?: string;
|
||||
closed?: boolean,
|
||||
tocWidth?: number,
|
||||
tocWidth?: number | 'auto',
|
||||
tocHeight?: number | 'auto'
|
||||
}
|
||||
) {
|
||||
const { toc, showToc, tocPosition, setShowToc, highlightBgColor, headerTop = 0, scrollId, fixed = false, closed = false, tocWidth = 228 } = props;
|
||||
const { toc, showToc, tocPosition, setShowToc, highlightBgColor, headerTop = 0, scrollId, fixed = false, closed = false, tocWidth, tocHeight } = props;
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty('--highlight-bg-color', highlightBgColor);
|
||||
|
|
@ -118,9 +119,12 @@ export function TocView(
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(Style.tocContainer, {
|
||||
<div
|
||||
className={classNames(Style.tocContainer, {
|
||||
[Style.fixed]: fixed
|
||||
})}>
|
||||
})}
|
||||
style={Object.assign({}, tocWidth ? { width: tocWidth } : {}, tocHeight ? { height: tocHeight } : {})}
|
||||
>
|
||||
{showToc ? (
|
||||
<>
|
||||
<div className={Style.catalogTitle}>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
height: 50px;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
|||
|
|
@ -157,10 +157,10 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
icon={<EditOutlined />}
|
||||
size="small"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setName(ele.name);
|
||||
setNameEditing(ele.id);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
style={{ marginRight: 4 }}
|
||||
/>
|
||||
<div className={Styles.name}>
|
||||
<div style={{ marginLeft: 4, overflow: 'hidden', width: '150px', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{ele?.name}</div>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export default OakComponent({
|
|||
onArticlePreview: (content?: string, title?: string) => undefined as void, //预览文章
|
||||
origin: 'qiniu', // 默认为七牛云
|
||||
scrollId: '', // 滚动条所在容器id,不传默认页面编辑器容器id
|
||||
height: 600 as number | 'auto',
|
||||
},
|
||||
listeners: {
|
||||
'editor,content'(prev, next) {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,21 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
.editorContainer {
|
||||
width: 100%;
|
||||
margin: 30px auto 30px auto;
|
||||
|
|
@ -45,10 +56,8 @@
|
|||
}
|
||||
|
||||
.footer {
|
||||
// position: absolute;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.contentNumber {
|
||||
|
|
@ -60,7 +69,7 @@
|
|||
}
|
||||
|
||||
.tocContainer {
|
||||
padding-top: 28px;
|
||||
// padding-top: 28px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
|
|
@ -101,7 +110,7 @@
|
|||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding: 6px 6px 10px 6px;
|
||||
padding: 34px 6px 10px 6px;
|
||||
box-sizing: border-box;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useRef, useEffect, useMemo } from "react";
|
||||
import { Alert, Card, Button, Row, Col, Space, Input, Modal, Tooltip, } from "antd";
|
||||
import { Alert, Button, Row, Col, Space, Input, Modal, Tooltip, } from "antd";
|
||||
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
|
||||
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
|
||||
import { IToolbarConfig, SlateNode } from "@wangeditor/editor";
|
||||
|
|
@ -10,11 +10,10 @@ import classNames from "classnames";
|
|||
import Prompt from "../../../components/common/prompt";
|
||||
import { EntityDict } from "./../../../oak-app-domain";
|
||||
import Style from "./web.module.less";
|
||||
import { TocItem, TocView } from '../toc/tocView';
|
||||
|
||||
import {
|
||||
CloseOutlined,
|
||||
EyeOutlined,
|
||||
MenuOutlined,
|
||||
CaretDownOutlined
|
||||
} from "@ant-design/icons";
|
||||
|
||||
type InsertFnType = (url: string, alt?: string, href?: string) => void;
|
||||
|
|
@ -46,141 +45,6 @@ function customCheckImageFn(
|
|||
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
|
||||
}
|
||||
|
||||
type TocItem = {
|
||||
text: string;
|
||||
level: number;
|
||||
id: string;
|
||||
}
|
||||
|
||||
function TocView(
|
||||
props: {
|
||||
toc: TocItem[];
|
||||
showToc: boolean;
|
||||
tocPosition: 'left' | 'right';
|
||||
setShowToc: (showToc: boolean) => void;
|
||||
highlightBgColor: string;
|
||||
scrollId?: string
|
||||
}
|
||||
) {
|
||||
const { toc, showToc, tocPosition, setShowToc, highlightBgColor, scrollId } = props;
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty('--highlight-bg-color', highlightBgColor);
|
||||
}, [highlightBgColor]);
|
||||
|
||||
const generateTocList = (items: TocItem[], currentLevel: number = 1, parentId: string | null = null): React.ReactNode => {
|
||||
//递归生成嵌套列表
|
||||
const result: React.ReactNode[] = [];
|
||||
let lastId: string | null = parentId;
|
||||
|
||||
while (items.length > 0) {
|
||||
const item = items[0];
|
||||
//有无子级标题,有则显示展开图标
|
||||
const hasChildren = items.length > 1 && items[1].level > item.level;
|
||||
|
||||
if (item.level > currentLevel) {
|
||||
// 递归生成子列表
|
||||
const sublist = generateTocList(items, item.level, item.id);
|
||||
result.push(
|
||||
<li key={item.id} id={`ul-${lastId}`} >
|
||||
<ul style={{ listStyleType: 'none', paddingInlineStart: '6px' }}>
|
||||
{sublist}
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
} else if (item.level < currentLevel) {
|
||||
// 结束当前层级
|
||||
break;
|
||||
} else {
|
||||
// 添加当前层级的 <li>
|
||||
result.push(
|
||||
<li className={Style.listItem} key={item.id} id={`li-${item.id}`} style={{ paddingLeft: `${(item.level - 1) * 6}px`, }}>
|
||||
<CaretDownOutlined id={`icon-${item.id}`} className={Style.icon} style={{ visibility: hasChildren && item.level < 5 ? 'visible' : 'hidden', color: '#A5A5A5' }}
|
||||
onClick={() => {
|
||||
const iconElem = document.getElementById(`icon-${item.id}`);
|
||||
const ulElem = document.getElementById(`ul-${item.id}`);
|
||||
const isFolded = iconElem?.className.includes('iconFold') || ulElem?.className.includes('fold');
|
||||
if (isFolded) {
|
||||
iconElem?.classList.remove(Style.iconFold);
|
||||
ulElem?.classList.remove(Style.fold);
|
||||
} else {
|
||||
iconElem?.classList.add(Style.iconFold);
|
||||
ulElem?.classList.add(Style.fold);
|
||||
}
|
||||
}} />
|
||||
<div
|
||||
style={{ fontSize: '1em', fontWeight: item.level === 1 ? 'bold' : 'normal' }}
|
||||
className={Style.tocItem}
|
||||
onClick={(event: any) => {
|
||||
// editor.scrollToElem(item.id);
|
||||
//编辑器滚动到对应元素
|
||||
const elem = document.getElementById(item.id);
|
||||
const elemTop = elem?.getBoundingClientRect().top;
|
||||
const scrollContainer = document.getElementById(scrollId || 'article-upsert-editorContainer');
|
||||
const containerTop = scrollContainer?.getBoundingClientRect().top;
|
||||
|
||||
scrollContainer?.scrollBy({
|
||||
top: elemTop! - containerTop!,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
//添加背景色
|
||||
elem?.classList.add(Style.highlight);
|
||||
//移除背景色类名
|
||||
setTimeout(function () {
|
||||
elem?.classList.remove(Style.highlight);
|
||||
}, 1000);
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{item.text}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
items.shift(); // 移除已处理的项
|
||||
lastId = item.id;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(Style.tocContainer, {
|
||||
// [Style.fixed]: fixed
|
||||
})}>
|
||||
{showToc ? (
|
||||
<>
|
||||
<div className={Style.catalogTitle}>
|
||||
<div style={{ color: '#A5A5A5' }}>大纲</div>
|
||||
<CloseOutlined style={{ color: '#A5A5A5' }} onClick={() => setShowToc(false)} />
|
||||
</div>
|
||||
|
||||
{(toc && toc.length > 0) ? (
|
||||
<ul style={{ listStyleType: 'none', paddingInlineStart: '0px' }}>{generateTocList([...toc])}</ul>
|
||||
) : (
|
||||
<div style={{ display: 'flex', alignItems: 'center', color: '#B1B1B1', height: '200px' }}>
|
||||
<div>
|
||||
对文档内容应用“标题”样式,即可生成大纲
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className={classNames(Style.tocButton, { [Style.tocButtonRight]: tocPosition === 'right' })}>
|
||||
<Tooltip title="显示大纲" placement={tocPosition === 'right' ? 'left' : 'right'}>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<MenuOutlined />}
|
||||
onClick={() => setShowToc(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Render(
|
||||
props: WebComponentProps<
|
||||
EntityDict,
|
||||
|
|
@ -191,14 +55,16 @@ export default function Render(
|
|||
name: string;
|
||||
editor: any;
|
||||
content?: string;
|
||||
html?: string;
|
||||
origin?: string;
|
||||
contentTip: boolean;
|
||||
articleMenuId: string;
|
||||
oakId: string;
|
||||
tocPosition: 'none' | 'left' | 'right';
|
||||
highlightBgColor: string;
|
||||
scrollId?: string
|
||||
scrollId?: string;
|
||||
tocWidth?: number | 'auto';
|
||||
tocHeight?: number | 'auto';
|
||||
height?: number | 'auto';
|
||||
},
|
||||
{
|
||||
setHtml: (content: string) => void;
|
||||
|
|
@ -225,19 +91,22 @@ export default function Render(
|
|||
clearContentTip,
|
||||
} = methods;
|
||||
const {
|
||||
oakFullpath,
|
||||
id,
|
||||
content,
|
||||
editor,
|
||||
origin = 'qiniu',
|
||||
oakFullpath,
|
||||
html,
|
||||
tocPosition = 'none',
|
||||
highlightBgColor = 'none',
|
||||
scrollId
|
||||
scrollId,
|
||||
tocWidth,
|
||||
tocHeight,
|
||||
height = 600
|
||||
} = data;
|
||||
const [articleId, setArticleId] = useState('');
|
||||
const [toc, setToc] = useState<TocItem[]>([]);
|
||||
const [showToc, setShowToc] = useState(false);
|
||||
const containerId = scrollId || 'article-upsert-editorContainer';
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
|
|
@ -253,37 +122,42 @@ export default function Render(
|
|||
|
||||
return (
|
||||
<div className={Style.container}>
|
||||
<Prompt when={!id || data.oakDirty} message={'您确认离开页面吗?'} />
|
||||
<div style={{ width: 'calc(100% - 16px)', }}>
|
||||
<Prompt
|
||||
when={!id || data.oakDirty}
|
||||
message={"您确认离开页面吗?"}
|
||||
/>
|
||||
<div style={{ width: "calc(100% - 16px)" }}>
|
||||
<Toolbar
|
||||
editor={editor}
|
||||
defaultConfig={toolbarConfig}
|
||||
mode="default"
|
||||
/>
|
||||
</div>
|
||||
<Row gutter={[16, 0]}>
|
||||
{tocPosition === 'left' ? (
|
||||
<Col flex="228px">
|
||||
<div className={Style.contentContainer}>
|
||||
{tocPosition === "left" ? (
|
||||
<TocView
|
||||
toc={toc}
|
||||
showToc={showToc}
|
||||
tocPosition='left'
|
||||
tocPosition="left"
|
||||
setShowToc={setShowToc}
|
||||
highlightBgColor={highlightBgColor}
|
||||
scrollId={scrollId}
|
||||
scrollId={containerId}
|
||||
tocWidth={tocWidth}
|
||||
tocHeight={tocHeight || height}
|
||||
/>
|
||||
</Col>
|
||||
) : null}
|
||||
|
||||
<Col flex="auto">
|
||||
<div className={Style.content}>
|
||||
<div id="article-upsert-editorContainer" className={classNames(Style.editorContainer, {
|
||||
[Style.editorExternalContainer]: !!scrollId
|
||||
})}>
|
||||
<div className={Style.content} style={{ maxWidth: `calc(100% - ${tocWidth || 228}px)` }}>
|
||||
<div
|
||||
id={containerId}
|
||||
className={classNames(Style.editorContainer, {
|
||||
[Style.editorExternalContainer]: !!scrollId,
|
||||
})}
|
||||
>
|
||||
{data.contentTip && (
|
||||
<Alert
|
||||
type="info"
|
||||
message={t('tips.content')}
|
||||
message={t("tips.content")}
|
||||
closable
|
||||
onClose={() => clearContentTip()}
|
||||
/>
|
||||
|
|
@ -294,10 +168,10 @@ export default function Render(
|
|||
update({ name: e.target.value })
|
||||
}
|
||||
value={data.name}
|
||||
placeholder={'请输入文章标题'}
|
||||
placeholder={"请输入文章标题"}
|
||||
size="large"
|
||||
maxLength={32}
|
||||
suffix={`${[...(data.name || '')].length}/32`}
|
||||
suffix={`${(data.name || "").length}/32`}
|
||||
className={Style.titleInput}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -305,7 +179,7 @@ export default function Render(
|
|||
<Editor
|
||||
defaultConfig={{
|
||||
autoFocus: true,
|
||||
placeholder: '请输入文章内容...',
|
||||
placeholder: "请输入文章内容...",
|
||||
MENU_CONF: {
|
||||
checkImage: customCheckImageFn,
|
||||
uploadImage: {
|
||||
|
|
@ -316,30 +190,50 @@ export default function Render(
|
|||
) {
|
||||
// TS 语法
|
||||
// file 即选中的文件
|
||||
const { name, size, type } = file;
|
||||
const extension = name.substring(name.lastIndexOf('.') + 1);
|
||||
const filename = name.substring(0,name.lastIndexOf('.'));
|
||||
const { name, size, type } =
|
||||
file;
|
||||
const extension =
|
||||
name.substring(
|
||||
name.lastIndexOf(
|
||||
"."
|
||||
) + 1
|
||||
);
|
||||
const filename =
|
||||
name.substring(
|
||||
0,
|
||||
name.lastIndexOf(
|
||||
"."
|
||||
)
|
||||
);
|
||||
const extraFile = {
|
||||
entity: 'article',
|
||||
entity: "article",
|
||||
entityId: articleId,
|
||||
origin: origin,
|
||||
type: 'image',
|
||||
tag1: 'source',
|
||||
objectId: generateNewId(),
|
||||
type: "image",
|
||||
tag1: "source",
|
||||
objectId:
|
||||
generateNewId(),
|
||||
filename,
|
||||
size,
|
||||
extension,
|
||||
bucket: '',
|
||||
bucket: "",
|
||||
id: generateNewId(),
|
||||
fileType: type
|
||||
} as EntityDict['extraFile']['CreateSingle']['data'];
|
||||
fileType: type,
|
||||
} as EntityDict["extraFile"]["CreateSingle"]["data"];
|
||||
|
||||
try {
|
||||
// 自己实现上传,并得到图片 url alt href
|
||||
const url = await uploadFile(extraFile, file);
|
||||
const url =
|
||||
await uploadFile(
|
||||
extraFile,
|
||||
file
|
||||
);
|
||||
// 最后插入图片
|
||||
insertFn( url, extraFile.filename);
|
||||
} catch (err) { }
|
||||
insertFn(
|
||||
url,
|
||||
extraFile.filename
|
||||
);
|
||||
} catch (err) {}
|
||||
},
|
||||
},
|
||||
uploadVideo: {
|
||||
|
|
@ -350,30 +244,51 @@ export default function Render(
|
|||
) {
|
||||
// TS 语法
|
||||
// file 即选中的文件
|
||||
const { name, size, type } = file;
|
||||
const extension = name.substring(name.lastIndexOf('.') + 1);
|
||||
const filename = name.substring(0,name.lastIndexOf('.'));
|
||||
const { name, size, type } =
|
||||
file;
|
||||
const extension =
|
||||
name.substring(
|
||||
name.lastIndexOf(
|
||||
"."
|
||||
) + 1
|
||||
);
|
||||
const filename =
|
||||
name.substring(
|
||||
0,
|
||||
name.lastIndexOf(
|
||||
"."
|
||||
)
|
||||
);
|
||||
const extraFile = {
|
||||
entity: 'article',
|
||||
entity: "article",
|
||||
entityId: articleId,
|
||||
origin: origin,
|
||||
type: 'video',
|
||||
tag1: 'source',
|
||||
objectId: generateNewId(),
|
||||
type: "video",
|
||||
tag1: "source",
|
||||
objectId:
|
||||
generateNewId(),
|
||||
filename,
|
||||
size,
|
||||
extension,
|
||||
bucket: '',
|
||||
bucket: "",
|
||||
id: generateNewId(),
|
||||
fileType: type
|
||||
} as EntityDict['extraFile']['CreateSingle']['data'];
|
||||
fileType: type,
|
||||
} as EntityDict["extraFile"]["CreateSingle"]["data"];
|
||||
|
||||
try {
|
||||
// 自己实现上传,并得到图片 url alt href
|
||||
const url = await uploadFile(extraFile,file);
|
||||
const url =
|
||||
await uploadFile(
|
||||
extraFile,
|
||||
file
|
||||
);
|
||||
// 最后插入图片
|
||||
insertFn(url, url + '?vframe/jpg/offset/0');
|
||||
} catch (err) { }
|
||||
insertFn(
|
||||
url,
|
||||
url +
|
||||
"?vframe/jpg/offset/0"
|
||||
);
|
||||
} catch (err) {}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -381,16 +296,20 @@ export default function Render(
|
|||
onCreated={setEditor}
|
||||
onChange={(editor) => {
|
||||
setHtml(editor.getHtml());
|
||||
const headers = editor.getElemsByTypePrefix('header');
|
||||
const tocItems = headers.map((header: any) => {
|
||||
const text = SlateNode.string(header)
|
||||
const { id, type } = header
|
||||
const headers = editor.getElemsByTypePrefix("header");
|
||||
const tocItems = headers.map(
|
||||
(header: any) => {
|
||||
const text = SlateNode.string(header);
|
||||
const { id, type } = header;
|
||||
return {
|
||||
text,
|
||||
level: parseInt(type.substring(6)),
|
||||
level: parseInt(
|
||||
type.substring(6)
|
||||
),
|
||||
id,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
setToc([...tocItems]);
|
||||
}}
|
||||
style={{
|
||||
|
|
@ -399,11 +318,8 @@ export default function Render(
|
|||
mode="default"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={Style.footer}>
|
||||
<Row align="middle">
|
||||
<Col flex="none">
|
||||
<Space>
|
||||
<Button
|
||||
disabled={
|
||||
|
|
@ -419,34 +335,28 @@ export default function Render(
|
|||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
gotoPreview(
|
||||
content,
|
||||
data.name
|
||||
);
|
||||
gotoPreview(content, data.name);
|
||||
}}
|
||||
icon={<EyeOutlined />}
|
||||
>
|
||||
<EyeOutlined />
|
||||
预览
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
{tocPosition === 'right' ? (
|
||||
<Col flex="228px">
|
||||
{tocPosition === "right" ? (
|
||||
<TocView
|
||||
toc={toc}
|
||||
showToc={showToc}
|
||||
tocPosition='right'
|
||||
tocPosition="right"
|
||||
setShowToc={setShowToc}
|
||||
highlightBgColor={highlightBgColor}
|
||||
scrollId={scrollId}
|
||||
scrollId={containerId}
|
||||
tocWidth={tocWidth}
|
||||
tocHeight={tocHeight}
|
||||
/>
|
||||
</Col>
|
||||
) : null}
|
||||
</Row>
|
||||
</div >
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
height: 50px;
|
||||
justify-content: space-between;
|
||||
|
||||
.ne {
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: 20px;
|
||||
margin-left: 10px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
|
|
@ -43,6 +43,8 @@
|
|||
}
|
||||
}
|
||||
.sub {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
|
@ -78,7 +80,6 @@
|
|||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
// padding: 10px;
|
||||
|
||||
.ph {
|
||||
width: 24px;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
}
|
||||
}, [editArticleId]);
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [name, setName] = useState('');
|
||||
const [nameEditing, setNameEditing] = useState(false);
|
||||
const [showSub, setShowSub] = useState(false);
|
||||
const [newBreadcrumbItems, setNewBreadcrumbItems] = useState([] as string[]);
|
||||
|
|
@ -145,9 +144,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
if (!row.parentId && articleMenuId) {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{Sub}
|
||||
</div>
|
||||
{contextHolder}
|
||||
</>
|
||||
);
|
||||
|
|
@ -294,30 +291,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
return (
|
||||
<>
|
||||
<div className={Styles.container}>
|
||||
<div
|
||||
className={Styles.ne}
|
||||
>
|
||||
{
|
||||
// nameEditing ? <div className={Styles.name}>
|
||||
// <Input
|
||||
// autoFocus
|
||||
// value={ name || row?.name}
|
||||
// onChange={(evt) => setName(evt.target.value)}
|
||||
// onPressEnter={async () => {
|
||||
// if (name && name !== row?.name) {
|
||||
// await onUpdateName(name);
|
||||
// }
|
||||
// setNameEditing(false);
|
||||
// }}
|
||||
// onBlur={async () => {
|
||||
// if (name && name !== row?.name) {
|
||||
// await onUpdateName(name);
|
||||
// }
|
||||
// setNameEditing(false);
|
||||
// }}
|
||||
// />
|
||||
// </div> :
|
||||
<>
|
||||
<div className={Styles.ne}>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
|
|
@ -325,25 +299,30 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
onClick={() => {
|
||||
setNameEditing(true);
|
||||
const modalInstance = modal.confirm({
|
||||
title: '编辑分类',
|
||||
cancelText: '取消',
|
||||
okText: '提交',
|
||||
title: "编辑分类",
|
||||
cancelText: "取消",
|
||||
okText: "提交",
|
||||
content: (
|
||||
<div>
|
||||
<Form.Item
|
||||
label="分类名称"
|
||||
>
|
||||
<Form.Item label="分类名称">
|
||||
<Input
|
||||
ref={menuNameRef}
|
||||
defaultValue={row.name}
|
||||
onChange={(val) => update({ name: val.target.value })}
|
||||
onChange={(val) =>
|
||||
update({
|
||||
name: val.target
|
||||
.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="LOGO"
|
||||
help={
|
||||
<div>
|
||||
<span>请上传LOGO高清图片,</span>
|
||||
<span>
|
||||
请上传LOGO高清图片,
|
||||
</span>
|
||||
<span>
|
||||
108*108像素,仅支持PNG、JPG格式,大小不超过300KB。
|
||||
</span>
|
||||
|
|
@ -374,28 +353,36 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
// });
|
||||
// }
|
||||
// }
|
||||
footer: <Space>
|
||||
footer: (
|
||||
<Space>
|
||||
<ExtraFileCommit
|
||||
entity={oakEntity}
|
||||
oakPath={oakFullpath}
|
||||
afterCommit={() => {
|
||||
modalInstance!.destroy()
|
||||
modalInstance!.destroy();
|
||||
}}
|
||||
beforeCommit={() => {
|
||||
if (menuNameRef.current!.input!.value) {
|
||||
return true
|
||||
if (
|
||||
menuNameRef.current!
|
||||
.input!.value
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button onClick={() => modalInstance!.destroy()}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
modalInstance!.destroy()
|
||||
}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
});
|
||||
}}
|
||||
style={{ marginRight: 4 }}
|
||||
/>
|
||||
<div className={Styles.name}>
|
||||
{logo ? (
|
||||
|
|
@ -406,26 +393,44 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
preview={false}
|
||||
/>
|
||||
) : null}
|
||||
<div style={{ marginLeft: 4, overflow: 'hidden', width: '100px', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{row?.name}</div>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: 4,
|
||||
overflow: "hidden",
|
||||
width: "100px",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{row?.name}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<Divider type="vertical" style={{ height: '100%', marginTop: 4, marginBottom: 4 }} />
|
||||
</div>
|
||||
<Divider
|
||||
type="vertical"
|
||||
style={{
|
||||
height: "100%",
|
||||
marginTop: 4,
|
||||
marginBottom: 4,
|
||||
}}
|
||||
/>
|
||||
<div className={Styles.control}>
|
||||
{
|
||||
!row.parentId && <Button
|
||||
{!row.parentId && (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
gotoDoc(row?.id);
|
||||
}}
|
||||
icon={<EyeOutlined />}
|
||||
></Button>
|
||||
)}
|
||||
|
||||
<Dropdown
|
||||
menu={{ items }}
|
||||
placement="bottomRight"
|
||||
arrow
|
||||
>
|
||||
</Button>
|
||||
}
|
||||
|
||||
|
||||
<Dropdown menu={{ items }} placement="bottomRight" arrow>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<PlusOutlined />}
|
||||
|
|
@ -439,42 +444,39 @@ export default function Render(props: WebComponentProps<EntityDict, 'articleMenu
|
|||
onClick={() => {
|
||||
if (!allowRemove) {
|
||||
modal.error({
|
||||
title: '无法删除',
|
||||
content: hasSubArticles ? '请先删除目录下的文章' : '请先删除目录下的子目录',
|
||||
okText: '确认'
|
||||
title: "无法删除",
|
||||
content: hasSubArticles
|
||||
? "请先删除目录下的文章"
|
||||
: "请先删除目录下的子目录",
|
||||
okText: "确认",
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
onRemove();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{
|
||||
(hasSubArticles || hasSubMenus) ? (
|
||||
showSub ?
|
||||
{hasSubArticles || hasSubMenus ? (
|
||||
showSub ? (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<UpOutlined />}
|
||||
size="small"
|
||||
onClick={() => setShowSub(false)}
|
||||
/> :
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<DownOutlined />}
|
||||
size="small"
|
||||
onClick={() => setShowSub(true)}
|
||||
/>
|
||||
) : <div className={Styles.ph} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
showSub && (
|
||||
<div className={Styles.sub}>
|
||||
{Sub}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
) : (
|
||||
<div className={Styles.ph} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{showSub ? Sub : null}
|
||||
{contextHolder}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ export default OakComponent({
|
|||
highlightBgColor: 'none', //点击文章目录时标题高亮背景色,none为不显示高亮背景色
|
||||
onMenuView: () => undefined as void, //查看全部菜单
|
||||
onMenuViewById: (articleMenuId: string) => undefined as void, //查看指定id菜单
|
||||
onArticleView: (oakId: string) => undefined as void, //查看文章
|
||||
onArticleView: (articleId: string) => undefined as void, //查看文章
|
||||
onArticlePreview: (content?: string, title?: string) => undefined as void, //预览文章
|
||||
onArticleEdit: (oakId: string) => undefined as void, //编辑文章
|
||||
setCopyArticleUrl: (id: string) => '' as string,
|
||||
onArticleEdit: (articleId: string) => undefined as void, //编辑文章
|
||||
setCopyArticleUrl: (articleId: string) => '' as string,
|
||||
origin: 'qiniu', // cos origin默认七牛云
|
||||
scrollId: '', // 滚动条所在容器id,不传默认页面编辑器容器id
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
overflow-x: hidden;
|
||||
|
||||
.menu {
|
||||
min-width: 320px;
|
||||
width: 320px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
|
@ -17,13 +17,14 @@
|
|||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-width: 320px;
|
||||
padding: 8px 10px;
|
||||
width: 320px;
|
||||
padding: 0px 10px;
|
||||
position: sticky;
|
||||
z-index: 99;
|
||||
background: #ffffff;
|
||||
top: 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
height: 50px;
|
||||
|
||||
.menuTitle {
|
||||
font-size: 16px;
|
||||
|
|
@ -129,7 +130,6 @@
|
|||
padding: 20px 15px;
|
||||
min-width: 320px;
|
||||
height: 100%;
|
||||
// max-height: 800px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Button, Divider, Tooltip, Space, Drawer, Tag } from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { Button, Divider, Tooltip, Space, Drawer } from 'antd';
|
||||
import { EyeOutlined, CopyOutlined, MenuFoldOutlined, MenuUnfoldOutlined, PlusOutlined, EditOutlined, FileOutlined } from '@ant-design/icons';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { WebComponentProps } from 'oak-frontend-base';
|
||||
|
|
|
|||
Loading…
Reference in New Issue