💄 UI: [前台]:书签管理页开发中

This commit is contained in:
fanxb 2019-07-13 17:58:40 +08:00
parent 2e9466ad6b
commit a05d57c95b
3 changed files with 264 additions and 58 deletions

View File

@ -1,18 +1,134 @@
import httpUtil from "../../../util/httpUtil"; import httpUtil from "../../../util/httpUtil";
import { Modal } from "antd"; import { Modal, message } from "antd";
/** /**
* 删除书签 * 选中的文件夹id列表
* @param {*} _this 组件实例
* @param {*} folderList 删除的文件夹列表
* @param {*} bookmarkList 删除的具体书签列表
*/ */
export function deleteBookmark(_this, folderList, bookmarkList) { let folderIdList = [];
/**
* 选中的书签id列表
*/
let bookmarkIdList = [];
/**
* 新增书签的父节点node
*/
let parentNode = null;
/**
* 展开/关闭
* @param {*} keys
*/
export function onExpand(keys) {
this.setState({ expandKeys: keys });
}
/**
* 关闭全部节点
*/
export function closeAll() {
this.setState({ expandKeys: [] });
}
/**
* 选中节点
* @param {*} keys
* @param {*} data
*/
export function onCheck(keys, data) {
this.setState({ checkedKeys: keys });
bookmarkIdList = [];
folderIdList = [];
parentNode = null;
data.checkedNodes.forEach(item => {
const bookmark = item.props.dataRef;
parentNode = bookmark;
bookmark.type === 0 ? bookmarkIdList.push(bookmark.bookmarkId) : folderIdList.push(bookmark.bookmarkId);
});
}
/**
* 弹出新增modal
*/
export function showAddModel() {
if (this.state.checkedKeys.length > 1) {
message.error("选中过多");
return;
} else if (this.state.checkedKeys.length === 1) {
const id = this.state.checkedKeys[0];
if (bookmarkIdList.indexOf(parseInt(id)) > -1) {
message.error("只能选择文件夹节点");
return;
}
}
this.setState({ isShowModal: true });
}
export function addOne() {
console.log(1);
let body = {
type: this.state.addType,
path: parentNode == null ? "" : parentNode.path + "." + parentNode.bookmarkId,
name: this.state.addName,
url: this.state.addValue
};
httpUtil.put("/bookmark", body).then(res => {
let arr;
if (parentNode == null) {
arr = this.data[""] ? this.data[""] : [];
} else {
arr = this.data[body.path] ? this.data[body.path] : [];
}
arr.push(res);
if (this.state.treeData.length === 0) {
this.state.treeData.push(arr);
}
this.data[body.path] = arr;
this.setState({ treeData: [...this.state.treeData], addType: 0, addName: "", addValue: "", isShowModal: false });
});
}
/**
* 批量删除
*/
export function batchDelete() {
console.log("1");
const _this = this;
Modal.confirm({ Modal.confirm({
title: "确认删除?", title: "确认删除?",
content: "删除后,无法找回", content: "删除后,无法找回",
onOk() { onOk() {
return httpUtil.post("/dele") return new Promise((resolve, reject) => {
httpUtil
.post("/bookmark/batchDelete", { folderIdList, bookmarkIdList })
.then(() => {
//遍历节点树数据,并删除
const set = new Set();
folderIdList.forEach(item => set.add(item));
bookmarkIdList.forEach(item => set.add(item));
deleteTreeData(_this.state.treeData, set);
_this.setState({ treeData: [..._this.state.treeData], checkedKeys: [] });
resolve();
})
.catch(() => reject());
});
} }
}); });
} }
/**
* 递归删除已经被删除的数据
* @param {*} treeData
*/
function deleteTreeData(treeData, set) {
for (let i = 0, length = treeData.length; i < length; i++) {
const item = treeData[i];
if (set.has(treeData[i].bookmarkId)) {
treeData.splice(i, 1);
length--;
i--;
} else if (item.children && item.children.length > 0) {
deleteTreeData(item.children, set);
}
}
}

View File

