<template>
<u-mask :show="visible" :class="['u-theme-' + cTheme]" :mask-click-able="false">
    <view class='tree' :class="[safeAreaInsetBottom ? 'safe-area-inset-bottom' : '']">
        <!-- 头部 -->
        <view class='tree-header' @tap='jumpToSearch'>
            <u-search class='search-input' disabled :theme='cTheme' :show-action="false"></u-search>
        </view>
        <!-- 待选列表 -->
        <scroll-view scroll-x class='tree-wrapper'>
            <view class='tree-list'>
                <!-- 第一级一定展示 -->
                <scroll-view scroll-y class="tree-panel">
                    <view
                        v-for='item in data'
                        :key="item[useProps.key]"
                        :class="['tree-item', (useValue[item[useProps.key]] || currentPathKeys[item[useProps.key]]) ? 'actived' : '']"
                        @tap="nodeClick(item, 0)"
                        :cancelable='false'>
                        <view class='tree-panel-item-icon'>
                            <u-image v-if="useValue[item[useProps.key]]" class='image' width="100%" height="100%" :showLoading="false" :fade="false" src='./image/success.svg'></u-image>
                            <template v-else>
                                <u-image v-if="!mustLeaf || !Array.isArray(item[useProps.children])" class='image' width="100%" height="100%" :showLoading="false" :fade="false" src="./image/selectable.svg"></u-image>
                            </template>
                        </view>
                        <view class="overflow-hidden tree-panel-item-label">{{item[useProps.label]}}</view>
                        <view class='tree-panel-item-icon' v-if="Array.isArray(item[useProps.children])">
                            <u-image v-if="currentPathKeys[item[useProps.key]]" class='image' width="100%" height="100%" :showLoading="false" :fade="false" src='./image/arrow-right-select.svg'></u-image>
                            <u-image v-else class='image' width="100%" height="100%" :showLoading="false" :fade="false" src='./image/arrow-right.svg'></u-image>
                        </view>
                    </view>
                </scroll-view>
                <scroll-view scroll-y class="tree-panel" v-for='(item, index) in currentPath' :key="index">
                    <view 
                        v-for="sItem in item[useProps.children]"
                        :key="sItem[useProps.key]"
                        :class="['tree-item', (useValue[sItem[useProps.key]] || currentPathKeys[sItem[useProps.key]]) ? 'actived' : '']"
                        @tap="nodeClick(sItem, index + 1)"
                        :cancelable='false'>
                        <view class='tree-panel-item-icon'>
                            <u-image v-if="useValue[sItem[useProps.key]]" class='image' width="100%" height="100%" :showLoading="false" :fade="false" src='./image/success.svg'></u-image>
                            <template v-else>
                                <u-image v-if="!mustLeaf || !Array.isArray(sItem[useProps.children])" class='image' width="100%" height="100%" :showLoading="false" :fade="false" src="./image/selectable.svg"></u-image>
                            </template>
                        </view>
                        <view class="overflow-hidden tree-panel-item-label">{{sItem[useProps.label]}}</view>
                        <view class='tree-panel-item-icon' v-if="Array.isArray(sItem[useProps.children])">
                            <u-image v-if="currentPathKeys[sItem[useProps.key]]" class='image' width="100%" height="100%" :showLoading="false" :fade="false" src='./image/arrow-right-select.svg'></u-image>
                            <u-image v-else class='image' width="100%" height="100%" :showLoading="false" :fade="false" src='./image/arrow-right.svg'></u-image>
                        </view>
                    </view>
                </scroll-view>
            </view>
        </scroll-view>
        <view class='tree-selected-scroll'>
            <template v-if="multiple">
                <text class='tree-selected-label'>已选择({{selectedList.length}})：</text>
                <scroll-view scroll-x class="tree-selected-content">
                    <view class="tree-selected-list">
                        <u-tag
                            v-for="item in selectedList"
                            :key="item[useProps.key]"
                            :text="item[useProps.label]"
                            shape="circle"
                            closeable
                            @close="tagClose(item[useProps.key])">
                        </u-tag>
                    </view>
                </scroll-view>
            </template>
            <template v-else>
                <!-- 单选模式，最多只有一个值 -->
                <text class='tree-selected-label'>已选择：</text>
                <text v-if="selectedList.length > 0" class="overflow-hidden tree-selected-single-label">{{selectedList[0][useProps.label]}}</text>
            </template>
        </view>
        <view class='tree-footer'>
            <u-button size="default" shape="circle" @tap='cancel'>{{cancelText}}</u-button>
            <u-button size="default" type="primary" shape="circle" :disabled="(selectedList.length === 0 && !canSubmitNullData) || !canSubmitData" @tap='submit'>{{confirmText}}</u-button>
        </view>
        <!-- 检索控件 -->
        <u-search-mask :show.sync='searchVisible' :data.sync='searchedData' :theme='cTheme' @search='search' @selected='selected' @updateData='updateData' @cancel='cancelSearch'>
            <template v-slot="scope">
                <view class='result-content'>
                    <view class='result-item' v-for='item, index in scope.row' :key='item[useProps.key]'>
                        <u-image v-if='index !== 0' :showLoading="false" :fade="false" src='./image/arrow-right.svg' width="16px" height="16px" class='node-gt'></u-image>
                        <text :class='["node", index === scope.row.length - 1 ? "node-current" : ""]'>{{item[useProps.label]}}</text>
                    </view>
                </view>
            </template>
        </u-search-mask>
    </view>
