ソースを参照

通讯录,工作台页面

张值绫 3 年 前
コミット
27c753af89
56 ファイル変更4957 行追加7 行削除
  1. BIN
      __MACOSX/components/._.DS_Store
  2. BIN
      __MACOSX/static/._.DS_Store
  3. BIN
      __MACOSX/static/vant-uni-nvue/._.DS_Store
  4. BIN
      __MACOSX/static/vant-uni-nvue/._loading.gif
  5. 19 1
      common/vmeitime-http/api.js
  6. 1 1
      common/vmeitime-http/interface.js
  7. BIN
      components/.DS_Store
  8. 9 0
      components/ly-tree-nvue/components/icons.js
  9. 94 0
      components/ly-tree-nvue/components/ly-checkbox.vue
  10. 375 0
      components/ly-tree-nvue/ly-tree-node.vue
  11. 550 0
      components/ly-tree-nvue/ly-tree.vue
  12. 538 0
      components/ly-tree-nvue/model/node.js
  13. 419 0
      components/ly-tree-nvue/model/tree-store.js
  14. BIN
      components/ly-tree-nvue/nvue-demo.zip
  15. 114 0
      components/ly-tree-nvue/tool/util.js
  16. 200 0
      components/ly-tree/components/ly-checkbox.vue
  17. 424 0
      components/ly-tree/ly-tree-node.vue
  18. 605 0
      components/ly-tree/ly-tree.vue
  19. 538 0
      components/ly-tree/model/node.js
  20. 419 0
      components/ly-tree/model/tree-store.js
  21. 115 0
      components/ly-tree/tool/util.js
  22. BIN
      components/t-c-contacts/icon/ICE.png
  23. BIN
      components/t-c-contacts/icon/bangong.png
  24. BIN
      components/t-c-contacts/icon/bumen.png
  25. BIN
      components/t-c-contacts/icon/jiagou.png
  26. BIN
      components/t-c-contacts/icon/qingjia.png
  27. BIN
      components/t-c-contacts/icon/qita.png
  28. BIN
      components/t-c-contacts/icon/yongyin.png
  29. BIN
      components/t-c-contacts/icon/you.png
  30. 144 0
      components/t-c-contacts/t-c-contacts.vue
  31. 127 0
      components/t-w-application/t-w-application.vue
  32. 100 0
      components/t-w-application1/t-w-application1.vue
  33. BIN
      components/t-w-icon/banzu.png
  34. BIN
      components/t-w-icon/daiban.png
  35. BIN
      components/t-w-icon/tongzhi.png
  36. BIN
      components/t-w-icon/zhiban.png
  37. 0 1
      main.js
  38. 21 2
      pages.json
  39. BIN
      pages/production/t-c-c-contacts-info/icon/ICE.png
  40. BIN
      pages/production/t-c-c-contacts-info/icon/searc1.png
  41. 118 0
      pages/production/t-c-c-contacts-info/t-c-c-contacts-info.vue
  42. 14 0
      pages/tabbar/contacts/contacts.vue
  43. 13 2
      pages/tabbar/workbench/workbench.vue
  44. BIN
      static/.DS_Store
  45. BIN
      static/img/banzu.png
  46. BIN
      static/img/daiban.png
  47. BIN
      static/img/tongzhi.png
  48. BIN
      static/img/zhiban.png
  49. BIN
      static/tabbar/contacts.png
  50. BIN
      static/tabbar/contacts_active.png
  51. BIN
      static/vant-uni-nvue/.DS_Store
  52. BIN
      static/vant-uni-nvue/2323.gif
  53. BIN
      static/vant-uni-nvue/Rolling-2s-200px.gif
  54. BIN
      static/vant-uni-nvue/Rolling-2s-60px.gif
  55. BIN
      static/vant-uni-nvue/loading.gif
  56. BIN
      static/vant-uni-nvue/logo.png

BIN
__MACOSX/components/._.DS_Store


BIN
__MACOSX/static/._.DS_Store


BIN
__MACOSX/static/vant-uni-nvue/._.DS_Store


BIN
__MACOSX/static/vant-uni-nvue/._loading.gif


+ 19 - 1
common/vmeitime-http/api.js

@@ -307,4 +307,22 @@ export const troubleshoot_getDBNum = (data) => {
         url: '/troubleshoot/getDBNum',
         data,
     })
-}
+}
+
+
+// 工作台
+export const workbench_index = (data) => {
+    return http.request({
+        url: '/workbench/index',
+        data,
+    })
+}
+
+// 组织架构
+export const section_getlisttree = (data) => {
+    return http.request({
+        url: '/section/getlisttree',
+        data,
+    })
+}
+

+ 1 - 1
common/vmeitime-http/interface.js

