美文网首页
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