wz-uniapp/pages/military/components/mind-map-columns.vue

238 lines
4.7 KiB
Vue

<template>
<view class="mind-columns" :style="{ '--accent-color': accentColor, '--accent-soft': accentSoft }">
<scroll-view class="columns-scroll" scroll-x :scroll-into-view="scrollTarget" :scroll-with-animation="true" show-scrollbar="false">
<transition-group name="col-slide" tag="view" class="columns-track">
<view v-for="(column, index) in columns" :id="`col-${index}`" :key="column.key" class="map-column">
<view v-if="index > 0" class="column-header">{{ column.parentTitle }}</view>
<view class="column-body" :class="{ child: index > 0 }">
<view
v-for="node in column.nodes"
:key="node.id"
class="column-item"
:class="{
active: column.selectedId === node.id,
branch: node.type === 'children',
leaf: node.type === 'detail'
}"
@click="handleNodeClick(node, index)"
>
<text class="item-title">{{ node.title }}</text>
<uni-icons
:type="node.type === 'children' ? (column.selectedId === node.id ? 'minus-filled' : 'plus-filled') : 'forward'"
:size="14"
:color="column.selectedId === node.id ? '#ffffff' : '#7e899b'"
/>
</view>
</view>
</view>
</transition-group>
</scroll-view>
</view>
</template>
<script>
let columnSeed = 0;
export default {
name: 'MindMapColumns',
props: {
treeData: {
type: Object,
required: true
},
accentColor: {
type: String,
default: '#2b9c57'
},
accentSoft: {
type: String,
default: 'rgba(43, 156, 87, 0.12)'
}
},
data() {
return {
columns: [],
scrollTarget: ''
};
},
watch: {
treeData: {
immediate: true,
handler(val) {
if (!val) {
this.columns = [];
return;
}
this.initColumns();
}
}
},
methods: {
initColumns() {
const rootChildren = this.getChildren(this.treeData);
this.columns = [this.createColumn(rootChildren, this.treeData.title || '根节点')];
this.$nextTick(() => {
this.scrollTarget = 'col-0';
});
},
createColumn(nodes, parentTitle) {
columnSeed += 1;
return {
key: `col-${columnSeed}`,
nodes,
selectedId: '',
parentTitle
};
},
getChildren(node) {
if (!node || !Array.isArray(node.children)) {
return [];
}
return node.children;
},
handleNodeClick(node, columnIndex) {
const current = this.columns[columnIndex];
if (!current) {
return;
}
if (node.type === 'detail') {
this.$emit('open-detail', node);
return;
}
if (node.type !== 'children') {
return;
}
const hasNextColumn = this.columns.length > columnIndex + 1;
const isSameActive = current.selectedId === node.id;
if (isSameActive && hasNextColumn) {
current.selectedId = '';
this.columns = this.columns.slice(0, columnIndex + 1);
return;
}
current.selectedId = node.id;
this.columns = this.columns.slice(0, columnIndex + 1);
const children = this.getChildren(node);
if (!children.length) {
return;
}
this.columns.push(this.createColumn(children, node.title));
this.$nextTick(() => {
this.scrollTarget = `col-${this.columns.length - 1}`;
});
}
}
};
</script>
<style scoped>
.mind-columns {
height: 100%;
}
.columns-scroll {
width: 100%;
height: 100%;
white-space: nowrap;
}
.columns-track {
display: inline-flex;
align-items: flex-start;
min-height: 100%;
padding-right: 20rpx;
}
.map-column {
position: relative;
width: 280rpx;
flex-shrink: 0;
margin-right: 22rpx;
}
.map-column::after {
content: '';
position: absolute;
right: -11rpx;
top: 0;
bottom: 0;
width: 2rpx;
background: #e5eaf2;
}
.map-column:last-child::after {
display: none;
}
.column-header {
font-size: 22rpx;
color: #5c6b80;
margin-bottom: 10rpx;
padding-left: 4rpx;
}
.column-body {
background: #ffffff;
border: 1px solid #e8edf6;
border-radius: 18rpx;
padding: 10rpx;
}
.column-body.child {
background: var(--accent-soft);
}
.column-item {
height: 72rpx;
padding: 0 14rpx;
margin-bottom: 10rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #f4f7fc;
border: 1px solid #dde5f3;
transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
}
.column-item:last-child {
margin-bottom: 0;
}
.column-item.active {
background: var(--accent-color);
border-color: var(--accent-color);
}
.column-item.active .item-title {
color: #ffffff;
}
.item-title {
font-size: 24rpx;
color: #33445c;
max-width: 86%;
line-height: 1.4;
}
.col-slide-enter-active,
.col-slide-leave-active {
transition: width 200ms ease, opacity 200ms ease, transform 200ms ease, margin-right 200ms ease;
}
.col-slide-enter,
.col-slide-leave-to {
width: 0;
opacity: 0;
transform: translateX(20rpx);
margin-right: 0;
overflow: hidden;
}
</style>