@@ -34,7 +34,7 @@ http.delete('user/1').then((res)=>{
 export default {
 	config: {
 		// baseUrl: "http://colliery.nxjiewei.com/api",
-		baseUrl: "http://meihuajing.nxjiewei.com:8011/api",
+		baseUrl: "http://zaoquan.nxjiewei.com:8011/api",
 		header: {
 			"Content-Type":"multipart/form-data",
 			'Content-Type':'application/json;charset=UTF-8',

BIN
components/.DS_Store


+ 9 - 0
components/ly-tree-nvue/components/icons.js

@@ -0,0 +1,9 @@
+export default {
+	'icon-unchecked': "\ue852",
+	'icon-radio-disabled': "\ue853",
+	'icon-radio-unchecked': "\ue854",
+	'icon-checked': "\ue855",
+	'icon-check-disabled': "\ue856",
+	'icon-indeterminate': "\ue85a",
+	'icon-radio-checked': "\ue86b"
+}

ファイルの差分が大きいため隠しています
+ 94 - 0
components/ly-tree-nvue/components/ly-checkbox.vue


ファイルの差分が大きいため隠しています
+ 375 - 0
components/ly-tree-nvue/ly-tree-node.vue


ファイルの差分が大きいため隠しています
+ 550 - 0
components/ly-tree-nvue/ly-tree.vue


+ 538 - 0
components/ly-tree-nvue/model/node.js

@@ -0,0 +1,538 @@
+import {
+	markNodeData,
+	objectAssign,
+	arrayFindIndex,
+	getChildState,
+	reInitChecked,
+	getPropertyFromData,
+	isNull,
+	NODE_KEY
+} from '../tool/util';
+
+const getStore = function(store) {
+	let thisStore = store;
+	
+	return function() {
+		return thisStore;
+	}
+}
+
+let nodeIdSeed = 0;
+
+export default class Node {
+	constructor(options) {
+		this.time = new Date().getTime();
+		this.id = nodeIdSeed++;
+		this.text = null;
+		this.checked = false;
+		this.indeterminate = false;
+		this.data = null;
+		this.expanded = false;
+		this.parentId = null;
+		this.visible = true;
+		this.isCurrent = false;
+
+		for (let name in options) {
+			if (options.hasOwnProperty(name)) {
+				if (name === 'store') {
+					this.store = getStore(options[name]);
+				} else {
+					this[name] = options[name];
+				}
+			}
+		}
+		
+		if (!this.store()) {
+			throw new Error('[Node]store is required!');
+		}
+
+		// internal
+		this.level = 0;
+		this.loaded = false;
+		this.childNodesId = [];
+		this.loading = false;
+		this.label = getPropertyFromData(this, 'label');
+		this.key = this._getKey();
+		this.disabled = getPropertyFromData(this, 'disabled');
+		this.nextSibling = null;
+		this.previousSibling = null;
+		this.icon = '';
+		
+		this._handleParentAndLevel();
+		this._handleProps();
+		this._handleExpand();
+		this._handleCurrent();
+		
+		if (this.store().lazy) {
+			this.store()._initDefaultCheckedNode(this);
+		}
+
+		this.updateLeafState();
+	}
+	
+	_getKey() {
+		if (!this.data || Array.isArray(this.data)) return null;
+		
+		if (typeof this.data === 'object') {
+			const nodeKey = this.store().key;
+			const key = this.data[nodeKey];
+			
+			if (typeof key === 'undefined') {
+				throw new Error(`您配置的node-key为"${nodeKey}",但数据中并未找到对应"${nodeKey}"属性的值,请检查node-key的配置是否合理`)
+			}
+			
+			return key;
+		}
+		
+		throw new Error('不合法的data数据');
+	}
+	
+	_handleParentAndLevel() {
+		if (this.parentId !== null) {
+			let parent = this.getParent(this.parentId);
+			
+			if (this.store().isInjectParentInNode) {
+				this.parent = parent;
+			}
+			
+			// 由于这里做了修改,默认第一个对象不会被注册到nodesMap中,所以找不到parent会报错,所以默认parent的level是0
+			if (!parent) {
+				parent = {
+					level: 0
+				}
+			} else {
+				const parentChildNodes = parent.getChildNodes(parent.childNodesId);
+				const index = parent.childNodesId.indexOf(this.key);
+				this.nextSibling = index > -1 ? parentChildNodes[index + 1] : null;
+				this.previousSibling = index > 0 ? parentChildNodes[index - 1] : null;
+			}
+			this.level = parent.level + 1;
+		}
+	}
+	
+	_handleProps() {
+		const props = this.store().props;
+		
+		if (this.store().showNodeIcon) {
+			if (props && typeof props.icon !== 'undefined') {
+				this.icon = getPropertyFromData(this, 'icon');
+			} else {
+				console.warn('请配置props属性中的"icon"字段')
+			}
+		}
+		
+		this.store().registerNode(this);
+		
+		if (props && typeof props.isLeaf !== 'undefined') {
+			const isLeaf = getPropertyFromData(this, 'isLeaf');
+			if (typeof isLeaf === 'boolean') {
+				this.isLeafByUser = isLeaf;
+			}
+		}
+	}
+	
+	_handleExpand() {
+		if (this.store().lazy !== true && this.data) {
+			this.setData(this.data);
+		
+			if (this.store().defaultExpandAll) {
+				this.expanded = true;
+			}
+		} else if (this.level > 0 && this.store().lazy && this.store().defaultExpandAll) {
+			this.expand();
+		}
+		
+		if (!Array.isArray(this.data)) {
+			markNodeData(this, this.data);
+		}
+		
+		if (!this.data) return;
+		
+		const defaultExpandedKeys = this.store().defaultExpandedKeys;
+		const key = this.store().key;
+		if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) {
+			this.expand(null, this.store().autoExpandparent);
+		}
+	}
+	
+	_handleCurrent() {
+		const key = this.store().key;
+		
+		if (key && this.store().currentNodeKey !== undefined && this.key === this.store().currentNodeKey) {
+			this.store().currentNode = this;
+			this.store().currentNode.isCurrent = true;
+		}
+	}
+	
+	destroyStore() {
+		getStore(null)
+	}
+
+	setData(data) {
+		if (!Array.isArray(data)) {
+			markNodeData(this, data);
+		}
+
+		this.data = data;
+		this.childNodesId = [];
+
+		let children;
+		if (this.level === 0 && Array.isArray(this.data)) {
+			children = this.data;
+		} else {
+			children = getPropertyFromData(this, 'children') || [];
+		}
+
+		for (let i = 0, j = children.length; i < j; i++) {
+			this.insertChild({
+				data: children[i]
+			});
+		}
+	}
+
+	contains(target, deep = true) {
+		const walk = function(parent) {
+			const children = parent.getChildNodes(parent.childNodesId) || [];
+			let result = false;
+			for (let i = 0, j = children.length; i < j; i++) {
+				const child = children[i];
+				if (child === target || (deep && walk(child))) {
+					result = true;
+					break;
+				}
+			}
+			return result;
+		};
+
+		return walk(this);
+	}
+
+	remove() {
+		if (this.parentId !== null) {
+			const parent = this.getParent(this.parentId);
+			parent.removeChild(this);
+		}
+	}
+
+	insertChild(child, index, batch) {
+		if (!child) throw new Error('insertChild error: child is required.');
+
+		if (!(child instanceof Node)) {
+			if (!batch) {
+				const children = this.getChildren(true);
+				if (children.indexOf(child.data) === -1) {
+					if (typeof index === 'undefined' || index < 0) {
+						children.push(child.data);
+					} else {
+						children.splice(index, 0, child.data);
+					}
+				}
+			}
+			
+			objectAssign(child, {
+				parentId: isNull(this.key) ? '' : this.key,
+				store: this.store()
+			});
+			child = new Node(child);
+		}
+
+		child.level = this.level + 1;
+
+		if (typeof index === 'undefined' || index < 0) {
+			this.childNodesId.push(child.key);
+		} else {
+			this.childNodesId.splice(index, 0, child.key);
+		}
+
+		this.updateLeafState();
+	}
+
+	insertBefore(child, ref) {
+		let index;
+		if (ref) {
+			index = this.childNodesId.indexOf(ref.id);
+		}
+		this.insertChild(child, index);
+	}
+
+	insertAfter(child, ref) {
+		let index;
+		if (ref) {
+			index = this.childNodesId.indexOf(ref.id);
+			if (index !== -1) index += 1;
+		}
+		this.insertChild(child, index);
+	}
+
+	removeChild(child) {
+		const children = this.getChildren() || [];
+		const dataIndex = children.indexOf(child.data);
+		if (dataIndex > -1) {
+			children.splice(dataIndex, 1);
+		}
+		
+		const index = this.childNodesId.indexOf(child.key);
+		
+		if (index > -1) {
+			this.store() && this.store().deregisterNode(child);
+			child.parentId = null;
+			this.childNodesId.splice(index, 1);
+		}
+		
+		this.updateLeafState();
+	}
+
+	removeChildByData(data) {
+		let targetNode = null;
+
+		for (let i = 0; i < this.childNodesId.length; i++) {
+			let node = this.getChildNodes(this.childNodesId);
+			if (node[i].data === data) {
+				targetNode = node[i];
+				break;
+			}
+		}
+
+		if (targetNode) {
+			this.removeChild(targetNode);
+		}
+	}
+
+	// 为了避免APP端parent嵌套结构导致报错,这里parent需要从nodesMap中获取
+	getParent(parentId) {
+		try {
+			if (!parentId.toString()) return null;
+			return this.store().nodesMap[parentId];
+		} catch (error) {
+			return null;
+		}
+	}
+
+	// 为了避免APP端childNodes嵌套结构导致报错,这里childNodes需要从nodesMap中获取
+	getChildNodes(childNodesId) {
+		let childNodes = [];
+		if (childNodesId.length === 0) return childNodes;
+		childNodesId.forEach((key) => {
+			childNodes.push(this.store().nodesMap[key]);
+		})
+		return childNodes;
+	}
+
+	expand(callback, expandparent) {
+		const done = () => {
+			if (expandparent) {
+				let parent = this.getParent(this.parentId);
+				while (parent && parent.level > 0) {
+					parent.expanded = true;
+					parent = this.getParent(parent.parentId);
+				}
+			}
+			this.expanded = true;
+			if (callback) callback();
+		};
+
+		if (this.shouldLoadData()) {
+			this.loadData(function(data) {
+				if (Array.isArray(data)) {
+					if (this.checked) {
+						this.setChecked(true, true);
+					} else if (!this.store().checkStrictly) {
+						reInitChecked(this);
+					}
+					done();
+				}
+			});
+		} else {
+			done();
+		}
+	}
+
+	doCreateChildren(array, defaultProps = {}) {
+		array.forEach((item) => {
+			this.insertChild(objectAssign({
+				data: item
+			}, defaultProps), undefined, true);
+		});
+	}
+
+	collapse() {
+		this.expanded = false;
+	}
+
+	shouldLoadData() {
+		return this.store().lazy === true && this.store().load && !this.loaded;
+	}
+
+	updateLeafState() {
+		if (this.store().lazy === true && this.loaded !== true && typeof this.isLeafByUser !== 'undefined') {
+			this.isLeaf = this.isLeafByUser;
+			return;
+		}
+		const childNodesId = this.childNodesId;
+		if (!this.store().lazy || (this.store().lazy === true && this.loaded === true)) {
+			this.isLeaf = !childNodesId || childNodesId.length === 0;
+			return;
+		}
+		this.isLeaf = false;
+	}
+
+	setChecked(value, deep, recursion, passValue) {
+		this.indeterminate = value === 'half';
+		this.checked = value === true;
+		
+		if (this.checked && this.store().expandOnCheckNode) {
+			this.expand(null, true)
+		}
+		
+		if (this.store().checkStrictly) return;
+		if (this.store().showRadio) return;
+
+		if (!(this.shouldLoadData() && !this.store().checkDescendants)) {
+			let childNodes = this.getChildNodes(this.childNodesId);
+			let {
+				all,
+				allWithoutDisable
+			} = getChildState(childNodes);
+
+			if (!this.isLeaf && (!all && allWithoutDisable)) {
+				this.checked = false;
+				value = false;
+			}
+
+			const handleDescendants = () => {
+				if (deep) {
+					let childNodes = this.getChildNodes(this.childNodesId)
+					for (let i = 0, j = childNodes.length; i < j; i++) {
+						const child = childNodes[i];
+						passValue = passValue || value !== false;
+						const isCheck = child.disabled ? child.checked : passValue;
+						child.setChecked(isCheck, deep, true, passValue);
+					}
+					const {
+						half,
+						all
+					} = getChildState(childNodes);
+					
+					if (!all) {
+						this.checked = all;
+						this.indeterminate = half;
+					}
+				}
+			};
+
+			if (this.shouldLoadData()) {
+				this.loadData(() => {
+					handleDescendants();
+					reInitChecked(this);
+				}, {
+					checked: value !== false
+				});
+				return;
+			} else {
+				handleDescendants();
+			}
+		}
+
+		if (!this.parentId) return;
+
+		let parent = this.getParent(this.parentId);
+		if (parent && parent.level === 0) return;
+
+		if (!recursion) {
+			reInitChecked(parent);
+		}
+	}
+
+	setRadioChecked(value) {
+		const allNodes = this.store()._getAllNodes().sort((a, b) => b.level - a.level);
+		allNodes.forEach(node => node.setChecked(false, false));
+		this.checked = value === true;
+	}
+
+	getChildren(forceInit = false) {
+		if (this.level === 0) return this.data;
+		const data = this.data;
+		if (!data) return null;
+
+		const props = this.store().props;
+		let children = 'children';
+		if (props) {
+			children = props.children || 'children';
+		}
+
+		if (data[children] === undefined) {
+			data[children] = null;
+		}
+
+		if (forceInit && !data[children]) {
+			data[children] = [];
+		}
+
+		return data[children];
+	}
+
+	updateChildren() {
+		let childNodes = this.getChildNodes(this.childNodesId);
+		const newData = this.getChildren() || [];
+		const oldData = childNodes.map((node) => node.data);
+
+		const newDataMap = {};
+		const newNodes = [];
+
+		newData.forEach((item, index) => {
+			const key = item[NODE_KEY];
+			const isNodeExists = !!key && arrayFindIndex(oldData, data => data[NODE_KEY] === key) >= 0;
+			if (isNodeExists) {
+				newDataMap[key] = {
+					index,
+					data: item
+				};
+			} else {
+				newNodes.push({
+					index,
+					data: item
+				});
+			}
+		});
+
+		if (!this.store().lazy) {
+			oldData.forEach((item) => {
+				if (!newDataMap[item[NODE_KEY]]) this.removeChildByData(item);
+			});
+		}
+
+		newNodes.forEach(({
+			index,
+			data
+		}) => {
+			this.insertChild({
+				data
+			}, index);
+		});
+
+		this.updateLeafState();
+	}
+
+	loadData(callback, defaultProps = {}) {
+		if (this.store().lazy === true && 
+			this.store().load && !this.loaded && 
+			(!this.loading || Object.keys(defaultProps).length)
+		) {
+			this.loading = true;
+
+			const resolve = (children) => {
+				this.loaded = true;
+				this.loading = false;
+				this.childNodesId = [];
+				this.doCreateChildren(children, defaultProps);
+				this.updateLeafState();
+				
+				callback && callback.call(this,children);
+			};
+
+			this.store().load(this, resolve);
+		} else {
+			callback && callback.call(this);
+		}
+	}
+}

+ 419 - 0
components/ly-tree-nvue/model/tree-store.js

@@ -0,0 +1,419 @@
+import Node from './node';
+import {
+	getNodeKey,
+	getPropertyFromData
+} from '../tool/util';
+
+export default class TreeStore {
+	constructor(options) {
+		this.ready = false;
+		this.currentNode = null;
+		this.currentNodeKey = null;
+
+		Object.assign(this, options);
+
+		if (!this.key) {
+			throw new Error('[Tree] nodeKey is required');
+		}
+
+		this.nodesMap = {};
+		this.root = new Node({
+			data: this.data,
+			store: this
+		});
+
+		if (this.lazy && this.load) {
+			const loadFn = this.load;
+			loadFn(this.root, (data) => {
+				this.root.doCreateChildren(data);
+				this._initDefaultCheckedNodes();
+				this.ready = true;
+			});
+		} else {
+			this._initDefaultCheckedNodes();
+			this.ready = true;
+		}
+	}
+
+	filter(value, data) {
+		const filterNodeMethod = this.filterNodeMethod;
+		const lazy = this.lazy;
+		const _self = this;
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if (data && typeof data === 'object') {
+					let nodePath = _self.getNodePath(child.data);
+					if (!nodePath.some(pathItem => pathItem[_self.key] === data[_self.key])) {
+						child.visible = false;
+						traverse(child);
+						return;
+					}
+				}
+				
+				if (_self.childVisibleForFilterNode) {
+					let parent = child.getParent(child.parentId);
+					child.visible = filterNodeMethod.call(child, value, child.data, child) || (parent && parent.visible);
+				} else {
+					child.visible = filterNodeMethod.call(child, value, child.data, child);
+				}
+				
+				traverse(child);
+			});
+
+			if (!node.visible && childNodes.length) {
+				let allHidden = true;
+				allHidden = !childNodes.some(child => child.visible);
+
+				if (node.root) {
+					node.root.visible = allHidden === false;
+				} else {
+					node.visible = allHidden === false;
+				}
+			}
+			
+			if (!value) return;
+
+			if (node.visible && !node.isLeaf && !lazy) node.expand();
+		};
+
+		traverse(this);
+	}
+
+	setData(newVal) {
+		const instanceChanged = newVal !== this.root.data;
+		if (instanceChanged) {
+			this.root.setData(newVal);
+			this._initDefaultCheckedNodes();
+		} else {
+			this.root.updateChildren();
+		}
+	}
+
+	getNode(data) {
+		if (data instanceof Node) return data;
+		const key = typeof data !== 'object' ? data : getNodeKey(this.key, data);
+		if (!key) return null;
+		return this.nodesMap[key] || null;
+	}
+
+	insertBefore(data, refData) {
+		const refNode = this.getNode(refData);
+		let parent = refNode.getParent(refNode.parentId);
+		parent.insertBefore({
+			data
+		}, refNode);
+	}
+
+	insertAfter(data, refData) {
+		const refNode = this.getNode(refData);
+		let parent = refNode.getParent(refNode.parentId);
+		parent.insertAfter({
+			data
+		}, refNode);
+	}
+
+	remove(data) {
+		const node = this.getNode(data);
+
+		if (node && node.parentId !== null) {
+			let parent = node.getParent(node.parentId);
+			if (node === this.currentNode) {
+				this.currentNode = null;
+			}
+			parent.removeChild(node);
+		}
+	}
+
+	append(data, parentData) {
+		const parentNode = parentData ? this.getNode(parentData) : this.root;
+
+		if (parentNode) {
+			parentNode.insertChild({
+				data
+			});
+		}
+	}
+
+	_initDefaultCheckedNodes() {
+		const defaultCheckedKeys = this.defaultCheckedKeys || [];
+		const nodesMap = this.nodesMap;
+		let checkedKeyfromData = [];
+		let totalCheckedKeys = []
+		
+		for (let key in nodesMap) {
+			let checked = getPropertyFromData(nodesMap[key], 'checked') || false;
+			checked && checkedKeyfromData.push(key);
+		}
+		
+		totalCheckedKeys = Array.from(new Set([...defaultCheckedKeys, ...checkedKeyfromData]));
+		totalCheckedKeys.forEach((checkedKey) => {
+			const node = nodesMap[checkedKey];
+			
+			if (node) {
+				node.setChecked(true, !this.checkStrictly);
+			}
+		});
+	}
+
+	_initDefaultCheckedNode(node) {
+		const defaultCheckedKeys = this.defaultCheckedKeys || [];
+
+		if (defaultCheckedKeys.indexOf(node.key) !== -1) {
+			node.setChecked(true, !this.checkStrictly);
+		}
+	}
+	
+	toggleExpendAll(isExpandAll) {
+		const allNodes = this._getAllNodes();
+		
+		allNodes.forEach(item => {
+			const node = this.getNode(item.key); 
+			
+			if (node) isExpandAll ? node.expand() : node.collapse();
+		});
+	}
+	
+	setCheckAll(isCkeckAll) {
+		const allNodes = this._getAllNodes();
+		
+		allNodes.forEach(item => {
+			item.setChecked(isCkeckAll, false);
+		}); 
+	}
+
+	setDefaultCheckedKey(newVal) {
+		if (newVal !== this.defaultCheckedKeys) {
+			this.defaultCheckedKeys = newVal;
+			this._initDefaultCheckedNodes();
+		}
+	}
+
+	registerNode(node) {
+
+		const key = this.key;
+		if (!key || !node || !node.data) return;
+
+		const nodeKey = node.key;
+		if (nodeKey !== undefined) this.nodesMap[node.key] = node;
+	}
+
+	deregisterNode(node) {
+		const key = this.key;
+		if (!key || !node || !node.data) return;
+
+		let childNodes = node.getChildNodes(node.childNodesId);
+		childNodes.forEach(child => {
+			this.deregisterNode(child);
+		});
+
+		delete this.nodesMap[node.key];
+	}
+	
+	getNodePath(data) {
+		if (!this.key) throw new Error('[Tree] nodeKey is required in getNodePath');
+		const node = this.getNode(data);
+		if (!node) return [];
+		
+		const path = [node.data];
+		let parent = node.getParent(node.parentId);
+		while (parent && parent !== this.root) {
+			path.push(parent.data);
+			parent = parent.getParent(parent.parentId);
+		}
+		return path.reverse();
+	}
+
+	getCheckedNodes(leafOnly = false, includeHalfChecked = false) {
+		const checkedNodes = [];
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if ((child.checked || (includeHalfChecked && child.indeterminate)) && (!leafOnly || (leafOnly && child.isLeaf))) {
+					checkedNodes.push(child.data);
+				}
+
+				traverse(child);
+			});
+		};
+
+		traverse(this);
+
+		return checkedNodes;
+	}
+
+	getCheckedKeys(leafOnly = false, includeHalfChecked = false) {
+		return this.getCheckedNodes(leafOnly, includeHalfChecked).map((data) => (data || {})[this.key]);
+	}
+
+	getHalfCheckedNodes() {
+		const nodes = [];
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if (child.indeterminate) {
+					nodes.push(child.data);
+				}
+
+				traverse(child);
+			});
+		};
+
+		traverse(this);
+
+		return nodes;
+	}
+
+	getHalfCheckedKeys() {
+		return this.getHalfCheckedNodes().map((data) => (data || {})[this.key]);
+	}
+
+	_getAllNodes() {
+		const allNodes = [];
+		const nodesMap = this.nodesMap;
+		for (let nodeKey in nodesMap) {
+			if (nodesMap.hasOwnProperty(nodeKey)) {
+				allNodes.push(nodesMap[nodeKey]);
+			}
+		}
+
+		return allNodes;
+	}
+
+	updateChildren(key, data) {
+		const node = this.nodesMap[key];
+		if (!node) return;
+		const childNodes = node.getChildNodes(node.childNodesId);
+		for (let i = childNodes.length - 1; i >= 0; i--) {
+			const child = childNodes[i];
+			this.remove(child.data);
+		}
+		for (let i = 0, j = data.length; i < j; i++) {
+			const child = data[i];
+			this.append(child, node.data);
+		}
+	}
+
+	_setCheckedKeys(key, leafOnly = false, checkedKeys) {
+		const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level);
+		const cache = Object.create(null);
+		const keys = Object.keys(checkedKeys);
+		allNodes.forEach(node => node.setChecked(false, false));
+		for (let i = 0, j = allNodes.length; i < j; i++) {
+			const node = allNodes[i];
+			let nodeKey = node.data[key];
+			
+			if (typeof nodeKey === 'undefined') continue;
+			
+			nodeKey = nodeKey.toString();
+			let checked = keys.indexOf(nodeKey) > -1;
+			if (!checked) {
+				if (node.checked && !cache[nodeKey]) {
+					node.setChecked(false, false);
+				}
+				continue;
+			}
+
+			let parent = node.getParent(node.parentId);
+			while (parent && parent.level > 0) {
+				cache[parent.data[key]] = true;
+				parent = parent.getParent(parent.parentId);
+			}
+
+			if (node.isLeaf || this.checkStrictly) {
+				node.setChecked(true, false);
+				continue;
+			}
+			node.setChecked(true, true);
+
+			if (leafOnly) {
+				node.setChecked(false, false);
+				const traverse = function(node) {
+					const childNodes = node.getChildNodes(node.childNodesId);
+					childNodes.forEach((child) => {
+						if (!child.isLeaf) {
+							child.setChecked(false, false);
+						}
+						traverse(child);
+					});
+				};
+				traverse(node);
+			}
+		}
+	}
+
+	setCheckedNodes(array, leafOnly = false) {
+		const key = this.key;
+		const checkedKeys = {};
+		array.forEach((item) => {
+			checkedKeys[(item || {})[key]] = true;
+		});
+
+		this._setCheckedKeys(key, leafOnly, checkedKeys);
+	}
+
+	setCheckedKeys(keys, leafOnly = false) {
+		this.defaultCheckedKeys = keys;
+		const key = this.key;
+		const checkedKeys = {};
+		keys.forEach((key) => {
+			checkedKeys[key] = true;
+		});
+
+		this._setCheckedKeys(key, leafOnly, checkedKeys);
+	}
+
+	setDefaultExpandedKeys(keys) {
+		keys = keys || [];
+		this.defaultExpandedKeys = keys;
+
+		keys.forEach((key) => {
+			const node = this.getNode(key);
+			if (node) node.expand(null, this.autoExpandParent);
+		});
+	}
+
+	setChecked(data, checked, deep) {
+		const node = this.getNode(data);
+
+		if (node) {
+			node.setChecked(!!checked, deep);
+		}
+	}
+
+	getCurrentNode() {
+		return this.currentNode;
+	}
+
+	setCurrentNode(currentNode) {
+		const prevCurrentNode = this.currentNode;
+		if (prevCurrentNode) {
+			prevCurrentNode.isCurrent = false;
+		}
+		this.currentNode = currentNode;
+		this.currentNode.isCurrent = true;
+		
+		this.expandCurrentNodeParent && this.currentNode.expand(null, true)
+	}
+
+	setUserCurrentNode(node) {
+		const key = node[this.key];
+		const currNode = this.nodesMap[key];
+		this.setCurrentNode(currNode);
+	}
+
+	setCurrentNodeKey(key) {
+		if (key === null || key === undefined) {
+			this.currentNode && (this.currentNode.isCurrent = false);
+			this.currentNode = null;
+			return;
+		}
+		const node = this.getNode(key);
+		if (node) {
+			this.setCurrentNode(node);
+		}
+	}
+};

