fix(接口自动化): JSON-SCHEMA 编辑器添加
This commit is contained in:
parent
4b975918e1
commit
0a85155932
|
@ -24,6 +24,8 @@
|
|||
"html2canvas": "^1.0.0-rc.7",
|
||||
"js-base64": "^3.4.4",
|
||||
"json-bigint": "^1.0.0",
|
||||
"json-schema-faker": "^0.5.0-rcv.32",
|
||||
"json5": "^2.1.3",
|
||||
"jsoneditor": "^9.1.2",
|
||||
"jspdf": "^2.1.1",
|
||||
"md5": "^2.3.0",
|
||||
|
@ -41,11 +43,7 @@
|
|||
"vuedraggable": "^2.23.2",
|
||||
"vuex": "^3.1.2",
|
||||
"xml-js": "^1.6.11",
|
||||
"yan-progress": "^1.0.3",
|
||||
"lodash": "^4.17.19",
|
||||
"json-schema-faker": "^0.5.0-rcv.24",
|
||||
"jsonlint": "^1.6.3",
|
||||
"codemirror": "^5.58.1"
|
||||
"yan-progress": "^1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.1.0",
|
||||
|
@ -56,10 +54,7 @@
|
|||
"eslint-plugin-vue": "^5.0.0",
|
||||
"file-writer": "^1.0.2",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"vue2-ace-editor": "0.0.15",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass-loader": "^9.0.2",
|
||||
"script-loader": "^0.7.2"
|
||||
"vue2-ace-editor": "0.0.15"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<div id="app">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="模版" name="apiTemplate">
|
||||
<div>
|
||||
<json-schema-editor :schema.sync="schema" :is-mock="true"></json-schema-editor>
|
||||
<div style="min-height: 400px">
|
||||
<json-schema-editor class="schema" :value.sync="schema" lang="zh_CN" custom/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="预览" name="preview">
|
||||
|
@ -18,21 +18,21 @@
|
|||
|
||||
<script>
|
||||
import {schemaToJson} from './common';
|
||||
import json5 from 'json5';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
schema: {
|
||||
type: 'object',
|
||||
title: 'title',
|
||||
properties: {
|
||||
field_1: {
|
||||
type: 'string'
|
||||
schema:
|
||||
{
|
||||
"root": {
|
||||
"type": "object",
|
||||
"mock": {"mock": ""},
|
||||
"properties": {},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
preview: null,
|
||||
activeName: "apiTemplate",
|
||||
}
|
||||
|
@ -40,9 +40,7 @@
|
|||
methods: {
|
||||
handleClick() {
|
||||
if (this.activeName === 'preview') {
|
||||
|
||||
console.log(this.schema)
|
||||
this.preview = schemaToJson(this.schema);
|
||||
this.preview = schemaToJson(json5.parse(JSON.stringify(this.schema)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ jsf.extend('mock', function () {
|
|||
|
||||
const defaultOptions = {
|
||||
failOnInvalidTypes: false,
|
||||
failOnInvalidFormat: false,
|
||||
alwaysFakeOptionals: true,
|
||||
useDefaultValue: true
|
||||
failOnInvalidFormat: false
|
||||
};
|
||||
|
||||
export const schemaToJson = (schema, options = {}) => {
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
title="基础设置(数组字段)"
|
||||
width="700px"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-row :gutter="15">
|
||||
<el-form ref="elForm" :model="formData" size="small">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最小元素个数" prop="minItems">
|
||||
<el-input-number
|
||||
v-model="formData.minItems"
|
||||
placeholder="请输入"
|
||||
:min="0"
|
||||
:step="1"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最大元素个数" prop="maxItems">
|
||||
<el-input-number
|
||||
v-model="formData.maxItems"
|
||||
placeholder="请输入"
|
||||
:max="100000"
|
||||
:step="1"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<div slot="footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getValidFormVal } from '../utils'
|
||||
export default {
|
||||
name: 'ArrayDialog',
|
||||
inheritAttrs: false,
|
||||
props: { initData: { type: Object, default: () => ({}) } },
|
||||
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
minItems: undefined,
|
||||
maxItems: undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
onOpen() {
|
||||
const { minItems, maxItems } = this.initData
|
||||
Object.assign(this.formData, { minItems, maxItems })
|
||||
},
|
||||
onClose() {
|
||||
this.$refs['elForm'].resetFields()
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$refs['elForm'].validate((valid) => {
|
||||
if (!valid) return
|
||||
const newData = getValidFormVal(this.formData)
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.initData.editorId}`, {
|
||||
eventType: 'save-setting',
|
||||
...this.initData, // 之前的参数
|
||||
newData, // 设置数据
|
||||
})
|
||||
this.close()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,65 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-dialog :title="initData.title" :visible.sync="visible" width="30%">
|
||||
<el-input
|
||||
v-model="data"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入内容"
|
||||
style="margin-bottom: 15px"
|
||||
></el-input>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="close">取 消</el-button>
|
||||
<el-button type="primary" @click="handleOk">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BasicDialog',
|
||||
props: {
|
||||
visible: { type: Boolean, default: false },
|
||||
initData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
title: '提示',
|
||||
value: '',
|
||||
}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
data: '',
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
initData: {
|
||||
handler() {
|
||||
this.data = this.initData.value
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handleOk() {
|
||||
this.initData.value = this.data
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.initData.editorId}`, {
|
||||
eventType: 'save-showedit',
|
||||
...this.initData,
|
||||
})
|
||||
this.close()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
|
@ -1,83 +0,0 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
title="基础设置(布尔型字段)"
|
||||
width="600px"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-form ref="elForm" :model="formData" size="small" label-width="100px">
|
||||
<el-form-item label="默认值:" prop="default">
|
||||
<el-select
|
||||
v-model="formData.default"
|
||||
placeholder="请下拉选择"
|
||||
clearable
|
||||
:style="{ width: '60%' }"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in defaultOptions"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:disabled="item.disabled"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
import { getValidFormVal } from '../utils'
|
||||
export default {
|
||||
name: 'BooleanDialog',
|
||||
inheritAttrs: false,
|
||||
props: { initData: { type: Object, default: () => ({}) } },
|
||||
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
default: undefined,
|
||||
},
|
||||
defaultOptions: [
|
||||
{
|
||||
label: 'true',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
label: 'false',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
onOpen() {
|
||||
Object.assign(this.formData, { default: this.initData.default })
|
||||
},
|
||||
onClose() {
|
||||
this.$refs['elForm'].resetFields()
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$refs['elForm'].validate((valid) => {
|
||||
if (!valid) return
|
||||
const newData = getValidFormVal(this.formData)
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.initData.editorId}`, {
|
||||
eventType: 'save-setting',
|
||||
...this.initData, // 之前的参数
|
||||
newData, // 设置数据
|
||||
})
|
||||
this.close()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,164 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="基础设置(数值型)"
|
||||
width="700px"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-row :gutter="15">
|
||||
<el-form
|
||||
ref="elForm"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
size="small"
|
||||
>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="默认值:" prop="default">
|
||||
<el-input
|
||||
v-model="formData.default"
|
||||
type="number"
|
||||
placeholder="请输入默认值"
|
||||
:maxlength="15"
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最小值:" prop="minLength">
|
||||
<el-input-number
|
||||
v-model="formData.minLength"
|
||||
placeholder="请输入"
|
||||
:min="-9007199254740992"
|
||||
:step="1"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最大值:" prop="maxLength">
|
||||
<el-input-number
|
||||
v-model="formData.maxLength"
|
||||
placeholder="请输入"
|
||||
:step="1"
|
||||
:max="9007199254740992"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :span="3">
|
||||
<div>
|
||||
<label for>枚举</label>
|
||||
<el-checkbox v-model="enableEnum">:</el-checkbox>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="21">
|
||||
<el-form-item label-width="0" prop="enum">
|
||||
<el-input
|
||||
v-model="formData.enum"
|
||||
type="textarea"
|
||||
placeholder="请输入枚举,一行一个"
|
||||
:maxlength="120"
|
||||
:disabled="!enableEnum"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col v-if="enableEnum" :span="24">
|
||||
<el-form-item label="枚举描述:" prop="enumDesc">
|
||||
<el-input
|
||||
v-model="formData.enumDesc"
|
||||
type="textarea"
|
||||
placeholder="请输入枚举描述"
|
||||
:maxlength="100"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<div slot="footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import compact from 'lodash/compact'
|
||||
import {getValidFormVal} from '../utils'
|
||||
|
||||
export default {
|
||||
name: 'NumberDialog',
|
||||
inheritAttrs: false,
|
||||
props: {initData: {type: Object, default: () => ({})}},
|
||||
data() {
|
||||
return {
|
||||
enableEnum: false,
|
||||
formData: {
|
||||
default: undefined,
|
||||
minLength: undefined,
|
||||
maxLength: undefined,
|
||||
enum: undefined,
|
||||
enumDesc: undefined,
|
||||
},
|
||||
rules: {
|
||||
default: [],
|
||||
minLength: [],
|
||||
maxLength: [],
|
||||
innerScope: [],
|
||||
enum: [],
|
||||
enumDesc: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen() {
|
||||
const {minLength, maxLength, enumDesc} = this.initData
|
||||
let enumValue = this.initData.enum
|
||||
if (enumValue) {
|
||||
try {
|
||||
enumValue = enumValue.join('\n')
|
||||
this.enableEnum = true
|
||||
} catch (e) {
|
||||
this.$message({text: '枚举数据格式不对,将丢失', type: 'warning'})
|
||||
enumValue = ''
|
||||
}
|
||||
}
|
||||
Object.assign(
|
||||
this.formData,
|
||||
{minLength, maxLength, enumDesc},
|
||||
{default: this.initData.default, enum: enumValue}
|
||||
)
|
||||
},
|
||||
onClose() {
|
||||
this.$refs['elForm'].resetFields()
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$refs['elForm'].validate((valid) => {
|
||||
if (!valid) return
|
||||
const newData = getValidFormVal(this.formData)
|
||||
if (newData.enum) {
|
||||
newData.enum = compact(newData.enum.split('\n'))
|
||||
}
|
||||
if (newData.default) {
|
||||
newData.default = Number(newData.default)
|
||||
}
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.initData.editorId}`, {
|
||||
eventType: 'save-setting',
|
||||
...this.initData, // 之前的参数
|
||||
newData, // 设置数据
|
||||
})
|
||||
this.close()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,78 +0,0 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
title="基础设置(对象字段)"
|
||||
width="600px"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-form ref="elForm" :model="formData" size="small" label-width="100px">
|
||||
<el-form-item label-width="0" prop="notEmpty" style="text-align: center">
|
||||
<el-radio-group v-model="formData.notEmpty" size="medium">
|
||||
<el-radio
|
||||
v-for="(item, index) in notEmptyOptions"
|
||||
:key="index"
|
||||
:label="item.value"
|
||||
:disabled="item.disabled"
|
||||
>{{ item.label }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
import { getValidFormVal } from '../utils'
|
||||
export default {
|
||||
name: 'ObjectDialog',
|
||||
inheritAttrs: false,
|
||||
props: { initData: { type: Object, default: () => ({}) } },
|
||||
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
notEmpty: false,
|
||||
},
|
||||
notEmptyOptions: [
|
||||
{
|
||||
label: '可为空',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
label: '不允许为空',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
onOpen() {
|
||||
Object.assign(this.formData, { notEmpty: this.initData.notEmpty })
|
||||
},
|
||||
onClose() {
|
||||
this.$refs['elForm'].resetFields()
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$refs['elForm'].validate((valid) => {
|
||||
if (!valid) return
|
||||
const newData = getValidFormVal(this.formData)
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.initData.editorId}`, {
|
||||
eventType: 'save-setting',
|
||||
...this.initData, // 之前的参数
|
||||
newData, // 设置数据
|
||||
})
|
||||
this.close()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,43 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="RAW源码查看"
|
||||
width="700px"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<div class="sourcecode">
|
||||
<s-json-editor :value="schema"></s-json-editor>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="close">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import SJsonEditor from '../json-editor/index'
|
||||
export default {
|
||||
name: 'RawDialog',
|
||||
components: { SJsonEditor },
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
schema: { type: Object, default: () => ({}) },
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
onOpen() {},
|
||||
onClose() {},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
|
@ -1,153 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="基础设置(字符串)"
|
||||
width="700px"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-row :gutter="15">
|
||||
<el-form
|
||||
ref="elForm"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
size="small">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="默认值:" prop="default">
|
||||
<el-input
|
||||
v-model="formData.default"
|
||||
placeholder="请输入默认值"
|
||||
:maxlength="200"
|
||||
clearable/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最小长度:" prop="minLength">
|
||||
<el-input-number
|
||||
v-model="formData.minLength"
|
||||
placeholder="请输入"
|
||||
:step="2"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最大长度:" prop="maxLength">
|
||||
<el-input-number
|
||||
v-model="formData.maxLength"
|
||||
placeholder="请输入"
|
||||
:step="2"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :span="3">
|
||||
<div>
|
||||
<label for>枚举</label>
|
||||
<el-checkbox v-model="enableEnum">:</el-checkbox>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="21">
|
||||
<el-form-item label-width="0" prop="enum">
|
||||
<el-input
|
||||
v-model="formData.enum"
|
||||
type="textarea"
|
||||
placeholder="请输入枚举,一行一个"
|
||||
:maxlength="120"
|
||||
:disabled="!enableEnum"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col v-if="enableEnum" :span="24">
|
||||
<el-form-item label="枚举描述:" prop="enumDesc">
|
||||
<el-input
|
||||
v-model="formData.enumDesc"
|
||||
type="textarea"
|
||||
placeholder="请输入枚举描述"
|
||||
:maxlength="100"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<div slot="footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import compact from 'lodash/compact'
|
||||
import { getValidFormVal } from '../utils'
|
||||
export default {
|
||||
name: 'StringDialog',
|
||||
inheritAttrs: false,
|
||||
props: { initData: { type: Object, default: () => ({}) } },
|
||||
data() {
|
||||
return {
|
||||
enableEnum: false,
|
||||
formData: {
|
||||
default: undefined,
|
||||
minLength: undefined,
|
||||
maxLength: undefined,
|
||||
enum: undefined,
|
||||
enumDesc: undefined,
|
||||
},
|
||||
rules: {
|
||||
default: [],
|
||||
minLength: [],
|
||||
maxLength: [],
|
||||
innerScope: [],
|
||||
enum: [],
|
||||
enumDesc: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen() {
|
||||
const { minLength, maxLength, enumDesc } = this.initData
|
||||
let enumValue = this.initData.enum
|
||||
if (enumValue) {
|
||||
try {
|
||||
enumValue = enumValue.join('\n')
|
||||
this.enableEnum = true
|
||||
} catch (e) {
|
||||
this.$message({ text: '枚举数据格式不对,将丢失', type: 'warning' })
|
||||
enumValue = ''
|
||||
}
|
||||
}
|
||||
Object.assign(
|
||||
this.formData,
|
||||
{ minLength, maxLength, enumDesc },
|
||||
{ default: this.initData.default, enum: enumValue }
|
||||
)
|
||||
},
|
||||
onClose() {
|
||||
this.$refs['elForm'].resetFields()
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$refs['elForm'].validate((valid) => {
|
||||
if (!valid) return
|
||||
const newData = getValidFormVal(this.formData)
|
||||
if (newData.enum) {
|
||||
newData.enum = compact(newData.enum.split('\n'))
|
||||
}
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.initData.editorId}`, {
|
||||
eventType: 'save-setting',
|
||||
...this.initData, // 之前的参数
|
||||
newData, // 设置数据
|
||||
})
|
||||
this.close()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
import BasicDialog from './BasicDialog';
|
||||
import StringDialog from './StringDialog';
|
||||
import NumberDialog from './NumberDialog';
|
||||
import ArrayDialog from './ArrayDialog';
|
||||
import BooleanDialog from './BooleanDialog';
|
||||
import ObjectDialog from './ObjectDialog';
|
||||
import RawDialog from './RawDialog';
|
||||
|
||||
export {
|
||||
BasicDialog,
|
||||
StringDialog,
|
||||
NumberDialog,
|
||||
ArrayDialog,
|
||||
BooleanDialog,
|
||||
ObjectDialog,
|
||||
RawDialog
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* @author: giscafer ,https://github.com/giscafer
|
||||
* @date: 2020-05-21 17:21:29
|
||||
* @description: 用一个Vue实例封装事件常用的方法并赋值给全局的变量,以便在任何一个组件都可调用这些方法来实现全局事件管理
|
||||
*
|
||||
* 使用如下:
|
||||
* mounted(){
|
||||
this.$jsEditorEvent.on('change_value',id);
|
||||
this.$jsEditorEvent.emit('change_value',1);
|
||||
...
|
||||
}
|
||||
*/
|
||||
|
||||
import Vue from 'vue';
|
||||
|
||||
const eventHub = new Vue({
|
||||
methods: {
|
||||
on(...args) {
|
||||
this.$on.apply(this, args);
|
||||
},
|
||||
emit(...args) {
|
||||
this.$emit.apply(this, args);
|
||||
},
|
||||
off(...args) {
|
||||
this.$off.apply(this, args);
|
||||
},
|
||||
once(...args) {
|
||||
this.$once.apply(this, args);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/* const CustomEventPlugin = V =>
|
||||
Object.defineProperty(V.prototype, '$event', {
|
||||
value: eventHub,
|
||||
writable: true
|
||||
}); */
|
||||
|
||||
const CustomEventPlugin = {
|
||||
install: function(V) {
|
||||
Object.defineProperty(V.prototype, '$jsEditorEvent', {
|
||||
value: eventHub,
|
||||
writable: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default CustomEventPlugin;
|
|
@ -1,17 +0,0 @@
|
|||
import JsonSchemaEditor from './json-schema-editor.vue';
|
||||
import CustomEventPlugin from './event';
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.use(CustomEventPlugin);
|
||||
|
||||
Vue.component(JsonSchemaEditor.name, JsonSchemaEditor);
|
||||
};
|
||||
|
||||
JsonSchemaEditor.install = install;
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (typeof window !== 'undefined' && window.Vue) {
|
||||
install(window.Vue);
|
||||
}
|
||||
|
||||
export default JsonSchemaEditor;
|
|
@ -1,8 +0,0 @@
|
|||
import JsonEditor from './src/json-editor';
|
||||
|
||||
/* istanbul ignore next */
|
||||
JsonEditor.install = function(Vue) {
|
||||
Vue.component(JsonEditor.name, JsonEditor);
|
||||
};
|
||||
|
||||
export default JsonEditor;
|
|
@ -1,90 +0,0 @@
|
|||
<template>
|
||||
<div class="json-editor">
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/addon/lint/lint.css'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/idea.css'
|
||||
import 'codemirror/theme/rubyblue.css'
|
||||
require('script-loader!jsonlint')
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/addon/lint/lint'
|
||||
import 'codemirror/addon/lint/json-lint'
|
||||
|
||||
export default {
|
||||
name: 'SJsonEditor',
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: 'idea',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
jsonEditor: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
const editorValue = this.jsonEditor.getValue()
|
||||
if (value !== editorValue) {
|
||||
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
this.jsonEditor.setOption({
|
||||
theme: this.theme,
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
lineNumbers: true,
|
||||
mode: 'application/json',
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
theme: this.theme || 'idea',
|
||||
readonly: this.readonly ? 'nocursor' : false,
|
||||
lint: true,
|
||||
})
|
||||
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
|
||||
this.jsonEditor.on('change', (cm) => {
|
||||
this.$emit('changed', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
return this.jsonEditor.getValue()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.json-editor {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.json-editor >>> .CodeMirror {
|
||||
height: auto;
|
||||
min-height: 300px;
|
||||
}
|
||||
.json-editor >>> .CodeMirror-scroll {
|
||||
min-height: 300px;
|
||||
}
|
||||
.json-editor >>> .cm-s-rubyblue span.cm-string {
|
||||
color: #f08047;
|
||||
}
|
||||
</style>
|
|
@ -1,617 +0,0 @@
|
|||
<template>
|
||||
<div style="min-height: 400px;">
|
||||
<el-button
|
||||
v-if="showRaw"
|
||||
type="primary"
|
||||
size="mini"
|
||||
style="margin-bottom: 10px"
|
||||
@click="handleReqBodyRaw"
|
||||
>RAW查看</el-button
|
||||
>
|
||||
<div class="json-schema-vue-editor">
|
||||
<el-row type="flex" align="middle">
|
||||
<el-col :span="8" class="col-item name-item col-item-name">
|
||||
<el-row type="flex" justify="space-around" align="middle">
|
||||
<el-col :span="2" class="down-style-col">
|
||||
<span
|
||||
v-if="schemaData.type === 'object'"
|
||||
class="down-style"
|
||||
@click="handleClickIcon"
|
||||
>
|
||||
<i v-if="show" class="el-icon-caret-bottom icon-object"></i>
|
||||
<i v-if="!show" class="el-icon-caret-right icon-object"></i>
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-input disabled value="root" size="small" />
|
||||
</el-col>
|
||||
<el-col :span="2" style="text-align: center">
|
||||
<el-tooltip placement="top" content="全选">
|
||||
<el-checkbox
|
||||
:checked="checked"
|
||||
:disabled="disabled"
|
||||
@change="changeCheckBox"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="3" class="col-item col-item-type">
|
||||
<el-select
|
||||
:value="schemaData.type"
|
||||
:disabled="schemaData.disabled && !schemaData.canChangeType"
|
||||
class="type-select-style"
|
||||
size="small"
|
||||
@change="handleChangeType2($event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in schemaTypes"
|
||||
:key="item"
|
||||
:value="item"
|
||||
:label="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col v-if="isMock" :span="3" class="col-item col-item-mock">
|
||||
<MockSelect
|
||||
:schema="schemaData"
|
||||
@showEdit="handleShowEdit"
|
||||
@change="handleChangeMock"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col
|
||||
v-if="showTitle"
|
||||
:span="isMock ? 4 : 5"
|
||||
class="col-item col-item-mock"
|
||||
>
|
||||
<el-input
|
||||
v-model="schemaData.title"
|
||||
placeholder="标题"
|
||||
:disabled="schemaData.disabled"
|
||||
size="small"
|
||||
>
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="
|
||||
handleSchemaUpdateEvent({
|
||||
eventType: 'show-edit',
|
||||
field: 'title',
|
||||
prefix: ['properties'],
|
||||
isRoot: true,
|
||||
})
|
||||
"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col
|
||||
v-if="!showTitle && showDefaultValue"
|
||||
:span="isMock ? 4 : 5"
|
||||
class="col-item col-item-mock"
|
||||
>
|
||||
<el-input
|
||||
v-model="schemaData.default"
|
||||
placeholder="默认值"
|
||||
size="small"
|
||||
:disabled="
|
||||
schemaData.type === 'object' ||
|
||||
schemaData.type === 'array' ||
|
||||
schemaData.disabled
|
||||
"
|
||||
>
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="
|
||||
handleSchemaUpdateEvent({
|
||||
eventType: 'show-edit',
|
||||
field: 'default',
|
||||
prefix: ['properties'],
|
||||
isRoot: true,
|
||||
})
|
||||
"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="isMock ? 4 : 5" class="col-item col-item-desc">
|
||||
<el-input
|
||||
v-model="schemaData.description"
|
||||
placeholder="备注"
|
||||
size="small"
|
||||
:disabled="schemaData.disabled"
|
||||
>
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="
|
||||
handleSchemaUpdateEvent({
|
||||
eventType: 'show-edit',
|
||||
field: 'description',
|
||||
prefix: ['properties'],
|
||||
isRoot: true,
|
||||
})
|
||||
"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="2" class="col-item col-item-setting">
|
||||
<span
|
||||
class="adv-set"
|
||||
@click="
|
||||
handleSchemaUpdateEvent({
|
||||
eventType: 'setting',
|
||||
schemaType: schemaData.type,
|
||||
prefix: ['properties'],
|
||||
isRoot: true,
|
||||
})
|
||||
"
|
||||
>
|
||||
<el-tooltip placement="top" content="高级设置">
|
||||
<i class="el-icon-setting"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="schemaData.type === 'object'"
|
||||
@click="
|
||||
handleSchemaUpdateEvent({
|
||||
eventType: 'add-field',
|
||||
isChild: false,
|
||||
prefix: ['properties'],
|
||||
})
|
||||
"
|
||||
>
|
||||
<el-tooltip placement="top" content="添加子节点">
|
||||
<i class="el-icon-plus plus"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<schema-json
|
||||
v-if="show"
|
||||
:data="schemaData"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
/>
|
||||
<!-- RAW弹窗 -->
|
||||
<RawDialog
|
||||
v-if="showRaw"
|
||||
:visible.sync="rawDialogVisible"
|
||||
:schema="schemaData"
|
||||
/>
|
||||
<!-- 高级设置弹窗 -->
|
||||
<BasicDialog
|
||||
:visible.sync="basicDialogVisible"
|
||||
:init-data="basicModalData"
|
||||
/>
|
||||
<StringDialog
|
||||
:visible.sync="settingDialogVisible.string"
|
||||
:init-data="settingModalData"
|
||||
/>
|
||||
<NumberDialog
|
||||
:visible.sync="settingDialogVisible.number"
|
||||
:init-data="settingModalData"
|
||||
/>
|
||||
<NumberDialog
|
||||
:visible.sync="settingDialogVisible.integer"
|
||||
:init-data="settingModalData"
|
||||
/>
|
||||
<ArrayDialog
|
||||
:visible.sync="settingDialogVisible.array"
|
||||
:init-data="settingModalData"
|
||||
/>
|
||||
<BooleanDialog
|
||||
:visible.sync="settingDialogVisible.boolean"
|
||||
:init-data="settingModalData"
|
||||
/>
|
||||
<ObjectDialog
|
||||
:visible.sync="settingDialogVisible.object"
|
||||
:init-data="settingModalData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import set from 'lodash/set'
|
||||
import get from 'lodash/get'
|
||||
import unset from 'lodash/unset'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import SchemaJson from './schema'
|
||||
import MockSelect from './mock'
|
||||
import './jsonschema.scss'
|
||||
import {
|
||||
BasicDialog,
|
||||
StringDialog,
|
||||
NumberDialog,
|
||||
ArrayDialog,
|
||||
BooleanDialog,
|
||||
ObjectDialog,
|
||||
RawDialog,
|
||||
} from './dialog'
|
||||
import {
|
||||
SCHEMA_TYPE,
|
||||
log,
|
||||
JSONPATH_JOIN_CHAR,
|
||||
defaultSchema,
|
||||
uuid,
|
||||
defaultInitSchemaData,
|
||||
handleSchemaRequired,
|
||||
cloneObject,
|
||||
deleteData,
|
||||
} from './utils'
|
||||
export default {
|
||||
name: 'JsonSchemaEditor',
|
||||
components: {
|
||||
MockSelect,
|
||||
SchemaJson,
|
||||
BasicDialog,
|
||||
StringDialog,
|
||||
NumberDialog,
|
||||
ArrayDialog,
|
||||
BooleanDialog,
|
||||
ObjectDialog,
|
||||
RawDialog,
|
||||
},
|
||||
props: {
|
||||
schema: { type: Object, default: () => {} },
|
||||
isMock: { type: Boolean, default: false },
|
||||
showTitle: { type: Boolean, default: false },
|
||||
showDefaultValue: { type: Boolean, default: false },
|
||||
showRaw: { type: Boolean, default: false },
|
||||
},
|
||||
data() {
|
||||
const visibleObj = {}
|
||||
SCHEMA_TYPE.map((type) => {
|
||||
visibleObj[type] = false
|
||||
})
|
||||
const initSchema = this.schema || defaultInitSchemaData
|
||||
return {
|
||||
editorId: uuid(),
|
||||
checked: false,
|
||||
disabled: false,
|
||||
show: true,
|
||||
schemaTypes: SCHEMA_TYPE,
|
||||
schemaData: initSchema,
|
||||
rawDialogVisible: false,
|
||||
basicDialogVisible: false,
|
||||
basicModalData: { title: '', value: '' },
|
||||
settingDialogVisible: visibleObj,
|
||||
settingModalData: {},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
schemaData: {
|
||||
handler(newVal) {
|
||||
log(this, 'watch', newVal)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
log(this, this.schemaData)
|
||||
this.$jsEditorEvent.on(
|
||||
`schema-update-${this.editorId}`,
|
||||
this.handleSchemaUpdateEvent
|
||||
)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$jsEditorEvent.off(
|
||||
`schema-update-${this.editorId}`,
|
||||
this.handleSchemaUpdateEvent
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
handleSchemaUpdateEvent(options) {
|
||||
const { eventType, ...opts } = options
|
||||
switch (eventType) {
|
||||
case 'add-field':
|
||||
this.addFieldAction(opts)
|
||||
break
|
||||
case 'delete-field':
|
||||
this.deleteFieldAction(opts)
|
||||
break
|
||||
case 'update-field-name':
|
||||
this.updateFieldNameAction(opts)
|
||||
break
|
||||
case 'schema-type':
|
||||
this.handleChangeType(opts)
|
||||
break
|
||||
case 'show-edit':
|
||||
this.handleShowEdit(opts)
|
||||
break
|
||||
case 'save-showedit':
|
||||
this.handleSaveShowEdit(opts)
|
||||
break
|
||||
case 'setting':
|
||||
this.handleSettingAction(opts)
|
||||
break
|
||||
case 'save-setting':
|
||||
this.handleSaveSetting(opts)
|
||||
break
|
||||
case 'toggle-required':
|
||||
this.enableRequireAction(opts)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
handleClickIcon() {
|
||||
this.show = !this.show
|
||||
},
|
||||
changeCheckBox(e) {
|
||||
this.requireAllAction({ required: e, value: this.schemaData })
|
||||
},
|
||||
requireAllAction(opts) {
|
||||
const { value, required } = opts
|
||||
const cloneSchema = cloneObject(value)
|
||||
handleSchemaRequired(cloneSchema, required)
|
||||
this.forceUpdate(cloneSchema)
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
enableRequireAction(opts) {
|
||||
const { prefix, name, required } = opts
|
||||
const prefixCopy = cloneDeep(prefix)
|
||||
prefixCopy.pop()
|
||||
const parentKeys = [...prefixCopy]
|
||||
const parentPrefix = parentKeys.join(JSONPATH_JOIN_CHAR)
|
||||
const cloneSchema = cloneDeep(this.schemaData)
|
||||
let parentData = null
|
||||
if (!parentPrefix) {
|
||||
// 一级属性
|
||||
parentData = cloneSchema
|
||||
} else {
|
||||
parentData = get(cloneSchema, parentPrefix)
|
||||
}
|
||||
const requiredData = [].concat(parentData.required || [])
|
||||
const index = requiredData.indexOf(name)
|
||||
// 取消必填
|
||||
if (!required && index >= 0) {
|
||||
requiredData.splice(index, 1)
|
||||
parentKeys.push('required')
|
||||
if (requiredData.length === 0) {
|
||||
deleteData(cloneSchema, parentKeys)
|
||||
} else {
|
||||
set(cloneSchema, parentKeys, requiredData)
|
||||
}
|
||||
} else if (required && index === -1) {
|
||||
// 必填
|
||||
requiredData.push(name)
|
||||
parentKeys.push('required')
|
||||
set(cloneSchema, parentKeys, requiredData)
|
||||
}
|
||||
this.forceUpdate(cloneSchema)
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
/**
|
||||
* 处理新增字段
|
||||
* @param isChild 新增子节点
|
||||
* @param action 字段和路径
|
||||
*/
|
||||
addFieldAction(opts) {
|
||||
log(this, opts)
|
||||
const { isChild, name, prefix } = opts
|
||||
let parentPrefix = ''
|
||||
let requirePrefix = []
|
||||
if (isChild) {
|
||||
const tempArr = [].concat(prefix, name)
|
||||
parentPrefix = tempArr.concat('properties').join(JSONPATH_JOIN_CHAR)
|
||||
requirePrefix = [...tempArr]
|
||||
} else {
|
||||
parentPrefix = prefix.join(JSONPATH_JOIN_CHAR)
|
||||
const tempPrefix = [].concat(prefix)
|
||||
tempPrefix.pop()
|
||||
requirePrefix = tempPrefix
|
||||
}
|
||||
log('addFieldAction>>>', parentPrefix, '\n\t')
|
||||
let newPropertiesData = {}
|
||||
const ranName = 'field_' + uuid()
|
||||
const propertiesData = get(this.schemaData, parentPrefix)
|
||||
newPropertiesData = Object.assign({}, propertiesData)
|
||||
newPropertiesData[ranName] = defaultSchema.string
|
||||
const cloneSchema = cloneDeep(this.schemaData)
|
||||
set(cloneSchema, parentPrefix, newPropertiesData)
|
||||
|
||||
// add required
|
||||
let pRequiredData = null
|
||||
if (!requirePrefix.length) {
|
||||
// 一级属性
|
||||
pRequiredData = cloneSchema
|
||||
} else {
|
||||
pRequiredData = get(cloneSchema, requirePrefix)
|
||||
}
|
||||
const requiredData = [].concat(pRequiredData.required || [])
|
||||
requiredData.push(ranName)
|
||||
requirePrefix.push('required')
|
||||
set(cloneSchema, requirePrefix, requiredData)
|
||||
// update schema
|
||||
this.schemaData = cloneSchema
|
||||
this.forceUpdate(cloneSchema)
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
|
||||
// 删除字段
|
||||
deleteFieldAction(opts) {
|
||||
const { name, prefix } = opts
|
||||
const curFieldPath = [].concat(prefix, name).join(JSONPATH_JOIN_CHAR)
|
||||
// console.log(curFieldPath)
|
||||
const cloneSchema = cloneDeep(this.schemaData)
|
||||
unset(cloneSchema, curFieldPath)
|
||||
this.schemaData = cloneSchema
|
||||
this.forceUpdate()
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
|
||||
// 更新字段名称
|
||||
updateFieldNameAction(opts) {
|
||||
log(this, opts)
|
||||
const { value, name, prefix } = opts
|
||||
let requirePrefix = []
|
||||
const prefixCopy = cloneDeep(prefix)
|
||||
prefixCopy.pop()
|
||||
requirePrefix = prefixCopy // 上级 required路径
|
||||
const parentPrefix = prefix.join(JSONPATH_JOIN_CHAR)
|
||||
const curFieldPath = prefix.concat(name).join(JSONPATH_JOIN_CHAR)
|
||||
const cloneSchema = cloneDeep(this.schemaData)
|
||||
const propertiesData = get(cloneSchema, curFieldPath) // 原来的值
|
||||
unset(cloneSchema, curFieldPath) // 移除
|
||||
|
||||
set(cloneSchema, `${parentPrefix}.${value}`, propertiesData) // 添加
|
||||
|
||||
// update required name
|
||||
let pRequiredData = null
|
||||
if (!requirePrefix.length) {
|
||||
// 一级属性
|
||||
pRequiredData = cloneSchema
|
||||
} else {
|
||||
pRequiredData = get(cloneSchema, requirePrefix)
|
||||
}
|
||||
let requiredData = [].concat(pRequiredData.required || [])
|
||||
requiredData = requiredData.map((item) => {
|
||||
if (item === name) return value
|
||||
return item
|
||||
})
|
||||
requirePrefix.push('required')
|
||||
set(cloneSchema, requirePrefix, requiredData)
|
||||
|
||||
this.schemaData = cloneSchema
|
||||
this.forceUpdate()
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
// root
|
||||
handleChangeType2(value) {
|
||||
this.schemaData.type = value
|
||||
const parentDataItem = this.schemaData.description
|
||||
? { description: this.schemaData.description }
|
||||
: {}
|
||||
const newParentDataItem = defaultSchema[value]
|
||||
const newParentData = Object.assign({}, newParentDataItem, parentDataItem)
|
||||
this.schemaData = newParentData
|
||||
this.handleEmitChange(this.schemaData)
|
||||
},
|
||||
// schema 类型变化
|
||||
handleChangeType(opts) {
|
||||
log(this, opts, 2)
|
||||
const { value, name, prefix } = opts
|
||||
const parentPrefix = [].concat(prefix, name)
|
||||
const cloneSchema = cloneDeep(this.schemaData)
|
||||
const parentData = get(cloneSchema, parentPrefix)
|
||||
const newParentDataItem = defaultSchema[value] // 重置当前 schema 为默认值
|
||||
// 保留备注信息
|
||||
const parentDataItem = parentData.description
|
||||
? { description: parentData.description }
|
||||
: {}
|
||||
|
||||
const newParentData = Object.assign({}, newParentDataItem, parentDataItem)
|
||||
set(cloneSchema, parentPrefix, newParentData)
|
||||
this.schemaData = cloneSchema
|
||||
this.forceUpdate()
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
// title & description 编辑
|
||||
handleShowEdit(opts) {
|
||||
const { field, name, prefix, isRoot } = opts
|
||||
log(this, 'handleShowEdit', name, prefix)
|
||||
|
||||
let parentData
|
||||
if (isRoot) {
|
||||
parentData = this.schemaData
|
||||
} else {
|
||||
const parentPrefix = [].concat(prefix, name)
|
||||
parentData = get(this.schemaData, parentPrefix)
|
||||
}
|
||||
// disable 的时候,return事件处理
|
||||
if (
|
||||
(field === 'default' && parentData.type === 'array') ||
|
||||
parentData.type === 'object'
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
this.basicDialogVisible = true
|
||||
|
||||
Object.assign(this.basicModalData, {
|
||||
title:
|
||||
field === 'title' ? '标题' : field === 'default' ? '默认值' : '描述',
|
||||
value: parentData[field],
|
||||
editorId: this.editorId,
|
||||
...opts,
|
||||
})
|
||||
},
|
||||
handleSaveShowEdit(opts) {
|
||||
const { value, field, name, prefix, isRoot } = opts
|
||||
// console.log(field, value)
|
||||
let parentPrefix
|
||||
const cloneSchema = cloneDeep(this.schemaData)
|
||||
if (isRoot) {
|
||||
cloneSchema[field] = value
|
||||
} else {
|
||||
parentPrefix = [].concat(prefix, name, field)
|
||||
set(cloneSchema, parentPrefix, value)
|
||||
}
|
||||
|
||||
this.schemaData = cloneSchema
|
||||
this.forceUpdate()
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
// 高级设置
|
||||
handleSettingAction(opts) {
|
||||
const { schemaType, name, prefix, isRoot } = opts
|
||||
// console.log(schemaType)
|
||||
this.settingDialogVisible[schemaType] = true
|
||||
|
||||
let parentData
|
||||
if (isRoot) {
|
||||
parentData = this.schemaData
|
||||
} else {
|
||||
const parentPrefix = [].concat(prefix, name)
|
||||
parentData = get(this.schemaData, parentPrefix)
|
||||
}
|
||||
|
||||
this.settingModalData = {
|
||||
schemaType,
|
||||
name,
|
||||
isRoot,
|
||||
prefix,
|
||||
editorId: this.editorId,
|
||||
...parentData,
|
||||
}
|
||||
},
|
||||
// 高级设置更新 schema
|
||||
handleSaveSetting(opts) {
|
||||
const { name, prefix, newData, isRoot } = opts
|
||||
const cloneSchema = cloneDeep(this.schemaData)
|
||||
if (isRoot) {
|
||||
Object.assign(cloneSchema, { ...newData })
|
||||
} else {
|
||||
const parentPrefix = [].concat(prefix, name)
|
||||
const oldData = get(cloneSchema, parentPrefix)
|
||||
set(cloneSchema, parentPrefix, { ...oldData, ...newData })
|
||||
}
|
||||
this.schemaData = cloneSchema
|
||||
this.forceUpdate()
|
||||
this.handleEmitChange(cloneSchema)
|
||||
},
|
||||
handleChangeMock() {},
|
||||
handleReqBodyRaw() {
|
||||
this.rawDialogVisible = true
|
||||
this.forceUpdate()
|
||||
},
|
||||
// 解决嵌套对象属性无法刷新页面问题
|
||||
forceUpdate(data) {
|
||||
const temp = data || this.schemaData
|
||||
this.schemaData = {}
|
||||
this.$nextTick(() => {
|
||||
this.schemaData = temp
|
||||
})
|
||||
},
|
||||
handleEmitChange(schema) {
|
||||
// console.log(schema)
|
||||
this.$emit('schema-change', schema)
|
||||
this.$emit('update:schema', schema)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,149 +0,0 @@
|
|||
.json-schema-vue-editor {
|
||||
cursor: pointer;
|
||||
.el-input--medium {
|
||||
height: 36px;
|
||||
}
|
||||
.el-input.is-disabled {
|
||||
background-color: #f5f7fa;
|
||||
border-color: #dfe4ed;
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.json-schema-vue-editor .option-formStyle {
|
||||
/* padding-left: 25px; */
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .required-icon {
|
||||
font-size: 1em;
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .object-style {
|
||||
/* border-left: 2px dotted gray; */
|
||||
/* padding-left: 8px; */
|
||||
padding-top: 6px;
|
||||
/* margin-left: 20px; */
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .col-item-type {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .down-style {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .col-item-desc {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .col-item-mock {
|
||||
text-align: center;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .col-item-setting {
|
||||
padding-left: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .plus {
|
||||
color: #2395f1;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .close {
|
||||
color: #ff561b;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .array-type {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .delete-item {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .object-style .name-item .ant-input-group-addon {
|
||||
background-color: unset;
|
||||
border: unset;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor
|
||||
.object-style
|
||||
.name-item
|
||||
.ant-input-group
|
||||
> .ant-input:first-child,
|
||||
.ant-input-group-addon:first-child {
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .icon-object {
|
||||
color: #0d1b3ea6;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .down-style-col {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .wrapper {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* .schema-content {
|
||||
margin-left: 20px;
|
||||
} */
|
||||
|
||||
.json-schema-vue-editor .adv-set {
|
||||
padding-right: 8px;
|
||||
color: #00a854;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor .type-select-style {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor-import-modal .ant-tabs-nav .ant-tabs-tab {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor-adv-modal .other-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor-adv-modal .other-label {
|
||||
text-align: right;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor-adv-modal .default-setting {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 16px;
|
||||
border-left: 3px solid #2395f1;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor-adv-modal .ant-modal-body {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor-adv-modal .ant-modal-body .ace_editor {
|
||||
min-height: 350px;
|
||||
}
|
||||
|
||||
.json-schema-vue-editor-adv-modal-select .format-items-title {
|
||||
color: #999;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<meta charset="utf-8">
|
||||
<title>json-schema-editor-vue demo</title>
|
||||
<script src="./json-schema-editor-vue.umd.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="./json-schema-editor-vue.css">
|
||||
|
||||
|
||||
<script>
|
||||
console.log(json-schema-editor-vue)
|
||||
</script>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
284
frontend/src/business/components/common/json-schema/ot/lib/json-schema-editor-vue.umd.min.js
vendored
Normal file
284
frontend/src/business/components/common/json-schema/ot/lib/json-schema-editor-vue.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,25 @@
|
|||
import JsonSchemaEditor from './json-schema-editor/index'
|
||||
const components = [
|
||||
JsonSchemaEditor
|
||||
]
|
||||
|
||||
// 定义 install 方法
|
||||
const install = function (Vue) {
|
||||
if (install.installed) return
|
||||
install.installed = true
|
||||
// 遍历并注册全局组件
|
||||
components.map(component => {
|
||||
Vue.component(component.name, component)
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.Vue) {
|
||||
install(window.Vue)
|
||||
}
|
||||
|
||||
export default {
|
||||
// 导出的对象必须具备一个 install 方法
|
||||
install,
|
||||
// 组件列表
|
||||
...components
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
const langs = {
|
||||
en_US: {
|
||||
'title': 'Title',
|
||||
'import_json': 'Import JSON',
|
||||
'base_setting': 'Base Setting',
|
||||
'all_setting': 'Source Code',
|
||||
'default': 'Default',
|
||||
'description':'Description',
|
||||
"adv_setting": "Advanced Settings",
|
||||
"add_child_node": "Add child node",
|
||||
'add_sibling_node': 'Add sibling nodes',
|
||||
'add_node':'Add sibling/child nodes',
|
||||
'remove_node': 'Remove node',
|
||||
'child_node': 'Child node',
|
||||
'sibling_node':'Sibling node',
|
||||
'ok':'OK',
|
||||
'cancel':'Cancel',
|
||||
'minLength':'Min length',
|
||||
'maxLength': 'Max length',
|
||||
'pattern':'MUST be a valid regular expression.',
|
||||
'exclusiveMinimum': 'Value strictly less than',
|
||||
'exclusiveMaximum': 'Value strictly more than',
|
||||
'minimum': 'Min',
|
||||
'maximum': 'Max',
|
||||
'uniqueItems': 'Unique Items',
|
||||
'minItems':'MinItems',
|
||||
'maxItems': 'MaxItems',
|
||||
'minProperties':'MinProperties',
|
||||
'maxProperties': 'MaxProperties',
|
||||
'checked_all': 'Checked All',
|
||||
'valid_json': 'Not valid json',
|
||||
'enum': 'Enum',
|
||||
'enum_msg': 'One value per line',
|
||||
'enum_desc': 'desc',
|
||||
'enum_desc_msg': 'enum description',
|
||||
'required': 'Required',
|
||||
'mock': 'mock',
|
||||
'mockLink': 'Help',
|
||||
'format': 'Format',
|
||||
'nothing': 'Nothing',
|
||||
'preview': 'Preview',
|
||||
'add_custom': 'Add Custom Prop',
|
||||
},
|
||||
zh_CN: {
|
||||
'title': '标题',
|
||||
'import_json': '导入 json',
|
||||
'base_setting': '基础设置',
|
||||
'all_setting': '编辑源码',
|
||||
'default': '默认值',
|
||||
'description':'描述',
|
||||
'adv_setting': '高级设置',
|
||||
"add_child_node": "添加子节点",
|
||||
'add_sibling_node': '添加兄弟节点',
|
||||
'add_node':'添加兄弟/子节点',
|
||||
'remove_node': '删除节点',
|
||||
'child_node': '子节点',
|
||||
'sibling_node':'兄弟节点',
|
||||
'ok':'确定',
|
||||
'cancel':'取消',
|
||||
'minLength':'最小长度',
|
||||
'maxLength': '最大长度',
|
||||
'pattern': '用正则表达式约束字符串',
|
||||
'exclusiveMinimum': '开启后,数据必须大于最小值',
|
||||
'exclusiveMaximum': '开启后,数据必须小于最大值',
|
||||
'minimum': '最小值',
|
||||
'maximum': '最大值',
|
||||
'uniqueItems': '开启后,每个元素都不相同',
|
||||
'minItems':'最小元素个数',
|
||||
'maxItems': '最大元素个数',
|
||||
'minProperties':'最小元素个数',
|
||||
'maxProperties': '最大元素个数',
|
||||
'checked_all': '全选',
|
||||
'valid_json': '不是合法的json字符串',
|
||||
'enum': '枚举',
|
||||
'enum_msg': '每行只能写一个值',
|
||||
'enum_desc': '备注',
|
||||
'enum_desc_msg': '备注描述信息',
|
||||
'required': '是否必须',
|
||||
'mock': 'mock',
|
||||
'mockLink': '查看文档',
|
||||
'format': '格式化',
|
||||
'nothing': '无',
|
||||
'preview': '预览',
|
||||
'add_custom': '添加自定义属性'
|
||||
}
|
||||
}
|
||||
|
||||
export default (lang) => {
|
||||
return langs[lang]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import JsonSchemaEditor from './main.vue'
|
||||
|
||||
JsonSchemaEditor.install = function (Vue) {
|
||||
Vue.component(JsonSchemaEditor.name, JsonSchemaEditor)
|
||||
}
|
||||
|
||||
export default JsonSchemaEditor
|
|
@ -0,0 +1,391 @@
|
|||
<template>
|
||||
<div class="json-schema-editor">
|
||||
<el-row class="row" :gutter="10">
|
||||
<el-col :span="12" class="ant-col-name">
|
||||
<div :style="{marginLeft:`${10*deep}px`}" class="ant-col-name-c">
|
||||
<span v-if="pickValue.type==='object'" :class="hidden? 'el-tree-node__expand-icon el-icon-caret-right':
|
||||
'expanded el-tree-node__expand-icon el-icon-caret-right'" @click="hidden = !hidden"/>
|
||||
<span v-else style="width:10px;display:inline-block"></span>
|
||||
<el-input :disabled="disabled || root" :value="pickKey" @blur="onInputName" size="small"/>
|
||||
</div>
|
||||
<el-tooltip v-if="root" content="全选">
|
||||
<input type="checkbox" :disabled="!isObject && !isArray" class="ant-col-name-required" @change="onRootCheck"/>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-else content="是否必填">
|
||||
<input type="checkbox" :disabled="isItem" :checked="checked" class="ant-col-name-required" @change="onCheck"/>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-select v-model="pickValue.type" :disabled="disabledType" class="ant-col-type" @change="onChangeType" size="small">
|
||||
<el-option :key="t" :value="t" :label="t" v-for="t in TYPE_NAME"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<ms-mock :schema="pickValue.mock"/>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-input v-model="pickValue.description" class="ant-col-title" :placeholder="local['description']" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="4" class="col-item-setting">
|
||||
<el-tooltip class="item" effect="dark" content="高级设置" placement="top">
|
||||
<i class="el-icon-setting" @click="onSetting"/>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="isObject" content="添加子节点" placement="top">
|
||||
<i class="el-icon-plus" @click="addChild" style="margin-left: 10px"/>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="!root && !isItem" content="删除节点" placement="top">
|
||||
<i class="el-icon-close" @click="removeNode" style="margin-left: 10px"/>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-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" :lang="lang" :custom="custom"/>
|
||||
</template>
|
||||
<template v-if="isArray">
|
||||
<json-schema-editor :value="{items:pickValue.items}" :deep="deep+1" disabled isItem :root="false" class="children" :lang="lang" :custom="custom"/>
|
||||
</template>
|
||||
<!-- 高级设置-->
|
||||
<el-dialog :close-on-click-modal="false" :title="local['adv_setting']" :visible.sync="modalVisible" width="60%" :destroy-on-close="true"
|
||||
@close="handleClose">
|
||||
<!--<el-dialog v-model="modalVisible" :title="local['adv_setting']" :maskClosable="false" :okText="local['ok']" :cancelText="local['cancel']" width="800px" @ok="handleOk" dialogClass="json-schema-editor-advanced-modal">-->
|
||||
<h3 v-text="local['base_setting']">基础设置</h3>
|
||||
<el-form v-model="advancedValue" class="ant-advanced-search-form">
|
||||
<el-row :gutter="6">
|
||||
<el-col :span="8" v-for="(item,key) in advancedValue" :key="key">
|
||||
<el-form-item>
|
||||
<el-input-number v-model="advancedValue[key]" v-if="advancedAttr[key].type === 'integer'" style="width:100%" :placeholder="key" size="small"/>
|
||||
<el-input-number v-model="advancedValue[key]" v-else-if="advancedAttr[key].type === 'number'" style="width:100%" :placeholder="key" size="small"/>
|
||||
<span v-else-if="advancedAttr[key].type === 'boolean'" style="display:inline-block;width:100%">
|
||||
<el-switch :active-text="local[key]" v-model="advancedValue[key]"/>
|
||||
</span>
|
||||
<el-select v-else-if="advancedAttr[key].type === 'array'" v-model="advancedValue[key]" style="width:100%" size="small">
|
||||
<el-option value="" :label="local['nothing']"></el-option>
|
||||
<el-option :key="t" :value="t" :label="t" v-for="t in advancedAttr[key].enums"/>
|
||||
</el-select>
|
||||
<el-input v-model="advancedValue[key]" v-else style="width:100%" :placeholder="key" size="small"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!--<h3 v-text="local['add_custom']" v-show="custom">添加自定义属性</h3>
|
||||
<el-form class="ant-advanced-search-form" v-show="custom">
|
||||
<el-row :gutter="6">
|
||||
<el-col :span="8" v-for="item in customProps" :key="item.key">
|
||||
<el-form-item :label="item.key">
|
||||
<el-input v-model="item.value" style="width:calc(100% - 30px)" size="small"/>
|
||||
<el-button icon="close" type="link" @click="customProps.splice(customProps.indexOf(item),1)" size="small"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-show="addProp.key != undefined">
|
||||
<el-form-item>
|
||||
<el-input slot="label" v-model="addProp.key" size="small"/>
|
||||
<el-input v-model="addProp.value" size="small"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item>
|
||||
<el-button icon="check" type="link" @click="confirmAddCustomNode" v-if="customing"/>
|
||||
<el-tooltip content="local['add_custom']" v-else>
|
||||
<el-button icon="el-icon-plus" type="link" @click="addCustomNode"/>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>-->
|
||||
<h3 v-text="local['preview']">预览</h3>
|
||||
<pre style="width:100%">{{completeNodeValue}}</pre>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<ms-dialog-footer
|
||||
@cancel="modalVisible = false"
|
||||
@confirm="handleOk"/>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {isNull} from './util'
|
||||
import {TYPE_NAME, TYPE} from './type/type'
|
||||
import MsMock from './mock/index'
|
||||
import MsDialogFooter from '../../../../../common/components/MsDialogFooter'
|
||||
import LocalProvider from './LocalProvider'
|
||||
|
||||
export default {
|
||||
name: 'JsonSchemaEditor',
|
||||
components: {MsMock, MsDialogFooter},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
disabled: { //name不可编辑,根节点name不可编辑,数组元素name不可编辑
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledType: { //禁用类型选择
|
||||
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
|
||||
},
|
||||
custom: { //enable custom properties
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
lang: { // i18n language
|
||||
type: String,
|
||||
default: 'zh_CN'
|
||||
}
|
||||
},
|
||||
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
|
||||
},
|
||||
advanced() {
|
||||
return TYPE[this.pickValue.type]
|
||||
},
|
||||
advancedAttr() {
|
||||
return TYPE[this.pickValue.type].attr
|
||||
},
|
||||
advancedNotEmptyValue() {
|
||||
const jsonNode = Object.assign({}, this.advancedValue);
|
||||
for (let key in jsonNode) {
|
||||
isNull(jsonNode[key]) && delete jsonNode[key]
|
||||
}
|
||||
return jsonNode
|
||||
},
|
||||
completeNodeValue() {
|
||||
const t = {}
|
||||
for (const item of this.customProps) {
|
||||
t[item.key] = item.value
|
||||
}
|
||||
return Object.assign({}, this.pickValue, this.advancedNotEmptyValue, t)
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
TYPE_NAME,
|
||||
hidden: false,
|
||||
countAdd: 1,
|
||||
modalVisible: false,
|
||||
advancedValue: {},
|
||||
addProp: {},// 自定义属性
|
||||
customProps: [],
|
||||
customing: false,
|
||||
local: LocalProvider(this.lang)
|
||||
}
|
||||
},
|
||||
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, 'items')
|
||||
this.$delete(this.pickValue, 'required')
|
||||
if (this.isArray) {
|
||||
this.$set(this.pickValue, 'items', {type: 'string',mock: {mock: ""}})
|
||||
}
|
||||
},
|
||||
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, mock: {mock: ""}})
|
||||
},
|
||||
addCustomNode() {
|
||||
this.$set(this.addProp, 'key', this._joinName())
|
||||
this.$set(this.addProp, 'value', '')
|
||||
this.customing = true
|
||||
},
|
||||
confirmAddCustomNode() {
|
||||
this.customProps.push(this.addProp)
|
||||
this.addProp = {}
|
||||
this.customing = false
|
||||
},
|
||||
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++}`
|
||||
},
|
||||
onSetting() {
|
||||
this.modalVisible = true;
|
||||
this.advancedValue = this.advanced.value
|
||||
for (const k in this.advancedValue) {
|
||||
if (this.pickValue[k]) this.advancedValue[k] = this.pickValue[k]
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.modalVisible = false;
|
||||
},
|
||||
handleOk() {
|
||||
this.modalVisible = false
|
||||
for (const key in this.advancedValue) {
|
||||
if (isNull(this.advancedValue[key])) {
|
||||
this.$delete(this.pickValue, key)
|
||||
} else {
|
||||
this.$set(this.pickValue, key, this.advancedValue[key])
|
||||
}
|
||||
}
|
||||
for (const item of this.customProps) {
|
||||
this.$set(this.pickValue, item.key, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.json-schema-editor .row {
|
||||
display: flex;
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .ant-col-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .ant-col-name .ant-col-name-c {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .ant-col-name .ant-col-name-required {
|
||||
flex: 0 0 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .ant-col-type {
|
||||
min-width: 100px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .ant-col-setting {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .setting-icon {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .plus-icon {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.json-schema-editor .row .close-icon {
|
||||
color: #888;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.json-schema-editor-advanced-modal {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
min-width: 600px;
|
||||
}
|
||||
|
||||
.json-schema-editor-advanced-modal pre {
|
||||
font-family: monospace;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.json-schema-editor-advanced-modal h3 {
|
||||
display: block;
|
||||
border-left: 3px solid #1890ff;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.json-schema-editor-advanced-modal .ant-advanced-search-form {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.json-schema-editor-advanced-modal .ant-advanced-search-form .ant-form-item .ant-form-item-control-wrapper {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.col-item-setting {
|
||||
padding-top: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -31,10 +31,6 @@
|
|||
default: () => {
|
||||
}
|
||||
},
|
||||
mock: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -42,6 +38,9 @@
|
|||
}
|
||||
},
|
||||
created() {
|
||||
if (!this.schema.mock) {
|
||||
this.schema.mock = "";
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
|
@ -0,0 +1,26 @@
|
|||
const value = {
|
||||
description: null,
|
||||
minItems:null,
|
||||
maxItems:null,
|
||||
uniqueItems:false
|
||||
}
|
||||
const attr = {
|
||||
description: {
|
||||
name: '描述',
|
||||
type: 'string'
|
||||
},
|
||||
maxItems:{
|
||||
name: '最大元素个数',
|
||||
type: 'integer'
|
||||
},
|
||||
minItems:{
|
||||
name: '最小元素个数',
|
||||
type: 'integer'
|
||||
},
|
||||
uniqueItems:{
|
||||
name:'元素不可重复',
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
const wrapper = {value, attr}
|
||||
export default wrapper
|
|
@ -0,0 +1,11 @@
|
|||
const value = {
|
||||
description: null
|
||||
}
|
||||
const attr = {
|
||||
description: {
|
||||
name: '描述',
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
const wrapper = {value, attr}
|
||||
export default wrapper
|
|
@ -0,0 +1,31 @@
|
|||
const value = {
|
||||
description: null,
|
||||
maximum: null,
|
||||
minimum: null,
|
||||
exclusiveMaximum:null,
|
||||
exclusiveMinimum:null
|
||||
}
|
||||
const attr = {
|
||||
description: {
|
||||
name: '描述',
|
||||
type: 'string',
|
||||
},
|
||||
maximum:{
|
||||
name:'最大值',
|
||||
type:'integer'
|
||||
},
|
||||
minimum:{
|
||||
name:'最小值',
|
||||
type:'integer'
|
||||
},
|
||||
exclusiveMaximum:{
|
||||
name:'不包含最大值',
|
||||
type:'boolean'
|
||||
},
|
||||
exclusiveMinimum:{
|
||||
name:'不包含最小值',
|
||||
type:'boolean'
|
||||
}
|
||||
}
|
||||
const wrapper = {value, attr}
|
||||
export default wrapper
|
|
@ -0,0 +1,31 @@
|
|||
const value = {
|
||||
description: null,
|
||||
maximum: null,
|
||||
minimum: null,
|
||||
exclusiveMaximum:null,
|
||||
exclusiveMinimum:null
|
||||
}
|
||||
const attr = {
|
||||
description: {
|
||||
name: '描述',
|
||||
type: 'string',
|
||||
},
|
||||
maximum:{
|
||||
name:'最大值',
|
||||
type:'number'
|
||||
},
|
||||
minimum:{
|
||||
name:'最小值',
|
||||
type:'number'
|
||||
},
|
||||
exclusiveMaximum:{
|
||||
name:'不包含最大值',
|
||||
type:'boolean'
|
||||
},
|
||||
exclusiveMinimum:{
|
||||
name:'不包含最小值',
|
||||
type:'boolean'
|
||||
}
|
||||
}
|
||||
const wrapper = {value, attr}
|
||||
export default wrapper
|
|
@ -0,0 +1,21 @@
|
|||
const value = {
|
||||
description: null,
|
||||
maxProperties: null,
|
||||
minProperties: null
|
||||
}
|
||||
const attr = {
|
||||
description: {
|
||||
name: '描述',
|
||||
type: 'string',
|
||||
},
|
||||
maxProperties:{
|
||||
name:'最大元素个数',
|
||||
type:'integer'
|
||||
},
|
||||
minProperties:{
|
||||
name:'最小元素个数',
|
||||
type:'integer'
|
||||
}
|
||||
}
|
||||
const wrapper = {value, attr}
|
||||
export default wrapper
|
|
@ -0,0 +1,32 @@
|
|||
const value = {
|
||||
description: null,
|
||||
maxLength: null,
|
||||
minLength: null,
|
||||
pattern: null,
|
||||
format:null
|
||||
}
|
||||
const attr = {
|
||||
description: {
|
||||
name: '描述',
|
||||
type: 'string',
|
||||
},
|
||||
maxLength:{
|
||||
name:'最大字符数',
|
||||
type:'integer'
|
||||
},
|
||||
minLength:{
|
||||
name:'最小字符数',
|
||||
type:'integer'
|
||||
},
|
||||
pattern: {
|
||||
name: '正则表达式',
|
||||
type:'string'
|
||||
},
|
||||
format: {
|
||||
name:'格式',
|
||||
type:'array',
|
||||
enums:['date','date-time','email','hostname','ipv4','ipv6','uri']
|
||||
}
|
||||
}
|
||||
const wrapper = {value, attr}
|
||||
export default wrapper
|
|
@ -0,0 +1,17 @@
|
|||
import _object from './object'
|
||||
import _string from './string'
|
||||
import _array from './array'
|
||||
import _boolean from './boolean'
|
||||
import _integer from './integer'
|
||||
import _number from './number'
|
||||
const TYPE_NAME = ['string', 'number', 'integer','object', 'array', 'boolean']
|
||||
|
||||
const TYPE = {
|
||||
'object': _object,
|
||||
'string': _string,
|
||||
'array': _array,
|
||||
'boolean': _boolean,
|
||||
'integer': _integer,
|
||||
'number': _number
|
||||
}
|
||||
export {TYPE ,TYPE_NAME}
|
|
@ -0,0 +1,24 @@
|
|||
export function clearAttr(obj) {
|
||||
for(let key in obj){
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 快速拷贝两个对象的属性值
|
||||
* @param {*} source
|
||||
* @param {*} target
|
||||
*/
|
||||
export function copyAttr(source, target){
|
||||
Object.keys(target).forEach(key=>{target[key]=source[key]})
|
||||
}
|
||||
|
||||
export function isNull(ele){
|
||||
if(typeof ele==='undefined'){
|
||||
return true;
|
||||
}else if(ele==null){
|
||||
return true;
|
||||
}else if(ele==''){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<template>
|
||||
<el-tooltip placement="top" content="添加兄弟/子节点">
|
||||
<el-dropdown trigger="click">
|
||||
<i class="el-icon-plus plus"></i>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item>
|
||||
<span @click="addFieldAction({type:'add-field',isChild:false})">兄弟节点</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<span @click="addFieldAction({type:'add-field',isChild:true})">子节点</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DropPlus',
|
||||
components: {},
|
||||
props: {
|
||||
prefix: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {
|
||||
addFieldAction(...args) {
|
||||
this.$emit('add-field', ...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
|
@ -1,224 +0,0 @@
|
|||
<template>
|
||||
<div class="array-type">
|
||||
<el-row type="flex" align="middle">
|
||||
<el-col
|
||||
:span="8"
|
||||
class="col-item name-item col-item-name"
|
||||
:style="tagPaddingLeftStyle"
|
||||
>
|
||||
<el-row type="flex" justify="space-around" align="middle">
|
||||
<el-col :span="2" class="down-style-col">
|
||||
<span
|
||||
v-if="items.type === 'object'"
|
||||
class="down-style"
|
||||
@click="handleClickIcon"
|
||||
>
|
||||
<i v-if="!showIcon" class="el-icon-caret-bottom icon-object"></i>
|
||||
<i v-else class="el-icon-caret-right icon-object"></i>
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-input disabled value="Items" size="small" />
|
||||
</el-col>
|
||||
<el-col :span="2" style="text-align: center">
|
||||
<el-tooltip placement="top" content="全选">
|
||||
<el-checkbox disabled />
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="3" class="col-item col-item-type">
|
||||
<el-select
|
||||
:value="items.type"
|
||||
size="small"
|
||||
class="type-select-style"
|
||||
@change="handleChangeType"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in schemaTypes"
|
||||
:key="item"
|
||||
:value="item"
|
||||
:label="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
|
||||
<el-col v-if="isMock" :span="3" class="col-item col-item-mock">
|
||||
<MockSelect
|
||||
:schema="items"
|
||||
@showEdit="handleAction({ eventType: 'mock-edit' })"
|
||||
@change="handleChangeMock"
|
||||
/>
|
||||
</el-col>
|
||||
|
||||
<el-col
|
||||
v-if="showTitle"
|
||||
:span="isMock ? 4 : 5"
|
||||
class="col-item col-item-mock"
|
||||
>
|
||||
<el-input v-model="items.title" placeholder="标题" size="small">
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="handleAction({ eventType: 'show-edit', field: 'title' })"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col
|
||||
v-if="!showTitle && showDefaultValue"
|
||||
:span="isMock ? 4 : 5"
|
||||
class="col-item col-item-mock"
|
||||
>
|
||||
<el-input v-model="items.default" placeholder="默认值" size="small">
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="handleAction({ eventType: 'show-edit', field: 'default' })"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="isMock ? 4 : 5" class="col-item col-item-desc">
|
||||
<el-input v-model="items.description" placeholder="备注" size="small">
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="
|
||||
handleAction({ eventType: 'show-edit', field: 'description' })
|
||||
"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="isMock ? 2 : 3" class="col-item col-item-setting">
|
||||
<span
|
||||
class="adv-set"
|
||||
@click="
|
||||
handleAction({ eventType: 'setting', schemaType: items.type })
|
||||
"
|
||||
>
|
||||
<el-tooltip placement="top" content="高级设置">
|
||||
<i class="el-icon-setting"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="items.type === 'object'"
|
||||
@click="handleAction({ eventType: 'add-field', isChild: true })"
|
||||
>
|
||||
<el-tooltip placement="top" content="添加子节点">
|
||||
<i class="el-icon-plus plus"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="option-formStyle">
|
||||
<template v-if="items.type === 'array'">
|
||||
<SchemaArray
|
||||
:prefix="prefixArray"
|
||||
:data="items"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="items.type === 'object' && !showIcon">
|
||||
<SchemaObject
|
||||
:prefix="nameArray"
|
||||
:data="items"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import isUndefined from 'lodash/isUndefined'
|
||||
import MockSelect from '../mock'
|
||||
import SchemaObject from './SchemaObject'
|
||||
import { SCHEMA_TYPE } from '../utils'
|
||||
export default {
|
||||
name: 'SchemaArray',
|
||||
components: { MockSelect, SchemaObject },
|
||||
props: {
|
||||
isMock: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showDefaultValue: { type: Boolean, default: false },
|
||||
editorId: {
|
||||
type: String,
|
||||
default: 'editor_id',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
prefix: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
action: {
|
||||
type: Function,
|
||||
default: () => () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tagPaddingLeftStyle: {},
|
||||
schemaTypes: SCHEMA_TYPE,
|
||||
items: this.data.items,
|
||||
showIcon: false,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
nameArray() {
|
||||
return [].concat(this.prefixArray, 'properties')
|
||||
},
|
||||
prefixArray() {
|
||||
return [].concat(this.prefix, 'items')
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
const length = this.prefix.filter((name) => name !== 'properties').length
|
||||
this.tagPaddingLeftStyle = {
|
||||
paddingLeft: `${20 * (length + 1)}px`,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isUndefined() {
|
||||
return isUndefined
|
||||
},
|
||||
handleClickIcon() {
|
||||
this.showIcon = !this.showIcon
|
||||
},
|
||||
handleAction(opts) {
|
||||
const { prefix, name } = this
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.editorId}`, {
|
||||
prefix,
|
||||
name: name || 'items',
|
||||
...opts,
|
||||
})
|
||||
},
|
||||
handleChangeMock() {},
|
||||
handleChangeType(value) {
|
||||
console.log(value)
|
||||
this.handleAction({ eventType: 'schema-type', value })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,290 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-row type="flex" align="middle">
|
||||
<el-col
|
||||
:span="8"
|
||||
class="col-item name-item col-item-name"
|
||||
:style="tagPaddingLeftStyle"
|
||||
>
|
||||
<el-row type="flex" justify="space-around" align="middle">
|
||||
<el-col :span="2" class="down-style-col">
|
||||
<span
|
||||
v-if="value.type === 'object'"
|
||||
class="down-style"
|
||||
@click="handleClickIcon"
|
||||
>
|
||||
<i v-if="showIcon" class="el-icon-caret-bottom icon-object"></i>
|
||||
<i v-if="!showIcon" class="el-icon-caret-right icon-object"></i>
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="20" class="el-input--small">
|
||||
<input
|
||||
size="small"
|
||||
class="el-input el-input__inner"
|
||||
:class="{ 'is-disabled': value.disabled }"
|
||||
:value="name"
|
||||
:disabled="value.disabled"
|
||||
@change="handleNameChange"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="2" style="text-align: center">
|
||||
<el-tooltip placement="top" content="是否必须">
|
||||
<el-checkbox
|
||||
:checked="
|
||||
(data.required && data.required.indexOf(name) != -1) || false
|
||||
"
|
||||
@change="handleEnableRequire"
|
||||
></el-checkbox>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="3" class="col-item col-item-type">
|
||||
<el-select
|
||||
size="small"
|
||||
:value="value.type"
|
||||
:disabled="value.disabled && !value.canChangeType"
|
||||
class="type-select-style"
|
||||
@change="handleChangeType"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in schemaTypes"
|
||||
:key="item"
|
||||
:value="item"
|
||||
:label="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
|
||||
<el-col v-if="isMock" :span="3" class="col-item col-item-mock">
|
||||
<MockSelect
|
||||
:schema="value"
|
||||
@showEdit="handleAction({ eventType: 'mock-edit' })"
|
||||
@change="handleChangeMock"
|
||||
/>
|
||||
</el-col>
|
||||
|
||||
<el-col
|
||||
v-if="showTitle"
|
||||
:span="isMock ? 4 : 5"
|
||||
class="col-item col-item-mock"
|
||||
>
|
||||
<el-input
|
||||
v-model="value.title"
|
||||
:disabled="value.disabled"
|
||||
size="small"
|
||||
placeholder="标题"
|
||||
>
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="handleAction({ eventType: 'show-edit', field: 'title' })"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<!-- 默认值输入框 -->
|
||||
<el-col
|
||||
v-if="!showTitle && showDefaultValue"
|
||||
:span="isMock ? 4 : 5"
|
||||
class="col-item col-item-mock"
|
||||
>
|
||||
<el-input
|
||||
v-model.trim="value.default"
|
||||
placeholder="默认值"
|
||||
size="small"
|
||||
:disabled="
|
||||
value.type === 'object' || value.type === 'array' || value.disabled
|
||||
"
|
||||
>
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="handleAction({ eventType: 'show-edit', field: 'default' })"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="isMock ? 4 : 5" class="col-item col-item-desc">
|
||||
<el-input
|
||||
v-model="value.description"
|
||||
:disabled="value.disabled"
|
||||
size="small"
|
||||
placeholder="备注"
|
||||
>
|
||||
<i
|
||||
slot="append"
|
||||
class="el-icon-edit"
|
||||
@click="
|
||||
handleAction({ eventType: 'show-edit', field: 'description' })
|
||||
"
|
||||
></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="isMock ? 2 : 3" class="col-item col-item-setting">
|
||||
<span
|
||||
class="adv-set"
|
||||
@click="
|
||||
handleAction({ eventType: 'setting', schemaType: value.type })
|
||||
"
|
||||
>
|
||||
<el-tooltip placement="top" content="高级设置">
|
||||
<i class="el-icon-setting"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span
|
||||
class="delete-item"
|
||||
:class="{ hidden: value.disabled }"
|
||||
@click="handleAction({ eventType: 'delete-field' })"
|
||||
>
|
||||
<i class="el-icon-close close"></i>
|
||||
</span>
|
||||
<DropPlus
|
||||
v-if="value.type === 'object'"
|
||||
:prefix="prefix"
|
||||
:name="name"
|
||||
@add-field="handleAction"
|
||||
/>
|
||||
<span
|
||||
v-if="value.type !== 'object'"
|
||||
@click="handleAction({ eventType: 'add-field', isChild: false })"
|
||||
>
|
||||
<el-tooltip placement="top" content="添加兄弟节点">
|
||||
<i class="el-icon-plus plus"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="option-formStyle">
|
||||
<!-- {mapping(prefixArray, value, showEdit, showAdv)} -->
|
||||
<template v-if="value.type === 'array'">
|
||||
<schema-array
|
||||
:prefix="prefixArray"
|
||||
:data="value"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="value.type === 'object' && showIcon">
|
||||
<schema-object
|
||||
:prefix="nameArray"
|
||||
:data="value"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import isUndefined from 'lodash/isUndefined'
|
||||
import MockSelect from '../mock'
|
||||
import DropPlus from './DropPlus'
|
||||
import SchemaObject from './SchemaObject'
|
||||
import SchemaArray from './SchemaArray'
|
||||
import { SCHEMA_TYPE } from '../utils'
|
||||
export default {
|
||||
name: 'SchemaItem',
|
||||
components: {
|
||||
MockSelect,
|
||||
DropPlus,
|
||||
'schema-array': SchemaArray,
|
||||
'schema-object': SchemaObject,
|
||||
},
|
||||
props: {
|
||||
isMock: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showDefaultValue: { type: Boolean, default: false },
|
||||
editorId: {
|
||||
type: String,
|
||||
default: 'editor_id',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
prefix: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showIcon: true,
|
||||
tagPaddingLeftStyle: {},
|
||||
schemaTypes: SCHEMA_TYPE,
|
||||
value: this.data.properties[this.name],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
nameArray() {
|
||||
const prefixArray = [].concat(this.prefix, this.name)
|
||||
return [].concat(prefixArray, 'properties')
|
||||
},
|
||||
prefixArray() {
|
||||
return [].concat(this.prefix, this.name)
|
||||
// return [].concat(this.prefix, 'items')
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
const length = this.prefix.filter((name) => name !== 'properties').length
|
||||
this.tagPaddingLeftStyle = {
|
||||
paddingLeft: `${20 * (length + 1)}px`,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isUndefined() {
|
||||
return isUndefined
|
||||
},
|
||||
handleClickIcon() {
|
||||
this.showIcon = !this.showIcon
|
||||
},
|
||||
|
||||
handleAction(options) {
|
||||
const { prefix, name } = this
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.editorId}`, {
|
||||
eventType: 'add-field',
|
||||
prefix,
|
||||
name,
|
||||
...options,
|
||||
})
|
||||
},
|
||||
|
||||
handleNameChange(e) {
|
||||
this.handleAction({
|
||||
eventType: 'update-field-name',
|
||||
value: e.target.value,
|
||||
})
|
||||
},
|
||||
handleEnableRequire(e) {
|
||||
const { prefix, name } = this
|
||||
this.$jsEditorEvent.emit(`schema-update-${this.editorId}`, {
|
||||
eventType: 'toggle-required',
|
||||
prefix,
|
||||
name,
|
||||
required: e,
|
||||
})
|
||||
},
|
||||
handleChangeMock() {},
|
||||
handleChangeType(value) {
|
||||
this.handleAction({ eventType: 'schema-type', value })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,54 +0,0 @@
|
|||
<template>
|
||||
<div class="object-style">
|
||||
<schema-item
|
||||
v-for="(name,index) in propertyKeys"
|
||||
:key="index"
|
||||
:data="data"
|
||||
:name="name"
|
||||
:prefix="prefix"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
></schema-item>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'SchemaObject',
|
||||
components: { 'schema-item': () => import('./SchemaItem.vue') },
|
||||
props: {
|
||||
prefix: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
isMock: { type: Boolean, default: false },
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showDefaultValue: { type: Boolean, default: false },
|
||||
editorId: {
|
||||
type: String,
|
||||
default: 'editor_id'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tagPaddingLeftStyle: {},
|
||||
items: this.data.items
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
propertyKeys() {
|
||||
return Object.keys(this.data.properties)
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
|
@ -1,65 +0,0 @@
|
|||
<template>
|
||||
<div class="schema-content" v-bind="$attrs">
|
||||
<template v-if="data.type==='array'">
|
||||
<schema-array
|
||||
:prefix="name"
|
||||
:data="data"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="data.type==='object'">
|
||||
<schema-object
|
||||
:prefix="nameArray"
|
||||
:data="data"
|
||||
:is-mock="isMock"
|
||||
:show-title="showTitle"
|
||||
:show-default-value="showDefaultValue"
|
||||
:editor-id="editorId"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import SchemaObject from './SchemaObject'
|
||||
import SchemaArray from './SchemaArray'
|
||||
export default {
|
||||
name: 'SchemaJson',
|
||||
components: {
|
||||
'schema-array': SchemaArray,
|
||||
'schema-object': SchemaObject
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
isMock: { type: Boolean, default: false },
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showDefaultValue: { type: Boolean, default: false },
|
||||
editorId: {
|
||||
type: String,
|
||||
default: 'editor_id'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
nameArray() {
|
||||
return [].concat(this.name, 'properties')
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
|
@ -1,200 +0,0 @@
|
|||
export const JSONPATH_JOIN_CHAR = '.';
|
||||
export const lang = 'zh_CN';
|
||||
export const format = [
|
||||
{name: 'date-time'},
|
||||
{name: 'date'},
|
||||
{name: 'email'},
|
||||
{name: 'hostname'},
|
||||
{name: 'ipv4'},
|
||||
{name: 'ipv6'},
|
||||
{name: 'uri'}
|
||||
];
|
||||
export const SCHEMA_TYPE = [
|
||||
'string',
|
||||
'number',
|
||||
'array',
|
||||
'object',
|
||||
'boolean',
|
||||
'integer'
|
||||
];
|
||||
export const defaultInitSchemaData = {
|
||||
type: 'object',
|
||||
title: 'title',
|
||||
properties: {}
|
||||
};
|
||||
export const defaultSchema = {
|
||||
string: {
|
||||
type: 'string'
|
||||
},
|
||||
number: {
|
||||
type: 'number'
|
||||
},
|
||||
array: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
object: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
},
|
||||
boolean: {
|
||||
type: 'boolean'
|
||||
},
|
||||
integer: {
|
||||
type: 'integer'
|
||||
}
|
||||
};
|
||||
|
||||
// 防抖函数,减少高频触发的函数执行的频率
|
||||
// 请在 constructor 里使用:
|
||||
|
||||
// this.func = debounce(this.func, 400);
|
||||
export const debounce = (func, wait) => {
|
||||
let timeout;
|
||||
return function () {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(func, wait);
|
||||
};
|
||||
};
|
||||
|
||||
export const getData = (state, keys) => {
|
||||
let curState = state;
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
curState = curState[keys[i]];
|
||||
}
|
||||
return curState;
|
||||
};
|
||||
|
||||
export const setData = function (state, keys, value) {
|
||||
let curState = state;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
curState = curState[keys[i]];
|
||||
}
|
||||
curState[keys[keys.length - 1]] = value;
|
||||
};
|
||||
|
||||
export const deleteData = function (state, keys) {
|
||||
let curState = state;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
curState = curState[keys[i]];
|
||||
}
|
||||
|
||||
delete curState[keys[keys.length - 1]];
|
||||
};
|
||||
|
||||
export const getParentKeys = function (keys) {
|
||||
if (keys.length === 1) return [];
|
||||
const arr = [].concat(keys);
|
||||
arr.splice(keys.length - 1, 1);
|
||||
return arr;
|
||||
};
|
||||
|
||||
export const clearSomeFields = function (keys, data) {
|
||||
const newData = Object.assign({}, data);
|
||||
keys.forEach(key => {
|
||||
delete newData[key];
|
||||
});
|
||||
return newData;
|
||||
};
|
||||
|
||||
function getFieldstitle(data) {
|
||||
const requiredtitle = [];
|
||||
Object.keys(data).map(title => {
|
||||
requiredtitle.push(title);
|
||||
});
|
||||
|
||||
return requiredtitle;
|
||||
}
|
||||
|
||||
export function handleSchemaRequired(schema, checked) {
|
||||
if (schema.type === 'object') {
|
||||
const requiredtitle = getFieldstitle(schema.properties);
|
||||
|
||||
// schema.required = checked ? [].concat(requiredtitle) : [];
|
||||
if (checked) {
|
||||
schema.required = [].concat(requiredtitle);
|
||||
} else {
|
||||
delete schema.required;
|
||||
}
|
||||
|
||||
handleObject(schema.properties, checked);
|
||||
} else if (schema.type === 'array') {
|
||||
handleSchemaRequired(schema.items, checked);
|
||||
} else {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
function handleObject(properties, checked) {
|
||||
for (var key in properties) {
|
||||
if (properties[key].type === 'array' || properties[key].type === 'object') {
|
||||
handleSchemaRequired(properties[key], checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function cloneObject(obj) {
|
||||
if (typeof obj === 'object') {
|
||||
if (Array.isArray(obj)) {
|
||||
var newArr = [];
|
||||
obj.forEach(function (item, index) {
|
||||
newArr[index] = cloneObject(item);
|
||||
});
|
||||
return newArr;
|
||||
} else {
|
||||
var newObj = {};
|
||||
for (var key in obj) {
|
||||
newObj[key] = cloneObject(obj[key]);
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
export const uuid = () => {
|
||||
return Math.random()
|
||||
.toString(16)
|
||||
.substr(2, 5);
|
||||
};
|
||||
|
||||
export const log = (...args) => {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* val值不为空字符,null,undefined
|
||||
*/
|
||||
export const isNotNil = val => {
|
||||
const arr = [undefined, null, ''];
|
||||
return !arr.includes(val);
|
||||
};
|
||||
|
||||
/**
|
||||
* form表单值校验是否为空,有值为空则返回true,值都正确则返回false
|
||||
*/
|
||||
export const isFormValid = obj => {
|
||||
if (typeof obj !== 'object') return true;
|
||||
const keys = Object.keys(obj);
|
||||
return keys.some(key => {
|
||||
return !isNotNil(obj[key]);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 只返回有值得属性新对象
|
||||
* @param {Object} formData 表单对象
|
||||
*/
|
||||
export const getValidFormVal = formData => {
|
||||
const obj = {};
|
||||
const keys = Object.keys(formData);
|
||||
keys.forEach(key => {
|
||||
if (isNotNil(formData[key])) {
|
||||
obj[key] = formData[key];
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
};
|
|
@ -19,10 +19,9 @@ import '../common/css/main.css';
|
|||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import VueFab from 'vue-float-action-button'
|
||||
import {horizontalDrag} from "../common/js/directive";
|
||||
import JsonSchemaEditor from './components/common/json-schema/index';
|
||||
import JsonSchemaEditor from './components/common/json-schema/ot/packages/index';
|
||||
Vue.use(JsonSchemaEditor);
|
||||
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.use(icon);
|
||||
Vue.use(ElementUI, {
|
||||
|
|
Loading…
Reference in New Issue