!1596 优化:<历史操作日志>打开文件时弹出历史窗口,可以在用户配置中设置开关

pull/1645/head
林三 3 years ago committed by ChenX
parent 2e43bf8696
commit 87647ec5e3

@ -0,0 +1,207 @@
import { Button, Checkbox, HTMLTable } from "@blueprintjs/core";
import { observable } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import { app } from "../../ApplicationServices/Application";
import { userConfig } from "../../Editor/UserConfig";
import { ModalState } from "../../UI/Components/Modal/ModalInterface";
import { HistoryProp } from "./OperLogsModal";
@observer
export class OpenHistoryBody extends React.Component<HistoryProp, {}>
{
@observable time = this.props.time / 1000;
timer: NodeJS.Timeout;
isOnHover = false;
componentDidMount()
{
let el = document.getElementById("HistoryFile");
el.onmousemove = () => { this.isOnHover = true; };
el.onmouseleave = () => { this.isOnHover = false; };
if (!this.timer)
this.timer = setInterval(() =>
{
if (!this.isOnHover)
this.time--;
if (this.time <= 0)
{
clearInterval(this.timer);
this.timer = undefined;
this.props.Toaster.clear();
}
}, 1000);
}
componentWillUnmount()
{
this.time = undefined;
this.timer = undefined;
}
render()
{
return (
<div>
<div id="HistoryFile">
<div><h3 style={{ margin: "0 0 5px 0" }}></h3></div>
<div style={{ overflow: "auto", maxHeight: "290px" }}>
<OpenHistoryList
fileId={this.props.fileId}
userName={this.props.userName}
files={this.props.files}
data={this.props.data}
OnClickOpenFile={this.props.OnClickOpenFile}
/>
</div>
</div>
{
<div style={{ float: "left", margin: "15px 0px 0px 0px" }}>
使OpenHistory
</div>
}
{
this.props.isOpenFile ? <div>
<Checkbox
style={{ margin: "15px 0px 0px 0px", float: "left", left: "18%" }}
label="下次不再显示"
onClick={() => userConfig.openHistoryList = !userConfig.openHistoryList}
/>
</div> : undefined
}
<div style={{ margin: "15px 0px 0px 0px", float: "right", right: "0%" }}>
{this.time}
</div>
</div>
);
}
}
@observer
export class OpenHistoryList extends React.Component<HistoryProp, {}>
{
_Index = 0;
_LastOper = "";
_Data = this.props.data;
@observable _CanOpen: boolean = false;
render()
{
return (
<HTMLTable bordered={true} interactive={true} style={{ lineHeight: 0.5, width: "100%", padding: "5px 5px 0px 5px", border: "1px solid #DBDCDD" }}>
<thead>
<tr>
<th></th>
<th style={{ width: "120px" }}></th>
<th>
<Checkbox
label="只显示能还原记录"
style={{ position: 'absolute', right: "10px", fontSize: "smaller", top: "15px" }}
checked={this._CanOpen}
onClick={(e) => { this._Index = 0, this._LastOper = "", this._CanOpen = !this._CanOpen; e.currentTarget.blur(); }}
/>
</th>
</tr>
</thead>
<tbody>
{
this._CanOpen ? this._CanOpenDataList() : this._AllDataList()
}
</tbody>
</HTMLTable>
);
}
_AllDataList = () =>
{
return (
<>
{
this._Data.map(d =>
{
if (d.oper_type_name === "打开")
if (this._LastOper === "打开")
return;
this._LastOper = d.oper_type_name;
return <tr>
<td>{d.oper_type_name}</td>
<td>{d.oper_user}</td>
<td>
{d.oper_date}
{this._ButtonEl(d.oper_type_name, d.oper_date, this.props.files[this._Index])}
</td>
</tr>;
})
}
</>
);
};
_CanOpenDataList = () =>
{
return (
<>
{
this._Data.map(d =>
{
if (d.oper_type_name !== "新建" && d.oper_type_name !== "修改") return;
if (!this._HasHistoryFile(d.oper_type_name, d.oper_date, this.props.files[this._Index])) return;
return <tr>
<td>{d.oper_type_name}</td>
<td>{d.oper_user}</td>
<td>
{d.oper_date}
<Button
className="button"
value={this._Index++}
text="打开"
onClick={(e) =>
{
this.props.OnClickOpenFile(this.props.userName, this.props.fileId, this.props.files, e.currentTarget.value);
if (this.props.isNotToaster)
app.Editor.ModalManage.m_PromisRes({ Status: ModalState.Ok });
app.Editor.ModalManage.Destory();
}}
/>
</td>
</tr>;
})
}
</>
);
};
_ButtonEl(name: string, oper_date: string, file_date: string): React.ReactElement<any>
{
if (!this._HasHistoryFile(name, oper_date, file_date)) return;
return (
<Button
className="button"
value={this._Index++}
text="打开"
onClick={(e) =>
{
this.props.OnClickOpenFile(this.props.userName, this.props.fileId, this.props.files, e.currentTarget.value);
if (this.props.isNotToaster)
app.Editor.ModalManage.m_PromisRes({ Status: ModalState.Ok });
app.Editor.ModalManage.Destory();
}}
/>
);
}
//验证是否有可以打开的历史文件
_HasHistoryFile(name: string, oper_date: string, file_date: string)
{
if (this._Index >= this.props.files.length) return;
if (name !== "修改" && name !== "新建") return;
let date = new Date(oper_date);
let operDate = date.getHours() * 60 * 60 + date.getMinutes() * 60 + date.getSeconds(); //时分秒转换number 好比较
let fileDateStr = file_date.split("-");
let fileDate = parseInt(fileDateStr[fileDateStr.length - 3]) * 60 * 60 + parseInt(fileDateStr[fileDateStr.length - 2]) * 60 + parseInt(fileDateStr[fileDateStr.length - 1]); //时分秒转换number 好比较
if (!(operDate - 3 <= fileDate && fileDate <= operDate + 3)) return false; //误差±3秒
else return true;
}
}