BIN
components/ly-tree-nvue/nvue-demo.zip


+ 114 - 0
components/ly-tree-nvue/tool/util.js

@@ -0,0 +1,114 @@
+export const NODE_KEY = '$treeNodeId';
+
+export const markNodeData = function(node, data) {
+	if (!data || data[NODE_KEY]) return;
+	Object.defineProperty(data, NODE_KEY, {
+		value: node.id,
+		enumerable: false,
+		configurable: false,
+		writable: false
+	});
+};
+
+export const getNodeKey = function(key, data) {
+	if (!key) return data[NODE_KEY];
+	return data[key];
+};
+
+export const objectAssign = function(target) {
+	for (let i = 1, j = arguments.length; i < j; i++) {
+		let source = arguments[i] || {};
+		for (let prop in source) {
+			if (source.hasOwnProperty(prop)) {
+				let value = source[prop];
+				if (value !== undefined) {
+					target[prop] = value;
+				}
+			}
+		}
+	}
+
+	return target;
+};
+
+// TODO: use native Array.find, Array.findIndex when IE support is dropped
+export const arrayFindIndex = function(arr, pred) {
+	for (let i = 0; i !== arr.length; ++i) {
+		if (pred(arr[i])) {
+			return i;
+		}
+	}
+	return -1;
+};
+
+export const getChildState = function(node) {
+	let all = true;
+	let none = true;
+	let allWithoutDisable = true;
+	for (let i = 0, j = node.length; i < j; i++) {
+		const n = node[i];
+		if (n.checked !== true || n.indeterminate) {
+			all = false;
+			if (!n.disabled) {
+				allWithoutDisable = false;
+			}
+		}
+		if (n.checked !== false || n.indeterminate) {
+			none = false;
+		}
+	}
+
+	return {
+		all,
+		none,
+		allWithoutDisable,
+		half: !all && !none
+	};
+};
+
+export const reInitChecked = function(node) {
+	if (!node || node.childNodesId.length === 0) return;
+
+	let childNodes = node.getChildNodes(node.childNodesId);
+	const {
+		all,
+		none,
+		half
+	} = getChildState(childNodes);
+	if (all) {
+		node.checked = true;
+		node.indeterminate = false;
+	} else if (half) {
+		node.checked = false;
+		node.indeterminate = true;
+	} else if (none) {
+		node.checked = false;
+		node.indeterminate = false;
+	}
+
+	let parent = node.getParent(node.parentId);
+	if (!parent || parent.level === 0) return;
+
+	if (!node.store().checkStrictly) {
+		reInitChecked(parent);
+	}
+};
+
+export const getPropertyFromData = function(node, prop) {
+	const props = node.store().props;
+	const data = node.data || {};
+	const config = props[prop];
+
+	if (typeof config === 'function') {
+		return config(data, node);
+	} else if (typeof config === 'string') {
+		return data[config];
+	} else if (typeof config === 'undefined') {
+		const dataProp = data[prop];
+		return dataProp === undefined ? '' : dataProp;
+	}
+};
+
+export const isNull = function(v) {
+	return v === undefined || v === null || v === '';
+}

+ 200 - 0
components/ly-tree/components/ly-checkbox.vue

@@ -0,0 +1,200 @@
+<template>
+	<text :class="classObj.wrapper" @click.stop="handleClick">
+		<text :class="[classObj.input, {'is-indeterminate': indeterminate, 'is-checked': checked, 'is-disabled': disabled}]">
+			<text :class="classObj.inner"></text>
+		</text>
+	</text>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				classObj: {}
+			}
+		},
+		
+		props: {
+			type: {
+				type: String,
+				validator(t) {
+					return t === 'radio' || t === 'checkbox'
+				}
+			},
+			checked: Boolean,
+			disabled: Boolean,
+			indeterminate: Boolean
+		},
+		
+		created() {
+			this.classObj = {
+				wrapper: `ly-${this.type}`,
+				input: `ly-${this.type}__input`,
+				inner: `ly-${this.type}__inner`
+			}
+		},
+		
+		methods: {
+			handleClick() {
+				this.$emit('check', this.checked);
+			}
+		}
+	}
+</script>
+
+<style>
+	/* lyRadio/lyCheckbox-start */
+	.ly-checkbox,
+	.ly-radio {
+		color: #606266;
+		font-weight: 500;
+		font-size: 28rpx;
+		cursor: pointer;
+		user-select: none;
+		padding-right: 16rpx
+	}
+	
+	.ly-checkbox__input,
+	.ly-radio__input {
+		cursor: pointer;
+		outline: 0;
+		line-height: 1;
+		vertical-align: middle
+	}
+	
+	.ly-checkbox__input.is-disabled .ly-checkbox__inner,
+	.ly-radio__input.is-disabled .ly-radio__inner {
+		background-color: #edf2fc;
+		border-color: #DCDFE6;
+		cursor: not-allowed
+	}
+	
+	.ly-checkbox__input.is-disabled .ly-checkbox__inner::after,
+	.ly-radio__input.is-disabled .ly-radio__inner::after {
+		cursor: not-allowed;
+		border-color: #C0C4CC
+	}
+	
+	.ly-checkbox__input.is-disabled .ly-checkbox__inner+.ly-checkbox__label,
+	.ly-radio__input.is-disabled .ly-radio__inner+.ly-radio__label {
+		cursor: not-allowed
+	}
+	
+	.ly-checkbox__input.is-disabled.is-checked .ly-checkbox__inner,
+	.ly-radio__input.is-disabled.is-checked .ly-radio__inner {
+		background-color: #F2F6FC;
+		border-color: #DCDFE6
+	}
+	
+	.ly-checkbox__input.is-disabled.is-checked .ly-checkbox__inner::after,
+	.ly-radio__input.is-disabled.is-checked .ly-radio__inner::after {
+		border-color: #C0C4CC
+	}
+	
+	.ly-checkbox__input.is-disabled.is-indeterminate .ly-checkbox__inner {
+		background-color: #F2F6FC;
+		border-color: #DCDFE6
+	}
+	
+	.ly-checkbox__input.is-disabled.is-indeterminate .ly-checkbox__inner::before {
+		background-color: #C0C4CC;
+		border-color: #C0C4CC
+	}
+	
+	.ly-checkbox__input.is-checked .ly-checkbox__inner,
+	.ly-radio__input.is-checked .ly-radio__inner,
+	.ly-checkbox__input.is-indeterminate .ly-checkbox__inner {
+		background-color: #409EFF;
+		border-color: #409EFF
+	}
+	
+	.ly-checkbox__input.is-disabled+text.ly-checkbox__label,
+	.ly-radio__input.is-disabled+text.ly-radio__label {
+		color: #C0C4CC;
+		cursor: not-allowed
+	}
+	
+	.ly-checkbox__input.is-checked .ly-checkbox__inner::after,
+	.ly-radio__input.is-checked .ly-radio__inner::after {
+		-webkit-transform: rotate(45deg) scaleY(1);
+		transform: rotate(45deg) scaleY(1)
+	}
+	
+	.ly-checkbox__input.is-checked+.ly-checkbox__label,
+	.ly-radio__input.is-checked+.ly-radio__label {
+		color: #409EFF
+	}
+	
+	.ly-checkbox__input.is-focus .ly-checkbox__inner,
+	.ly-radio__input.is-focus .ly-radio__inner {
+		border-color: #409EFF
+	}
+	
+	.ly-checkbox__input.is-indeterminate .ly-checkbox__inner::before {
+		content: '';
+		position: absolute;
+		display: block;
+		background-color: #FFF;
+		height: 6rpx;
+		-webkit-transform: scale(.5);
+		transform: scale(.5);
+		left: 0;
+		right: 0;
+		top: 10rpx
+	}
+	
+	.ly-checkbox__input.is-indeterminate .ly-checkbox__inner::after {
+		display: none
+	}
+	
+	.ly-checkbox__inner,
+	.ly-radio__inner {
+		display: inline-block;
+		position: relative;
+		border: 2rpx solid #DCDFE6;
+		border-radius: 4rpx;
+		-webkit-box-sizing: border-box;
+		box-sizing: border-box;
+		width: 28rpx;
+		height: 28rpx;
+		background-color: #FFF;
+		z-index: 1;
+		-webkit-transition: border-color .25s cubic-bezier(.71, -.46, .29, 1.46), background-color .25s cubic-bezier(.71, -.46, .29, 1.46);
+		transition: border-color .25s cubic-bezier(.71, -.46, .29, 1.46), background-color .25s cubic-bezier(.71, -.46, .29, 1.46)
+	}
+	
+	.ly-radio__inner {
+		border-radius: 50%;
+		width: 34rpx !important;
+		height: 34rpx !important;
+	}
+	
+	.ly-checkbox__inner::after,
+	.ly-radio__inner::after {
+		-webkit-box-sizing: content-box;
+		box-sizing: content-box;
+		content: "";
+		border: 2rpx solid #FFF;
+		border-left: 0;
+		border-top: 0;
+		height: 14rpx;
+		left: 10rpx;
+		position: absolute;
+		top: 2rpx;
+		-webkit-transform: rotate(45deg) scaleY(0);
+		transform: rotate(45deg) scaleY(0);
+		width: 6rpx;
+		-webkit-transition: -webkit-transform .15s ease-in .05s;
+		transition: -webkit-transform .15s ease-in .05s;
+		transition: transform .15s ease-in .05s;
+		transition: transform .15s ease-in .05s, -webkit-transform .15s ease-in .05s;
+		-webkit-transform-origin: center;
+		transform-origin: center
+	}
+	
+	.ly-radio__inner::after {
+		left: 12rpx !important;
+		top: 6rpx !important;
+	}
+	/* lyRadio/lyCheckbox-end */
+</style>