</u-mask>
</template>

<script>
export default {
  emits: ["submit", "update:modelValue", "input", "update:visible", "cancel", "node-click"],
    props: {
        data: { // 源数据
            type: Array,
            default() {
                return []
            }
        },
        visible: { // 组件是否可见
            type: Boolean,
            default: true
        },
        value: { // 当前选中的节点id
            type: [Array, Number, String],
            default: () => []
        },
		modelValue: { // 当前选中的节点id
			type: [Array, Number, String],
			default: () => []
		},
        props: { // 节点属性定义
            type: Object,
            default () {
                return {}
            }
        },
        mustLeaf: { // 选中的节点必须是叶子节点
            type: Boolean,
            default: true
        },
        multiple: {
            type: Boolean,
            default: true
        },
        // 关怀模式: normal, care
		theme: {
			type: String,
			default: ''
		},
        safeAreaInsetBottom: {
            type: Boolean,
            default: true
        },
        // 是否可以提交
        canSubmit: {
            type: Boolean,
            default: true
        },
        // 是否可以提交空数组
        canSubmitNullData: {
            type: Boolean,
            default: false
        },
        //确认按钮
        confirmText: {
            type: String,
            default: '确定' //文字
        },
        //取消按钮
        cancelText: {
            type: String,
            default: '取消' //文字
        },
    },
    data () {
        return {
            // useValue: {}, // 实际使用的值
            selectedList: [], // 选中的列表
            searchVisible: false, // 检索页显示
            searchedData: [], // 检索结果
            currentPath: [], // 当前路径
            canSubmitData: this.canSubmit,
        }
    },
    computed: {
        cTheme() {
			return this.theme || (uni.$u || {}).theme;
		},
        // 实际使用的props
        useProps () {
            return {
                key: 'key', // 节点唯一标识
                label: 'label', // 节点显示名称
                children: 'children', // 子节点
                ...this.props
            }
        },
        // 实际使用的value
        useValue () {
            const keys = {}
            const { key } = this.useProps
            this.selectedList.forEach(item => {
                keys[item[key]] = true
            })
            return keys
        },
        // 当前路线key
        currentPathKeys () {
            const keys = {}
            const { key } = this.useProps
            this.currentPath.forEach(item => {
                keys[item[key]] = true
            })
            return keys
        },
    },
    watch: {
        visible () {
            if (this.visible) {
                this.init()
            }
        },
        canSubmit: {
            handler(value) {
                this.canSubmitData = value
            },
            immediate: true
        }
    },
    methods: {
        getValue() {
			// #ifndef VUE3
			return this.value;
			// #endif

			// #ifdef VUE3
			return this.modelValue;
            // #endif
		},
        /**
         * @description: 数据初始化
         * @return {*}
         */
        init (value = this.getValue()) {
            if (this.multiple) {
                // 如果默认值为空认为无效
                if (!Array.isArray(value) || !value) {
                    value = []
                }
            } else {
                // 如果默认值为数据或者为空，都认为是无效节点
                if (Array.isArray(value) || value === '' || value === undefined || value === null) {
                    value = []
                } else {
                    value = [value]
                }
            }
            // this.useValue = this.transformArrToMap(value)
            // 找到选中的节点，及其路径
            const { nodes, fullPath } = this.findNodesAndPathByIds(this.data, value)

            this.selectedList = nodes
            
            // 找到第一个有效value，作为选中
            for (let i = 0; i < value.length; i++) {
                if (fullPath[value[i]]) {
                   this.currentPath = fullPath[value[i]]
                   break
                }
            }
        },
        /**
         * @description: 将数组转为map结构
         * @return {*}
         */
        transformArrToMap (arr = []) {
            const keys = {}
            arr.forEach(item => {
                keys[item] = true
            })
            return keys
        },
        /**
         * @description: 根据id数组,找到所有的节点及其路径
         * @return {*}
         */
        findNodesAndPathByIds (data, ids) {
            const nodes = []
            const fullPath = {}
            const key = this.useProps.key
            const children = this.useProps.children
            const deepFindNodeAndPathById = (sData, sIds, parentList) => {
                const itemParent = JSON.parse(JSON.stringify(parentList))
                sData.forEach(item => {
                    if (sIds.includes(item[key])) {
                        // 保存节点
                        nodes.push(item)
                        // 保存路径
                        fullPath[item[key]] = [...itemParent]
                    }
                    if (Array.isArray(item[children])) {
                        deepFindNodeAndPathById(item[children], sIds, [...itemParent, item])
                    }
                })
            }
            deepFindNodeAndPathById(data, ids, [])
            return {
                nodes,
                fullPath
            };
        },
        /**
         * @description: 
         * @param {*} data: 当前节点数据
         * @param {*} index：当前层级
         * @return {*}
         */
        nodeClick(data, index) {
            const { children, key } = this.useProps
            // 区分展开和选中事件,叶子节点为选中，非叶子为需要根据是否只可选中叶子节点决定
            if (Array.isArray(data[children])) { // 展开行为
                // 清除当前层级之后的数据
                this.currentPath.splice(index)
                this.$set(this.currentPath, index, data)
            }
            
            // 如果当前是非叶子节点，且仅叶子节点可选中，直接跳出
            if (this.mustLeaf && Array.isArray(data[children])) {
                return
            }
            // 如果已经选中，去除，未选中，则添加
            if (this.useValue[data[key]]) {
                for (let i = 0, j = this.selectedList.length; i < j; i++) {
                    if (this.selectedList[i][key] === data[key]) {
                        this.selectedList.splice(i, 1)
                        data.isChecked = false
                        break
                    }
                }
            } else {
                // 判断是否为多选模式，单选模式不可选中多个节点
                if (this.multiple) {
                    data.isChecked = true
                    this.selectedList.push(data)
                } else {
                    // 单选模式仅可选中一个
                    data.isChecked = true
                    this.selectedList = [data]
                }
            }

            this.$emit('node-click', data, index)

            // if (Array.isArray(data[children])) { // 展开行为
            //     // 清除当前层级之后的数据
            //     this.currentPath.splice(index)
            //     this.$set(this.currentPath, index, data)
            // } else { // 叶子节点维护当前选中节点
            //     // 如果已经选中，去除，未选中，则添加
            //     if (this.useValue[data[key]]) {
            //         for (let i = 0, j = this.selectedList.length; i < j; i++) {
            //             if (this.selectedList[i][key] === data[key]) {
            //                 this.selectedList.splice(i, 1)
            //                 break
            //             }
            //         }
            //     } else {
            //         // 判断是否为多选模式，单选模式不可选中多个节点
            //         if (this.multiple) {
            //             this.selectedList.push(data)
            //         } else {
            //             // 单选模式仅可选中一个
            //             this.selectedList = [data]
            //         }
            //     }
            // }
        },
        /**
         * @description: 移除选中
         * @return {*}
         */
        tagClose (id) {
            const { key } = this.useProps
            for (let i = 0, j = this.selectedList.length; i < j; i++) {
                if (this.selectedList[i][key] === id) {
                    this.selectedList.splice(i, 1)
                    break
                }
            }
        },
        /* 
            跳转到检索页
         */
        jumpToSearch () {
            this.searchVisible = true
        },
        /* 
            点击确定键
            触发emit submit，返回节点信息和全id路径
            触发emit value 返回当前选中值
         */
        submit () {
            // 选中的节点全路径
            let ids = this.selectedList.map(item => item[this.useProps.key])
            // 如果是单选，返回字符串
            if (!this.multiple) {
                ids = ids.join(',')
            }
            this.$emit('input', ids)
            this.$emit('submit', ids)
			this.$emit("update:modelValue", ids);
            this.$emit('update:visible', false)
        },
        /* 
            点击取消
         */
        cancel () {
            this.$emit('update:visible', false)
            this.$emit('cancel')
        },
        /* 
            搜索
         */
        search (text) {
            const { label, children } = this.useProps
            const results = []
            const deepFindNodeAndPathByText = (data, text, parentList) => {
                const itemParent = JSON.parse(JSON.stringify(parentList))
                data.forEach(item => {
                    if (item[label].indexOf(text) > -1) {
                        // 保存路径
                        results.push([...itemParent, item])
                    }
                    if (Array.isArray(item[children])) {
                        deepFindNodeAndPathByText(item[children], text, [...itemParent, item])
                    }
                })
            }
            if (text) {
                deepFindNodeAndPathByText(this.data, text, [])
            }
            // 将结果回传给查询页
            this.searchedData = results
        },
        /* 
            搜索结果选中
         */
        selected (data) {
            // 去除末级节点
            data.pop()
            this.currentPath = data
            // 定位到选择的数据项
            this.searchVisible = false
        },
        cancelSearch() {
            this.searchVisible = false
            this.searchedData = []
        },
        updateData() {
            this.searchedData = []
        },
    }

}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";