@ -1,9 +1,8 @@
import { Button, Checkbox, Classes, HTMLTable, Icon } from "@blueprintjs/core";
import { observable } from "mobx";
import { Button, Classes, Icon, IToaster } from "@blueprintjs/core";
import { observer } from "mobx-react";
import React from "react";
import { app } from "../../ApplicationServices/Application";
import { ModalState } from "../../UI/Components/Modal/ModalInterface";
import { OpenHistoryList } from "./OpenHistoryBody";
export interface OperInfo
{
@ -18,17 +17,16 @@ export interface HistoryProp
userName: string,
files: string[], //保存记录
data: OperInfo[],
isNotToaster?: boolean,
isOpenFile?: boolean,
time?: number,
Toaster?: IToaster;
OnClickOpenFile: (userName: string, fileId: string, files: string[], number: number) => void;
}
@observer
export class OperLogsModal extends React.Component<HistoryProp, {}>
{
_Index = 0;
_LastOper = "";
_Data = this.props.data.reverse();
@observable _CanOpen: boolean = false;
render()
{
return (
@ -48,122 +46,17 @@ export class OperLogsModal extends React.Component<HistoryProp, {}>
className={Classes.DIALOG_BODY}
style={{ width: 500, height: "100%", backgroundColor: "white", margin: 0, overflow: "auto", maxHeight: 400, minHeight: 200 }}
>
<HTMLTable bordered={true} interactive={true} style={{ lineHeight: 0.7, width: "100%", padding: 5, }}
>
<thead>
<tr>
<th></th>
<th style={{ width: "120px" }}></th>
<th>
<Checkbox
label="只显示能还原记录"
style={{ position: 'absolute', right: "0", fontSize: "smaller" }}
checked={this._CanOpen}
onClick={(e) => { this._Index = 0, this._LastOper = "", this._CanOpen = !this._CanOpen; e.currentTarget.blur(); }}
<OpenHistoryList
fileId={this.props.fileId}
userName={this.props.userName}
files={this.props.files}
data={this.props.data}
OnClickOpenFile={this.props.OnClickOpenFile}
isNotToaster={this.props.isNotToaster}
/>
</th>
</tr>
</thead>
<tbody>
{
this._CanOpen ? this._CanOpenDataList() : this._AllDataList()
}
</tbody>
</HTMLTable>
</div>
</div>
</div >
);
}
_AllDataList = () =>
{
return (
<>
{
this._Data.map(d =>
{
if (d.oper_type_name === "打开")
if (this._LastOper === "打开")
return;
this._LastOper = d.oper_type_name;
return <tr>
<td>{d.oper_type_name}</td>
<td>{d.oper_user}</td>
<td>
{d.oper_date}
{this._ButtonEl(d.oper_type_name, d.oper_date, this.props.files[this._Index])}
</td>
</tr>;
})
}
</>
);
};
_CanOpenDataList = () =>
{
return (
<>
{
this._Data.map(d =>
{
if (d.oper_type_name !== "新建" && d.oper_type_name !== "修改") return;
if (!this._HasHistoryFile(d.oper_type_name, d.oper_date, this.props.files[this._Index])) return;
return <tr>
<td>{d.oper_type_name}</td>
<td>{d.oper_user}</td>
<td>
{d.oper_date}
<Button
className="button"
value={this._Index++}
text="打开"
onClick={(e) =>
{
this.props.OnClickOpenFile(this.props.userName, this.props.fileId, this.props.files, e.currentTarget.value);
app.Editor.ModalManage.m_PromisRes({ Status: ModalState.Ok, Data: { index: parseInt(e.currentTarget.value) } });
app.Editor.ModalManage.Destory();
}}
/>
</td>
</tr>;
})
}
</>
);
};
_ButtonEl(name: string, oper_date: string, file_date: string): React.ReactElement<any>
{
if (!this._HasHistoryFile(name, oper_date, file_date)) return;
return (
<Button
className="button"
value={this._Index++}
text="打开"
onClick={(e) =>
{
this.props.OnClickOpenFile(this.props.userName, this.props.fileId, this.props.files, e.currentTarget.value);
app.Editor.ModalManage.m_PromisRes({ Status: ModalState.Ok, Data: { index: parseInt(e.currentTarget.value) } });
app.Editor.ModalManage.Destory();
}}
/>
);
}
//验证是否有可以打开的历史文件
_HasHistoryFile(name: string, oper_date: string, file_date: string)
{
if (this._Index >= this.props.files.length) return;
if (name !== "修改" && name !== "新建") return;
let date = new Date(oper_date);
let operDate = date.getHours() * 60 * 60 + date.getMinutes() * 60 + date.getSeconds(); //时分秒转换number 好比较
let fileDateStr = file_date.split("-");
let fileDate = parseInt(fileDateStr[fileDateStr.length - 3]) * 60 * 60 + parseInt(fileDateStr[fileDateStr.length - 2]) * 60 + parseInt(fileDateStr[fileDateStr.length - 1]); //时分秒转换number 好比较
if (!(operDate - 3 <= fileDate && fileDate <= operDate + 3)) return false; //误差±3秒
else return true;
}
}