ファイルの差分が大きいため隠しています
+ 424 - 0
components/ly-tree/ly-tree-node.vue


+ 605 - 0
components/ly-tree/ly-tree.vue

@@ -0,0 +1,605 @@
+<template>
+	<view>
+		<template v-if="showLoading">
+			<view class="ly-loader ly-flex-center">
+				<view class="ly-loader-inner">加载中...</view>
+			</view>
+		</template>
+		
+		<template v-else>
+			<view v-if="isEmpty || !visible" 
+				class="ly-empty">
+				{{emptyText}}
+			</view>
+			<view :key="updateKey"
+				class="ly-tree" 
+				:class="{'is-empty': isEmpty || !visible}" 
+				role="tree" 
+				name="LyTreeExpand">
+				<ly-tree-node v-for="nodeId in childNodesId" 
+					:nodeId="nodeId" 
+					:render-after-expand="renderAfterExpand"
+					:show-checkbox="showCheckbox" 
+					:show-radio="showRadio" 
+					:check-only-leaf="checkOnlyLeaf"
+					:key="getNodeKey(nodeId)" 
+					:indent="indent" 
+					:icon-class="iconClass">
+				</ly-tree-node>
+			</view>
+		</template>
+	</view>
+</template>
+
+<script>
+	import Vue from 'vue'
+	import TreeStore from './model/tree-store.js';
+	import {getNodeKey} from './tool/util.js';
+	import LyTreeNode from './ly-tree-node.vue';
+
+	export default {
+		name: 'LyTree',
+		
+		componentName: 'LyTree',
+		
+		components: {
+			LyTreeNode
+		},
+		
+		data() {
+			return {
+				updateKey: new Date().getTime(), // 数据更新的时候,重新渲染树
+				elId: `ly_${Math.ceil(Math.random() * 10e5).toString(36)}`,
+				visible: true,
+				store: {
+					ready: false
+				},
+				currentNode: null,
+				childNodesId: []
+			};
+		},
+		
+		provide() {
+		    return {
+		       tree: this
+		    }
+		},
+		
+		props: {
+			// 展示数据
+			treeData: Array,
+			
+			// 自主控制loading加载,避免数据还没获取到的空档出现“暂无数据”字样
+			ready: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 内容为空的时候展示的文本
+			emptyText: {
+				type: String,
+				default: '暂无数据'
+			},
+			
+			// 是否在第一次展开某个树节点后才渲染其子节点
+			renderAfterExpand: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
+			nodeKey: String,
+			
+			// 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
+			checkStrictly: Boolean,
+			
+			// 是否默认展开所有节点
+			defaultExpandAll: Boolean,
+			
+			// 切换全部展开、全部折叠
+			toggleExpendAll: Boolean,
+			
+			// 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
+			expandOnClickNode: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 选中的时候展开节点
+			expandOnCheckNode: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
+			checkOnClickNode: Boolean,
+			checkDescendants: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 展开子节点的时候是否自动展开父节点
+			autoExpandParent: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 默认勾选的节点的 key 的数组
+			defaultCheckedKeys: Array,
+			
+			// 默认展开的节点的 key 的数组
+			defaultExpandedKeys: Array,
+			
+			// 是否展开当前节点的父节点
+			expandCurrentNodeParent: Boolean,
+			
+			// 当前选中的节点
+			currentNodeKey: [String, Number],
+			
+			// 是否最后一层叶子节点才显示单选/多选框
+			checkOnlyLeaf: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 节点是否可被选择
+			showCheckbox: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 节点单选
+			showRadio: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 配置选项
+			props: {
+				type: [Object, Function],
+				default () {
+					return {
+						children: 'children', // 指定子树为节点对象的某个属性值
+						label: 'label', // 指定节点标签为节点对象的某个属性值
+						disabled: 'disabled' //	指定节点选择框是否禁用为节点对象的某个属性值
+					};
+				}
+			},
+			
+			// 是否懒加载子节点,需与 load 方法结合使用
+			lazy: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 是否高亮当前选中节点,默认值是 false
+			highlightCurrent: Boolean,
+			
+			// 加载子树数据的方法,仅当 lazy 属性为true 时生效
+			load: Function,
+			
+			// 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
+			filterNodeMethod: Function,
+			
+			// 搜索时是否展示匹配项的所有子节点
+			childVisibleForFilterNode: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 是否每次只打开一个同级树节点展开
+			accordion: Boolean,
+			
+			// 相邻级节点间的水平缩进,单位为像素
+			indent: {
+				type: Number,
+				default: 18
+			},
+			
+			// 自定义树节点的展开图标
+			iconClass: String,
+			
+			// 是否显示节点图标,如果配置为true,需要配置props中对应的图标属性名称
+			showNodeIcon: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 当节点图标显示出错时,显示的默认图标
+			defaultNodeIcon: {
+				type: String,
+				default: 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/doc/github.svg'
+			},
+			
+			// 如果数据量较大,建议不要在node节点中添加parent属性,会造成性能损耗
+			isInjectParentInNode: {
+				type: Boolean,
+				default: false
+			}
+		},
+		
+		computed: {
+			isEmpty() {
+				if (this.store.root) {
+					const childNodes = this.store.root.getChildNodes(this.childNodesId);
+					
+					return !childNodes || childNodes.length === 0 || childNodes.every(({visible}) => !visible);
+				}
+				
+				return true;
+			},
+			showLoading() {
+				return !(this.store.ready && this.ready);
+			}
+		},
+		
+		watch: {
+			toggleExpendAll(newVal) {
+				this.store.toggleExpendAll(newVal);
+			},
+			defaultCheckedKeys(newVal) {
+				this.store.setDefaultCheckedKey(newVal);
+			},
+			defaultExpandedKeys(newVal) {
+				this.store.defaultExpandedKeys = newVal;
+				this.store.setDefaultExpandedKeys(newVal);
+			},
+			checkStrictly(newVal) {
+				this.store.checkStrictly = newVal || this.checkOnlyLeaf;
+			},
+			'store.root.childNodesId'(newVal) {
+				this.childNodesId = newVal;
+			},
+			'store.root.visible'(newVal) {
+				this.visible = newVal;
+			},
+			childNodesId(){
+				this.$nextTick(() => {
+					this.$emit('ly-tree-render-completed');
+				});
+			},
+			treeData: {
+				handler(newVal) {
+					this.updateKey = new Date().getTime();
+					this.store.setData(newVal);
+				},
+				deep: true
+			}
+		},
+		
+		methods: {
+			/*
+			 * @description 对树节点进行筛选操作
+			 * @method filter
+			 * @param {all} value 在 filter-node-method 中作为第一个参数
+			 * @param {Object} data 搜索指定节点的节点数据,不传代表搜索所有节点,假如要搜索A节点下面的数据,那么nodeData代表treeData中A节点的数据
+			*/
+			filter(value, data) {
+				if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter');
+				this.store.filter(value, data);
+			},
+			
+			/*
+			 * @description 获取节点的唯一标识符
+			 * @method getNodeKey
+			 * @param {String, Number} nodeId
+			 * @return {String, Number} 匹配到的数据中的某一项数据
+			*/
+			getNodeKey(nodeId) {
+				let node = this.store.root.getChildNodes([nodeId])[0];
+				return getNodeKey(this.nodeKey, node.data);
+			},
+			
+		   /*
+		    * @description 获取节点路径
+		    * @method getNodePath
+		    * @param {Object} data 节点数据
+		    * @return {Array} 路径数组
+		   */
+			getNodePath(data) {
+				return this.store.getNodePath(data);
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
+			 * @method getCheckedNodes
+			 * @param {Boolean} leafOnly 是否只是叶子节点,默认false
+			 * @param {Boolean} includeHalfChecked 是否包含半选节点,默认false
+			 * @return {Array} 目前被选中的节点所组成的数组
+			*/
+			getCheckedNodes(leafOnly, includeHalfChecked) {
+				return this.store.getCheckedNodes(leafOnly, includeHalfChecked);
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
+			 * @method getCheckedKeys
+			 * @param {Boolean} leafOnly 是否只是叶子节点,默认false,若为 true 则仅返回被选中的叶子节点的 keys
+			 * @param {Boolean} includeHalfChecked 是否返回indeterminate为true的节点,默认false
+			 * @return {Array} 目前被选中的节点所组成的数组
+			*/
+			getCheckedKeys(leafOnly, includeHalfChecked) {
+				return this.store.getCheckedKeys(leafOnly, includeHalfChecked);
+			},
+			
+			/*
+			 * @description 获取当前被选中节点的 data,若没有节点被选中则返回 null
+			 * @method getCurrentNode
+			 * @return {Object} 当前被选中节点的 data,若没有节点被选中则返回 null
+			*/
+			getCurrentNode() {
+				const currentNode = this.store.getCurrentNode();
+				return currentNode ? currentNode.data : null;
+			},
+			
+			/*
+			 * @description 获取当前被选中节点的 key,若没有节点被选中则返回 null
+			 * @method getCurrentKey
+			 * @return {all} 当前被选中节点的 key, 若没有节点被选中则返回 null
+			*/
+			getCurrentKey() {
+				const currentNode = this.getCurrentNode();
+				return currentNode ? currentNode[this.nodeKey] : null;
+			},
+			
+			/*
+			 * @description 设置全选/取消全选
+			 * @method setCheckAll
+			 * @param {Boolean} isCheckAll 选中状态,默认为true
+			*/
+			setCheckAll(isCheckAll = true) {
+				if (this.showRadio) throw new Error('You set the "show-radio" property, so you cannot select all nodes');
+				
+				if (!this.showCheckbox) console.warn('You have not set the property "show-checkbox". Please check your settings');
+				
+				this.store.setCheckAll(isCheckAll);
+			},
+			
+			/*
+			 * @description 设置目前勾选的节点
+			 * @method setCheckedNodes
+			 * @param {Array} nodes 接收勾选节点数据的数组
+			 * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
+			*/
+			setCheckedNodes(nodes, leafOnly) {
+				this.store.setCheckedNodes(nodes, leafOnly);
+			},
+			
+			/*
+			 * @description 通过 keys 设置目前勾选的节点
+			 * @method setCheckedKeys
+			 * @param {Array} keys 勾选节点的 key 的数组 
+			 * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
+			*/
+			setCheckedKeys(keys, leafOnly) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys');
+				this.store.setCheckedKeys(keys, leafOnly);
+			},
+			
+			/*
+			 * @description 通过 key / data 设置某个节点的勾选状态
+			 * @method setChecked
+			 * @param {all} data 勾选节点的 key 或者 data 
+			 * @param {Boolean} checked 节点是否选中
+			 * @param {Boolean} deep 是否设置子节点 ,默认为 false
+			*/
+			setChecked(data, checked, deep) {
+				this.store.setChecked(data, checked, deep);
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组
+			 * @method getHalfCheckedNodes
+			 * @return {Array} 目前半选中的节点所组成的数组
+			*/
+			getHalfCheckedNodes() {
+				return this.store.getHalfCheckedNodes();
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组
+			 * @method getHalfCheckedKeys
+			 * @return {Array} 目前半选中的节点的 key 所组成的数组
+			*/
+			getHalfCheckedKeys() {
+				return this.store.getHalfCheckedKeys();
+			},
+			
+			/*
+			 * @description 通过 node 设置某个节点的当前选中状态
+			 * @method setCurrentNode
+			 * @param {Object} node 待被选节点的 node
+			*/
+			setCurrentNode(node) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode');
+				this.store.setUserCurrentNode(node);
+			},
+			
+			/*
+			 * @description 通过 key 设置某个节点的当前选中状态
+			 * @method setCurrentKey
+			 * @param {all} key 待被选节点的 key,若为 null 则取消当前高亮的节点
+			*/
+			setCurrentKey(key) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey');
+				this.store.setCurrentNodeKey(key);
+			},
+			
+			/*
+			 * @description 根据 data 或者 key 拿到 Tree 组件中的 node
+			 * @method getNode
+			 * @param {all} data 要获得 node 的 key 或者 data
+			*/
+			getNode(data) {
+				return this.store.getNode(data);
+			},
+			
+			/*
+			 * @description 删除 Tree 中的一个节点
+			 * @method remove
+			 * @param {all} data 要删除的节点的 data 或者 node
+			*/
+			remove(data) {
+				this.store.remove(data);
+			},
+			
+			/*
+			 * @description 为 Tree 中的一个节点追加一个子节点
+			 * @method append
+			 * @param {Object} data 要追加的子节点的 data 
+			 * @param {Object} parentNode 子节点的 parent 的 data、key 或者 node
+			*/
+			append(data, parentNode) {
+				this.store.append(data, parentNode);
+			},
+			
+			/*
+			 * @description 为 Tree 的一个节点的前面增加一个节点
+			 * @method insertBefore
+			 * @param {Object} data 要增加的节点的 data 
+			 * @param {all} refNode 要增加的节点的后一个节点的 data、key 或者 node
+			*/
+			insertBefore(data, refNode) {
+				this.store.insertBefore(data, refNode);
+			},
+			
+			/*
+			 * @description 为 Tree 的一个节点的后面增加一个节点
+			 * @method insertAfter
+			 * @param {Object} data 要增加的节点的 data 
+			 * @param {all} refNode 要增加的节点的前一个节点的 data、key 或者 node
+			*/
+			insertAfter(data, refNode) {
+				this.store.insertAfter(data, refNode);
+			},
+			
+			/*
+			 * @description 通过 keys 设置节点子元素
+			 * @method updateKeyChildren
+			 * @param {String, Number} key 节点 key 
+			 * @param {Object} data 节点数据的数组
+			*/
+			updateKeyChildren(key, data) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
+				this.store.updateChildren(key, data);
+			}
+		},
+		
+		created() {
+			this.isTree = true;
+			
+			let props = this.props;
+			if (typeof this.props === 'function') props = this.props();
+			if (typeof props !== 'object') throw new Error('props must be of object type.');
+			
+			this.store = new TreeStore({
+				key: this.nodeKey,
+				data: this.treeData,
+				lazy: this.lazy,
+				props: props,
+				load: this.load,
+				showCheckbox: this.showCheckbox,
+				showRadio: this.showRadio,
+				currentNodeKey: this.currentNodeKey,
+				checkStrictly: this.checkStrictly || this.checkOnlyLeaf,
+				checkDescendants: this.checkDescendants,
+				expandOnCheckNode: this.expandOnCheckNode,
+				defaultCheckedKeys: this.defaultCheckedKeys,
+				defaultExpandedKeys: this.defaultExpandedKeys,
+				expandCurrentNodeParent: this.expandCurrentNodeParent,
+				autoExpandParent: this.autoExpandParent,
+				defaultExpandAll: this.defaultExpandAll,
+				filterNodeMethod: this.filterNodeMethod,
+				childVisibleForFilterNode: this.childVisibleForFilterNode,
+				showNodeIcon: this.showNodeIcon,
+				isInjectParentInNode: this.isInjectParentInNode
+			});
+
+			this.childNodesId = this.store.root.childNodesId;
+		},
+		
+		beforeDestroy() {
+			if (this.accordion) {
+				uni.$off(`${this.elId}-tree-node-expand`)
+			}
+		}
+	};
+</script>
+
+<style>
+	.ly-tree {
+		position: relative;
+		cursor: default;
+		background: #FFF;
+		color: #606266;
+		padding: 30rpx;
+	}
+	
+	.ly-tree.is-empty {
+		background: transparent;
+	}
+	
+	/* lyEmpty-start */
+	.ly-empty {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		margin-top: 100rpx;
+	}
+	/* lyEmpty-end */
+	
+	/* lyLoader-start */
+	.ly-loader {
+		margin-top: 100rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+	
+	.ly-loader-inner,
+	.ly-loader-inner:before,
+	.ly-loader-inner:after {
+		background: #efefef;
+		animation: load 1s infinite ease-in-out;
+		width: .5em;
+		height: 1em;
+	}
+	
+	.ly-loader-inner:before,
+	.ly-loader-inner:after {
+		position: absolute;
+		top: 0;
+		content: '';
+	}
+	
+	.ly-loader-inner:before {
+		left: -1em;
+	}
+	
+	.ly-loader-inner {
+		text-indent: -9999em;
+		position: relative;
+		font-size: 22rpx;
+		animation-delay: 0.16s;
+	}
+	
+	.ly-loader-inner:after {
+		left: 1em;
+		animation-delay: 0.32s;
+	}
+	/* lyLoader-end */
+	
+	@keyframes load {
+		0%,
+		80%,
+		100% {
+			box-shadow: 0 0 #efefef;
+			height: 1em;
+		}
+	
+		40% {
+			box-shadow: 0 -1.5em #efefef;
+			height: 1.5em;
+		}
+	}
+</style>

+ 538 - 0
components/ly-tree/model/node.js

@@ -0,0 +1,538 @@
+import {
+	markNodeData,
+	objectAssign,
+	arrayFindIndex,
+	getChildState,
+	reInitChecked,
+	getPropertyFromData,
+	isNull,
+	NODE_KEY
+} from '../tool/util';
+
+const getStore = function(store) {
+	let thisStore = store;
+	
+	return function() {
+		return thisStore;
+	}
+}
+
+let nodeIdSeed = 0;
+
+export default class Node {
+	constructor(options) {
+		this.time = new Date().getTime();
+		this.id = nodeIdSeed++;
+		this.text = null;
+		this.checked = false;
+		this.indeterminate = false;
+		this.data = null;
+		this.expanded = false;
+		this.parentId = null;
+		this.visible = true;
+		this.isCurrent = false;
+
+		for (let name in options) {
+			if (options.hasOwnProperty(name)) {
+				if (name === 'store') {
+					this.store = getStore(options[name]);
+				} else {
+					this[name] = options[name];
+				}
+			}
+		}
+		
+		if (!this.store()) {
+			throw new Error('[Node]store is required!');
+		}
+
+		// internal
+		this.level = 0;
+		this.loaded = false;
+		this.childNodesId = [];
+		this.loading = false;
+		this.label = getPropertyFromData(this, 'label');
+		this.key = this._getKey();
+		this.disabled = getPropertyFromData(this, 'disabled');
+		this.nextSibling = null;
+		this.previousSibling = null;
+		this.icon = '';
+		
+		this._handleParentAndLevel();
+		this._handleProps();
+		this._handleExpand();
+		this._handleCurrent();
+		
+		if (this.store().lazy) {
+			this.store()._initDefaultCheckedNode(this);
+		}
+
+		this.updateLeafState();
+	}
+	
+	_getKey() {
+		if (!this.data || Array.isArray(this.data)) return null;
+		
+		if (typeof this.data === 'object') {
+			const nodeKey = this.store().key;
+			const key = this.data[nodeKey];
+			
+			if (typeof key === 'undefined') {
+				throw new Error(`您配置的node-key为"${nodeKey}",但数据中并未找到对应"${nodeKey}"属性的值,请检查node-key的配置是否合理`)
+			}
+			
+			return key;
+		}
+		
+		throw new Error('不合法的data数据');
+	}
+	
+	_handleParentAndLevel() {
+		if (this.parentId !== null) {
+			let parent = this.getParent(this.parentId);
+			
+			if (this.store().isInjectParentInNode) {
+				this.parent = parent;
+			}
+			
+			// 由于这里做了修改,默认第一个对象不会被注册到nodesMap中,所以找不到parent会报错,所以默认parent的level是0
+			if (!parent) {
+				parent = {
+					level: 0
+				}
+			} else {
+				const parentChildNodes = parent.getChildNodes(parent.childNodesId);
+				const index = parent.childNodesId.indexOf(this.key);
+				this.nextSibling = index > -1 ? parentChildNodes[index + 1] : null;
+				this.previousSibling = index > 0 ? parentChildNodes[index - 1] : null;
+			}
+			this.level = parent.level + 1;
+		}
+	}
+	
+	_handleProps() {
+		const props = this.store().props;
+		
+		if (this.store().showNodeIcon) {
+			if (props && typeof props.icon !== 'undefined') {
+				this.icon = getPropertyFromData(this, 'icon');
+			} else {
+				console.warn('请配置props属性中的"icon"字段')
+			}
+		}
+		
+		this.store().registerNode(this);
+		
+		if (props && typeof props.isLeaf !== 'undefined') {
+			const isLeaf = getPropertyFromData(this, 'isLeaf');
+			if (typeof isLeaf === 'boolean') {
+				this.isLeafByUser = isLeaf;
+			}
+		}
+	}
+	
+	_handleExpand() {
+		if (this.store().lazy !== true && this.data) {
+			this.setData(this.data);
+		
+			if (this.store().defaultExpandAll) {
+				this.expanded = true;
+			}
+		} else if (this.level > 0 && this.store().lazy && this.store().defaultExpandAll) {
+			this.expand();
+		}
+		
+		if (!Array.isArray(this.data)) {
+			markNodeData(this, this.data);
+		}
+		
+		if (!this.data) return;
+		
+		const defaultExpandedKeys = this.store().defaultExpandedKeys;
+		const key = this.store().key;
+		if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) {
+			this.expand(null, this.store().autoExpandparent);
+		}
+	}
+	
+	_handleCurrent() {
+		const key = this.store().key;
+		
+		if (key && this.store().currentNodeKey !== undefined && this.key === this.store().currentNodeKey) {
+			this.store().currentNode = this;
+			this.store().currentNode.isCurrent = true;
+		}
+	}
+	
+	destroyStore() {
+		getStore(null)
+	}
+
+	setData(data) {
+		if (!Array.isArray(data)) {
+			markNodeData(this, data);
+		}
+
+		this.data = data;
+		this.childNodesId = [];
+
+		let children;
+		if (this.level === 0 && Array.isArray(this.data)) {
+			children = this.data;
+		} else {
+			children = getPropertyFromData(this, 'children') || [];
+		}
+
+		for (let i = 0, j = children.length; i < j; i++) {
+			this.insertChild({
+				data: children[i]
+			});
+		}
+	}
+
+	contains(target, deep = true) {
+		const walk = function(parent) {
+			const children = parent.getChildNodes(parent.childNodesId) || [];
+			let result = false;
+			for (let i = 0, j = children.length; i < j; i++) {
+				const child = children[i];
+				if (child === target || (deep && walk(child))) {
+					result = true;
+					break;
+				}
+			}
+			return result;
+		};
+
+		return walk(this);
+	}
+
+	remove() {
+		if (this.parentId !== null) {
+			const parent = this.getParent(this.parentId);
+			parent.removeChild(this);
+		}
+	}
+
+	insertChild(child, index, batch) {
+		if (!child) throw new Error('insertChild error: child is required.');
+
+		if (!(child instanceof Node)) {
+			if (!batch) {
+				const children = this.getChildren(true);
+				if (children.indexOf(child.data) === -1) {
+					if (typeof index === 'undefined' || index < 0) {
+						children.push(child.data);
+					} else {
+						children.splice(index, 0, child.data);
+					}
+				}
+			}
+			
+			objectAssign(child, {
+				parentId: isNull(this.key) ? '' : this.key,
+				store: this.store()
+			});
+			child = new Node(child);
+		}
+
+		child.level = this.level + 1;
+
+		if (typeof index === 'undefined' || index < 0) {
+			this.childNodesId.push(child.key);
+		} else {
+			this.childNodesId.splice(index, 0, child.key);
+		}
+
+		this.updateLeafState();
+	}
+
+	insertBefore(child, ref) {
+		let index;
+		if (ref) {
+			index = this.childNodesId.indexOf(ref.id);
+		}
+		this.insertChild(child, index);
+	}
+
+	insertAfter(child, ref) {
+		let index;
+		if (ref) {
+			index = this.childNodesId.indexOf(ref.id);
+			if (index !== -1) index += 1;
+		}
+		this.insertChild(child, index);
+	}
+
+	removeChild(child) {
+		const children = this.getChildren() || [];
+		const dataIndex = children.indexOf(child.data);
+		if (dataIndex > -1) {
+			children.splice(dataIndex, 1);
+		}
+		
+		const index = this.childNodesId.indexOf(child.key);
+		
+		if (index > -1) {
+			this.store() && this.store().deregisterNode(child);
+			child.parentId = null;
+			this.childNodesId.splice(index, 1);
+		}
+		
+		this.updateLeafState();
+	}
+
+	removeChildByData(data) {
+		let targetNode = null;
+
+		for (let i = 0; i < this.childNodesId.length; i++) {
+			let node = this.getChildNodes(this.childNodesId);
+			if (node[i].data === data) {
+				targetNode = node[i];
+				break;
+			}
+		}
+
+		if (targetNode) {
+			this.removeChild(targetNode);
+		}
+	}
+
+	// 为了避免APP端parent嵌套结构导致报错,这里parent需要从nodesMap中获取
+	getParent(parentId) {
+		try {
+			if (!parentId.toString()) return null;
+			return this.store().nodesMap[parentId];
+		} catch (error) {
+			return null;
+		}
+	}
+
+	// 为了避免APP端childNodes嵌套结构导致报错,这里childNodes需要从nodesMap中获取
+	getChildNodes(childNodesId) {
+		let childNodes = [];
+		if (childNodesId.length === 0) return childNodes;
+		childNodesId.forEach((key) => {
+			childNodes.push(this.store().nodesMap[key]);
+		})
+		return childNodes;
+	}
+
+	expand(callback, expandparent) {
+		const done = () => {
+			if (expandparent) {
+				let parent = this.getParent(this.parentId);
+				while (parent && parent.level > 0) {
+					parent.expanded = true;
+					parent = this.getParent(parent.parentId);
+				}
+			}
+			this.expanded = true;
+			if (callback) callback();
+		};
+
+		if (this.shouldLoadData()) {
+			this.loadData(function(data) {
+				if (Array.isArray(data)) {
+					if (this.checked) {
+						this.setChecked(true, true);
+					} else if (!this.store().checkStrictly) {
+						reInitChecked(this);
+					}
+					done();
+				}
+			});
+		} else {
+			done();
+		}
+	}
+
+	doCreateChildren(array, defaultProps = {}) {
+		array.forEach((item) => {
+			this.insertChild(objectAssign({
+				data: item
+			}, defaultProps), undefined, true);
+		});
+	}
+
+	collapse() {
+		this.expanded = false;
+	}
+
+	shouldLoadData() {
+		return this.store().lazy === true && this.store().load && !this.loaded;
+	}
+
+	updateLeafState() {
+		if (this.store().lazy === true && this.loaded !== true && typeof this.isLeafByUser !== 'undefined') {
+			this.isLeaf = this.isLeafByUser;
+			return;
+		}
+		const childNodesId = this.childNodesId;
+		if (!this.store().lazy || (this.store().lazy === true && this.loaded === true)) {
+			this.isLeaf = !childNodesId || childNodesId.length === 0;
+			return;
+		}
+		this.isLeaf = false;
+	}
+
+	setChecked(value, deep, recursion, passValue) {
+		this.indeterminate = value === 'half';
+		this.checked = value === true;
+		
+		if (this.checked && this.store().expandOnCheckNode) {
+			this.expand(null, true)
+		}
+		
+		if (this.store().checkStrictly) return;
+		if (this.store().showRadio) return;
+
+		if (!(this.shouldLoadData() && !this.store().checkDescendants)) {
+			let childNodes = this.getChildNodes(this.childNodesId);
+			let {
+				all,
+				allWithoutDisable
+			} = getChildState(childNodes);
+
+			if (!this.isLeaf && (!all && allWithoutDisable)) {
+				this.checked = false;
+				value = false;
+			}
+
+			const handleDescendants = () => {
+				if (deep) {
+					let childNodes = this.getChildNodes(this.childNodesId)
+					for (let i = 0, j = childNodes.length; i < j; i++) {
+						const child = childNodes[i];
+						passValue = passValue || value !== false;
+						const isCheck = child.disabled ? child.checked : passValue;
+						child.setChecked(isCheck, deep, true, passValue);
+					}
+					const {
+						half,
+						all
+					} = getChildState(childNodes);
+					
+					if (!all) {
+						this.checked = all;
+						this.indeterminate = half;
+					}
+				}
+			};
+
+			if (this.shouldLoadData()) {
+				this.loadData(() => {
+					handleDescendants();
+					reInitChecked(this);
+				}, {
+					checked: value !== false
+				});
+				return;
+			} else {
+				handleDescendants();
+			}
+		}
+
+		if (!this.parentId) return;
+
+		let parent = this.getParent(this.parentId);
+		if (parent && parent.level === 0) return;
+
+		if (!recursion) {
+			reInitChecked(parent);
+		}
+	}
+
+	setRadioChecked(value) {
+		const allNodes = this.store()._getAllNodes().sort((a, b) => b.level - a.level);
+		allNodes.forEach(node => node.setChecked(false, false));
+		this.checked = value === true;
+	}
+
+	getChildren(forceInit = false) {
+		if (this.level === 0) return this.data;
+		const data = this.data;
+		if (!data) return null;
+
+		const props = this.store().props;
+		let children = 'children';
+		if (props) {
+			children = props.children || 'children';
+		}
+
+		if (data[children] === undefined) {
+			data[children] = null;
+		}
+
+		if (forceInit && !data[children]) {
+			data[children] = [];
+		}
+
+		return data[children];
+	}
+
+	updateChildren() {
+		let childNodes = this.getChildNodes(this.childNodesId);
+		const newData = this.getChildren() || [];
+		const oldData = childNodes.map((node) => node.data);
+
+		const newDataMap = {};
+		const newNodes = [];
+
+		newData.forEach((item, index) => {
+			const key = item[NODE_KEY];
+			const isNodeExists = !!key && arrayFindIndex(oldData, data => data[NODE_KEY] === key) >= 0;
+			if (isNodeExists) {
+				newDataMap[key] = {
+					index,
+					data: item
+				};
+			} else {
+				newNodes.push({
+					index,
+					data: item
+				});
+			}
+		});
+
+		if (!this.store().lazy) {
+			oldData.forEach((item) => {
+				if (!newDataMap[item[NODE_KEY]]) this.removeChildByData(item);
+			});
+		}
+
+		newNodes.forEach(({
+			index,
+			data
+		}) => {
+			this.insertChild({
+				data
+			}, index);
+		});
+
+		this.updateLeafState();
+	}
+
+	loadData(callback, defaultProps = {}) {
+		if (this.store().lazy === true && 
+			this.store().load && !this.loaded && 
+			(!this.loading || Object.keys(defaultProps).length)
+		) {
+			this.loading = true;
+
+			const resolve = (children) => {
+				this.loaded = true;
+				this.loading = false;
+				this.childNodesId = [];
+				this.doCreateChildren(children, defaultProps);
+				this.updateLeafState();
+				
+				callback && callback.call(this,children);
+			};
+
+			this.store().load(this, resolve);
+		} else {
+			callback && callback.call(this);
+		}
+	}
+}

