Files
json-schema-editor-vue/packages/json-schema-editor/main.vue
zyqwst 0ee5a0f388 --
2020-06-28 00:07:58 +08:00

229 lines
7.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="json-schema-editor">
<a-row class="row" :gutter="10">
<a-col :span="8" class="ant-col-name">
<div :style="{marginLeft:`${20*deep}px`}" class="ant-col-name-c">
<a-button v-if="pickValue.type==='object'" type="link" :icon="hidden?'caret-right':'caret-down'" style="color:rgba(0,0,0,.65)" @click="hidden = !hidden"/>
<span v-else style="width:32px;display:inline-block"></span>
<a-input :disabled="disabled || root" :value="pickKey" class="ant-col-name-input" @blur="onInputName"/>
</div>
<a-tooltip v-if="root">
<span slot="title">全选</span>
<a-checkbox :disabled="!isObject && !isArray" class="ant-col-name-required" @change="onRootCheck"/>
</a-tooltip>
<a-tooltip v-else>
<span slot="title">是否必填</span>
<a-checkbox :disabled="isItem" :checked="checked" class="ant-col-name-required" @change="onCheck"/>
</a-tooltip>
</a-col>
<a-col :span="4">
<a-select v-model="pickValue.type" class="ant-col-type" @change="onChangeType">
<a-select-option :key="t" v-for="t in TYPE">
{{t}}
</a-select-option>
</a-select>
</a-col>
<a-col>
<a-input v-model="pickValue.title" class="ant-col-title" placeholder="标题"/>
</a-col>
<a-col :span="6" class="ant-col-setting">
<a-tooltip>
<span slot="title">设置属性</span>
<a-button type="link" icon="setting" class="setting-icon"/>
</a-tooltip>
<a-tooltip v-if="isObject">
<span slot="title">添加子节点</span>
<a-button type="link" icon="plus" class="plus-icon" @click="addChild"/>
</a-tooltip>
<a-tooltip v-if="!root && !isItem">
<span slot="title">删除节点</span>
<a-button type="link" class="close-icon ant-btn-icon-only" @click="removeNode">
<i aria-label="icon: plus" class="anticon anticon-plus">
<svg viewBox="64 64 896 896" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M810.666667 273.493333L750.506667 213.333333 512 451.84 273.493333 213.333333 213.333333 273.493333 451.84 512 213.333333 750.506667 273.493333 810.666667 512 572.16 750.506667 810.666667 810.666667 750.506667 572.16 512z" p-id="1142"></path></svg>
</i>
</a-button>
</a-tooltip>
</a-col>
</a-row>
<template v-if="!hidden&&pickValue.properties && !isArray">
<json-schema-editor v-for="(item,key,index) in pickValue.properties" :value="{[key]:item}" :parent="pickValue" :key="index" :deep="deep+1" :root="false" class="children"/>
</template>
<template v-if="isArray">
<json-schema-editor :value="{items:pickValue.items}" :deep="deep+1" disabled isItem :root="false" class="children"/>
</template>
</div>
</template>
<script>
import TYPE from './type/type'
import { Row,Col,Button,Input, Icon,Checkbox,Select,Tooltip } from 'ant-design-vue'
export default {
name:'JsonSchemaEditor',
components: {
ARow:Row,ACol:Col,
AButton: Button,
// eslint-disable-next-line vue/no-unused-components
AIcon: Icon,
AInput: Input,
ACheckbox: Checkbox,
ASelect: Select,
ASelectOption:Select.Option,
ATooltip: Tooltip,
},
props:{
value: {
type: Object,
required:true
},
disabled: { //name不可编辑根节点name不可编辑,数组元素name不可编辑
type: Boolean,
default: false
},
isItem: { //是否数组元素
type: Boolean,
default: false
},
deep:{ // 节点深度根节点deep=0
type:Number,
default: 0
},
root:{ //是否root节点
type:Boolean,
default:true
},
parent: { //父节点
type: Object,
default: null
}
},
computed: {
pickValue(){
return Object.values(this.value)[0]
},
pickKey(){
return Object.keys(this.value)[0]
},
isObject(){
return this.pickValue.type === 'object'
},
isArray(){
return this.pickValue.type === 'array'
},
checked(){
return this.parent && this.parent.required && this.parent.required.indexOf(this.pickKey) >= 0
}
},
data(){
return {
TYPE,
hidden:false,
countAdd: 1
}
},
methods: {
onInputName(e){
const val = e.target.value
const p = {};
for(let key in this.parent.properties){
if(key != this.pickKey){
p[key] = this.parent.properties[key]
}else{
p[val] = this.parent.properties[key]
delete this.parent.properties[key]
}
}
this.$set(this.parent,'properties',p)
},
onChangeType() {
this.$delete(this.pickValue,'properties')
this.$delete(this.pickValue,'required')
if(this.isArray){
this.$set(this.pickValue,'items',{type:'string'})
}
},
onCheck(e){
this._checked(e.target.checked,this.parent)
},
onRootCheck(e){
const checked = e.target.checked
this._deepCheck(checked,this.pickValue)
},
_deepCheck(checked,node){
if(node.type === 'object' && node.properties){
checked ? this.$set(node,'required',Object.keys(node.properties)) : this.$delete(node,'required')
Object.keys(node.properties).forEach(key => this._deepCheck(checked,node.properties[key]))
} else if(node.type === 'array' && node.items.type === 'object'){
checked ? this.$set(node.items,'required',Object.keys(node.items.properties)) : this.$delete(node.items,'required')
Object.keys(node.items.properties).forEach(key => this._deepCheck(checked,node.items.properties[key]))
}
},
_checked(checked,parent){
let required = parent.required
if(checked){
required || this.$set(this.parent,'required',[])
required = this.parent.required
required.indexOf(this.pickKey) === -1 && required.push(this.pickKey)
}else{
const pos = required.indexOf(this.pickKey)
pos >=0 && required.splice(pos,1)
}
required.length === 0 && this.$delete(parent,'required')
},
addChild(){
const name = this._joinName()
const type = 'string'
const node = this.pickValue
node.properties || this.$set(node,'properties',{})
const props = node.properties
this.$set(props,name,{type: type})
},
removeNode(){
const { properties,required } = this.parent
this.$delete(properties,this.pickKey)
if(required){
const pos = required.indexOf(this.pickKey)
pos >=0 && required.splice(pos,1)
required.length === 0 && this.$delete(this.parent,'required')
}
},
_joinName(){
return `feild_${this.deep}_${this.countAdd++}`
}
}
}
</script>
<style lang="less" scoped>
.json-schema-editor{
.row{
display: flex;
margin:12px;
.ant-col-name{
display:flex;
align-items:center;
.ant-col-name-c{
display: flex;
align-items: center;
}
.ant-col-name-required{
flex:0 0 24px;
text-align:center;
}
}
.ant-col-type{
width: 100%;
}
.ant-col-setting{
display: inline-block;
}
.setting-icon{
color:rgba(0,0,0,.45);
}
.plus-icon{
color:#5B8FF9;
}
.close-icon{
color:#E8684A
}
}
}
</style>