@ -1,4 +1,4 @@
import { Button, Intent, Toaster } from "@blueprintjs/core";
import { Intent, Toaster } from "@blueprintjs/core";
import pako from "pako";
import React from "react";
import { app } from "../../ApplicationServices/Application";
@ -11,12 +11,21 @@ import { FileServer } from "../../DatabaseServices/FileServer";
import { Command } from "../../Editor/CommandMachine";
import { ModalState } from "../../UI/Components/Modal/ModalInterface";
import { AppToaster } from "../../UI/Components/Toaster";
import { OpenHistoryBody } from "./OpenHistoryBody";
import { OperLogsModal } from "./OperLogsModal";
const ADDSTRS = ["秒", "分", "时", "号", "月"];
export const HistoryToaster = Toaster.create({
className: "historyToaster",
canEscapeKeyClear: true,
position: "top-right"
});
export class OperLogs implements Command
{
constructor(private _IsOpenFile: boolean = true) { }
async exec()
{
let fid = (FileServer.GetInstance() as FileServer).m_CurFileId;
@ -35,75 +44,48 @@ export class OperLogs implements Command
let res = await fetch(`${FileHistoryUrl}?user=${userName}&fileId=${fid}`);
if (res.status !== 200) return;
let Res = await PostJson(SignUrl.operLogs, { obj_type: 1, obj_value: fid });
if (Res.err_code !== RequestStatus.Ok) return;
let files = await res.json() as string[];
files.sort((f1, f2) =>
{
return this.ParseTime(f2) - this.ParseTime(f1);
});
files.length = Math.min(files.length, 35);
let Res = await PostJson(SignUrl.operLogs, { obj_type: 1, obj_value: fid });
if (Res.err_code !== RequestStatus.Ok) return;
const data = Res.data.reverse();
if (!this._IsOpenFile)
{
app.Editor.ModalManage.RenderModal(OperLogsModal, {
fileId: fid,
userName: userName,
files: files,
data: Res.data,
data: data,
OnClickOpenFile: this.OnClickOpenFile,
isNotToaster: true,
});
let Rm = await app.Editor.ModalManage.Wait();
if (Rm.Status !== ModalState.Ok) return;
let fileNames: string[] = files.map((f, i) =>
{
let strs = f.split("-");
let str = "";
for (let i = 0; i < ADDSTRS.length; i++)
{
str = strs[strs.length - 1 - i].padStart(2, "0") + ADDSTRS[i] + str;
}
str = "文件名:" + (strs.splice(0, strs.length - ADDSTRS.length).join("-").padEnd(15, "-")) + str;
return str;
});
const HistoryToaster = Toaster.create({
className: "historyToaster",
canEscapeKeyClear: false,
});
let time = 100000;
let inputEls: React.RefObject<Button>[] = [];
let time = files.length === 0 ? 5000 : 10000; //窗口持续时间
if (HistoryToaster)
HistoryToaster.clear();
HistoryToaster.show({
message: <div id="HistoryFile">
<div><h2 style={{ margin: 0, marginBottom: 10 }}></h2></div>
<div style={{ maxHeight: 280, overflow: "auto", paddingRight: 10, }}>
{
fileNames.map((fileName, i) =>
{
let ip = React.createRef<Button>();
inputEls.push(ip);
return <div style={{ marginTop: 5 }}>
<Button
style={{ backgroundColor: Rm.Data.index === i ? "bisque" : "white" }}
text={fileName}
ref={inputEls[i]}
onClick={(e) =>
{
time += 10000;
this.OnClickChangeColor(inputEls, i);
this.OnClickOpenFile(userName, fid, files, i);
e.currentTarget.blur();
}}
/>
<span style={{ color: "cornflowerblue", fontSize: 14, marginLeft: 10, fontFamily: "monospace", display: i === 0 ? "" : "none" }}>NEW</span>
</div>;
})
}
</div>
</div>,
timeout: time,
message:
<OpenHistoryBody
fileId={fid}
userName={userName}
files={files}
data={data}
time={time}
isOpenFile={this._IsOpenFile}
Toaster={HistoryToaster}
OnClickOpenFile={this.OnClickOpenFile}
/>,
timeout: time + 500,
intent: Intent.NONE,
});
}
@ -121,14 +103,6 @@ export class OperLogs implements Command
app.Database.hm.ReadFile(hisf);
}
private OnClickChangeColor(inputEls: React.RefObject<Button>[], number: number)
{
for (let i = 0; i < inputEls.length; i++)
i === number ?
inputEls[i].current.buttonRef.style.backgroundColor = "bisque" :
inputEls[i].current.buttonRef.style.backgroundColor = "white";
}
private ParseTime(f1: string)
{
let time = f1.split("-");

@ -53,7 +53,7 @@ export enum CommandNames
New = "NEW",
Open = "OPEN",
OpenHistory = "OPENHISTORY",
OperLogs = "OPERLOGS",
OpenHistory2 = "OPENHISTORY2",
CustomUCS = "UCS",
Copy = "COPY",
SuperCopy = "SUPERCOPY",

@ -1,4 +1,5 @@
import { Intent } from "@blueprintjs/core";
import { HistoryToaster, OperLogs } from "../Add-on/File/OperLog";
import { app } from "../ApplicationServices/Application";
import { FileSystem } from "../Common/FileSystem";
import { FileUrls } from "../Common/HostUrl";
@ -9,6 +10,7 @@ import { Sleep } from "../Common/Sleep";
import { StoreageKeys } from "../Common/StoreageKeys";
import { GetIndexDBID } from "../Common/Utils";
import { TempEditor } from "../Editor/TempEditor";
import { userConfig } from "../Editor/UserConfig";
import { IndexedDbStore, StoreName } from "../IndexedDb/IndexedDbStore";
import { AppConfirm } from "../UI/Components/Common/Confirm";
import { IDirectoryProps } from "../UI/Components/SourceManage/CommonPanel";
@ -82,6 +84,8 @@ export class FileServer extends Singleton
this.CurrentFileMd5 = undefined;
this.currentFileInfo.name = "未命名";
this.currentFileInfo.dir = undefined;
HistoryToaster.clear();
app.CreateDocument();
return true;
}
@ -94,6 +98,7 @@ export class FileServer extends Singleton
}
async OpenFile(fid: string, file?: string): Promise<boolean>
{
HistoryToaster.clear();
AppToaster.clear();
AppToaster.show({
@ -159,6 +164,14 @@ export class FileServer extends Singleton
await Sleep(5000);
location.reload();//重启WebCAD,避免因为错误导致的程序状态错误
}
else
{
if (userConfig.openHistoryList)
{
let operLogs = new OperLogs();
operLogs.exec();
}
}
return status;
}

@ -342,8 +342,8 @@ export function registerCommand()
commandMachine.RegisterCommand(CommandNames.Open, new Open());
commandMachine.RegisterCommand("reopen", new ReOpen());
commandMachine.RegisterCommand(CommandNames.OpenHistory, new OperLogs());
commandMachine.RegisterCommand(CommandNames.OperLogs, new Command_OpenHistory());
commandMachine.RegisterCommand(CommandNames.OpenHistory, new OperLogs(false));
commandMachine.RegisterCommand(CommandNames.OpenHistory2, new Command_OpenHistory());
commandMachine.RegisterCommand(CommandNames.Arc, new DrawArc());

@ -33,7 +33,7 @@ export interface ICursorConfig extends IBaseOption
export class UserConfig implements IConfigStore
{
private readonly _version = 21;
private readonly _version = 22;
_renderType: RenderType = RenderType.Wireframe;
@observable maxSize: IMaxSizeProps = {
isShow: false,
@ -118,6 +118,7 @@ export class UserConfig implements IConfigStore
textHight: 30,
};
showShareModule: boolean = false;
@observable openHistoryList = true;
constructor()
{
this.Init();
@ -192,6 +193,7 @@ export class UserConfig implements IConfigStore
this.smalliconmode = false;
this.dimensions = true;
this.switchBackground = false;
this.openHistoryList = true;
this.synchronousEnable = false;
this.isLargeIcon = false;
this.isModifyMaterial = false;
@ -227,6 +229,7 @@ export class UserConfig implements IConfigStore
smalliconmode: this.smalliconmode,
dimensions: this.dimensions,
switchBackground: this.switchBackground,
openHistoryList: this.openHistoryList,
synchronousEnable: this.synchronousEnable,
isLargeIcon: this.isLargeIcon,
isModifyMaterial: this.isModifyMaterial,
@ -327,6 +330,10 @@ export class UserConfig implements IConfigStore
{
this.isModifyMaterial = config.option.isModifyMaterial;
}
if (config.option.version > 21)
{
this.openHistoryList = config.option.openHistoryList;
}
}
}

@ -45,6 +45,7 @@ export class ConfigStore extends Singleton
maxViewHeight: userConfig.viewSize.maxViewHeight,
zoomSpeed: userConfig.viewSize.zoomSpeed,
};
oldOpenHistoryList: boolean = userConfig.openHistoryList;
@observable checkSealing = false;
@observable sealingArr: [string, string][] = Array.from({ length: 10 }, () => ["", ""]);
@ -64,6 +65,9 @@ export class ConfigStore extends Singleton
userConfig.viewSize.maxViewHeight = this.oldViewSize.maxViewHeight;
userConfig.viewSize.zoomSpeed = this.oldViewSize.zoomSpeed;
//显示历史记录恢复上一次保存
userConfig.openHistoryList = this.oldOpenHistoryList;
app.Editor.ModalManage.Destory();
app.Viewer.Renderer.setClearColor(new Color(app.Viewer.isLayout ? this.oldLyBg : this.oldBgcolor), 1);
@ -84,6 +88,9 @@ export class ConfigStore extends Singleton
this.oldViewSize.maxViewHeight = userConfig.viewSize.maxViewHeight;
this.oldViewSize.zoomSpeed = userConfig.viewSize.zoomSpeed;
//更新打开文件时是否显示历史记录
this.oldOpenHistoryList = userConfig.openHistoryList;
localStorage.setItem(StoreageKeys.kjlConfig, JSON.stringify(toJS(userConfig.kjlConfig)));
app.Editor.ModalManage.Destory();
app.Viewer.AAType = userConfig.SystemConfig.aaType;