+ 419 - 0
components/ly-tree/model/tree-store.js

@@ -0,0 +1,419 @@
+import Node from './node';
+import {
+	getNodeKey,
+	getPropertyFromData
+} from '../tool/util';
+
+export default class TreeStore {
+	constructor(options) {
+		this.ready = false;
+		this.currentNode = null;
+		this.currentNodeKey = null;
+
+		Object.assign(this, options);
+
+		if (!this.key) {
+			throw new Error('[Tree] nodeKey is required');
+		}
+
+		this.nodesMap = {};
+		this.root = new Node({
+			data: this.data,
+			store: this
+		});
+
+		if (this.lazy && this.load) {
+			const loadFn = this.load;
+			loadFn(this.root, (data) => {
+				this.root.doCreateChildren(data);
+				this._initDefaultCheckedNodes();
+				this.ready = true;
+			});
+		} else {
+			this._initDefaultCheckedNodes();
+			this.ready = true;
+		}
+	}
+
+	filter(value, data) {
+		const filterNodeMethod = this.filterNodeMethod;
+		const lazy = this.lazy;
+		const _self = this;
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if (data && typeof data === 'object') {
+					let nodePath = _self.getNodePath(child.data);
+					if (!nodePath.some(pathItem => pathItem[_self.key] === data[_self.key])) {
+						child.visible = false;
+						traverse(child);
+						return;
+					}
+				}
+				
+				if (_self.childVisibleForFilterNode) {
+					let parent = child.getParent(child.parentId);
+					child.visible = filterNodeMethod.call(child, value, child.data, child) || (parent && parent.visible);
+				} else {
+					child.visible = filterNodeMethod.call(child, value, child.data, child);
+				}
+				
+				traverse(child);
+			});
+
+			if (!node.visible && childNodes.length) {
+				let allHidden = true;
+				allHidden = !childNodes.some(child => child.visible);
+
+				if (node.root) {
+					node.root.visible = allHidden === false;
+				} else {
+					node.visible = allHidden === false;
+				}
+			}
+			
+			if (!value) return;
+
+			if (node.visible && !node.isLeaf && !lazy) node.expand();
+		};
+
+		traverse(this);
+	}
+
+	setData(newVal) {
+		const instanceChanged = newVal !== this.root.data;
+		if (instanceChanged) {
+			this.root.setData(newVal);
+			this._initDefaultCheckedNodes();
+		} else {
+			this.root.updateChildren();
+		}
+	}
+
+	getNode(data) {
+		if (data instanceof Node) return data;
+		const key = typeof data !== 'object' ? data : getNodeKey(this.key, data);
+		if (!key) return null;
+		return this.nodesMap[key] || null;
+	}
+
+	insertBefore(data, refData) {
+		const refNode = this.getNode(refData);
+		let parent = refNode.getParent(refNode.parentId);
+		parent.insertBefore({
+			data
+		}, refNode);
+	}
+
+	insertAfter(data, refData) {
+		const refNode = this.getNode(refData);
+		let parent = refNode.getParent(refNode.parentId);
+		parent.insertAfter({
+			data
+		}, refNode);
+	}
+
+	remove(data) {
+		const node = this.getNode(data);
+
+		if (node && node.parentId !== null) {
+			let parent = node.getParent(node.parentId);
+			if (node === this.currentNode) {
+				this.currentNode = null;
+			}
+			parent.removeChild(node);
+		}
+	}
+
+	append(data, parentData) {
+		const parentNode = parentData ? this.getNode(parentData) : this.root;
+
+		if (parentNode) {
+			parentNode.insertChild({
+				data
+			});
+		}
+	}
+
+	_initDefaultCheckedNodes() {
+		const defaultCheckedKeys = this.defaultCheckedKeys || [];
+		const nodesMap = this.nodesMap;
+		let checkedKeyfromData = [];
+		let totalCheckedKeys = []
+		
+		for (let key in nodesMap) {
+			let checked = getPropertyFromData(nodesMap[key], 'checked') || false;
+			checked && checkedKeyfromData.push(key);
+		}
+		
+		totalCheckedKeys = Array.from(new Set([...defaultCheckedKeys, ...checkedKeyfromData]));
+		totalCheckedKeys.forEach((checkedKey) => {
+			const node = nodesMap[checkedKey];
+			
+			if (node) {
+				node.setChecked(true, !this.checkStrictly);
+			}
+		});
+	}
+
+	_initDefaultCheckedNode(node) {
+		const defaultCheckedKeys = this.defaultCheckedKeys || [];
+
+		if (defaultCheckedKeys.indexOf(node.key) !== -1) {
+			node.setChecked(true, !this.checkStrictly);
+		}
+	}
+	
+	toggleExpendAll(isExpandAll) {
+		const allNodes = this._getAllNodes();
+		
+		allNodes.forEach(item => {
+			const node = this.getNode(item.key); 
+			
+			if (node) isExpandAll ? node.expand() : node.collapse();
+		});
+	}
+	
+	setCheckAll(isCkeckAll) {
+		const allNodes = this._getAllNodes();
+		
+		allNodes.forEach(item => {
+			item.setChecked(isCkeckAll, false);
+		}); 
+	}
+
+	setDefaultCheckedKey(newVal) {
+		if (newVal !== this.defaultCheckedKeys) {
+			this.defaultCheckedKeys = newVal;
+			this._initDefaultCheckedNodes();
+		}
+	}
+
+	registerNode(node) {
+
+		const key = this.key;
+		if (!key || !node || !node.data) return;
+
+		const nodeKey = node.key;
+		if (nodeKey !== undefined) this.nodesMap[node.key] = node;
+	}
+
+	deregisterNode(node) {
+		const key = this.key;
+		if (!key || !node || !node.data) return;
+
+		let childNodes = node.getChildNodes(node.childNodesId);
+		childNodes.forEach(child => {
+			this.deregisterNode(child);
+		});
+
+		delete this.nodesMap[node.key];
+	}
+	
+	getNodePath(data) {
+		if (!this.key) throw new Error('[Tree] nodeKey is required in getNodePath');
+		const node = this.getNode(data);
+		if (!node) return [];
+		
+		const path = [node.data];
+		let parent = node.getParent(node.parentId);
+		while (parent && parent !== this.root) {
+			path.push(parent.data);
+			parent = parent.getParent(parent.parentId);
+		}
+		return path.reverse();
+	}
+
+	getCheckedNodes(leafOnly = false, includeHalfChecked = false) {
+		const checkedNodes = [];
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if ((child.checked || (includeHalfChecked && child.indeterminate)) && (!leafOnly || (leafOnly && child.isLeaf))) {
+					checkedNodes.push(child.data);
+				}
+
+				traverse(child);
+			});
+		};
+
+		traverse(this);
+
+		return checkedNodes;
+	}
+
+	getCheckedKeys(leafOnly = false, includeHalfChecked = false) {
+		return this.getCheckedNodes(leafOnly, includeHalfChecked).map((data) => (data || {})[this.key]);
+	}
+
+	getHalfCheckedNodes() {
+		const nodes = [];
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if (child.indeterminate) {
+					nodes.push(child.data);
+				}
+
+				traverse(child);
+			});
+		};
+
+		traverse(this);
+
+		return nodes;
+	}
+
+	getHalfCheckedKeys() {
+		return this.getHalfCheckedNodes().map((data) => (data || {})[this.key]);
+	}
+
+	_getAllNodes() {
+		const allNodes = [];
+		const nodesMap = this.nodesMap;
+		for (let nodeKey in nodesMap) {
+			if (nodesMap.hasOwnProperty(nodeKey)) {
+				allNodes.push(nodesMap[nodeKey]);
+			}
+		}
+
+		return allNodes;
+	}
+
+	updateChildren(key, data) {
+		const node = this.nodesMap[key];
+		if (!node) return;
+		const childNodes = node.getChildNodes(node.childNodesId);
+		for (let i = childNodes.length - 1; i >= 0; i--) {
+			const child = childNodes[i];
+			this.remove(child.data);
+		}
+		for (let i = 0, j = data.length; i < j; i++) {
+			const child = data[i];
+			this.append(child, node.data);
+		}
+	}
+
+	_setCheckedKeys(key, leafOnly = false, checkedKeys) {
+		const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level);
+		const cache = Object.create(null);
+		const keys = Object.keys(checkedKeys);
+		allNodes.forEach(node => node.setChecked(false, false));
+		for (let i = 0, j = allNodes.length; i < j; i++) {
+			const node = allNodes[i];
+			let nodeKey = node.data[key];
+			
+			if (typeof nodeKey === 'undefined') continue;
+			
+			nodeKey = nodeKey.toString();
+			let checked = keys.indexOf(nodeKey) > -1;
+			if (!checked) {
+				if (node.checked && !cache[nodeKey]) {
+					node.setChecked(false, false);
+				}
+				continue;
+			}
+
+			let parent = node.getParent(node.parentId);
+			while (parent && parent.level > 0) {
+				cache[parent.data[key]] = true;
+				parent = parent.getParent(parent.parentId);
+			}
+
+			if (node.isLeaf || this.checkStrictly) {
+				node.setChecked(true, false);
+				continue;
+			}
+			node.setChecked(true, true);
+
+			if (leafOnly) {
+				node.setChecked(false, false);
+				const traverse = function(node) {
+					const childNodes = node.getChildNodes(node.childNodesId);
+					childNodes.forEach((child) => {
+						if (!child.isLeaf) {
+							child.setChecked(false, false);
+						}
+						traverse(child);
+					});
+				};
+				traverse(node);
+			}
+		}
+	}
+
+	setCheckedNodes(array, leafOnly = false) {
+		const key = this.key;
+		const checkedKeys = {};
+		array.forEach((item) => {
+			checkedKeys[(item || {})[key]] = true;
+		});
+
+		this._setCheckedKeys(key, leafOnly, checkedKeys);
+	}
+
+	setCheckedKeys(keys, leafOnly = false) {
+		this.defaultCheckedKeys = keys;
+		const key = this.key;
+		const checkedKeys = {};
+		keys.forEach((key) => {
+			checkedKeys[key] = true;
+		});
+
+		this._setCheckedKeys(key, leafOnly, checkedKeys);
+	}
+
+	setDefaultExpandedKeys(keys) {
+		keys = keys || [];
+		this.defaultExpandedKeys = keys;
+
+		keys.forEach((key) => {
+			const node = this.getNode(key);
+			if (node) node.expand(null, this.autoExpandParent);
+		});
+	}
+
+	setChecked(data, checked, deep) {
+		const node = this.getNode(data);
+
+		if (node) {
+			node.setChecked(!!checked, deep);
+		}
+	}
+
+	getCurrentNode() {
+		return this.currentNode;
+	}
+
+	setCurrentNode(currentNode) {
+		const prevCurrentNode = this.currentNode;
+		if (prevCurrentNode) {
+			prevCurrentNode.isCurrent = false;
+		}
+		this.currentNode = currentNode;
+		this.currentNode.isCurrent = true;
+		
+		this.expandCurrentNodeParent && this.currentNode.expand(null, true)
+	}
+
+	setUserCurrentNode(node) {
+		const key = node[this.key];
+		const currNode = this.nodesMap[key];
+		this.setCurrentNode(currNode);
+	}
+
+	setCurrentNodeKey(key) {
+		if (key === null || key === undefined) {
+			this.currentNode && (this.currentNode.isCurrent = false);
+			this.currentNode = null;
+			return;
+		}
+		const node = this.getNode(key);
+		if (node) {
+			this.setCurrentNode(node);
+		}
+	}
+};

