This commit is contained in:
2024-03-01 20:28:14 +08:00
commit 076c21dc36
491 changed files with 84482 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, Checkbox } from 'antd';
import Editor from 'common/tui-editor/dist/tui-editor-Editor-all.min.js';
require('common/tui-editor/dist/tui-editor.min.css'); // editor ui
require('common/tui-editor/dist/tui-editor-contents.min.css'); // editor content
class WikiEditor extends Component {
constructor(props) {
super(props);
}
static propTypes = {
isConflict: PropTypes.bool,
onUpload: PropTypes.func,
onCancel: PropTypes.func,
notice: PropTypes.bool,
onEmailNotice: PropTypes.func,
desc: PropTypes.string
};
componentDidMount() {
this.editor = new Editor({
el: document.querySelector('#desc'),
initialEditType: 'wysiwyg',
height: '500px',
initialValue: this.props.desc
});
}
onUpload = () => {
let desc = this.editor.getHtml();
let markdown = this.editor.getMarkdown();
this.props.onUpload(desc, markdown);
};
render() {
const { isConflict, onCancel, notice, onEmailNotice } = this.props;
return (
<div>
<div
id="desc"
className="wiki-editor"
style={{ display: !isConflict ? 'block' : 'none' }}
/>
<div className="wiki-title wiki-up">
<Button
icon="upload"
type="primary"
className="upload-btn"
disabled={isConflict}
onClick={this.onUpload}
>
更新
</Button>
<Button onClick={onCancel} className="upload-btn">
取消
</Button>
<Checkbox checked={notice} onChange={onEmailNotice}>
通知相关人员
</Checkbox>
</div>
</div>
);
}
}
export default WikiEditor;

View File

@@ -0,0 +1,41 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from 'antd';
import { Link } from 'react-router-dom';
const WikiView = props => {
const { editorEable, onEditor, uid, username, editorTime, desc } = props;
return (
<div className="wiki-view-content">
<div className="wiki-title">
<Button icon="edit" onClick={onEditor} disabled={!editorEable}>
编辑
</Button>
{username && (
<div className="wiki-user">
{' '}
<Link className="user-name" to={`/user/profile/${uid || 11}`}>
{username}
</Link>{' '}
修改于 {editorTime}
</div>
)}
</div>
<div
className="tui-editor-contents"
dangerouslySetInnerHTML={{ __html: desc }}
/>
</div>
);
};
WikiView.propTypes = {
editorEable: PropTypes.bool,
onEditor: PropTypes.func,
uid: PropTypes.number,
username: PropTypes.string,
editorTime: PropTypes.string,
desc: PropTypes.string
};
export default WikiView;

View File

