美文网首页
json-schema-vue3

json-schema-vue3

作者: 青春向来如此 | 来源:发表于2021-05-17 14:23 被阅读0次

src/components/json-schema/index.vue

<!--  -->
<template>
    <div>
        <div class="tree-part-test flex">
            <pre>{{ showData }}</pre>
            <el-tree :data="treeData" node-key="id" default-expand-all :expand-on-click-node="false">
                <template #default="{ node, data }">
                    <span class="doc-tree-node mb6">
                        <el-input
                            v-model="data.label"
                            placeholder="请输入内容"
                            class="mr6"
                            size="small"
                            :disabled="data.disabled"
                            style="width:180px"
                        ></el-input>
                        <el-tooltip effect="dark" content="是否必须" placement="top" v-if="data.id != 1">
                            <el-checkbox v-model="data.required" style="margin-right:6px"></el-checkbox>
                        </el-tooltip>

                        <el-select
                            v-model="data.type"
                            class="mr6"
                            size="small"
                            :disabled="data.id === 1"
                            style="width:180px"
                            @change="val => handleSelect(val, data)"
                        >
                            <el-option
                                v-for="item in options"
                                :key="item.value"
                                :label="item.label"
                                :value="item.value"
                            >
                            </el-option>
                        </el-select>
                        <el-select
                            v-model="data.format"
                            class="mr6"
                            size="small"
                            v-if="['integer', 'number'].includes(data.type)"
                            style="width:180px"
                            placeholder="format方式"
                        >
                            <el-option v-for="item in formatOptions[data.type]" :key="item" :label="item" :value="item">
                            </el-option>
                        </el-select>
                        <el-select
                            v-if="data.type === 'ref'"
                            v-model="data.$ref"
                            class="mr6"
                            size="small"
                            :disabled="data.id === 1"
                            style="width:180px"
                        >
                            <el-option v-for="item in definitionOptions" :key="item" :label="item" :value="item">
                            </el-option>
                        </el-select>

                        <el-input
                            v-else
                            v-model="data.description"
                            placeholder="请输入内容"
                            class="mr6"
                            size="small"
                            style="width:200px"
                        ></el-input>
                        <span v-if="data.type === 'object'" class="classNameWrapper">
                            <el-input
                                v-model="data.title"
                                placeholder="请输入类名"
                                :class="['mr6', !data.title ? 'error-required' : '']"
                                size="small"
                                style="width:200px"
                            />
                            <div v-if="!data.title" class="tips">类名不能为空</div>
                        </span>

                        <span v-if="['object', 'array'].includes(data.type) && node.level == 2">
                            <span class="text-tips">引用:</span>
                            <el-switch v-model="data.defination" class="mr6"></el-switch>
                        </span>

                        <i
                            v-if="data.type === 'object'"
                            class="el-icon-plus mr12 ml6"
                            style="font-size:18px;color:#36f;font-weight:bolder"
                            @click="append(data)"
                        ></i>
                        <i
                            v-if="data.id !== 1"
                            class="el-icon-delete"
                            @click="remove(node, data)"
                            style="font-size:18px"
                        ></i>
                        <el-button v-if="node.level == 1" type="primary" size="mini" @click="dialogVisible = true">
                            导入
                        </el-button>
                    </span>
                </template>
            </el-tree>
        </div>
        <el-dialog title="提示" v-model="dialogVisible" width="30%" append-to-body>
            <!-- <el-input v-model="form.json" type="textarea" :rows="20"></el-input> -->
            <Monaco v-model:value="form.json"></Monaco>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="dialogVisible = false">取 消</el-button>
                    <el-button type="primary" @click="handleImport()">确 定</el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>

<script>
import { onMounted, reactive, ref, watch } from "vue";
import { getUUID } from "@/utils";
import { useStore } from "vuex";
import Monaco from "@/components/online-editor";

const options = [
    { label: "string", value: "string" },
    { label: "number", value: "number" },
    { label: "integer", value: "integer" },
    { label: "object", value: "object" },
    { label: "array", value: "array" },
    { label: "boolean", value: "boolean" },
    { label: "ref", value: "ref" }
];

const formatOptions = {
    integer: ["int32", "int64"],
    number: ["double", "float"]
};

let number = 0;
let missingTitle = false;

export default {
    components: { Monaco },
    props: {
        value: {
            type: String,
            default() {
                return JSON.stringify([
                    {
                        label: "root",
                        id: 1,
                        disabled: true,
                        title: "",
                        type: "object",
                        description: "root",
                        children: []
                    }
                ]);
            }
        }
    },

    setup(props, { emit }) {
        const store = useStore();
        const definitionOptions = ref(new Set());
        const dialogVisible = ref(false);
        const show = ref(false);

        const form = reactive({
            json: ""
        });
        const showData = ref({});
        const handleImport = () => {
            const res = transform(transformByImportJson(form.json));
            treeData.value = res;
            dialogVisible.value = false;
            setTimeout(() => {
                show.value = true;
            }, 1000);
        };
        const transformByImportJson = json => {
            json = JSON.parse(json);
            const process = (target, dataSource) => {
                target.properties = {};
                Object.keys(dataSource).forEach(key => {
                    const value = dataSource[key];
                    const type = typeof value;
                    console.log("value :>> ", value);
                    if (Array.isArray(value)) {
                        target.properties[key] = {
                            type: "array",
                            description: "",
                            items: {
                                type: "object",
                                properties: {}
                            }
                        };
                        console.log(target.properties[key].items, value[0]);
                        process(target.properties[key].items, value[0]);
                    } else {
                        target.properties[key] = {
                            type,
                            description: type === "object" ? "" : value
                        };
                        if (type === "object") {
                            process(target.properties[key], value);
                        }
                    }
                });
            };
            const res = {
                type: "object",
                description: "",
                title: ""
            };
            process(res, json);
            console.log("res :>> ", res);
            return JSON.stringify(res);
        };

        const transform = value => {
            let data;
            try {
                data = JSON.parse(value);
            } catch (error) {
                data = {
                    type: "object",
                    description: "root",
                    title: "",
                    properties: {},
                    definitions: {}
                };
            }
            //properties, definitions,
            const { description, title } = data;

            const res = [
                {
                    label: "root",
                    id: 1,
                    disabled: true,
                    type: "object",
                    title: title,
                    description: description,
                    children: []
                }
            ];

            const process = (schemaObj, treeObj) => {
                // console.log(11, schemaObj, treeObj);
                const properties = schemaObj.properties;
                const items = schemaObj.items;
                const definitions = schemaObj.definitions;
                const children = treeObj.children;
                // console.log("items :>> ", items);
                // if (!children) return;
                if (items) {
                    const child = {
                        label: "items",
                        type: items.type,
                        description: items.description,
                        disabled: true,
                        $ref: items.$ref ? items.$ref.split("#/definitions/")[1] : undefined,
                        children: []
                    };
                    process(items, child);
                    children.push(child);
                }
                properties &&
                    Object.keys(properties).forEach(key => {
                        const item = properties[key];

                        const child = {
                            label: key,
                            type: item.type,
                            description: item.description,
                            title: item.title,
                            format: ["integer", "number"].includes(item.type) ? item.format : undefined,
                            required: item.required,
                            id: getUUID(),
                            children: [],
                            disabled: schemaObj.type === "array"
                        };

                        if (item.type === "ref") {
                            child.$ref = item.$ref.split("#/definitions/")[1];
                        }

                        process(item, child);
                        children.push(child);
                    });
                definitions &&
                    Object.keys(definitions).forEach(key => {
                        const item = definitions[key];
                        const child = {
                            label: key,
                            type: item.type,
                            description: item.description,
                            title: item.title,
                            format: ["integer", "number"].includes(item.type) ? item.format : undefined,
                            required: item.required,
                            id: getUUID(),
                            defination: true,
                            children: []
                        };
                        process(item, child);
                        children.push(child);
                    });
            };

            process(data, res[0]);

            return res;
        };
        const treeData = ref(transform(props.value));

        const checkTitle = title => {
            missingTitle = !title;
        };
        watch(
            () => treeData,
            treeData => {
                const res = {
                    type: "object",
                    description: treeData.value[0].description,
                    title: treeData.value[0].title,
                    properties: {},
                    definitions: {}
                };
                checkTitle(treeData.value[0].title);

                const process = (children, target, parent) => {
                    if (!children) return;
                    children.forEach(item => {
                        target.properties = target.properties || {};
                        // console.log("target :>> ", target);
                        const child = {
                            type: item.type,
                            description: item.description,
                            title: item.type === "object" ? item.title : undefined,
                            format: ["integer", "number"].includes(item.type) ? item.format : undefined,
                            required: item.required
                        };
                        if (item.type === "object") checkTitle(item.title);
                        if (target.type === "array") {
                            delete target.properties;
                            target[item.label] = {
                                ...child,
                                $ref: item.type === "ref" ? `#/definitions/${item.$ref}` : undefined
                            };
                        } else {
                            target.properties[item.label] = {
                                ...child,
                                $ref: item.type === "ref" ? `#/definitions/${item.$ref}` : undefined
                            };
                        }

                        if (parent.defination) {
                            const copy = res.properties[parent.label] || res.definitions[parent.label];
                            definitionOptions.value.add(parent.label);

                            res.definitions[parent.label] = copy;
                            delete res.properties[parent.label];
                        }

                        if (item.children) {
                            // console.log("target :>> ", target);
                            if (target.type === "array") {
                                process(item.children, target[item.label], item);
                            } else {
                                process(item.children, target.properties[item.label], item);
                            }
                        }
                    });
                };
                process(treeData.value[0].children, res, treeData.value[0]);
                emit("update:value", JSON.stringify(res));
                store.commit("api/SET_TITLEREQUIRED", missingTitle);
                showData.value = res;
            },
            {
                deep: true, // 深度监听的参数
                immediate: true
            }
        );

        onMounted(() => {});

        const remove = (node, data) => {
            const parent = node.parent;
            const children = parent.data.children || parent.data;
            const index = children.findIndex(d => d.id === data.id);
            children.splice(index, 1);
        };
        const append = data => {
            const newChild = {
                id: getUUID(),
                label: "field_" + number++,
                type: "string",
                description: "",
                title: "",
                children: []
            };
            if (!data.children) {
                data.children = [];
            }
            data.children.push(newChild);
        };
        const handleSelect = (val, data) => {
            if (val === "array") {
                const newChild = { id: getUUID(), label: "items", type: "string", children: [], disabled: true };
                if (!data.children) {
                    data.children = [];
                }
                data.children.push(newChild);
            }
            if (["integer", "number"].includes(val)) {
                data.format = "";
            }
        };

        return {
            treeData,
            remove,
            options,
            append,
            handleSelect,
            definitionOptions,
            showData,
            formatOptions,
            dialogVisible,
            handleImport,
            form,
            show
        };
    }
};
</script>
<style lang="scss">
.tree-part-test {
    .doc-tree-node {
        flex: 1;
        display: flex;
        align-items: center;
        font-size: 14px;
        padding-right: 8px;
    }
    .el-tree-node__content {
        padding: 25px 0;
    }
    .el-tree-node__content {
        height: 50px;
    }
    .el-tree-node__content:hover {
        background-color: transparent;
    }
    pre {
        width: 300px;
        font-family: monospace;
        margin-top: 0;
        margin-bottom: 1em;
        overflow: auto;
        height: 100%;
        overflow-y: auto;
        border: 1px solid rgba(0, 0, 0, 0.1);
        border-radius: 8px;
        padding: 12px;
        font-size: 14px;
        color: rgba(0, 0, 0, 0.65);
    }
    .error-required .el-input__inner {
        border-color: #f56c6c;
    }
    .classNameWrapper {
        position: relative;
        .tips {
            color: #f56c6c;
            position: absolute;
            left: 0px;
            bottom: -27px;
            font-size: 12px;
        }
    }
}
</style>


使用

 <JsonSchema v-model:value="form.schema" />

效果图


image.png

相关文章

网友评论

      本文标题:json-schema-vue3

      本文链接:https://www.haomeiwen.com/subject/dtsjjltx.html