+ 115 - 0
components/ly-tree/tool/util.js

@@ -0,0 +1,115 @@
+export const NODE_KEY = '$treeNodeId';
+
+export const markNodeData = function(node, data) {
+	if (!data || data[NODE_KEY]) return;
+	Object.defineProperty(data, NODE_KEY, {
+		value: node.id,
+		enumerable: false,
+		configurable: false,
+		writable: false
+	});
+};
+
+export const getNodeKey = function(key, data) {
+	if (!data) return null;
+	if (!key) return data[NODE_KEY];
+	return data[key];
+};
+
+export const objectAssign = function(target) {
+	for (let i = 1, j = arguments.length; i < j; i++) {
+		let source = arguments[i] || {};
+		for (let prop in source) {
+			if (source.hasOwnProperty(prop)) {
+				let value = source[prop];
+				if (value !== undefined) {
+					target[prop] = value;
+				}
+			}
+		}
+	}
+
+	return target;
+};
+
+// TODO: use native Array.find, Array.findIndex when IE support is dropped
+export const arrayFindIndex = function(arr, pred) {
+	for (let i = 0; i !== arr.length; ++i) {
+		if (pred(arr[i])) {
+			return i;
+		}
+	}
+	return -1;
+};
+
+export const getChildState = function(node) {
+	let all = true;
+	let none = true;
+	let allWithoutDisable = true;
+	for (let i = 0, j = node.length; i < j; i++) {
+		const n = node[i];
+		if (n.checked !== true || n.indeterminate) {
+			all = false;
+			if (!n.disabled) {
+				allWithoutDisable = false;
+			}
+		}
+		if (n.checked !== false || n.indeterminate) {
+			none = false;
+		}
+	}
+
+	return {
+		all,
+		none,
+		allWithoutDisable,
+		half: !all && !none
+	};
+};
+
+export const reInitChecked = function(node) {
+	if (!node || node.childNodesId.length === 0) return;
+
+	let childNodes = node.getChildNodes(node.childNodesId);
+	const {
+		all,
+		none,
+		half
+	} = getChildState(childNodes);
+	if (all) {
+		node.checked = true;
+		node.indeterminate = false;
+	} else if (half) {
+		node.checked = false;
+		node.indeterminate = true;
+	} else if (none) {
+		node.checked = false;
+		node.indeterminate = false;
+	}
+
+	let parent = node.getParent(node.parentId);
+	if (!parent || parent.level === 0) return;
+
+	if (!node.store().checkStrictly) {
+		reInitChecked(parent);
+	}
+};
+
+export const getPropertyFromData = function(node, prop) {
+	const props = node.store().props;
+	const data = node.data || {};
+	const config = props[prop];
+
+	if (typeof config === 'function') {
+		return config(data, node);
+	} else if (typeof config === 'string') {
+		return data[config];
+	} else if (typeof config === 'undefined') {
+		const dataProp = data[prop];
+		return dataProp === undefined ? '' : dataProp;
+	}
+};
+
+export const isNull = function(v) {
+	return v === undefined || v === null || v === '';
+}

