<template>
    <div>
        <el-select v-model="category" placeholder="分类" clearable>
            <el-option v-for="item in categories" :label="item" :key="item" :value="item"></el-option>
        </el-select>
        <el-row :gutter="20">
            <el-col :span="12">
                <div class="menu-tree">
                    <el-tree
                        :data="menus"
                        :render-content="renderContent"
                        :highlight-current="true"
                        :expand-on-click-node="true"
                        node-key="id"
                        v-loading="loading"
                        accordion
                        @node-click="nodeClick"
                        :default-expanded-keys="expandKeys"
                        :default-checked-keys="expandKeys"
                    >
                    </el-tree>
                    <el-button type="text" @click="addRootMenu" style="margin-left: 24px;">添加 </el-button>
                </div>
            </el-col>
            <transition name="el-fade-in">
                <el-col :span="12" v-if="dialogVisible">
                    <div class="menu-tree">
                        <div style="font-weight:bold;padding:10px 0">{{ menu.id ? '编辑菜单' : '新增菜单' }}</div>
                        <el-form :model="menu" ref="form" label-position="top">
                            <el-form-item
                                label="菜单名"
                                prop="name"
                                :rules="[{ required: true, message: '请填写菜单名', trigger: 'blur' }]"
                            >
                                <el-input v-model="menu.name"></el-input>
                            </el-form-item>
                            <el-form-item label="菜单地址" prop="path">
                                <el-input v-model="menu.path"></el-input>
                            </el-form-item>
                            <el-form-item prop="icon">
                                <template slot="label"
                                    >图标
                                    <a
                                        href="https://fontawesome.com/icons?d=gallery&s=brands,solid&m=free"
                                        target="_blank"
                                        class="available-icons"
                                        >查看所有可用图标</a
                                    ></template
                                >
                                <el-input v-model="icon">
                                    <template slot="append"
                                        ><span ref="iconContainer" style="font-size: 18px;"
                                            ><i class="fas fa-"></i></span
                                    ></template>
                                </el-input>
                            </el-form-item>
                            <el-form-item prop="category" label="分类" v-if="menu.root">
                                <el-input v-model="menu.category"></el-input>
                            </el-form-item>
                            <el-form-item prop="authorities" label="权限">
                                <el-select
                                    v-model="menu.authorities"
                                    clearable
                                    multiple
                                    value-key="name"
                                    style="width:100%"
                                >
                                    <el-option
                                        v-for="item in authorities"
                                        :label="item.description"
                                        :value="item"
                                        :key="item.name"
                                    ></el-option>
                                </el-select>
                            </el-form-item>
                        </el-form>
                        <div slot="footer">
                            <el-button @click="dialogVisible = false">取消 </el-button>
                            <el-button type="primary" @click="addMenu" :loading="loading">保存</el-button>
                        </div>
                    </div>
                </el-col>
            </transition>
        </el-row>
    </div>
</template>
<script>
export default {
    created() {
        this.getData();
        this.$http.get('/authority/all').then(res => {
            this.authorities = res;
        });
    },
    data() {
        return {
            dialogVisible: false,
            curr: null,
            loading: false,
            menus: [],
            menu: {
                name: '',
                path: '',
                icon: '',
                root: false,
                active: true
            },
            parent: null,
            currentRef: null,
            edit: false,
            icon: '',
            categories: [],
            category: null,
            expandKeys: [],
            authorities: []
        };
    },
    methods: {
        addRootMenu() {
            this.menu = {
                name: '',
                path: '',
                active: true,
                root: true,
                icon: 'bars',
                category: this.category || null,
                authorities: [{ name: 'ROLE_ADMIN' }]
            };
            this.parent = null;
            this.icon = 'bars';
            this.dialogVisible = true;
            setTimeout(() => {
                this.showIcon('bars');
            }, 100);
        },
        showAddDialog(node, data) {
            this.edit = false;
            this.parent = node.data;
            this.menu = {
                parent: node.data.id,
                name: '',
                path: '',
                active: true,
                root: false,
                icon: null,
                authorities: [{ name: 'ROLE_ADMIN' }]
            };
            this.icon = '';
            this.dialogVisible = true;
            setTimeout(() => {
                this.showIcon('');
            }, 100);
        },
        showEditDialog(node, data) {
            this.edit = true;
            this.currentRef = node.data;
            const getIconName = icon => {
                let iconName = '';
                if (icon) {
                    iconName = icon
                        .replace('fas ', '')
                        .replace('fab ', '')
                        .replace('fa-', '');
                }
                return iconName || null;
            };
            let iconName = getIconName(data.icon);
            this.menu = {
                ...data
            };
            this.icon = iconName;
            this.dialogVisible = true;
            setTimeout(() => {
                this.showIcon(iconName);
            }, 100);
        },
        addMenu() {
            this.$refs.form.validate(valid => {
                if (valid) {
                    this.loading = true;
                    let menu = { ...this.menu };
                    delete menu.children;
                    this.$http
                        .post('/menu/save', menu, { body: 'json' })
                        .then(res => {
                            this.loading = false;
                            this.$message.success('添加成功');
                            this.dialogVisible = false;
                            this.getData();
                        })
                        .catch(e => {
                            console.log(e);
                            this.loading = false;
                            this.$message.error(e.error);
                        });
                }
            });
        },
        remove(node, data) {
            console.log(node);
            this.$confirm('确定删除菜单？', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'error'
            })
                .then(() => {
                    return this.$http.post(
                        '/menu/save',
                        {
                            ...data,
                            active: false,
                            children: null
                        },
                        { body: 'json' }
                    );
                })
                .then(res => {
                    this.$message.success('删除成功');
                    this.getData();
                })
                .catch(e => {
                    this.loading = false;
                    if (e !== 'cancel') {
                        console.log(e);
                        this.$message.error(e.error);
                    }
                });
        },
        moveUp(node, data) {
            if (node.previousSibling) {
                this.loading = true;
                let sort0 = node.previousSibling.data.sort,
                    sort1 = node.data.sort;
                Promise.all([
                    this.$http.post(
                        '/menu/save',
                        {
                            ...node.data,
                            children: null,
                            sort: sort0
                        },
                        { body: 'json' }
                    ),
                    this.$http.post(
                        '/menu/save',
                        {
                            ...node.previousSibling.data,
                            children: null,
                            sort: sort1
                        },
                        { body: 'json' }
                    )
                ])
                    .then(_ => {
                        this.loading = false;
                        this.getData();
                    })
                    .catch(e => {
                        console.log(e);
                        this.loading = false;
                        this.$message.error(e.error);
                    });
            }
        },
        moveDown(node, data) {
            if (node.nextSibling) {
                this.loading = true;
                let sort0 = node.data.sort,
                    sort1 = node.nextSibling.data.sort;
                Promise.all([
                    this.$http.post(
                        '/menu/save',
                        {
                            ...node.data,
                            children: null,
                            sort: sort1
                        },
                        { body: 'json' }
                    ),
                    this.$http.post(
                        '/menu/save',
                        {
                            ...node.nextSibling.data,
                            children: null,
                            sort: sort0
                        },
                        { body: 'json' }
                    )
                ])
                    .then(_ => {
                        this.loading = false;
                        this.getData();
                    })
                    .catch(e => {
                        console.log(e);
                        this.loading = false;
                        this.$message.error(e.error);
                    });
            }
        },
        getData() {
            this.$http
                .get('/menu/all', { category: this.category })
                .then(res => {
                    this.menus = res;
                })
                .catch(e => {
                    console.log(e);
                    this.$message.error(e.error);
                });
            this.$http.get('/menu/categories').then(res => {
                this.categories = res;
            });
        },
        renderContent(h, { node, data, store }) {
            return (
                <span
                    class={
                        this.menu.id == data.id || (this.menu.parent == data.id && !this.menu.id)
                            ? 'custom-tree-node selected'
                            : 'custom-tree-node'
                    }
                >
                    <span>{data.name}</span>
                    <span class="url">{data.path}</span>
                    <span class="opt">
                        <el-button
                            type="text"
                            on-click={e => {
                                this.moveUp(node, data), e.stopPropagation();
                            }}
                            class="up"
                            icon="el-icon-top"
                        >
                            上移
                        </el-button>
                        <el-button
                            type="text"
                            on-click={e => {
                                this.moveDown(node, data), e.stopPropagation();
                            }}
                            icon="el-icon-bottom"
                        >
                            下移
                        </el-button>
                        <el-button
                            type="text"
                            on-click={e => {
                                this.showEditDialog(node, data), e.stopPropagation();
                            }}
                            icon="el-icon-edit"
                        >
                            编辑
                        </el-button>
                        <el-button
                            type="text"
                            on-click={e => {
                                this.showAddDialog(node, data), e.stopPropagation();
                            }}
                            icon="el-icon-plus"
                        >
                            添加
                        </el-button>
                        <el-button
                            type="text"
                            on-click={e => {
                                this.remove(node, data), e.stopPropagation();
                            }}
                            icon="el-icon-delete"
                        >
                            删除
                        </el-button>
                    </span>
                </span>
            );
        },
        showIcon(val) {
            if (!this.$refs.iconContainer) return;
            if (FontAwesome.icon({ prefix: 'fas', iconName: val })) {
                this.$refs.iconContainer.innerHTML = '';
                let i = document.createElement('i');
                i.className = 'fas fa-' + val;
                this.$refs.iconContainer.append(i);
                FontAwesome.dom.i2svg();
                this.menu.icon = 'fas fa-' + val;
            } else if (FontAwesome.icon({ prefix: 'fab', iconName: val })) {
                this.$refs.iconContainer.innerHTML = '';
                let i = document.createElement('i');
                i.className = 'fab fa-' + val;
                this.$refs.iconContainer.append(i);
                FontAwesome.dom.i2svg();
                this.menu.icon = 'fab fa-' + val;
            } else {
                this.$refs.iconContainer.innerHTML = '';
                let i = document.createElement('i');
                i.className = 'fab fa-' + val;
                this.$refs.iconContainer.append(i);
                FontAwesome.dom.i2svg();
                this.menu.icon = '';
            }
        },
        nodeClick(data, node, el) {
            if (this.expandKeys[0] != data.id) {
                this.expandKeys = [data.id];
            }
        }
    },
    watch: {
        icon(val) {
            this.showIcon(val);
        },
        category() {
            this.getData();
        }
    }
};
</script>
<style lang="less" scoped>
.menu-tree {
    border-radius: 4px;
    background: white;
    margin-top: 20px;
    padding: 10px;
}
</style>
<style lang="less">
.menu-tree {
    .el-tree-node__content {
        height: 40px;
        line-height: 40px;
    }
}
.custom-tree-node {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    padding-right: 8px;
    line-height: 40px;
    height: 40px;
    .url {
        flex-grow: 1;
        text-align: right;
        margin-right: 20px;
        color: #999;
    }
    .opt {
        opacity: 0;
    }
    &.selected {
        border: 2px solid #409eff;
        border-radius: 4px;
        padding: 0 10px;
        box-sizing: border-box;
        .opt {
            opacity: 1;
        }
    }
}

.custom-tree-node:hover {
    .opt {
        opacity: 1;
    }
}

.available-icons {
    color: #409eff;
    text-decoration: none;
    &:hover {
        color: #409eff;
        text-decoration: none;
    }
}
</style>