@@ -0,0 +1,255 @@
import React, { Component } from 'react';
import { message } from 'antd';
import { connect } from 'react-redux';
import axios from 'axios';
import PropTypes from 'prop-types';
import './index.scss';
import { timeago } from '../../../common/utils';
import { Link } from 'react-router-dom';
import WikiView from './View.js';
import WikiEditor from './Editor.js';
@connect(
state => {
return {
projectMsg: state.project.currProject
};
},
{}
)
class WikiPage extends Component {
constructor(props) {
super(props);
this.state = {
isEditor: false,
isUpload: true,
desc: '',
markdown: '',
notice: props.projectMsg.switch_notice,
status: 'INIT',
editUid: '',
editName: '',
curdata: null
};
}
static propTypes = {
match: PropTypes.object,
projectMsg: PropTypes.object
};
async componentDidMount() {
const currProjectId = this.props.match.params.id;
await this.handleData({ project_id: currProjectId });
this.handleConflict();
}
componentWillUnmount() {
// willUnmount
try {
if (this.state.status === 'CLOSE') {
this.WebSocket.send('end');
this.WebSocket.close();
}
} catch (e) {
return null;
}
}
// 结束编辑websocket
endWebSocket = () => {
try {
if (this.state.status === 'CLOSE') {
const sendEnd = () => {
this.WebSocket.send('end');
};
this.handleWebsocketAccidentClose(sendEnd);
}
} catch (e) {
return null;
}
};
// 处理多人编辑冲突问题
handleConflict = () => {
// console.log(location)
let domain = location.hostname + (location.port !== '' ? ':' + location.port : '');
let s;
//因后端 node 仅支持 ws 暂不支持 wss
let wsProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
s = new WebSocket(
wsProtocol +
'://' +
domain +
'/api/ws_plugin/wiki_desc/solve_conflict?id=' +
this.props.match.params.id
);
s.onopen = () => {
this.WebSocket = s;
s.send('start');
};
s.onmessage = e => {
let result = JSON.parse(e.data);
if (result.errno === 0) {
// 更新
if (result.data) {
this.setState({
// curdata: result.data,
desc: result.data.desc,
username: result.data.username,
uid: result.data.uid,
editorTime: timeago(result.data.up_time)
});
}
// 新建
this.setState({
isEditor: !this.state.isEditor,
status: 'CLOSE'
});
} else {
this.setState({
editUid: result.data.uid,
editName: result.data.username,
status: 'EDITOR'
});
}
};
s.onerror = () => {
this.setState({
status: 'CLOSE'
});
console.warn('websocket 连接失败,将导致多人编辑同一个接口冲突。');
};
};
// 点击编辑按钮 发送 websocket 获取数据
onEditor = () => {
// this.WebSocket.send('editor');
const sendEditor = () => {
this.WebSocket.send('editor');
};
this.handleWebsocketAccidentClose(sendEditor, status => {
// 如果websocket 启动不成功用户依旧可以对wiki 进行编辑
if (!status) {
this.setState({
isEditor: !this.state.isEditor
});
}
});
};
// 处理websocket 意外断开问题
handleWebsocketAccidentClose = (fn, callback) => {
// websocket 是否启动
if (this.WebSocket) {
// websocket 断开
if (this.WebSocket.readyState !== 1) {
message.error('websocket 链接失败,请重新刷新页面');
} else {
fn();
}
callback(true);
} else {
callback(false);
}
};
// 获取数据
handleData = async params => {
let result = await axios.get('/api/plugin/wiki_desc/get', { params });
if (result.data.errcode === 0) {
const data = result.data.data;
if (data) {
this.setState({
desc: data.desc,
markdown: data.markdown,
username: data.username,
uid: data.uid,
editorTime: timeago(data.up_time)
});
}
} else {
message.error(`请求数据失败: ${result.data.errmsg}`);
}
};
// 数据上传
onUpload = async (desc, markdown) => {
const currProjectId = this.props.match.params.id;
let option = {
project_id: currProjectId,
desc,
markdown,
email_notice: this.state.notice
};
let result = await axios.post('/api/plugin/wiki_desc/up', option);
if (result.data.errcode === 0) {
await this.handleData({ project_id: currProjectId });
this.setState({ isEditor: false });
} else {
message.error(`更新失败: ${result.data.errmsg}`);
}
this.endWebSocket();
// this.WebSocket.send('end');
};
// 取消编辑
onCancel = () => {
this.setState({ isEditor: false });
this.endWebSocket();
};
// 邮件通知
onEmailNotice = e => {
this.setState({
notice: e.target.checked
});
};
render() {
const { isEditor, username, editorTime, notice, uid, status, editUid, editName } = this.state;
const editorEable =
this.props.projectMsg.role === 'admin' ||
this.props.projectMsg.role === 'owner' ||
this.props.projectMsg.role === 'dev';
const isConflict = status === 'EDITOR';
return (
<div className="g-row">
<div className="m-panel wiki-content">
<div className="wiki-content">
{isConflict && (
<div className="wiki-conflict">
<Link to={`/user/profile/${editUid || uid}`}>
<b>{editName || username}</b>
</Link>
<span>正在编辑该wiki请稍后再试...</span>
</div>
)}
</div>
{!isEditor ? (
<WikiView
editorEable={editorEable}
onEditor={this.onEditor}
uid={uid}
username={username}
editorTime={editorTime}
desc={this.state.desc}
/>
) : (
<WikiEditor
isConflict={isConflict}
onUpload={this.onUpload}
onCancel={this.onCancel}
notice={notice}
onEmailNotice={this.onEmailNotice}
desc={this.state.desc}
/>
)}
</div>
</div>
);
}
}
export default WikiPage;

View File

@@ -0,0 +1,27 @@
.wiki-content {
.wiki-user {
padding-top: 8px;
}
.wiki-editor {
padding-top: 16px;
}
.upload-btn {
margin-right: 16px;
}
.wiki-conflict {
text-align: center;
font-size: 14px;
padding-top: 10px;
}
.wiki-up {
text-align: right;
padding-top: 16px;
}
.wiki-title {
padding-bottom: 16px;
}
}