BIN
components/t-c-contacts/icon/ICE.png


BIN
components/t-c-contacts/icon/bangong.png


BIN
components/t-c-contacts/icon/bumen.png


BIN
components/t-c-contacts/icon/jiagou.png


BIN
components/t-c-contacts/icon/qingjia.png


BIN
components/t-c-contacts/icon/qita.png


BIN
components/t-c-contacts/icon/yongyin.png


BIN
components/t-c-contacts/icon/you.png


+ 144 - 0
components/t-c-contacts/t-c-contacts.vue

@@ -0,0 +1,144 @@
+<template>
+	<view class="box">
+		<view class="ul">
+			<view class="li">
+				<image src="./icon/bangong.png" mode=""></image>
+				<text>办公品领用</text>
+			</view>
+			<view class="li">
+				<image src="./icon/yongyin.png" mode=""></image>
+				<text>用印申请</text>
+			</view>
+			<view class="li">
+				<image src="./icon/qingjia.png" mode=""></image>
+				<text>请假</text>
+			</view>
+			<view class="li">
+				<image src="./icon/qita.png" mode=""></image>
+				<text>其他申请</text>
+			</view>
+		</view>
+
+		<view class="box_info">
+			<view class="box_info_nengyuan">
+				<image src="./icon/ICE.png" mode=""></image>
+				<text>国家能源集团枣泉煤矿</text>
+			</view>
+			<view class="box_info_item">
+				<view class="box_info_item_li" @click="dianji()">
+					<image src="./icon/jiagou.png" mode=""></image>
+					<text>组织架构</text>
+					<image src="./icon/you.png" mode="" class="box_info_item_li_img"></image>
+				</view>
+				<view class="box_info_item_li">
+					<image src="./icon/bumen.png" mode=""></image>
+					<text>我的部门</text>
+					<image src="./icon/you.png" mode="" class="box_info_item_li_img"></image>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+
+			};
+		},
+		methods: {
+			dianji(){
+				uni.navigateTo({
+					url:"../../production/t-c-c-contacts-info/t-c-c-contacts-info"
+				})
+			}
+		},
+		onLoad() {
+
+		}
+	}
+</script>
+
+<style>
+	.ul {
+		width: 100%;
+		display: flex;
+		justify-content: space-around;
+		background-color: #fff;
+		padding: 20rpx 0px;
+	}
+
+	.li {
+		width: 20%;
+		text-align: center;
+	}
+
+	.li>image {
+		width: 90rpx;
+		height: 90rpx;
+		display: block;
+		margin: 0 auto;
+	}
+
+	.li>text {
+		font-size: 28rpx;
+	}
+
+	.box_info {
+		width: 100%;
+		background-color: #fff;
+		margin-top: 10px;
+	}
+
+	.box_info_nengyuan {
+		width: 95%;
+		height: 70px;
+		line-height: 80px;
+		margin: auto;
+		display: flex;
+		border-bottom: 1px solid #f5f5f5;
+	}
+
+	.box_info_nengyuan>image {
+		margin-top: 20px;
+		width: 80rpx;
+		height: 80rpx;
+	}
+
+	.box_info_nengyuan>text {
+		font-size: 18px;
+		font-weight: 800;
+		margin-left: 10px;
+	}
+
+	.box_info_item {
+		width: 90%;
+		margin: auto;
+	}
+
+	.box_info_item_li {
+		display: flex;
+		height: 60px;
+		line-height: 60px;
+		border-bottom: 1px solid #f5f5f5;
+	}
+
+	.box_info_item_li>image {
+		width: 40rpx;
+		height: 40rpx;
+		margin-top: 20px;
+	}
+
+	.box_info_item_li>text {
+		width: 80%;
+		font-size: 16px;
+		margin-left: 10px;
+	}
+
+	.box_info_item_li>.box_info_item_li_img {
+		width: 25rpx;
+		height: 25rpx;
+		margin-top: 25px;
+	}
+</style>

+ 127 - 0
components/t-w-application/t-w-application.vue

@@ -0,0 +1,127 @@
+<template>
+	<view class="headers">
+		<view class="headers_img">
+			<image :src="header" mode=""></image>
+		</view>
+		<view class="headers_nav">
+			<view class="headers_ul">
+				<view class="headers_li" v-for="(item,index) in nav" :key="index" @click="getnav(item.link)">
+					<view class="headers_li_img">
+						<image :src="item.icon" mode=""></image>
+					</view>
+					<view class="headers_li_text">  
+						{{item.title}}
+					</view>
+			   </view>
+			</view>  
+		</view>
+		
+		
+		<view class="headers_list" v-for="(item,index) in module" :key="index">
+			<view class="headers_title">
+				<text class="headers_title_h1">
+					{{item.title}}				
+				</text>	
+				<!-- <text class="headers_title_h2">
+					{{item.titles}}				
+				</text>	 -->
+			</view>
+			<view class="headers_ul" >
+				<view class="headers_li" v-for="(items,index) in item.children" :key="index" @click="getmodule(items.link)">
+					<view class="headers_li_img">
+						<image :src="items.icon" mode=""></image>
+					</view>
+					<view class="headers_li_text">  
+						{{items.title}}
+					</view>
+					
+				</view>
+			</view>
+		</view>	
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				module:[],
+				nav:[],
+				header:"",
+			};
+		},
+		created() {
+			this.getworkbench()
+		},
+		methods:{
+			getworkbench(){
+				this.$api.workbench_index({
+		
+				}).then((res)=>{
+					this.header=res.data.data.header
+					this.nav=res.data.data.nav
+					this.module=res.data.data.module
+				})
+			},
+			getnav(link){
+				console.log(link)
+			},
+			getmodule(link){
+				console.log(link)
+			}
+		},
+		
+	}
+</script>
+
+<style>
+	.headers{
+		width: 100%;
+		background-color: #F5F5F5;
+	}
+	.headers_img>image{
+		width: 100%;
+		height: 360rpx;
+	}
+	.headers_nav{
+		width: 100%;
+		background-color: #FFFFFF;
+		margin-bottom: 25rpx;
+	}
+	.headers_ul{
+		width: 100%;
+		display: flex;
+		 flex-wrap:wrap;
+		justify-content: space-around;
+		margin-top: 10rpx;
+	}
+	.headers_ul::after{
+		content: '';
+		width: 25%;
+	}
+	.headers_title{
+		margin-left: 40rpx;
+		padding-top: 30rpx;
+		padding-bottom: 5rpx;
+	}
+	.headers_li{
+		width: 25%;
+		text-align: center;
+		margin-bottom: 25rpx;
+		margin-top: 25rpx;
+	}
+	.headers_li_img>image{
+		width: 100rpx;
+		height: 100rpx;
+	}
+	.headers_li_text{
+		font-size: 14px;
+	}
+	.headers_title_h1{
+		font-weight: 800;
+	}
+	.headers_list{
+		width: 100%;
+		background-color: #fff;
+	}
+</style>

+ 100 - 0
components/t-w-application1/t-w-application1.vue

@@ -0,0 +1,100 @@
+<template>
+	<view class="headers">
+		<view class="headers_list" v-for="(item,index) in infos" :key="index">
+			<view class="headers_title">
+				<text class="headers_title_h1">
+					{{item.title}}				
+				</text>	
+				<!-- <text class="headers_title_h2">
+					{{item.titles}}				
+				</text>	 -->
+			</view>	
+			<view class="headers_ul" >
+				<view class="headers_li" v-for="(items,index) in item.lists" :key="index">
+					<view class="headers_li_img">
+						<image :src="items.imgs" mode=""></image>
+					</view>
+					<view class="headers_li_text">  
+						{{items.texts}}
+					</view>
+				</view>
+			</view>
+		</view>	
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				infos:[
+					{title:"我的常用",
+					titles:"(长按图标可添加删除)",
+					lists:[
+						{imgs:"/../static/img/tongzhi.png",texts:"通知公告"},
+						{imgs:"/../static/img/daiban.png",texts:"待办工作"},
+						{imgs:"/../static/img/zhiban.png",texts:"值班信息"},
+						{imgs:"/../static/img/banzu.png",texts:"班组信息"},
+					]},
+					{title:"我的常用",
+					titles:"(长按图标可添加删除)",
+					lists:[
+						{imgs:"/../static/img/tongzhi.png",texts:"通知公告"},
+						{imgs:"/../static/img/daiban.png",texts:"待办工作"},
+						{imgs:"/../static/img/zhiban.png",texts:"值班信息"},
+						{imgs:"/../static/img/banzu.png",texts:"班组信息"},
+					]},
+					{title:"我的常用",
+					titles:"(长按图标可添加删除)",
+					lists:[
+						{imgs:"/../static/img/tongzhi.png",texts:"通知公告"},
+						{imgs:"/../static/img/daiban.png",texts:"待办工作"},
+						{imgs:"/../static/img/zhiban.png",texts:"值班信息"},
+						{imgs:"/../static/img/banzu.png",texts:"班组信息"},
+					]},
+				]
+			};
+		}
+	}
+</script>
+
+<style >
+	.headers{
+		width: 95%;
+		margin: 15rpx auto;
+		/* background-color: #fff; */
+		/* border-radius: 40rpx; */
+	}
+	.headers_ul{
+		display: flex;
+		flex-direction: row;
+		justify-content: space-around;
+		margin-top: 10rpx;
+	}
+	.headers_title{
+		margin-left: 40rpx;
+		padding-top: 10rpx;
+		padding-bottom: 5rpx;
+	}
+	.headers_li{
+		text-align: center;
+		margin-bottom: 10rpx;
+	}
+	.headers_li_img>image{
+		width: 100rpx;
+		height: 100rpx;
+	}
+	.headers_li_text{
+		font-size: 14px;
+	}
+	.headers_title_h2{
+		font-size: 12rpx;
+		color: #8F939C;
+	}
+	.headers_list{
+		background-color: #FFFFFF;
+		margin-bottom: 20rpx;
+		margin-top: 20rpx;
+		border-radius: 40rpx;
+	}
+</style>

BIN
components/t-w-icon/banzu.png


BIN
components/t-w-icon/daiban.png


BIN
components/t-w-icon/tongzhi.png


BIN
components/t-w-icon/zhiban.png


+ 0 - 1
main.js

@@ -2,7 +2,6 @@ import App from './App'
 
 // #ifndef VUE3
 import Vue from 'vue'
-
 // 基础业务api
 import api from './common/vmeitime-http/index.js'
 // 集团api 生产数据

+ 21 - 2
pages.json

@@ -7,7 +7,14 @@
 			"style": {
 				"navigationStyle": "custom"
 			}
-		}, {
+		},
+		{
+			"path": "pages/tabbar/contacts/contacts",
+			"style": {
+				"navigationBarTitleText": "通讯录"
+			}
+		
+		},{
 			"path": "pages/tabbar/workbench/workbench",
 			"style": {
 				"navigationBarTitleText": "工作台"
@@ -133,7 +140,13 @@
                 "enablePullDownRefresh": false
             }
             
-        }
+        },{
+			"path":"pages/production/t-c-c-contacts-info/t-c-c-contacts-info",
+			"style" :
+			{
+			    "navigationBarTitleText": "组织架构"
+			}
+		}
     ],
 	"globalStyle": {
 		"navigationBarTextStyle": "white",
@@ -150,6 +163,12 @@
 				"selectedIconPath": "static/tabbar/home_active.png"
 			},
 			{
+				"text": "通讯录",
+				"pagePath": "pages/tabbar/contacts/contacts",
+				"iconPath": "static/tabbar/contacts.png",
+				"selectedIconPath": "static/tabbar/contacts_active.png"
+			},
+			{
 				"text": "工作台",
 				"pagePath": "pages/tabbar/workbench/workbench",
 				"iconPath": "static/tabbar/work.png",

BIN
pages/production/t-c-c-contacts-info/icon/ICE.png


BIN
pages/production/t-c-c-contacts-info/icon/searc1.png


+ 118 - 0
pages/production/t-c-c-contacts-info/t-c-c-contacts-info.vue

@@ -0,0 +1,118 @@
+<template>
+	<view class="box">
+		<view class="headers">
+			<view class="height"></view>
+			<view class="headers_input">
+				<image src="./icon/searc1.png" mode=""></image>
+				<input type="filter" placeholder="搜索" v-model="filterText" />
+			</view>
+		</view>
+		<view class="box_info_nengyuan">
+			<image src="./icon/ICE.png" mode=""></image>
+			<text>国家能源集团枣泉矿场</text>
+		</view>
+		<ly-tree :tree-data="treeData" :ready="ready" node-key="id" ref="tree" :filter-node-method="filterNodeMethod">
+		</ly-tree>
+	</view>
+</template>
+
+<script>
+	import LyTree from '@/components/ly-tree/ly-tree.vue'
+	export default {
+		components: {
+			LyTree
+		},
+		data() {
+			return {
+				ready: false,
+				treeData: [
+
+				],
+				searchVal: "",
+				filterText: ""
+			};
+		},
+		created() {
+			this.ready = true;
+			this.getcontactsinfo()
+		},
+		watch: {
+			filterText(val) {
+				 this.$refs.tree.filter(val);
+			}
+		},
+		methods: {
+			getcontactsinfo() {
+				this.$api.section_getlisttree({
+					txl: "txl"
+				}).then((res) => {
+					this.treeData = res.data.data
+				})
+			},
+			filterNodeMethod(value, data) {
+				if (!value) return true;
+				return data.label.indexOf(value) !== -1;
+			},
+		},
+	}
+</script>
+
+<style>
+	page {
+		background-color: #f5f5f5;
+	}
+
+	.box_info_nengyuan {
+		width: 100%;
+		height: 60px;
+		line-height: 60px;
+		margin: auto;
+		display: flex;
+		border-bottom: 1px solid #f5f5f5;
+		background-color: #fff;
+		/* margin-top: 10px; */
+	}
+
+	.box_info_nengyuan>image {
+		margin-top: 10px;
+		margin-left: 15px;
+		width: 40px;
+		height: 40px;
+	}
+
+	.box_info_nengyuan>text {
+		font-size: 18px;
+		font-weight: 500;
+		margin-left: 10px;
+	}
+	.headers{
+		width: 100%;
+		height: 120rpx;
+		background-color: #FFF;
+		margin-bottom:20rpx;
+	}
+	.headers_input{
+		width: 80%;
+		background-color: #f5f5f5;
+		margin: 0 auto;
+		border-radius: 40rpx;
+		position: relative;
+		margin-top: 30rpx;
+	}
+	.headers_input>input{
+		width: 80%;
+		margin-left: 50rpx;
+		height: 60rpx;
+	}
+	.headers_input>image{
+		width: 40rpx;
+		height: 40rpx;
+		position: absolute;
+		z-index: 99999;
+		left: 1%;
+		top: 16%;
+	}
+	.height{
+		height: 1rpx;
+	}
+</style>

+ 14 - 0
pages/tabbar/contacts/contacts.vue

@@ -0,0 +1,14 @@
+<template>
+	<view class="">
+		<t-c-contacts></t-c-contacts>
+	</view>
+</template>
+
+<script>
+</script>
+
+<style>
+	page{
+		background-color: #f5f5f5;
+	}
+</style>

+ 13 - 2
pages/tabbar/workbench/workbench.vue

@@ -1,5 +1,8 @@
 <template>
-	<view>
+	<view class="headers">
+		<!-- <t-w-application></t-w-application> -->
+		<t-w-application></t-w-application>
+	</view>
 		
 	</view>
 </template>
@@ -10,10 +13,18 @@
 			return {
 				
 			};
+		},
+		methods:{
+			
+		},
+		onLoad() {
+			
 		}
 	}
 </script>
 
 <style lang="scss">
-
+	page{
+		background-color: #f5f5f5;
+	}
 </style>

BIN
static/.DS_Store


BIN
static/img/banzu.png


BIN
static/img/daiban.png


BIN
static/img/tongzhi.png


BIN
static/img/zhiban.png


BIN
static/tabbar/contacts.png


BIN
static/tabbar/contacts_active.png


BIN
static/vant-uni-nvue/.DS_Store


BIN
static/vant-uni-nvue/2323.gif


BIN
static/vant-uni-nvue/Rolling-2s-200px.gif


BIN
static/vant-uni-nvue/Rolling-2s-60px.gif


BIN
static/vant-uni-nvue/loading.gif


BIN
static/vant-uni-nvue/logo.png