| <!-- |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| --> |
| |
| <template> |
| <div class="plugin-dialog"> |
| <el-dialog |
| :title="'Plugin ' + name + ' Edit'" |
| :visible.sync="showDialog" |
| :before-close="onCancel" |
| :close-on-press-escape="false" |
| :close-on-click-modal="false" |
| > |
| <el-form |
| v-if="schema.oneOf" |
| ref="form" |
| :model="data" |
| :rules="rules" |
| :show-message="false" |
| class="oneof-plugin-wrapper" |
| > |
| <el-form-item |
| label="Option" |
| :rules="{ |
| required: true, trigger: 'blur' |
| }" |
| > |
| <el-radio-group |
| v-model="data['radioKey']" |
| @change="handleOneOfChange" |
| > |
| <el-radio |
| v-for="(value, key) in schema.properties" |
| :key="key" |
| :label="key" |
| > |
| {{ key }} |
| </el-radio> |
| </el-radio-group> |
| </el-form-item> |
| |
| <el-form-item |
| v-for="(value, index) in data.values" |
| :key="index" |
| :label="'Value' + (index + 1)" |
| :rules="{ |
| required: true, trigger: 'blur' |
| }" |
| > |
| <el-input v-model="data['values'][index]" /> |
| <el-button |
| v-if="data.values.length !== 1" |
| class="remove-value-btn" |
| type="danger" |
| @click.prevent="removeOneOfPropValue(index)" |
| > |
| Remove |
| </el-button> |
| </el-form-item> |
| |
| <el-form-item> |
| <el-button |
| :disabled="oneOfPropHasEmptyValue" |
| @click="addValueInput" |
| > |
| {{ $t('button.addValue') }} |
| </el-button> |
| </el-form-item> |
| </el-form> |
| |
| <el-form |
| v-if="!schema.oneOf" |
| ref="form" |
| :model="data" |
| :rules="rules" |
| :show-message="false" |
| > |
| <el-form-item |
| v-for="(index, key) in schema.properties" |
| :key="key" |
| :label="key" |
| label-width="160px" |
| :prop="key" |
| > |
| <label slot="label"> |
| {{ key }} |
| <el-tooltip |
| v-if="schema.properties[key].description" |
| placement="top" |
| effect="light" |
| > |
| <div slot="content">{{ schema.properties[key].description }}</div> |
| <i class="el-icon-info" /> |
| </el-tooltip> |
| </label> |
| |
| <!-- 分情况讨论 --> |
| <!-- number property --> |
| <el-input-number |
| v-if="schema.properties[key].type === 'integer' || schema.properties[key].type === 'number'" |
| v-model="data[key]" |
| :min="schema.properties[key].minimum || -99999999999" |
| :max="schema.properties[key].maximum || 99999999999" |
| label="描述文字" |
| @change="onPropertyChange(key, $event)" |
| /> |
| |
| <!-- enum property --> |
| <el-select |
| v-if="schema.properties[key].hasOwnProperty('enum')" |
| v-model="data[key]" |
| :clearable="true" |
| :placeholder="`Select a ${key}`" |
| @change="onPropertyChange(key, $event)" |
| > |
| <el-option |
| v-for="value in schema.properties[key]['enum']" |
| :key="value" |
| :label="value" |
| :value="value" |
| /> |
| </el-select> |
| |
| <!-- string property --> |
| <el-input |
| v-if="schema.properties[key].type === 'string' && !schema.properties[key].hasOwnProperty('enum')" |
| v-model="data[key]" |
| :placeholder="key" |
| @input="onPropertyChange(key, $event)" |
| /> |
| |
| <!-- boolean property --> |
| <el-switch |
| v-if="schema.properties[key].type === 'boolean' && !schema.properties[key].hasOwnProperty('enum')" |
| v-model="data[key]" |
| active-color="#13ce66" |
| inactive-color="#ff4949" |
| /> |
| |
| <!-- array property --> |
| <div |
| v-if="schema.properties[key].type === 'array'" |
| class="array-input-container" |
| > |
| <div |
| v-for="(item, arrayIndex) in data[key]" |
| :key="arrayIndex" |
| class="object-input-item" |
| > |
| <el-input |
| :key="arrayIndex" |
| v-model="data[key][arrayIndex]" |
| :placeholder="`${key} [${arrayIndex}]`" |
| @input="isDataChanged = true" |
| /> |
| <el-button |
| @click="deleteArrayItem(key, arrayIndex)" |
| > |
| {{ $t('button.delete') }} |
| </el-button> |
| </div> |
| |
| <el-button |
| @click="addArrayItem(key)" |
| > |
| {{ $t('button.addValue') }} |
| </el-button> |
| </div> |
| |
| <!-- object property --> |
| <div |
| v-if="schema.properties[key].type === 'object'" |
| class="object-input-container" |
| > |
| <div |
| v-for="(objectItem, objectIndex) in objectPropertiesArray[key]" |
| :key="objectIndex" |
| class="object-input-item" |
| > |
| <el-input |
| v-model="objectPropertiesArray[key][objectIndex].key" |
| :placeholder="`${key} [key ${objectIndex}]`" |
| @input="onObjectPropertyChange(key, $event, true)" |
| /> |
| <el-input |
| v-model="objectPropertiesArray[key][objectIndex].value" |
| :placeholder="`${key} [value ${objectIndex}]`" |
| @input="onObjectPropertyChange(key, $event, false)" |
| /> |
| <el-button |
| @click="deleteObjectItem(key, objectIndex)" |
| > |
| {{ $t('button.delete') }} |
| </el-button> |
| </div> |
| <el-button |
| @click="addObjectItem(key)" |
| > |
| {{ $t('button.addValue') }} |
| </el-button> |
| </div> |
| </el-form-item> |
| </el-form> |
| <span |
| slot="footer" |
| class="dialog-footer" |
| > |
| <el-button |
| @click="onCancel" |
| > |
| {{ $t('button.cancel') }} |
| </el-button> |
| <el-button |
| type="primary" |
| :disabled="!isDataChanged && oneOfPropHasEmptyValue" |
| @click="onSave" |
| > |
| {{ $t('button.confirm') }} |
| </el-button> |
| </span> |
| </el-dialog> |
| </div> |
| </template> |
| |
| <script lang="ts"> |
| import { Component, Prop, Vue, Watch } from 'vue-property-decorator' |
| |
| import { getPluginSchema } from '../../api/schema/schema' |
| |
| const uuidv1 = require('uuid/v1') |
| |
| @Component({ |
| name: 'PluginDialog' |
| }) |
| export default class extends Vue { |
| @Prop({ default: false }) private show!: boolean |
| @Prop({ default: '' }) private name!: string |
| @Prop({ default: null }) private pluginData!: any |
| |
| private schema: any = { |
| properties: {} |
| } |
| private rules: any = {} |
| private data: any = {} |
| private isDataChanged: boolean = false |
| private showDialog: boolean = false |
| private objectPropertiesArray = {} |
| |
| @Watch('show') |
| private onShowChange(value: boolean) { |
| this.resetPlugin() |
| if (value) { |
| this.getschema(this.name) |
| } |
| this.showDialog = value |
| } |
| |
| private resetPlugin() { |
| this.schema = { |
| properties: {} |
| } |
| this.rules = {} |
| this.data = {} |
| this.isDataChanged = false |
| } |
| |
| private async getschema(name: string) { |
| const schema = await getPluginSchema(name) as any |
| |
| if (!schema.properties) { |
| this.isDataChanged = true |
| return |
| } |
| |
| this.schema = Object.assign({}, { |
| ...schema, |
| name: this.name |
| }) |
| |
| const rules = Object.assign({}, schema.properties) |
| |
| for (let pluginName in rules) { |
| const plugin = Object.assign({}, rules[pluginName]) |
| |
| rules[pluginName] = { |
| trigger: 'blur' |
| } |
| |
| if (schema.required) { |
| rules[pluginName].required = schema.required.includes(pluginName) |
| } |
| |
| if (plugin.hasOwnProperty('type')) { |
| rules[pluginName]['type'] = plugin['type'] |
| } |
| |
| if (plugin.hasOwnProperty('minimum')) { |
| rules[pluginName]['min'] = plugin['minimum'] |
| } |
| |
| if (plugin.hasOwnProperty('maximum')) { |
| rules[pluginName]['max'] = plugin['maximum'] |
| } |
| |
| if (plugin.hasOwnProperty('enum')) { |
| rules[pluginName]['type'] = 'enum' |
| rules[pluginName]['enum'] = plugin['enum'] |
| } |
| } |
| |
| this.rules = rules |
| |
| // Generate initial data and merge current data |
| let schemaKeys = {} |
| for (let key in schema.properties) { |
| if (schema.properties[key].default) { |
| schemaKeys[key] = schema.properties[key].default |
| continue |
| } |
| |
| switch (schema.properties[key].type) { |
| case 'array': |
| schemaKeys[key] = [] |
| break |
| case 'object': |
| schemaKeys[key] = {} |
| this.objectPropertiesArray[key] = [] |
| if (this.pluginData && this.pluginData[key]) { |
| Object.keys(this.pluginData[key]).map(item => { |
| this.objectPropertiesArray[key].push({ |
| key: item, |
| value: this.pluginData[key][item] |
| }) |
| }) |
| } |
| break |
| case 'boolean': |
| schemaKeys[key] = false |
| break |
| default: |
| schemaKeys[key] = '' |
| } |
| } |
| |
| if (this.pluginData) { |
| this.data = Object.assign(schemaKeys, this.pluginData) |
| } else { |
| this.data = schemaKeys |
| } |
| |
| if (this.name === 'key-auth' && !this.pluginData) { |
| this.data = { |
| key: uuidv1() |
| } |
| |
| this.isDataChanged = true |
| } |
| |
| // Edit plugin data |
| if (this.name === 'ip-restriction' && this.pluginData) { |
| const key = Object.keys(this.pluginData)[0] |
| this.$nextTick(() => { |
| this.data = { |
| radioKey: key, |
| values: this.pluginData[key] |
| } |
| }) |
| } |
| |
| // Create new plugin data |
| if (this.schema.oneOf) { |
| this.data = { |
| radioKey: Object.keys(this.schema.properties)[0], |
| values: [''] |
| } |
| } |
| } |
| |
| private onCancel() { |
| this.$emit('hidePlugin') |
| } |
| |
| private onSave() { |
| (this.$refs.form as any).validate((valid: boolean) => { |
| // 标记该插件数据是否通过校验 |
| if (valid) { |
| // Reorganize an array of object properties into objects |
| this.data = Object.assign({}, this.data, this.reorganizeObjectProperty()) |
| this.data = this.processOneOfProp(this.data) |
| this.$emit('save', this.name, this.data) |
| this.$message.warning(`${this.$t('message.clickSaveButton')}`) |
| } else { |
| return false |
| } |
| }) |
| } |
| |
| /** |
| * Add item to array property |
| * @param key |
| */ |
| private addArrayItem(key: any) { |
| if (this.data[key].length < this.schema.properties[key].maxItems) { |
| this.data[key].push('') |
| this.$forceUpdate() |
| } else { |
| this.$message.warning(`${this.$t('message.cannotAddMoreItems')}`) |
| } |
| } |
| /** |
| * Delete item to array property |
| * @param key |
| * @param index |
| */ |
| private deleteArrayItem(key: any, index: number) { |
| this.data[key].splice(index, 1) |
| this.isDataChanged = true |
| this.$forceUpdate() |
| } |
| /** |
| * Add item to object property |
| * @param key |
| */ |
| private addObjectItem(key: any) { |
| this.objectPropertiesArray[key].push({ |
| key: '', |
| value: '' |
| }) |
| this.isDataChanged = true |
| this.$forceUpdate() |
| } |
| |
| /** |
| * Delete item form object property |
| * @param key |
| * @param index |
| */ |
| private deleteObjectItem(key: any, index: number) { |
| this.objectPropertiesArray[key].splice(index, 1) |
| this.isDataChanged = true |
| this.$forceUpdate() |
| } |
| |
| /** |
| * Reorganize an array of object properties into objects |
| */ |
| private reorganizeObjectProperty() { |
| let data = {} |
| for (let i in this.objectPropertiesArray) { |
| let objectItem = {} |
| this.objectPropertiesArray[i].map((item: any) => { |
| objectItem[item.key] = item.value |
| }) |
| data[i] = objectItem |
| } |
| return data |
| } |
| |
| /** |
| * Force rerender on object property content changed |
| */ |
| private onObjectPropertyChange() { |
| this.isDataChanged = true |
| this.$forceUpdate() |
| } |
| |
| private onPropertyChange(key: any, value: any) { |
| this.data[key] = value |
| this.isDataChanged = true |
| } |
| |
| private handleOneOfChange(e: any) { |
| this.data.values = [''] |
| } |
| |
| get oneOfPropHasEmptyValue() { |
| return this.data.values ? this.data.values.find((value: string) => value === '') : true |
| } |
| |
| private addValueInput() { |
| this.data.values = this.data.values.concat(['']) |
| } |
| |
| private removeOneOfPropValue(index: number) { |
| this.data.values = this.data.values.filter((item: any, _index: number) => index !== _index) |
| } |
| |
| private processOneOfProp(data: any) { |
| if (!this.schema.oneOf) { |
| // remove empty field |
| for (let key in data) { |
| if (data[key] === '') { |
| delete data[key] |
| } |
| if (typeof data[key] === 'object' && Object.keys(data[key]).length === 0) { |
| delete data[key] |
| } |
| } |
| return data |
| } |
| |
| return { |
| [this.data.radioKey]: this.data.values |
| } |
| } |
| } |
| </script> |
| |
| <style lang="scss"> |
| .plugin-dialog { |
| .el-form { |
| .el-form-item { |
| .el-form-item__content { |
| .el-input { |
| width: 200px !important; |
| } |
| } |
| } |
| } |
| |
| .oneof-plugin-wrapper { |
| .remove-value-btn { |
| margin-left: 10px; |
| } |
| } |
| |
| .array-input-container > * { |
| display: flex; |
| margin-top: 5px; |
| } |
| |
| .object-input-container > * { |
| display: flex; |
| margin-top: 5px; |
| :not(:first-child){ |
| margin-left: 5px; |
| } |
| } |
| } |
| </style> |