feat:转账插件
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<view class="u-tree-node" :style="{ paddingLeft: depth * 20 + 'px' }">
|
||||
<view class="u-tree-node-content" @click="toggle">
|
||||
<!-- <text v-if="hasChildren" class="u-tree-node-toggle">
|
||||
{{ node.expanded ? '▼' : '▶' }}
|
||||
</text> -->
|
||||
<up-icon v-if="hasChildren" class="u-tree-node-toggle"
|
||||
:name="node.expanded ? 'arrow-down-fill' : 'play-right-fill'" size="12" />
|
||||
<up-checkbox
|
||||
v-if="showCheckbox"
|
||||
usedAlone
|
||||
:size="12"
|
||||
:checked="node.checked"
|
||||
@change="toggleCheck"
|
||||
style="margin-right: 10px;"
|
||||
/>
|
||||
<slot :nodeData="node" :level="depth + 1">
|
||||
{{ node[props.label] }}
|
||||
</slot>
|
||||
</view>
|
||||
<view v-if="hasChildren && (node.expanded === undefined ? true : node.expanded)"
|
||||
class="u-tree-node-children"
|
||||
:style="{ paddingLeft: (depth + 1) * 20 + 'px' }">
|
||||
<tree-node
|
||||
v-for="child in node[props.children]"
|
||||
:key="child[props.nodeKey]"
|
||||
:node="child"
|
||||
:props="props"
|
||||
:show-checkbox="showCheckbox"
|
||||
:check-strictly="checkStrictly"
|
||||
:expand-on-click-node="expandOnClickNode"
|
||||
:depth="depth + 1"
|
||||
@node-click="$emit('node-click', $event)"
|
||||
@check-change="$emit('check-change', $event)">
|
||||
<template #default="{ nodeData, level }">
|
||||
<slot name="default" :nodeData="nodeData" :level="level"></slot>
|
||||
</template>
|
||||
</tree-node>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'tree-node',
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
props: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
showCheckbox: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
checkStrictly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expandOnClickNode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
depth: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasChildren() {
|
||||
return this.node[this.props.children] && this.node[this.props.children].length > 0;
|
||||
},
|
||||
isExpanded() {
|
||||
return this.node.expanded === undefined ? false : this.node.expanded;
|
||||
}
|
||||
},
|
||||
emits: ['node-click', 'check-change'],
|
||||
methods: {
|
||||
toggle() {
|
||||
if (this.expandOnClickNode && this.hasChildren) {
|
||||
this.node.expanded = !this.node.expanded;
|
||||
}
|
||||
this.$emit('node-click', this.node);
|
||||
},
|
||||
toggleCheck(checked) {
|
||||
this.node.checked = checked;
|
||||
if (!this.checkStrictly) {
|
||||
this.updateChildCheckStatus(this.node, checked);
|
||||
this.updateParentCheckStatus(this.node);
|
||||
}
|
||||
this.$emit('check-change', this.node);
|
||||
},
|
||||
updateChildCheckStatus(node, checked) {
|
||||
if (node[this.props.children]) {
|
||||
node[this.props.children].forEach(child => {
|
||||
child.checked = checked;
|
||||
this.updateChildCheckStatus(child, checked);
|
||||
});
|
||||
}
|
||||
},
|
||||
updateParentCheckStatus(node) {
|
||||
let parent = this.$parent;
|
||||
while (parent && parent.node) {
|
||||
const allChecked = parent.node[this.props.children].every(
|
||||
child => child.checked
|
||||
);
|
||||
parent.node.checked = allChecked;
|
||||
parent = parent.$parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.u-tree-node-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.u-tree-node-toggle {
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,557 @@
|
||||
<template>
|
||||
<view class="u-tree">
|
||||
<view
|
||||
v-for="item in visibleNodes"
|
||||
:key="item.key"
|
||||
class="u-tree-node"
|
||||
:class="getNodeClass(item)"
|
||||
>
|
||||
<view
|
||||
class="u-tree-node__content"
|
||||
:style="getNodeContentStyle(item)"
|
||||
@tap="handleNodeClick(item)"
|
||||
>
|
||||
<view class="u-tree-node__switcher" @tap.stop="handleExpandClick(item)">
|
||||
<up-icon
|
||||
v-if="item.hasChildren"
|
||||
:name="item.expanded ? collapseIcon : expandIcon"
|
||||
:size="iconSize"
|
||||
:color="switcherColor"
|
||||
/>
|
||||
</view>
|
||||
<up-checkbox
|
||||
v-if="showCheckbox"
|
||||
class="u-tree-node__checkbox"
|
||||
usedAlone
|
||||
:size="checkboxSize"
|
||||
:checked="item.checked"
|
||||
:disabled="item.disabled"
|
||||
@change="handleCheckboxChange(item, $event)"
|
||||
/>
|
||||
<view class="u-tree-node__label">
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<slot
|
||||
:node="item.node"
|
||||
:data="item.node"
|
||||
:level="item.level + 1"
|
||||
:expanded="item.expanded"
|
||||
:checked="item.checked"
|
||||
:indeterminate="item.indeterminate"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<text class="u-tree-node__text">{{ getNodeLabel(item.node) }}</text>
|
||||
</slot>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<text class="u-tree-node__text">{{ getNodeLabel(item.node) }}</text>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
|
||||
export default {
|
||||
name: 'u-tree',
|
||||
mixins: [mpMixin, mixin],
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
props: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
nodeKey: 'id',
|
||||
disabled: 'disabled'
|
||||
})
|
||||
},
|
||||
nodeKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
showCheckbox: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultExpandAll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultExpandedKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
defaultCheckedKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
expandOnClickNode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
checkOnClickNode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
checkStrictly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
accordion: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
highlightCurrent: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
currentNodeKey: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
indent: {
|
||||
type: [String, Number],
|
||||
default: 32
|
||||
},
|
||||
iconSize: {
|
||||
type: [String, Number],
|
||||
default: 14
|
||||
},
|
||||
checkboxSize: {
|
||||
type: [String, Number],
|
||||
default: 16
|
||||
},
|
||||
expandIcon: {
|
||||
type: String,
|
||||
default: 'play-right-fill'
|
||||
},
|
||||
collapseIcon: {
|
||||
type: String,
|
||||
default: 'arrow-down-fill'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
treeData: [],
|
||||
currentKey: '',
|
||||
nodeMap: {},
|
||||
privateKeySeed: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
treeProps() {
|
||||
return Object.assign({
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
nodeKey: 'id',
|
||||
disabled: 'disabled'
|
||||
}, this.props || {});
|
||||
},
|
||||
labelKey() {
|
||||
return this.treeProps.label;
|
||||
},
|
||||
childrenKey() {
|
||||
return this.treeProps.children;
|
||||
},
|
||||
disabledKey() {
|
||||
return this.treeProps.disabled;
|
||||
},
|
||||
keyField() {
|
||||
return this.nodeKey || this.treeProps.nodeKey || 'id';
|
||||
},
|
||||
visibleNodes() {
|
||||
const result = [];
|
||||
this.collectVisibleNodes(this.treeData, 0, result);
|
||||
return result;
|
||||
},
|
||||
switcherColor() {
|
||||
return this.upThemeVar('--up-content-color', '#606266');
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
handler() {
|
||||
this.initTree();
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
},
|
||||
props: {
|
||||
handler() {
|
||||
this.initTree();
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
nodeKey() {
|
||||
this.initTree();
|
||||
},
|
||||
defaultExpandAll() {
|
||||
this.initTree();
|
||||
},
|
||||
defaultExpandedKeys() {
|
||||
this.initTree();
|
||||
},
|
||||
defaultCheckedKeys() {
|
||||
this.initTree();
|
||||
},
|
||||
checkStrictly() {
|
||||
this.initTree();
|
||||
},
|
||||
currentNodeKey: {
|
||||
handler(value) {
|
||||
this.currentKey = value;
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
emits: [
|
||||
'node-click',
|
||||
'check-change',
|
||||
'check',
|
||||
'node-expand',
|
||||
'node-collapse',
|
||||
'current-change'
|
||||
],
|
||||
methods: {
|
||||
initTree() {
|
||||
this.privateKeySeed = 0;
|
||||
this.nodeMap = {};
|
||||
this.treeData = this.cloneNodes(this.data || [], null, 0);
|
||||
if (!this.checkStrictly) {
|
||||
this.syncParentChecked(this.treeData);
|
||||
}
|
||||
},
|
||||
cloneNodes(nodes, parent, level) {
|
||||
const list = [];
|
||||
nodes.forEach((node, index) => {
|
||||
const clone = Object.assign({}, node);
|
||||
const key = this.resolveNodeKey(clone, parent, index);
|
||||
const children = Array.isArray(node[this.childrenKey]) ? node[this.childrenKey] : [];
|
||||
const expanded = this.defaultExpandAll || this.includesKey(this.defaultExpandedKeys, key) || clone.expanded === true;
|
||||
const checked = this.includesKey(this.defaultCheckedKeys, key) || clone.checked === true;
|
||||
|
||||
clone.__uTreeKey = key;
|
||||
clone.expanded = expanded;
|
||||
clone.checked = checked;
|
||||
clone.indeterminate = false;
|
||||
clone[this.childrenKey] = this.cloneNodes(children, clone, level + 1);
|
||||
if (checked && !this.checkStrictly) {
|
||||
this.setChildrenChecked(clone, true);
|
||||
}
|
||||
|
||||
this.nodeMap[key] = {
|
||||
node: clone,
|
||||
parent,
|
||||
level
|
||||
};
|
||||
list.push(clone);
|
||||
});
|
||||
return list;
|
||||
},
|
||||
resolveNodeKey(node, parent, index) {
|
||||
const rawKey = node[this.keyField];
|
||||
if (rawKey !== undefined && rawKey !== null && rawKey !== '') {
|
||||
return rawKey;
|
||||
}
|
||||
const parentKey = parent ? parent.__uTreeKey : 'root';
|
||||
this.privateKeySeed += 1;
|
||||
return parentKey + '-' + index + '-' + this.privateKeySeed;
|
||||
},
|
||||
collectVisibleNodes(nodes, level, result) {
|
||||
nodes.forEach(node => {
|
||||
const children = this.getChildren(node);
|
||||
const key = this.getNodeKey(node);
|
||||
result.push({
|
||||
key,
|
||||
node,
|
||||
level,
|
||||
hasChildren: children.length > 0,
|
||||
expanded: node.expanded === true,
|
||||
checked: node.checked === true,
|
||||
indeterminate: node.indeterminate === true,
|
||||
disabled: this.isNodeDisabled(node)
|
||||
});
|
||||
if (children.length > 0 && node.expanded === true) {
|
||||
this.collectVisibleNodes(children, level + 1, result);
|
||||
}
|
||||
});
|
||||
},
|
||||
getChildren(node) {
|
||||
const children = node ? node[this.childrenKey] : [];
|
||||
return Array.isArray(children) ? children : [];
|
||||
},
|
||||
getNodeKey(node) {
|
||||
return node ? node.__uTreeKey : '';
|
||||
},
|
||||
getNodeLabel(node) {
|
||||
const label = node ? node[this.labelKey] : '';
|
||||
return label === undefined || label === null ? '' : label;
|
||||
},
|
||||
isNodeDisabled(node) {
|
||||
return !!(node && node[this.disabledKey]);
|
||||
},
|
||||
includesKey(keys, key) {
|
||||
return Array.isArray(keys) && keys.indexOf(key) !== -1;
|
||||
},
|
||||
getNodeClass(item) {
|
||||
const classes = [];
|
||||
if (this.highlightCurrent && item.key === this.currentKey) {
|
||||
classes.push('u-tree-node--current');
|
||||
}
|
||||
if (item.disabled) {
|
||||
classes.push('u-tree-node--disabled');
|
||||
}
|
||||
return classes.join(' ');
|
||||
},
|
||||
getNodeContentStyle(item) {
|
||||
return {
|
||||
paddingLeft: this.getIndentValue(item.level)
|
||||
};
|
||||
},
|
||||
getIndentValue(level) {
|
||||
const indent = String(this.indent);
|
||||
const matched = indent.match(/^(\d+(?:\.\d+)?)(.*)$/);
|
||||
if (!matched) {
|
||||
return indent;
|
||||
}
|
||||
const unit = matched[2] || 'rpx';
|
||||
return (Number(matched[1]) * level) + unit;
|
||||
},
|
||||
handleNodeClick(item) {
|
||||
const node = item.node;
|
||||
const oldCurrentNode = this.getCurrentNode();
|
||||
|
||||
this.currentKey = item.key;
|
||||
if (this.expandOnClickNode && item.hasChildren) {
|
||||
this.toggleExpand(item);
|
||||
}
|
||||
if (this.checkOnClickNode && this.showCheckbox && !item.disabled) {
|
||||
this.setNodeChecked(node, !node.checked, true);
|
||||
this.emitCheck(node);
|
||||
}
|
||||
|
||||
this.$emit('node-click', node);
|
||||
if (oldCurrentNode !== node) {
|
||||
this.$emit('current-change', node, oldCurrentNode);
|
||||
}
|
||||
},
|
||||
handleExpandClick(item) {
|
||||
if (!item.hasChildren) {
|
||||
return;
|
||||
}
|
||||
this.toggleExpand(item);
|
||||
},
|
||||
toggleExpand(item) {
|
||||
const node = item.node;
|
||||
const nextExpanded = node.expanded !== true;
|
||||
if (this.accordion && nextExpanded) {
|
||||
this.collapseSiblingNodes(node);
|
||||
}
|
||||
node.expanded = nextExpanded;
|
||||
this.$emit(nextExpanded ? 'node-expand' : 'node-collapse', node);
|
||||
},
|
||||
collapseSiblingNodes(node) {
|
||||
const parent = this.getParentNode(node);
|
||||
const siblings = parent ? this.getChildren(parent) : this.treeData;
|
||||
siblings.forEach(sibling => {
|
||||
if (sibling !== node) {
|
||||
sibling.expanded = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCheckboxChange(item, checked) {
|
||||
if (item.disabled) {
|
||||
return;
|
||||
}
|
||||
this.setNodeChecked(item.node, checked, true);
|
||||
this.$emit('check-change', item.node, checked);
|
||||
this.emitCheck(item.node);
|
||||
},
|
||||
setNodeChecked(node, checked, deep) {
|
||||
node.checked = checked;
|
||||
node.indeterminate = false;
|
||||
if (!this.checkStrictly && deep !== false) {
|
||||
this.setChildrenChecked(node, checked);
|
||||
}
|
||||
if (!this.checkStrictly) {
|
||||
this.updateParentChecked(node);
|
||||
}
|
||||
},
|
||||
setChildrenChecked(node, checked) {
|
||||
this.getChildren(node).forEach(child => {
|
||||
if (!this.isNodeDisabled(child)) {
|
||||
child.checked = checked;
|
||||
child.indeterminate = false;
|
||||
this.setChildrenChecked(child, checked);
|
||||
}
|
||||
});
|
||||
},
|
||||
updateParentChecked(node) {
|
||||
const parent = this.getParentNode(node);
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
const children = this.getChildren(parent).filter(child => !this.isNodeDisabled(child));
|
||||
const allChecked = children.length > 0 && children.every(child => child.checked === true);
|
||||
const someChecked = children.some(child => child.checked === true || child.indeterminate === true);
|
||||
parent.checked = allChecked;
|
||||
parent.indeterminate = !allChecked && someChecked;
|
||||
this.updateParentChecked(parent);
|
||||
},
|
||||
syncParentChecked(nodes) {
|
||||
nodes.forEach(node => {
|
||||
const children = this.getChildren(node);
|
||||
if (children.length > 0) {
|
||||
this.syncParentChecked(children);
|
||||
const enabledChildren = children.filter(child => !this.isNodeDisabled(child));
|
||||
const allChecked = enabledChildren.length > 0 && enabledChildren.every(child => child.checked === true);
|
||||
const someChecked = enabledChildren.some(child => child.checked === true || child.indeterminate === true);
|
||||
node.checked = allChecked;
|
||||
node.indeterminate = !allChecked && someChecked;
|
||||
}
|
||||
});
|
||||
},
|
||||
getParentNode(node) {
|
||||
const mapItem = this.nodeMap[this.getNodeKey(node)];
|
||||
return mapItem ? mapItem.parent : null;
|
||||
},
|
||||
walkNodes(nodes, callback) {
|
||||
nodes.forEach(node => {
|
||||
callback(node);
|
||||
this.walkNodes(this.getChildren(node), callback);
|
||||
});
|
||||
},
|
||||
getNodeByKey(key) {
|
||||
const mapItem = this.nodeMap[key];
|
||||
return mapItem ? mapItem.node : null;
|
||||
},
|
||||
getCheckedNodes(leafOnly) {
|
||||
const result = [];
|
||||
this.walkNodes(this.treeData, node => {
|
||||
const isLeaf = this.getChildren(node).length === 0;
|
||||
if (node.checked === true && (!leafOnly || isLeaf)) {
|
||||
result.push(node);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
getCheckedKeys(leafOnly) {
|
||||
return this.getCheckedNodes(leafOnly).map(node => this.getNodeKey(node));
|
||||
},
|
||||
getHalfCheckedNodes() {
|
||||
const result = [];
|
||||
this.walkNodes(this.treeData, node => {
|
||||
if (node.indeterminate === true) {
|
||||
result.push(node);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
getHalfCheckedKeys() {
|
||||
return this.getHalfCheckedNodes().map(node => this.getNodeKey(node));
|
||||
},
|
||||
setCheckedKeys(keys, leafOnly) {
|
||||
const checkedKeys = Array.isArray(keys) ? keys : [];
|
||||
this.walkNodes(this.treeData, node => {
|
||||
node.checked = false;
|
||||
node.indeterminate = false;
|
||||
});
|
||||
checkedKeys.forEach(key => {
|
||||
const node = this.getNodeByKey(key);
|
||||
if (node) {
|
||||
const isLeaf = this.getChildren(node).length === 0;
|
||||
if (!leafOnly || isLeaf) {
|
||||
this.setNodeChecked(node, true, !this.checkStrictly);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!this.checkStrictly) {
|
||||
this.syncParentChecked(this.treeData);
|
||||
}
|
||||
},
|
||||
setChecked(key, checked, deep) {
|
||||
const node = typeof key === 'object' ? key : this.getNodeByKey(key);
|
||||
if (node) {
|
||||
this.setNodeChecked(node, checked, deep);
|
||||
}
|
||||
},
|
||||
setCurrentKey(key) {
|
||||
this.currentKey = key;
|
||||
},
|
||||
getCurrentKey() {
|
||||
return this.currentKey;
|
||||
},
|
||||
getCurrentNode() {
|
||||
return this.getNodeByKey(this.currentKey);
|
||||
},
|
||||
emitCheck(node) {
|
||||
this.$emit('check', node, {
|
||||
checkedNodes: this.getCheckedNodes(false),
|
||||
checkedKeys: this.getCheckedKeys(false),
|
||||
halfCheckedNodes: this.getHalfCheckedNodes(),
|
||||
halfCheckedKeys: this.getHalfCheckedKeys()
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-tree {
|
||||
font-size: 28rpx;
|
||||
color: var(--up-main-color, #303133);
|
||||
}
|
||||
|
||||
.u-tree-node__content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
min-height: 72rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.u-tree-node--current .u-tree-node__content {
|
||||
background-color: var(--up-primary-light, #ecf5ff);
|
||||
}
|
||||
|
||||
.u-tree-node--disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.u-tree-node__switcher {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.u-tree-node__checkbox {
|
||||
margin-left: 8rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.u-tree-node__label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-tree-node__text {
|
||||
color: var(--up-main-color, #303133);
|
||||
font-size: 28rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user