mirror of https://gitee.com/cf-fz/WebCAD.git
!2306 功能:增加企业私有模型展示与管理
parent
a11d62dfba
commit
cdc238da32
@ -0,0 +1,587 @@
|
||||
import { Button, Checkbox, Classes, ContextMenu, Dialog, ITreeNode, Icon, InputGroup, Intent, Menu, MenuItem, Navbar, NavbarGroup, NavbarHeading, NonIdealState, Popover, Position, Spinner, Tree, TreeNodeInfo } from '@blueprintjs/core';
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import { PrivateModule } from '../../../../Common/HostUrl';
|
||||
import { IResponseData, PostJson } from '../../../../Common/Request';
|
||||
import { AppToaster } from '../../Toaster';
|
||||
import { ModuleType, PrivateDirs, PrivateModuleItem } from './ResourceInterfaces';
|
||||
import { ResourcePanelType } from './ResourcePanel';
|
||||
import { GetCssObj, R_Pagination, RenderDetailImg } from './Resource_ResourceList';
|
||||
import ResourceStore from './RsourceStore';
|
||||
|
||||
@observer
|
||||
export default class Resoure_Enterprise extends Component<{}, {}>
|
||||
{
|
||||
resourceStore: ResourceStore = ResourceStore.GetInstance();
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<div className='enterprise'>
|
||||
{this.resourceStore.enterpriseType === ResourcePanelType.enterpriseFolder && <PrivateDirTree />}
|
||||
{this.resourceStore.enterpriseType === ResourcePanelType.enterpriseModule && <PrivateModuleList />}
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class EnterpriseInputGroup extends React.Component<{ defaultValue: string, placeholder: string, onSuccess: (value: string) => void, onCancel: () => void; className?: string, }, {}>
|
||||
{
|
||||
private onKeydown = async (e: React.KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Enter')
|
||||
{
|
||||
let { value } = e.target as HTMLInputElement;
|
||||
await this.props.onSuccess(value);
|
||||
}
|
||||
else if (e.key === "Escape")
|
||||
{
|
||||
this.props.onCancel();
|
||||
}
|
||||
};
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<>
|
||||
<InputGroup
|
||||
className={this.props.className}
|
||||
defaultValue={this.props.defaultValue}
|
||||
autoFocus
|
||||
placeholder={this.props.placeholder}
|
||||
onClick={(e) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onKeyDown={(e) => this.onKeydown(e)}
|
||||
onKeyUp={(e) => { e.currentTarget.value = e.currentTarget.value.replaceAll(' ', ''); }} //过滤空格
|
||||
onBlur={(e) =>
|
||||
{
|
||||
let { value } = e.target;
|
||||
if (value)
|
||||
this.props.onSuccess(value);
|
||||
else
|
||||
this.props.onCancel();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@observer
|
||||
class PrivateDirTree extends Component<{ handMoveModule?: (id: string) => void; }, { nodes: ITreeNode[]; }> {
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
this.state = {
|
||||
nodes: []
|
||||
};
|
||||
}
|
||||
|
||||
@observable currentInfo = { id: "", name: "" };
|
||||
|
||||
initNodes = async () =>
|
||||
{
|
||||
//初始化目录
|
||||
this.setState({ nodes: this.ParseDirNodes(this.resourceStore.privateDirs, "0", true) });
|
||||
};
|
||||
async componentWillMount()
|
||||
{
|
||||
if (this.resourceStore.privateDirs.length)
|
||||
this.initNodes();
|
||||
else
|
||||
{
|
||||
await this.resourceStore.GetPrivateDir();
|
||||
this.initNodes();
|
||||
}
|
||||
}
|
||||
resourceStore: ResourceStore = ResourceStore.GetInstance();
|
||||
SwitchFolder = (nodeData: ITreeNode) =>
|
||||
{
|
||||
const id = String(nodeData.id);
|
||||
const label = String(nodeData.label);
|
||||
if (this.props.handMoveModule)
|
||||
{
|
||||
this.props.handMoveModule(id);
|
||||
return;
|
||||
}
|
||||
this.resourceStore.privateModule.params.curr_page = 1;
|
||||
this.resourceStore.privateModule.params.dir_id = id;
|
||||
this.resourceStore.enterpriseType = ResourcePanelType.enterpriseModule;
|
||||
this.resourceStore.currentfolder = { dir_id: id, dir_name: label };
|
||||
this.resourceStore.GetPrivateModule();
|
||||
};
|
||||
|
||||
ParseDirNodes = (dirs: PrivateDirs[], defaultId: string, isRoot = true, renameId?: string, parent_id?: string) =>
|
||||
{
|
||||
let newNodes: ITreeNode[] = [];
|
||||
if (isRoot)
|
||||
{
|
||||
newNodes = [
|
||||
{
|
||||
id: defaultId,
|
||||
label: "未分类",
|
||||
icon: "folder-close",
|
||||
hasCaret: false,
|
||||
}
|
||||
];
|
||||
}
|
||||
for (let i = 0; i < dirs.length; i += 1)
|
||||
{
|
||||
const dir = dirs[i];
|
||||
let tempCteateId = "";
|
||||
if (parent_id && parent_id !== "0" && dir.dir_id === parent_id)//子节点新建文件夹
|
||||
{
|
||||
tempCteateId = String(new Date().getTime());
|
||||
dir.childs.push({ dir_id: tempCteateId, dir_name: "", childs: [] });
|
||||
}
|
||||
|
||||
let node: ITreeNode = {
|
||||
id: dir.dir_id,
|
||||
label: (renameId || parent_id) && (dir.dir_id === renameId || !dir.dir_name) ?//重命名和子节点新建文件夹
|
||||
<EnterpriseInputGroup
|
||||
className={"dirRename"}
|
||||
defaultValue={dir.dir_name}
|
||||
placeholder='请输入文件夹名称'
|
||||
onSuccess={(value) =>
|
||||
{
|
||||
this.HandlePrivateDirName(value);
|
||||
}}
|
||||
onCancel={() =>
|
||||
{
|
||||
this.currentInfo = { id: "", name: "" };
|
||||
if (parent_id && !dir.dir_name)
|
||||
dirs.splice(i, 1);
|
||||
this.initNodes();
|
||||
}}
|
||||
/>
|
||||
: dir.dir_name,
|
||||
icon: "folder-close",
|
||||
hasCaret: dir.childs.length > 0,
|
||||
isExpanded: dir.childs.length > 0,
|
||||
childNodes: this.ParseDirNodes(dir.childs, defaultId, false, renameId, parent_id),
|
||||
};
|
||||
newNodes.push(node);
|
||||
}
|
||||
if (isRoot && parent_id === "0")//根节点新建文件夹
|
||||
{
|
||||
let createNode: ITreeNode = {
|
||||
id: String(new Date().getTime()),
|
||||
label:
|
||||
<EnterpriseInputGroup
|
||||
className={"dirRename"}
|
||||
defaultValue={""}
|
||||
placeholder='请输入文件夹名称'
|
||||
onSuccess={(value) =>
|
||||
{
|
||||
this.HandlePrivateDirName(value);
|
||||
}}
|
||||
onCancel={() =>
|
||||
{
|
||||
this.currentInfo = { id: "", name: "" };
|
||||
this.initNodes();
|
||||
}}
|
||||
/>
|
||||
,
|
||||
icon: "folder-close",
|
||||
};
|
||||
newNodes.push(createNode);
|
||||
}
|
||||
return newNodes;
|
||||
};
|
||||
|
||||
HandleNodeCollapse = (nodeData: ITreeNode, isCollapse: boolean) =>
|
||||
{
|
||||
nodeData.isExpanded = !isCollapse;
|
||||
this.setState(this.state);
|
||||
};
|
||||
|
||||
RemoveFolder = async (id: string) =>
|
||||
{
|
||||
await PostJson(PrivateModule.DeleteModuleDirs, { dir_id: id });
|
||||
await this.resourceStore.GetPrivateDir();
|
||||
this.initNodes();
|
||||
};
|
||||
|
||||
HandlePrivateDirName = async (newName: string) =>
|
||||
{
|
||||
newName = newName.trim();
|
||||
if (newName === '' || newName === this.currentInfo?.name || !this.currentInfo.id)
|
||||
{
|
||||
this.initNodes();
|
||||
return;
|
||||
}
|
||||
|
||||
let res: IResponseData;
|
||||
if (!this.currentInfo.name)
|
||||
res = await PostJson(PrivateModule.CreateModuleDirs, { dir_name: newName, parent_dir_id: this.currentInfo.id });
|
||||
else
|
||||
res = await PostJson(PrivateModule.UpdateModuleDirs, { dir_id: this.currentInfo.id, dir_name: newName });
|
||||
if (res.err_msg === 'success')
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "创建成功",
|
||||
timeout: 2000,
|
||||
intent: Intent.SUCCESS,
|
||||
}, "Resoure_Enterprise");
|
||||
}
|
||||
await this.resourceStore.GetPrivateDir();
|
||||
this.initNodes();
|
||||
this.currentInfo = { id: "", name: "" };
|
||||
};
|
||||
|
||||
ShowContextMenu = async (curNodeData: TreeNodeInfo, nodePath: number[], e: React.MouseEvent<HTMLElement> | MouseEvent) =>
|
||||
{
|
||||
e.preventDefault();
|
||||
ContextMenu.show(
|
||||
<Menu>
|
||||
<MenuItem icon='folder-new' disabled={curNodeData.id === "0"} text='新建目录' onClick={() =>
|
||||
{
|
||||
if (nodePath.length >= 3)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "只允许创建3层子目录",
|
||||
timeout: 3000,
|
||||
intent: Intent.WARNING
|
||||
}, "Resoure_Enterprise");
|
||||
return;
|
||||
}
|
||||
this.currentInfo = { id: curNodeData.id as string, name: "" };
|
||||
this.setState({ nodes: this.ParseDirNodes(this.resourceStore.privateDirs, "0", true, undefined, curNodeData.id as string) });
|
||||
}}
|
||||
/>
|
||||
<MenuItem icon='select' disabled={curNodeData.id === "0"} text='重命名' onClick={() =>
|
||||
{
|
||||
this.currentInfo = { id: curNodeData.id as string, name: curNodeData.label as string };
|
||||
this.setState({ nodes: this.ParseDirNodes(this.resourceStore.privateDirs, "0", true, curNodeData.id as string) });
|
||||
}}
|
||||
/>
|
||||
<MenuItem icon="trash" disabled={curNodeData.id === "0"} text="删除目录" onClick={() =>
|
||||
{
|
||||
this.RemoveFolder(curNodeData.id as string);
|
||||
}}
|
||||
/>
|
||||
</Menu>,
|
||||
{ left: e.clientX, top: e.clientY },
|
||||
);
|
||||
};
|
||||
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<>
|
||||
{
|
||||
!this.props.handMoveModule && <Navbar style={{ marginTop: "5px" }} >
|
||||
<NavbarGroup className="navbarGroup">
|
||||
<NavbarHeading >
|
||||
企业库文件目录
|
||||
</NavbarHeading>
|
||||
<div>
|
||||
<Button
|
||||
title='新建文件夹'
|
||||
minimal
|
||||
icon='folder-new'
|
||||
onClick={() =>
|
||||
{
|
||||
this.currentInfo = {
|
||||
id: "0",
|
||||
name: ""
|
||||
};
|
||||
this.setState({ nodes: this.ParseDirNodes(this.resourceStore.privateDirs, "0", true, undefined, "0") });
|
||||
}} />
|
||||
</div>
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
}
|
||||
<div style={{ maxHeight: this.props.handMoveModule ? "50vh" : "unset", overflow: "auto" }}>
|
||||
<Tree
|
||||
contents={this.state.nodes}
|
||||
onNodeClick={this.SwitchFolder}
|
||||
onNodeCollapse={(node) => this.HandleNodeCollapse(node, true)}
|
||||
onNodeExpand={(node) => this.HandleNodeCollapse(node, false)}
|
||||
onNodeContextMenu={!this.props.handMoveModule && this.ShowContextMenu}
|
||||
className={Classes.ELEVATION_0}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class PrivateModuleList extends Component<{}, {}>
|
||||
{
|
||||
resourceStore: ResourceStore = ResourceStore.GetInstance();
|
||||
|
||||
@observable isBatch: boolean = false;
|
||||
@observable moveModalVisible: boolean = false;
|
||||
@observable currentInfo: PrivateModuleItem = undefined;
|
||||
@observable selectedIds: Set<string> = new Set();
|
||||
|
||||
ShowModuleContextMenu = async (e: React.MouseEvent<HTMLDivElement>, module: PrivateModuleItem) =>
|
||||
{
|
||||
e.preventDefault();
|
||||
ContextMenu.show(
|
||||
<Menu>
|
||||
{ //批量选择时不能重命名
|
||||
!this.isBatch && <MenuItem icon='select' text='重命名' onClick={() =>
|
||||
{
|
||||
this.currentInfo = module;
|
||||
}}
|
||||
/>
|
||||
}
|
||||
<MenuItem icon="move" text="移动" onClick={() =>
|
||||
{
|
||||
this.moveModalVisible = true;
|
||||
}}
|
||||
/>
|
||||
</Menu>,
|
||||
{ left: e.clientX, top: e.clientY },
|
||||
() => { if (!this.isBatch && !this.moveModalVisible) this.selectedIds.clear(); }
|
||||
);
|
||||
};
|
||||
|
||||
HandMoveModule = async (id: string) =>
|
||||
{
|
||||
if (id === this.resourceStore.privateModule.params.dir_id)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "模型已处于当前目录下",
|
||||
timeout: 2000,
|
||||
intent: Intent.WARNING,
|
||||
}, "Resoure_Enterprise");
|
||||
return;
|
||||
}
|
||||
const res: IResponseData = await PostJson(PrivateModule.UpdateModule, { module_ids: Array.from(this.selectedIds), dir_id: id });
|
||||
if (res.err_msg === 'success')
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "移动成功",
|
||||
timeout: 2000,
|
||||
intent: Intent.SUCCESS,
|
||||
}, "Resoure_Enterprise");
|
||||
this.resourceStore.GetPrivateModule();
|
||||
}
|
||||
this.moveModalVisible = false;
|
||||
};
|
||||
|
||||
HandleRenameModule = async (value: string) =>
|
||||
{
|
||||
if (!value || this.currentInfo.name === value)
|
||||
{
|
||||
this.currentInfo = undefined;
|
||||
return;
|
||||
}
|
||||
const res: IResponseData = await PostJson(PrivateModule.UpdateModule, { module_ids: [this.currentInfo.module_id], name: value, dir_id: this.currentInfo.dir_id });
|
||||
if (res.err_msg === 'success')
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "重命名成功",
|
||||
timeout: 2000,
|
||||
intent: Intent.SUCCESS,
|
||||
}, "Resoure_Enterprise");
|
||||
this.resourceStore.GetPrivateModule();
|
||||
}
|
||||
this.currentInfo = undefined;
|
||||
};
|
||||
|
||||
HandleCheckChange = (item: PrivateModuleItem) =>
|
||||
{
|
||||
const selectIds = this.selectedIds;
|
||||
if (selectIds.has(item.module_id))
|
||||
selectIds.delete(item.module_id);
|
||||
else
|
||||
selectIds.add(item.module_id);
|
||||
};
|
||||
|
||||
HandlePageChange = async (page: number) =>
|
||||
{
|
||||
if (isNaN(page)) return;
|
||||
if (page < 1 || page > this.resourceStore.privateModule.total) return;
|
||||
this.resourceStore.resourceLoading = true;
|
||||
this.resourceStore.privateModule.params.curr_page = page;
|
||||
this.resourceStore.GetPrivateModule();
|
||||
this.resourceStore.resourceLoading = false;
|
||||
};
|
||||
|
||||
render(): React.ReactNode
|
||||
{
|
||||
const selectIds = this.selectedIds;
|
||||
const { privateModule } = this.resourceStore;
|
||||
|
||||
return (
|
||||
<div className="moduleList" >
|
||||
<div className='header'>
|
||||
<span>
|
||||
<Button minimal small icon="arrow-left"
|
||||
onClick={() =>
|
||||
{
|
||||
this.resourceStore.enterpriseType = ResourcePanelType.enterpriseFolder;
|
||||
this.isBatch = false;
|
||||
this.selectedIds.clear();
|
||||
}}
|
||||
/>
|
||||
{this.resourceStore.currentfolder.dir_name}
|
||||
</span>
|
||||
</div>
|
||||
<div className={"moduleControl"}>
|
||||
<div className='delete'>
|
||||
<Button icon="align-justify" title='批量选择' minimal
|
||||
onClick={() =>
|
||||
{
|
||||
this.isBatch = !this.isBatch;
|
||||
if (!this.isBatch)
|
||||
selectIds.clear();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Popover position={Position.BOTTOM_LEFT} minimal>
|
||||
<Button minimal icon='sort' title='排序' />
|
||||
<Menu className="orderPanel">
|
||||
<MenuItem
|
||||
icon={<Icon icon='sort-alphabetical' />}
|
||||
text="按名称"
|
||||
onClick={() =>
|
||||
{
|
||||
this.resourceStore.privateModule.params.order = 'name';
|
||||
this.resourceStore.GetPrivateModule();
|
||||
}}
|
||||
/>
|
||||
<MenuItem
|
||||
text="按时间"
|
||||
icon={<Icon icon='sort-numerical' />}
|
||||
onClick={() =>
|
||||
{
|
||||
this.resourceStore.privateModule.params.order = 'create_date';
|
||||
this.resourceStore.GetPrivateModule();
|
||||
}}
|
||||
/>
|
||||
</Menu>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="itemList">
|
||||
{/* 加载中 */}
|
||||
{this.resourceStore.privateModule.loading ? <Spinner />
|
||||
:
|
||||
this.resourceStore.privateModule.data.length
|
||||
? this.resourceStore.privateModule.data.map((item: PrivateModuleItem) =>
|
||||
{
|
||||
return (
|
||||
<div
|
||||
key={item.module_id}
|
||||
className={`moduleItem ${selectIds.has(item.module_id) ? "moduleSelected" : ""}`}
|
||||
data-id={item.module_id}
|
||||
onContextMenu={(e: React.MouseEvent<HTMLDivElement>) =>
|
||||
{
|
||||
if (!selectIds.has(item.module_id))
|
||||
{
|
||||
selectIds.clear();
|
||||
selectIds.add(item.module_id);
|
||||
}
|
||||
this.ShowModuleContextMenu(e, item);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={GetCssObj(item.logo, "120x120")}
|
||||
className="items"
|
||||
onClick={(e) =>
|
||||
{
|
||||
if (this.isBatch)
|
||||
{
|
||||
this.HandleCheckChange(item);
|
||||
return;
|
||||
}
|
||||
if (item.state === "1")
|
||||
this.resourceStore.WhenResourceDrag(e, item, ModuleType.Private);
|
||||
else
|
||||
AppToaster.show({
|
||||
message: "模型已失效,请重新上传",
|
||||
timeout: 3000,
|
||||
intent: Intent.WARNING
|
||||
}, "Resoure_Enterprise");
|
||||
}}
|
||||
>
|
||||
<div className="itemOptions">
|
||||
<Popover hoverCloseDelay={100} interactionKind='hover' minimal position='top' >
|
||||
<Button minimal small icon='issue' />
|
||||
<div className='moduleDetail'>
|
||||
{RenderDetailImg(item.logo, "320x320")}
|
||||
<span> {item.name}</span>
|
||||
<span> 模型id:{item.module_id}</span>
|
||||
<span> 模型尺寸:{item.dimension}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
<Checkbox
|
||||
style={{
|
||||
display: this.isBatch ? "block" : "none",
|
||||
position: "absolute",
|
||||
bottom: "0", right: "0"
|
||||
}}
|
||||
onChange={() => this.HandleCheckChange(item)}
|
||||
checked={selectIds.has(item.module_id)}
|
||||
/>
|
||||
</div>
|
||||
<div className='moduleName'>
|
||||
{
|
||||
this.currentInfo && this.currentInfo?.module_id === item.module_id ?
|
||||
<EnterpriseInputGroup
|
||||
defaultValue={item.name}
|
||||
placeholder='请输入模型名称'
|
||||
onSuccess={(value) =>
|
||||
{
|
||||
this.HandleRenameModule(value);
|
||||
}}
|
||||
onCancel={() =>
|
||||
{
|
||||
this.currentInfo = undefined;
|
||||
}}
|
||||
/>
|
||||
:
|
||||
<div>{item.name}</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
})
|
||||
:
|
||||
!this.resourceStore.collectLoadgin && <NonIdealState
|
||||
icon='inbox-search'
|
||||
title="暂无数据"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<div className='enterpriseOption'>
|
||||
<R_Pagination
|
||||
currentPage={privateModule.params.curr_page}
|
||||
totalPage={privateModule.total}
|
||||
HandlePageChange={this.HandlePageChange}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
this.moveModalVisible &&
|
||||
<Dialog
|
||||
isOpen={true}
|
||||
onClose={() =>
|
||||
{
|
||||
this.moveModalVisible = false;
|
||||
if (!this.isBatch)
|
||||
this.selectedIds.clear();
|
||||
}}
|
||||
icon="move"
|
||||
title="移动到目录"
|
||||
canEscapeKeyClose
|
||||
canOutsideClickClose
|
||||
style={{ width: "30vw", maxHeight: "50vh" }}
|
||||
>
|
||||
<PrivateDirTree handMoveModule={this.HandMoveModule} />
|
||||
</Dialog>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue