From 59e828a166520849e544716e337b960924ab545e Mon Sep 17 00:00:00 2001 From: sunFlower <787203167@qq.com> Date: Thu, 29 Sep 2022 14:10:45 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=89=E6=9C=9F?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jero-web/src/common/lang/en-us.js | 1 + jero-web/src/common/lang/zh-cn.js | 1 + .../components/evaluationResultsList.vue | 48 ++++++++++++++++++- .../components/evaluationResultsClause.vue | 39 ++++++++++++--- .../components/evaluationResultsWhole.vue | 32 +++++++++++++ .../technologyAssessment/index.vue | 6 ++- 6 files changed, 117 insertions(+), 10 deletions(-) diff --git a/jero-web/src/common/lang/en-us.js b/jero-web/src/common/lang/en-us.js index 446ed7756..81110e8fe 100644 --- a/jero-web/src/common/lang/en-us.js +++ b/jero-web/src/common/lang/en-us.js @@ -1315,4 +1315,5 @@ module.exports = { consistentAssessment:'Consistent assessment', viewConsistentAssessment:'View Consistent Assessment', viewAll:'View All', + endProcess:'End Process', } \ No newline at end of file diff --git a/jero-web/src/common/lang/zh-cn.js b/jero-web/src/common/lang/zh-cn.js index c587adabc..915e962fd 100644 --- a/jero-web/src/common/lang/zh-cn.js +++ b/jero-web/src/common/lang/zh-cn.js @@ -1416,4 +1416,5 @@ module.exports = { consistentAssessment:'一致评估', viewConsistentAssessment:'查看一致评估', viewAll:'查看全部', + endProcess:'结束流程', } \ No newline at end of file diff --git a/jero-web/src/views/businessSupport/collectionOfRegulatoryOpinions/components/evaluationResultsList.vue b/jero-web/src/views/businessSupport/collectionOfRegulatoryOpinions/components/evaluationResultsList.vue index e559e1e27..1e8d605e3 100644 --- a/jero-web/src/views/businessSupport/collectionOfRegulatoryOpinions/components/evaluationResultsList.vue +++ b/jero-web/src/views/businessSupport/collectionOfRegulatoryOpinions/components/evaluationResultsList.vue @@ -8,6 +8,14 @@ {{$route.query.serialNumber +' '+$t('evaluationResults')}} +
+
+ + {{$t('endProcess')}} +
+
@@ -70,6 +78,7 @@ import viewFileModel from '@/components/viewFileModel/index' import { getAction, putAction, downloadFile } from '@/api/manage' import { mapGetters } from 'vuex' + import { postAction } from '../../../../api/manage' export default { name: 'evaluationResultsList', @@ -85,6 +94,7 @@ pageNo: 1, dataSource: [], formInline: {}, + administrators:false, userInfoQuery: {}, columns: [ { @@ -143,6 +153,14 @@ mounted() { this.userInfoQuery = this.userInfo() this.getData() + this.administrators = false + if (this.userInfo().userRoleList && this.userInfo().userRoleList.length > 0) { + this.userInfo().userRoleList.forEach(res => { + if (res.roleCode == 'admin') { + this.administrators = true + } + }) + } }, methods: { ...mapGetters(['userInfo']), @@ -232,6 +250,20 @@ }, seeFileClick(item) { this.$refs.viewFileModelRef.clickButtonToUpload(item) + }, + endProcessClick() { + let query = { + ids: this.$route.query.id + } + postAction('/lawsOpinionGather/lawsOpinionGatherEO/batchCompleteTask', query).then((res) => { + if (res.success) { + this.$message.success(this.$t('OperationSuccessful')) + this.pageNo = 1 + this.getList() + } else { + this.$message.warning(this.$t('operationFailed')) + } + }) } } } @@ -239,6 +271,7 @@ \ No newline at end of file diff --git a/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsClause.vue b/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsClause.vue index 900365c82..02b4003c7 100644 --- a/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsClause.vue +++ b/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsClause.vue @@ -10,19 +10,26 @@
- + {{$t('initiateSupplementaryProcess')}}
+
+ + {{$t('endProcess')}} +
- - - - - + + + + +
{{$t('processBackground')}}: {{$route.query.processBackground}} @@ -122,6 +129,7 @@ import PersonnelSelection from '@/components/PersonnelSelection/index' import { getAction, postAction, downloadFile } from '@/api/manage' import viewFileModel from '@/components/viewFileModel/index' + import { mapGetters } from 'vuex' export default { name: 'evaluationResultsClause', @@ -228,14 +236,30 @@ ellipsis: true, width: 160 } - ] + ], + userInfoQuery: {} } }, mounted() { document.title = this.$route.query.serialNumber + ' ' + this.$t('regulatoryTechnicalEvaluationResults') + this.userInfoQuery = this.userInfo() this.getList() }, methods: { + ...mapGetters(['userInfo']), + endProcessClick() { + let query = { + ids: this.$route.query.id + } + postAction('/lawsTechnologyEvaluation/lawsTechnologyEvaluationEO/batchCompleteTask', query).then((res) => { + if (res.success) { + this.$message.success(this.$t('OperationSuccessful')) + this.getList() + } else { + this.$message.warning(this.$t('operationFailed')) + } + }) + }, CurrentStandard() { let query = { firstInitiation: '2', @@ -439,6 +463,7 @@ ::v-deep .ant-table-body { background: transparent !important; } + .operator-text-text { cursor: pointer; margin-right: 23px; diff --git a/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsWhole.vue b/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsWhole.vue index 3756367b3..220707ec5 100644 --- a/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsWhole.vue +++ b/jero-web/src/views/businessSupport/technologyAssessment/components/evaluationResultsWhole.vue @@ -13,6 +13,13 @@ {{$t('initiateSupplementaryProcess')}}
+
+ + {{$t('endProcess')}} +
@@ -77,6 +84,7 @@ import PersonnelSelection from '@/components/PersonnelSelection/index' import { getAction, postAction, downloadFile } from '@/api/manage' import viewFileModel from '@/components/viewFileModel/index' + import { mapGetters } from 'vuex' export default { name: 'evaluationResultsWhole', @@ -88,6 +96,7 @@ return { queryParam: {}, dataSource: [], + administrators:false, url: { list: '/lawsTechnologyEvaluation/lawsTechnologyEvaluationComplianceResultEO/list', exportData: 'lawsTechnologyEvaluation/lawsTechnologyEvaluationComplianceResultEO/exportZip' @@ -121,10 +130,33 @@ } }, mounted() { + this.userInfoQuery = this.userInfo() document.title = this.$route.query.serialNumber + ' ' + this.$t('regulatoryTechnicalEvaluationResults') this.getList() + this.administrators = false + if (this.userInfo().userRoleList && this.userInfo().userRoleList.length > 0) { + this.userInfo().userRoleList.forEach(res => { + if (res.roleCode == 'admin') { + this.administrators = true + } + }) + } }, methods: { + ...mapGetters(['userInfo']), + endProcessClick() { + let query = { + ids: this.$route.query.id + } + postAction('/lawsTechnologyEvaluation/lawsTechnologyEvaluationEO/batchCompleteTask', query).then((res) => { + if (res.success) { + this.$message.success(this.$t('OperationSuccessful')) + this.getList() + } else { + this.$message.warning(this.$t('operationFailed')) + } + }) + }, CurrentStandard() { let query = { firstInitiation: '2', diff --git a/jero-web/src/views/businessSupport/technologyAssessment/index.vue b/jero-web/src/views/businessSupport/technologyAssessment/index.vue index 36d555566..b10acce8b 100644 --- a/jero-web/src/views/businessSupport/technologyAssessment/index.vue +++ b/jero-web/src/views/businessSupport/technologyAssessment/index.vue @@ -463,7 +463,8 @@ serialNumber: row.serialNumber, lawsTechnologyEvaluationId: row.id, standId: row.standId, - remark: row.remark + remark: row.remark, + createBy:row.createBy } }) window.open(newUrl.href, '_blank') @@ -476,7 +477,8 @@ serialNumber: row.serialNumber, lawsTechnologyEvaluationId: row.id, standId: row.standId, - remark: row.remark + remark: row.remark, + createBy:row.createBy } }) window.open(newUrl.href, '_blank') From 1a3d0f01ca2c96776f627c91f3f60336afa61b91 Mon Sep 17 00:00:00 2001 From: sunFlower <787203167@qq.com> Date: Thu, 29 Sep 2022 14:12:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=89=E6=9C=9F?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jero-web/package.json | 1 + .../components/PersonnelSelection/index.vue | 103 +- .../components/virtualNodeTree/mixin/node.js | 116 ++ .../components/virtualNodeTree/model/node.js | 486 +++++++ .../virtualNodeTree/model/tree-store.js | 342 +++++ .../components/virtualNodeTree/model/util.js | 27 + .../virtualNodeTree/tree-virtual-node.vue | 138 ++ .../src/components/virtualNodeTree/tree.vue | 1133 +++++++++++++++++ 8 files changed, 2272 insertions(+), 74 deletions(-) create mode 100644 jero-web/src/components/virtualNodeTree/mixin/node.js create mode 100644 jero-web/src/components/virtualNodeTree/model/node.js create mode 100644 jero-web/src/components/virtualNodeTree/model/tree-store.js create mode 100644 jero-web/src/components/virtualNodeTree/model/util.js create mode 100644 jero-web/src/components/virtualNodeTree/tree-virtual-node.vue create mode 100644 jero-web/src/components/virtualNodeTree/tree.vue diff --git a/jero-web/package.json b/jero-web/package.json index 0a928ac24..f144b3265 100644 --- a/jero-web/package.json +++ b/jero-web/package.json @@ -21,6 +21,7 @@ "dayjs": "^1.8.0", "dom-align": "1.12.0", "echarts": "^5.0.2", + "element-ui": "^2.15.10", "enquire.js": "^2.1.6", "jquery": "^3.6.0", "js-base64": "^3.7.2", diff --git a/jero-web/src/components/PersonnelSelection/index.vue b/jero-web/src/components/PersonnelSelection/index.vue index 0ed9c5187..bcb3ce1c5 100644 --- a/jero-web/src/components/PersonnelSelection/index.vue +++ b/jero-web/src/components/PersonnelSelection/index.vue @@ -31,35 +31,22 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + import { getAction, postAction } from '@/api/manage' + import virtualNodeTree from '@/components/virtualNodeTree/tree' import XEUtils from 'xe-utils' import VXETable from 'vxe-table' export default { name: 'index', props: ['query', 'value', 'personneQuery', 'isSingleChoice', 'isInput', 'isClass', 'isDelete', 'disabled'], + components: { + virtualNodeTree + }, data() { return { loading: true, @@ -114,60 +105,20 @@ }, methods: { - // loadTree(size) { - // this.loading = true - // setTimeout(() => { - // const trerData = JSON.parse(JSON.stringify(this.gData)) - // // 将树结构拍平,构建列表树结构 - // XEUtils.eachTree(trerData, (item, index, items, paths, parent, nodes) => { - // // 层级 - // item._LEVEL = nodes.length - 1 - // // 是否展开 - // item._EXPAND = false - // // 是否可视 - // item._VISIBLE = !item._LEVEL - // // 是否有子节点 - // item._HAS_CHILDREN = item.children && item.children.length > 0 - // // 是否叶子节点 - // item._IS_LEAF = !item._HAS_CHILDREN - // }) - // this.tree = trerData - // this.refreshTree() - // this.loading = false - // }, 200) - // }, - // // 切换树节点的展开、收缩 - // toggleTreeNode(row) { - // if (row._HAS_CHILDREN) { - // this.setTreeExpand(row, !row._EXPAND) - // } - // }, - // setTreeExpand(row, isExpand) { - // const matchObj = XEUtils.findTree(this.tree, item => item === row) - // row._EXPAND = isExpand - // if (matchObj) { - // XEUtils.eachTree(matchObj.item.children, (item, index, items, path, parent) => { - // item._VISIBLE = parent ? parent._EXPAND && parent._VISIBLE : isExpand - // }) - // } - // this.refreshTree() - // }, - // refreshTree() { - // const treeList = XEUtils.toTreeArray(this.tree) - // this.fullList = treeList - // this.gData = treeList.filter(item => item._VISIBLE) - // }, + handleCheckChange(val, checked, indeterminate) { + console.log(val) + console.log(this.checkboxList) + }, standardClick() { this.treeVisible = false this.departId = '' this.searchModel = '' this.userIds = [] this.userName = [] - // let gData = localStorage.getItem('gData') + let gData = localStorage.getItem('gData') // if (gData) { // this.gData = this.toTree(JSON.parse(gData)) // this.treeVisible = true - // this.loadTree() // } else { this.queryDepartUserTreeList(1) // } @@ -469,4 +420,8 @@ width: 100%; height: 600px; } + + .treeWrap { + height: 600px; + } \ No newline at end of file diff --git a/jero-web/src/components/virtualNodeTree/mixin/node.js b/jero-web/src/components/virtualNodeTree/mixin/node.js new file mode 100644 index 000000000..9c7850718 --- /dev/null +++ b/jero-web/src/components/virtualNodeTree/mixin/node.js @@ -0,0 +1,116 @@ +import { getNodeKey } from '../model/util'; + +export default { + methods: { + creator(parent, nodeTag) { + const node = this[nodeTag]; + + if (parent.isTree) { + this.tree = parent; + } else { + this.tree = parent.tree; + } + + const tree = this.tree; + if (!tree) { + console.warn('Can not find node\'s tree.'); + } + + const props = tree.props || {}; + const childrenKey = props['children'] || 'children'; + + this.$watch(`${nodeTag}.data.${childrenKey}`, () => { + node.updateChildren(); + }); + + if (node.expanded) { + this.expanded = true; + this.childNodeRendered = true; + } + + if (this.tree.accordion) { + this.$on('tree-node-expand', currentNode => { + if (node !== currentNode) { + node.collapse(); + } + }); + } + }, + + getNodeKey(node) { + return getNodeKey(this.tree.nodeKey, node.data); + }, + + handleSelectChange(checked, indeterminate) { + const node = this.node || this.source; + + if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) { + this.tree.$emit('check-change', node.data, checked, indeterminate); + } + this.oldChecked = checked; + this.indeterminate = indeterminate; + }, + + handleClick() { + const node = this.node || this.source; + const store = this.tree.store; + + store.setCurrentNode(node); + this.tree.$emit('current-change', store.currentNode ? store.currentNode.data : null, store.currentNode); + this.tree.currentNode = this; + if (this.tree.expandOnClickNode) { + this.handleExpandIconClick(); + } + if (this.tree.checkOnClickNode && !node.disabled) { + this.handleCheckChange(null, { + target: { checked: !node.checked } + }); + } + + this.tree.$emit('node-click', node.data, node, this); + }, + + handleContextMenu(event) { + const node = this.node || this.source; + + if (this.tree._events['node-contextmenu'] && this.tree._events['node-contextmenu'].length > 0) { + event.stopPropagation(); + event.preventDefault(); + } + this.tree.$emit('node-contextmenu', event, node.data, node, this); + }, + + handleExpandIconClick() { + const node = this.node || this.source; + + if (node.isLeaf) return; + if (this.expanded) { + this.tree.$emit('node-collapse', node.data, node, this); + node.collapse(); + } else { + node.expand(); + this.$emit('node-expand', node.data, node, this); + } + }, + + handleCheckChange(_, ev) { + const node = this.node || this.source; + + node.setChecked(ev.target.checked, !this.tree.checkStrictly); + this.$nextTick(() => { + const store = this.tree.store; + this.tree.$emit('check', node.data, { + checkedNodes: store.getCheckedNodes(), + checkedKeys: store.getCheckedKeys(), + halfCheckedNodes: store.getHalfCheckedNodes(), + halfCheckedKeys: store.getHalfCheckedKeys() + }); + }); + }, + + handleChildNodeExpand(nodeData, node, instance) { + this.broadcast('ElTreeNode', 'tree-node-expand', node); + this.tree.$emit('node-expand', nodeData, node, instance); + } + } +}; diff --git a/jero-web/src/components/virtualNodeTree/model/node.js b/jero-web/src/components/virtualNodeTree/model/node.js new file mode 100644 index 000000000..91f461be6 --- /dev/null +++ b/jero-web/src/components/virtualNodeTree/model/node.js @@ -0,0 +1,486 @@ +import objectAssign from 'element-ui/src/utils/merge'; +import { markNodeData, NODE_KEY } from './util'; +import { arrayFindIndex } from 'element-ui/src/utils/util'; + +export const getChildState = 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 }; +}; + +const reInitChecked = function(node) { + if (node.childNodes.length === 0) return; + + const {all, none, half} = getChildState(node.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; + } + + const parent = node.parent; + if (!parent || parent.level === 0) return; + + if (!node.store.checkStrictly) { + reInitChecked(parent); + } +}; + +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; + } +}; + +let nodeIdSeed = 0; + +export default class Node { + constructor(options) { + this.id = nodeIdSeed++; + this.text = null; + this.checked = false; + this.indeterminate = false; + this.data = null; + this.expanded = false; + this.parent = null; + this.visible = true; + this.isCurrent = false; + // console.log(22, Object.prototype.hasOwnProperty.call(options, name)); + for (let name in options) { + if (Object.prototype.hasOwnProperty.call(options, name)) { + // if (options.hasOwnProperty(name)) { + this[name] = options[name]; + } + } + + // internal + this.level = 0; + this.loaded = false; + this.childNodes = []; + this.loading = false; + + if (this.parent) { + this.level = this.parent.level + 1; + } + + const store = this.store; + if (!store) { + throw new Error('[Node]store is required!'); + } + store.registerNode(this); + + const props = store.props; + if (props && typeof props.isLeaf !== 'undefined') { + const isLeaf = getPropertyFromData(this, 'isLeaf'); + if (typeof isLeaf === 'boolean') { + this.isLeafByUser = isLeaf; + } + } + + if (store.lazy !== true && this.data) { + this.setData(this.data); + + if (store.defaultExpandAll) { + this.expanded = true; + } + } else if (this.level > 0 && store.lazy && store.defaultExpandAll) { + this.expand(); + } + if (!Array.isArray(this.data)) { + markNodeData(this, this.data); + } + if (!this.data) return; + const defaultExpandedKeys = store.defaultExpandedKeys; + const key = store.key; + if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) { + this.expand(null, store.autoExpandParent); + } + + if (key && store.currentNodeKey !== undefined && this.key === store.currentNodeKey) { + store.currentNode = this; + store.currentNode.isCurrent = true; + } + + if (store.lazy) { + store._initDefaultCheckedNode(this); + } + + this.updateLeafState(); + } + + setData(data) { + if (!Array.isArray(data)) { + markNodeData(this, data); + } + + this.data = data; + this.childNodes = []; + + let children; + if (this.level === 0 && this.data instanceof Array) { + children = this.data; + } else { + children = getPropertyFromData(this, 'children') || []; + } + + for (let i = 0, j = children.length; i < j; i++) { + this.insertChild({ data: children[i] }); + } + } + + get label() { + return getPropertyFromData(this, 'label'); + } + + get key() { + const nodeKey = this.store.key; + if (this.data) return this.data[nodeKey]; + return null; + } + + get disabled() { + return getPropertyFromData(this, 'disabled'); + } + + get nextSibling() { + const parent = this.parent; + if (parent) { + const index = parent.childNodes.indexOf(this); + if (index > -1) { + return parent.childNodes[index + 1]; + } + } + return null; + } + + get previousSibling() { + const parent = this.parent; + if (parent) { + const index = parent.childNodes.indexOf(this); + if (index > -1) { + return index > 0 ? parent.childNodes[index - 1] : null; + } + } + return null; + } + + contains(target, deep = true) { + const walk = function(parent) { + const children = parent.childNodes || []; + 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() { + const parent = this.parent; + if (parent) { + 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, { + parent: this, + store: this.store + }); + child = new Node(child); + } + + child.level = this.level + 1; + + if (typeof index === 'undefined' || index < 0) { + this.childNodes.push(child); + } else { + this.childNodes.splice(index, 0, child); + } + + this.updateLeafState(); + } + + insertBefore(child, ref) { + let index; + if (ref) { + index = this.childNodes.indexOf(ref); + } + this.insertChild(child, index); + } + + insertAfter(child, ref) { + let index; + if (ref) { + index = this.childNodes.indexOf(ref); + 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.childNodes.indexOf(child); + + if (index > -1) { + this.store && this.store.deregisterNode(child); + child.parent = null; + this.childNodes.splice(index, 1); + } + + this.updateLeafState(); + } + + removeChildByData(data) { + let targetNode = null; + + for (let i = 0; i < this.childNodes.length; i++) { + if (this.childNodes[i].data === data) { + targetNode = this.childNodes[i]; + break; + } + } + + if (targetNode) { + this.removeChild(targetNode); + } + } + + expand(callback, expandParent) { + const done = () => { + if (expandParent) { + let parent = this.parent; + while (parent.level > 0) { + parent.expanded = true; + parent = parent.parent; + } + } + this.expanded = true; + if (callback) callback(); + }; + + if (this.shouldLoadData()) { + this.loadData((data) => { + if (data instanceof Array) { + 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 childNodes = this.childNodes; + if (!this.store.lazy || (this.store.lazy === true && this.loaded === true)) { + this.isLeaf = !childNodes || childNodes.length === 0; + return; + } + this.isLeaf = false; + } + + setChecked(value, deep, recursion, passValue) { + this.indeterminate = value === 'half'; + this.checked = value === true; + + if (this.store.checkStrictly) return; + + if (!(this.shouldLoadData() && !this.store.checkDescendants)) { + let { all, allWithoutDisable } = getChildState(this.childNodes); + + if (!this.isLeaf && (!all && allWithoutDisable)) { + this.checked = false; + value = false; + } + + const handleDescendants = () => { + if (deep) { + const childNodes = this.childNodes; + 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()) { + // Only work on lazy load data. + this.loadData(() => { + handleDescendants(); + reInitChecked(this); + }, { + checked: value !== false + }); + return; + } else { + handleDescendants(); + } + } + + const parent = this.parent; + if (!parent || parent.level === 0) return; + + if (!recursion) { + reInitChecked(parent); + } + } + + getChildren(forceInit = false) { // this is data + 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() { + const newData = this.getChildren() || []; + const oldData = this.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.childNodes = []; + + this.doCreateChildren(children, defaultProps); + + this.updateLeafState(); + if (callback) { + callback.call(this, children); + } + }; + + this.store.load(this, resolve); + } else { + if (callback) { + callback.call(this); + } + } + } +} diff --git a/jero-web/src/components/virtualNodeTree/model/tree-store.js b/jero-web/src/components/virtualNodeTree/model/tree-store.js new file mode 100644 index 000000000..c9df39c83 --- /dev/null +++ b/jero-web/src/components/virtualNodeTree/model/tree-store.js @@ -0,0 +1,342 @@ +import Node from './node'; +import { getNodeKey } from './util'; + +export default class TreeStore { + constructor(options) { + this.currentNode = null; + this.currentNodeKey = null; + + for (let option in options) { + if (Object.prototype.hasOwnProperty.call(options, option)) { + // if (options.hasOwnProperty(option)) { + this[option] = options[option]; + } + } + + 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(); + }); + } else { + this._initDefaultCheckedNodes(); + } + } + + filter(value) { + const filterNodeMethod = this.filterNodeMethod; + const lazy = this.lazy; + const traverse = function(node) { + const childNodes = node.root ? node.root.childNodes : node.childNodes; + + childNodes.forEach((child) => { + 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); + return this.nodesMap[key] || null; + } + + insertBefore(data, refData) { + const refNode = this.getNode(refData); + refNode.parent.insertBefore({ data }, refNode); + } + + insertAfter(data, refData) { + const refNode = this.getNode(refData); + refNode.parent.insertAfter({ data }, refNode); + } + + remove(data) { + const node = this.getNode(data); + + if (node && node.parent) { + if (node === this.currentNode) { + this.currentNode = null; + } + node.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; + + defaultCheckedKeys.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); + } + } + + 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; + + node.childNodes.forEach(child => { + this.deregisterNode(child); + }); + + delete this.nodesMap[node.key]; + } + + getCheckedNodes(leafOnly = false, includeHalfChecked = false) { + const checkedNodes = []; + const traverse = function(node) { + const childNodes = node.root ? node.root.childNodes : node.childNodes; + + 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) { + return this.getCheckedNodes(leafOnly).map((data) => (data || {})[this.key]); + } + + getHalfCheckedNodes() { + const nodes = []; + const traverse = function(node) { + const childNodes = node.root ? node.root.childNodes : node.childNodes; + + 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 (Object.prototype.hasOwnProperty.call(nodesMap, nodeKey)) { + // if (nodesMap.hasOwnProperty(nodeKey)) { + allNodes.push(nodesMap[nodeKey]); + } + } + + return allNodes; + } + + updateChildren(key, data) { + const node = this.nodesMap[key]; + if (!node) return; + const childNodes = node.childNodes; + 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]; + const nodeKey = node.data[key].toString(); + let checked = keys.indexOf(nodeKey) > -1; + if (!checked) { + if (node.checked && !cache[nodeKey]) { + node.setChecked(false, false); + } + continue; + } + + let parent = node.parent; + while (parent && parent.level > 0) { + cache[parent.data[key]] = true; + parent = parent.parent; + } + + 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.childNodes; + 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; + } + + 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); + } + } +} diff --git a/jero-web/src/components/virtualNodeTree/model/util.js b/jero-web/src/components/virtualNodeTree/model/util.js new file mode 100644 index 000000000..dcc891b1b --- /dev/null +++ b/jero-web/src/components/virtualNodeTree/model/util.js @@ -0,0 +1,27 @@ +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 findNearestComponent = (element, componentName) => { + let target = element; + while (target && target.tagName !== 'BODY') { + if (target.__vue__ && target.__vue__.$options.name === componentName) { + return target.__vue__; + } + target = target.parentNode; + } + return null; +}; diff --git a/jero-web/src/components/virtualNodeTree/tree-virtual-node.vue b/jero-web/src/components/virtualNodeTree/tree-virtual-node.vue new file mode 100644 index 000000000..acc2ba88e --- /dev/null +++ b/jero-web/src/components/virtualNodeTree/tree-virtual-node.vue @@ -0,0 +1,138 @@ + + + diff --git a/jero-web/src/components/virtualNodeTree/tree.vue b/jero-web/src/components/virtualNodeTree/tree.vue new file mode 100644 index 000000000..e346d48e3 --- /dev/null +++ b/jero-web/src/components/virtualNodeTree/tree.vue @@ -0,0 +1,1133 @@ + + + + +