@include themify($themes) {
    .tree {
        display: flex;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        text-align: left;
        flex-direction: column;
        background-color: #fff;
        z-index: 1;
        /* 头部样式 */
        .tree-header {
            padding: themed('grid-size-16');
            .search-input {
                width: 100%;
            }
        }
        /* 待选列表样式 */
        .tree-wrapper {
            flex: 1;
            // 不设定最小高度/高度，导致滚动条无效
            min-height: 0;
            // border的1px大小应是固定值
            border-top: 1px solid themed('u-border-color-disabled');
            border-bottom: 1px solid themed('u-border-color-disabled');

            .tree-list {
                display: flex;
                height: 100%;
                .tree-panel {
                    min-width: themed('grid-size-144');
                    max-width: themed('grid-size-144');
                    &:not(:last-child) {
                        border-right: 1px solid themed('u-border-color-disabled');
                    }
                    .tree-item {
                        display: flex;
                        align-items: center;
                        padding: themed('grid-size-10') themed('grid-size-4');
                        color: themed('u-content-color');
                        font-weight: 400;
                        &.actived {
                            color: themed('u-type-primary');
                            font-weight: 500;
                        }
                        .tree-panel-item-label {
                            flex: 1;
                            margin-left: themed('grid-size-4');
                        }
                        .tree-panel-item-icon {
                            // flex: 0;
                            min-width: themed('grid-size-16');
                            height: themed('grid-size-16');
                            text-align: right;
                            // .image {
                            //     width: themed('grid-size-16');
                            //     height: themed('grid-size-16');
                            // }
                        }
                    }
                }
            }
        }

        .tree-selected-scroll {
            // flex: 0;
            min-height: themed('grid-size-24');
            display: flex;
            padding: themed('grid-size-16');
            box-sizing: content-box;
            .tree-selected-label {
                // flex: 0;
                min-width: fit-content;
                font-size: themed('font-size-14');
                color: themed('u-tips-color');
                text-align: right;
                line-height: themed('grid-size-24');
            }
            .tree-selected-single-label {
                color: themed('u-main-color');
                font-size: themed('font-size-14');
                line-height: themed('grid-size-24');
            }
            .tree-selected-content {
                flex: 1;
                min-width: 0;
                .tree-selected-list {
                    width: max-content;
                }
            }
        }

        /* 底部样式 */
        .tree-footer {
            // flex: 0;
            padding-bottom: themed('grid-size-14');
            display: flex;
            justify-content: space-evenly;
        }

        /* 超出隐藏样式 */
        .overflow-hidden {
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
    }
    .result-content {
        display: flex;
        width: max-content;
        .result-item {
            display: flex;
            .node {
                font-size: themed('grid-size-14');
            }
            .node-current {
                color: themed('u-type-primary');
            }
            .node-gt {
                margin: 0 themed('grid-size-8');
            }
        }
    }
}
// /* 超出隐藏样式 */
// .overflow-hidden {
//     overflow: hidden;
//     text-overflow: ellipsis;
//     white-space: nowrap;
// }
</style>