@ -1,12 +1,12 @@
import * as React from 'react';
import { Card, Label, Classes, RadioGroup, Radio, Checkbox, Tooltip } from "@blueprintjs/core";
import { Card, Checkbox, Classes, Label, Radio, RadioGroup, Tooltip } from "@blueprintjs/core";
import { observer } from "mobx-react";
import { userConfig, ISystemConfig, ICursorConfig } from "../../../../Editor/UserConfig";
import { ToasterInput } from '../../Toaster';
import { IUiOption } from '../../../Store/BoardInterface';
import { DataAdapter } from '../../../../Common/DataAdapter';
import * as React from 'react';
import { CheckObjectType } from '../../../../Common/CheckoutVaildValue';
import { DataAdapter } from '../../../../Common/DataAdapter';
import { safeEval } from '../../../../Common/eval';
import { ICursorConfig, ISystemConfig, userConfig } from "../../../../Editor/UserConfig";
import { IUiOption } from '../../../Store/BoardInterface';
import { ToasterInput } from '../../Toaster';
@observer
export class SystemConfigPanel extends React.Component
@ -88,6 +88,14 @@ export class SystemConfigPanel extends React.Component
<Radio inline label="Ctrl" value="ctrl" />
<Radio inline label="Shift" value="shift" />
</RadioGroup>
<Label className={Classes.INLINE}>
<Checkbox
label="打开文件时显示历史记录"
inline
checked={userConfig.openHistoryList}
onChange={() => userConfig.openHistoryList = !userConfig.openHistoryList}
/>
</Label>
<Tooltip content="自动备份不等于保存,如果软件意外的崩溃或者断电,你可以在登录后打开备份的图纸!">
<Label className={Classes.INLINE}>
<Checkbox