@ -1,9 +1,10 @@
import React from "react"; import React from "react";
import { Tree, Empty, Button, Tooltip } from "antd"; import { Icon, Tree, Empty, Button, Tooltip, Modal, Form, Input, Radio, Upload } from "antd";
import MainLayout from "../../../layout/MainLayout"; import MainLayout from "../../../layout/MainLayout";
import httpUtil from "../../../util/httpUtil"; import httpUtil from "../../../util/httpUtil";
import IconFont from "../../../components/IconFont"; import IconFont from "../../../components/IconFont";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { batchDelete, onExpand, closeAll, onCheck, addOne, showAddModel } from "./function.js";
const { TreeNode } = Tree; const { TreeNode } = Tree;
@ -14,7 +15,15 @@ export default class OverView extends React.Component {
treeData: [], treeData: [],
isEdit: false, isEdit: false,
isLoading: true, isLoading: true,
expandKeys: ["0"] checkedKeys: [],
expandKeys: [],
//
isShowModal: false,
//
addType: 0,
addName: "",
addValue: "",
file: null
}; };
} }
@ -34,12 +43,7 @@ export default class OverView extends React.Component {
loadData = e => loadData = e =>
new Promise(resolve => { new Promise(resolve => {
const item = e.props.dataRef; const item = e.props.dataRef;
let newPath; const newPath = item.path + "." + item.bookmarkId;
if (item.path === "") {
newPath = item.bookmarkId;
} else {
newPath = item.path + "." + item.bookmarkId;
}
if (this.data[newPath]) { if (this.data[newPath]) {
item.children = this.data[newPath]; item.children = this.data[newPath];
this.setState({ treeData: [...this.state.treeData] }); this.setState({ treeData: [...this.state.treeData] });
@ -48,23 +52,14 @@ export default class OverView extends React.Component {
resolve(); resolve();
} }
}); });
/**
* 复制url到剪贴板
* @param {*} key
* @param {*} e
*/
copyUrl(key, e) {}
deleteOne = e => {
const id = e.target.id;
};
treeNodeSelect(key, e) { treeNodeSelect(key, e) {
const { expandKeys } = this.state;
const item = e.node.props.dataRef; const item = e.node.props.dataRef;
if (item.type === 0) { if (item.type === 0) {
window.open(item.url); window.open(item.url);
} else { } else {
expandKeys.push(item.bookmarkId);
} }
} }
@ -73,19 +68,19 @@ export default class OverView extends React.Component {
* @param {*} item * @param {*} item
*/ */
renderNodeContent(item) { renderNodeContent(item) {
const { isEdit } = this.state; // const { isEdit } = this.state;
const bts = ( // const bts = (
<React.Fragment> // <div className={styles.btns}>
<Button size="small" type="primary" name="copy" icon="copy" shape="circle" /> // <Button size="small" type="primary" name="copy" icon="copy" shape="circle" />
<Button size="small" type="danger" id={item.bookmarkId} icon="delete" shape="circle" onClick={this.deleteOne} /> // <Button size="small" type="danger" id={item.bookmarkId} icon="delete" shape="circle" onClick={this.deleteOne} />
</React.Fragment> // </div>
); // );
return ( return (
<React.Fragment> <React.Fragment>
<Tooltip placement="bottom" title={item.url}> <Tooltip placement="bottom" title={item.url}>
{item.name} <span>{item.name}</span>
</Tooltip> </Tooltip>
{isEdit ? bts : null} {/* {isEdit ? bts : null} */}
</React.Fragment> </React.Fragment>
); );
} }
@ -120,38 +115,108 @@ export default class OverView extends React.Component {
} }
render() { render() {
const { isLoading, isEdit, treeData, expandKeys } = this.state; const { isLoading, isEdit, treeData, expandKeys, checkedKeys, isShowModal, addType, addValue, addName } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 4 },
sm: { span: 4 }
},
wrapperCol: {
xs: { span: 20 },
sm: { span: 20 }
}
};
const uploadProps = {
accept: ".html,.htm",
onChange: e => {
this.setState({ file: e.fileList[0] });
},
beforeUpload: file => {
return false;
},
fileList: []
};
return ( return (
<MainLayout> <MainLayout>
<div className={styles.main}> <div className={styles.main}>
<div className={styles.header}> <div className={styles.header}>
<span>我的书签树</span> <div className={styles.left}>
<div> <span className={styles.myTree}>我的书签树</span>
{expandKeys.length > 0 ? (
<Button type="primary" size="small" onClick={closeAll.bind(this)}>
收起
</Button>
) : null}
</div>
<div className={styles.right}>
{isEdit ? (
<React.Fragment>
<Button size="small" type="danger" onClick={batchDelete.bind(this)}>
删除选中
</Button>
<Button size="small" type="primary" onClick={showAddModel.bind(this)}>
新增
</Button>
</React.Fragment>
) : null}
<Button size="small" type="primary" onClick={() => this.setState({ isEdit: !isEdit })}> <Button size="small" type="primary" onClick={() => this.setState({ isEdit: !isEdit })}>
{isEdit ? "完成" : "编辑"} {isEdit ? "完成" : "编辑"}
</Button> </Button>
</div> </div>
</div> </div>
{treeData.length ? ( {/* {treeData.length ? ( */}
<Tree <Tree
showIcon showIcon
loadData={this.loadData} checkedKeys={checkedKeys}
defaultExpandParent onCheck={onCheck.bind(this)}
checkable={isEdit} expandedKeys={expandKeys}
onSelect={this.treeNodeSelect} loadData={this.loadData}
onDoubleClick={this.copyUrl} onExpand={onExpand.bind(this)}
blockNode defaultExpandParent
> checkable={isEdit}
{this.renderTreeNodes(treeData)} onSelect={this.treeNodeSelect}
</Tree> onDoubleClick={this.copyUrl}
) : null} blockNode
{isLoading === false && treeData.length === 0 ? ( >
<Empty description="还没有数据"> {this.renderTreeNodes(treeData)}
<Button size="small" type="primary" onClick={() => this.setState({ isEdit: true })}> </Tree>
新增 {/* ) : null} */}
</Button> {isLoading === false && treeData.length === 0 ? <Empty description="还没有数据" /> : null}
</Empty>
) : null} <Modal destroyOnClose title="新增" visible={isShowModal} onCancel={() => this.setState({ isShowModal: false })} footer={false}>
<Form {...formItemLayout}>
<Form.Item label="类别">
<Radio.Group defaultValue={0} onChange={e => this.setState({ addType: e.target.value })}>
<Radio value={0}>书签</Radio>
<Radio value={1}>文件夹</Radio>
<Radio value={2}>上传书签html</Radio>
</Radio.Group>
</Form.Item>
{addType < 2 ? (
<Form.Item label="名称">
<Input type="text" onChange={e => this.setState({ addName: e.target.value })} value={addName} />
</Form.Item>
) : null}
{addType === 0 ? (
<Form.Item label="URL">
<Input type="text" value={addValue} onChange={e => this.setState({ addValue: e.target.value })} />
</Form.Item>
) : null}
{addType === 2 ? (
<Upload {...uploadProps}>
<Button type="primary">
<Icon type="upload" />
{this.state.file == null ? "选择文件" : this.state.file.name.substr(0, 20)}
</Button>
</Upload>
) : null}
<div style={{ textAlign: "center", paddingTop: "1em" }}>
<Button type="primary" onClick={addOne.bind(this)}>
提交
</Button>
</div>
</Form>
</Modal>
</div> </div>
</MainLayout> </MainLayout>
); );

View File

@ -5,5 +5,30 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.left {
display: flex;
align-items: center;
.myTree {
font-size: 1.3em;
line-height: 1.3em;
font-weight: 600;
}
}
}
.title {
display: inline-block;
max-width: 70%;
margin-right: 1em;
overflow-x: hidden;
text-overflow: ellipsis;
}
.btns {
display: inline-block;
position: relative;
top: -0.45em;
} }
} }