@ -1000,16 +1000,18 @@ img {
#OperLogsModal{
.bp3-running-text table th, table.bp3-html-table th,
.bp3-running-text table td, table.bp3-html-table td{
vertical-align : middle;
vertical-align: middle;
padding : 10px;
height : 40px;
height : 35px;
position : relative;
}
.button{
right : 10%;
top : 15%;
position : absolute;
padding : 0px 5px 0px 5px;
position: absolute;
margin-top: -2px;
}
.bp3-dialog-body{
@ -1018,26 +1020,43 @@ img {
}
#HistoryFile{
min-height : 100px;
max-height : 300px;
min-height: 100px;
max-height: 300px;
width : 480px;
.bp3-button-group.bp3-minimal{
float: left;
}
.bp3-running-text table th, table.bp3-html-table th,
.bp3-running-text table td, table.bp3-html-table td{
vertical-align: middle;
padding : 10px;
height : 30px;
position : relative;
}
.button{
right : 10%;
top : 15%;
padding : 0px 5px 0px 5px;
position : absolute;
margin-top: -2px;
min-height: 26px;
}
}
.historyToaster{
top : 10%;
left: 65%;
top: 10%;
.bp3-button-group.bp3-minimal{
position : absolute;
position: absolute;
right : 0;
}
}
.bp3-toast-container.bp3-toast-container-top.historyToaster{
top : 10%;
left: 65%;
top: 10%;
}
//-----文件编辑记录结束--------

Loading…
Cancel
Save