refactor: input render

master
xiaojunnuo 2021-01-30 00:06:50 +08:00
parent 2f03e18c59
commit b4ee3d0dfc
12 changed files with 122 additions and 120 deletions

View File

@ -1,60 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-mocha" target="_blank" rel="noopener">unit-mocha</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@ -0,0 +1,32 @@
<script>
import { h, resolveComponent } from 'vue'
import _ from 'lodash-es'
export default {
name: 'component-render',
props: {
name: {
type: String,
default: 'a-input'
},
children: {
type: Array
},
on: {
type: Object
}
},
setup (props, context) {
const attrs = {
...context.$attrs
}
_.forEach(props.on, (value, key) => {
attrs[key] = value
if (typeof value === 'string') {
// eslint-disable-next-line no-eval
attrs[key] = eval(value)
}
})
return () => h(resolveComponent(props.name), context.$attrs, props.children)
}
}
</script>

View File

@ -0,0 +1,16 @@
import DContainer from './d-container'
import ComponentRender from './component-render'
import ProviderSelector from './provider-selector/provider-selector'
const list = [
DContainer,
ComponentRender,
ProviderSelector
]
export default {
install (app) {
for (const item of list) {
app.component(item.name, item)
}
}
}

View File

@ -23,7 +23,9 @@
<a-button type="danger" @click="remove(item,index)"><template #icon ><DeleteOutlined /></template></a-button> <a-button type="danger" @click="remove(item,index)"><template #icon ><DeleteOutlined /></template></a-button>
</template> </template>
<a-radio :checked="item.key===selectedKey" @update:checked="selectedKey = item.key">{{item.key}} {{ item.name }}</a-radio> <a-radio :checked="item.key===selectedKey" @update:checked="selectedKey = item.key">
{{ item.name }} ({{item.type}})
</a-radio>
</a-list-item> </a-list-item>
</template> </template>
@ -38,7 +40,7 @@
<a-modal v-model:visible="editVisible" dialogClass="d-dialog" width="700px" title="编辑授权" @ok="onSubmit"> <a-modal v-model:visible="editVisible" dialogClass="d-dialog" width="700px" title="编辑授权" @ok="onSubmit">
<a-form ref="formRef" class="domain-form" :model="formData" labelWidth="150px" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form ref="formRef" class="domain-form" :model="formData" labelWidth="150px" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="类型" :rules="rules.type"> <a-form-item label="类型" name="type" :rules="rules.type">
<a-radio-group :disabled="editIndex!=null" v-model:value="formData.type" @change="onTypeChanged" > <a-radio-group :disabled="editIndex!=null" v-model:value="formData.type" @change="onTypeChanged" >
<a-radio-button v-for="(option) of providerDefineList" :key="option.name" :value="option.name"> <a-radio-button v-for="(option) of providerDefineList" :key="option.name" :value="option.name">
{{option.label}} {{option.label}}
@ -47,19 +49,15 @@
</a-form-item> </a-form-item>
<template v-if="formData.type && currentProvider"> <template v-if="formData.type && currentProvider">
<a-form-item label="key" name="key" :rules="rules.key">
<a-input :disabled="editIndex!=null" v-model:value="formData.key"/>
<div class="helper">不重复的key</div>
</a-form-item>
<a-form-item label="名称" name="name" :rules="rules.name"> <a-form-item label="名称" name="name" :rules="rules.name">
<a-input v-model:value="formData.name"/> <a-input v-model:value="formData.name"/>
</a-form-item> </a-form-item>
<a-form-item v-for="(item,key) in currentProvider.input" <a-form-item v-for="(item,key,index) in currentProvider.input"
:key="key" :key="index"
v-bind="item.component||{}"
:label="item.label || key" :label="item.label || key"
:name="key" :name="key">
:rules="[{ required: true, message: '必填项' }]"> <component-render v-model:value="formData[key]" v-bind="item.component || {}"></component-render>
<a-input v-model:value="formData[key]" v-bind="item.attrs" ></a-input>
<div class="helper">{{item.desc}}</div> <div class="helper">{{item.desc}}</div>
</a-form-item> </a-form-item>
</template> </template>
@ -75,42 +73,21 @@ import { ref, reactive, nextTick, watch } from 'vue'
import { useForm } from '@ant-design-vue/use' import { useForm } from '@ant-design-vue/use'
import _ from 'lodash-es' import _ from 'lodash-es'
import providerApi from '@/api/api.providers' import providerApi from '@/api/api.providers'
function useEdit (props, context, providerList, onSave) { function useEdit (props, context, providerList, onEditSave) {
const formData = reactive({ const formData = reactive({
key: '', key: '',
name: '', name: '',
type: '' type: undefined
}) })
const rules = reactive({ const rules = ref({
type: [{ type: [{
type: 'string',
required: true, required: true,
message: '请选择类型' message: '请选择类型'
}], }],
key: [{
required: true,
message: '请输入key'
}, {
validator (rule, value) {
const providers = providerList.value
if (!providers || providers.length === 0) {
return Promise.resolve()
}
if (editIndex.value != null) {
return Promise.resolve()
}
const filter = providers.filter(item => item.key === value)
console.log('validate', filter)
if (filter.length === 0) {
return Promise.resolve()
} else {
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('key不能重复')
}
},
message: 'key不能与其他授权配置重复'
}],
name: [{ name: [{
type: 'string',
required: true, required: true,
message: '请输入名称' message: '请输入名称'
}] }]
@ -121,9 +98,10 @@ function useEdit (props, context, providerList, onSave) {
// const { resetFields, validate, validateInfos } = useForm(formData, rules) // const { resetFields, validate, validateInfos } = useForm(formData, rules)
const onSubmit = async e => { const onSubmit = async e => {
e.preventDefault() e.preventDefault()
await formRef.value.validate() const res = await formRef.value.validate()
console.log('validation:', res)
const newProvider = _.cloneDeep(formData) const newProvider = _.cloneDeep(formData)
onSave(newProvider, editIndex.value) onEditSave(newProvider, editIndex.value)
closeEdit() closeEdit()
} }
@ -139,6 +117,7 @@ function useEdit (props, context, providerList, onSave) {
changeType(item.type) changeType(item.type)
} else { } else {
editIndex.value = null editIndex.value = null
formData.type = null
} }
editVisible.value = true editVisible.value = true
} }
@ -192,6 +171,23 @@ function useEdit (props, context, providerList, onSave) {
add add
} }
} }
let index = 0
const keyPrefix = 'provider_'
function generateNewKey (list) {
index++
let exists = false
for (const item of list) {
if (item.key === keyPrefix + index) {
exists = true
break
}
}
if (exists) {
return generateNewKey(list)
}
return keyPrefix + index
}
export default { export default {
name: 'provider-manager', name: 'provider-manager',
props: { props: {
@ -219,11 +215,13 @@ export default {
selectedKey.value = props.value selectedKey.value = props.value
}, { immediate: true }) }, { immediate: true })
const onEditSave = (newProvier, editIndex) => { const onEditSave = (newProvider, editIndex) => {
if (editIndex == null) { if (editIndex == null) {
providerList.value.push(newProvier) // add key
newProvider.key = generateNewKey(providerList.value)
providerList.value.push(newProvider)
} else { } else {
_.merge(providerList.value[editIndex], newProvier) _.merge(providerList.value[editIndex], newProvider)
} }
} }

View File

@ -4,7 +4,7 @@
:value="value" :value="value"
@update:value="valueUpdate" @update:value="valueUpdate"
> >
<a-select-option v-for="item of providers" :key="item.key" :value="item.key"> <a-select-option v-for="item of providers" :key="item.key" :value="item.key" :disabled="isDisabled(item)">
{{ item.name }} {{ item.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -35,7 +35,8 @@ export default {
}, },
providers: { providers: {
type: Object type: Object
} },
filter: {}
}, },
setup (props, context) { setup (props, context) {
const providerManagerRef = ref(null) const providerManagerRef = ref(null)
@ -52,11 +53,19 @@ export default {
const valueUpdate = (val) => { const valueUpdate = (val) => {
context.emit('update:value', val) context.emit('update:value', val)
} }
const isDisabled = (item) => {
if (!props.filter) {
return false
}
return item.type === props.filter
}
return { return {
providersUpdate, providersUpdate,
valueUpdate, valueUpdate,
providerManagerOpen, providerManagerOpen,
providerManagerRef providerManagerRef,
isDisabled
} }
} }
} }

View File

@ -6,11 +6,11 @@ import 'ant-design-vue/dist/antd.css'
import '@/style/common.less' import '@/style/common.less'
import { i18n } from '@/i18n' import { i18n } from '@/i18n'
import icons from './icons' import icons from './icons'
import DContainer from '@/components/d-container' import components from './components'
const app = createApp(App) const app = createApp(App)
app.config.productionTip = false app.config.productionTip = false
app.use(i18n) app.use(i18n)
app.use(Antd) app.use(Antd)
icons(app) icons(app)
app.component('d-container', DContainer) app.use(components)
app.use(router).mount('#app') app.use(router).mount('#app')

View File

@ -71,7 +71,6 @@
import { reactive, toRaw, ref, watch } from 'vue' import { reactive, toRaw, ref, watch } from 'vue'
import { useForm } from '@ant-design-vue/use' import { useForm } from '@ant-design-vue/use'
import _ from 'lodash-es' import _ from 'lodash-es'
import ProviderSelector from '@/views/detail/components/provider-selector'
function useDrawer () { function useDrawer () {
const visible = ref(false) const visible = ref(false)
@ -94,7 +93,6 @@ function useDrawer () {
export default { export default {
name: 'cert-form', name: 'cert-form',
components: { ProviderSelector },
emits: ['update:accessProviders', 'update:cert'], emits: ['update:accessProviders', 'update:cert'],
// //
props: { props: {

View File

@ -39,8 +39,8 @@
></a-input> ></a-input>
</a-form-item> </a-form-item>
<a-form-item v-for="(value,key) in currentPlugin.input" :key="key" :label="value.label"> <a-form-item v-for="(item,key) in currentPlugin.input" :key="key" :label="item.label">
<a-input v-model:value="currentTask[key]"></a-input> <component-render v-model:value="currentTask[key]" v-bind="item.component || {}"></component-render>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -57,12 +57,10 @@
</template> </template>
</a-drawer> </a-drawer>
<provider-manager ref="providerManager"></provider-manager>
</template> </template>
<script> <script>
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import ProviderManager from '@/views/detail/components/provider-manager'
import pluginsApi from '@/api/api.plugins' import pluginsApi from '@/api/api.plugins'
import { ref } from 'vue' import { ref } from 'vue'
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -176,7 +174,6 @@ function useProviderManager () {
} }
export default { export default {
name: 'task-form', name: 'task-form',
components: { ProviderManager },
emits: ['update'], emits: ['update'],
props: { props: {
options: {} options: {}

View File

@ -81,6 +81,7 @@
<PlusCircleOutlined title="添加部署流程" class="add-icon" @click="deployAdd"/> <PlusCircleOutlined title="添加部署流程" class="add-icon" @click="deployAdd"/>
</h3> </h3>
<a-divider></a-divider> <a-divider></a-divider>
<div class="deploy-list"> <div class="deploy-list">
<a-card class="deploy-item" v-for="(deploy,index) of options.deploy" :key="index"> <a-card class="deploy-item" v-for="(deploy,index) of options.deploy" :key="index">

View File

@ -8,7 +8,11 @@ const define = {
input: { input: {
domainName: { domainName: {
label: 'cdn加速域名', label: 'cdn加速域名',
required: true required: true,
attrs: {
placeholder: 'cdn加速域名',
rules: [{ required: true, message: '该项必填' }]
}
}, },
certName: { certName: {
label: '证书名称' label: '证书名称'
@ -34,7 +38,10 @@ const define = {
label: 'Access提供者', label: 'Access提供者',
type: [String, Object], type: [String, Object],
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象', desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
options: 'accessProviders[type=aliyun]', component: {
name: 'provider-selector',
filter: 'aliyun'
},
required: true required: true
} }
}, },

View File

@ -10,7 +10,6 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
input: { input: {
accessKeyId: { accessKeyId: {
type: String, type: String,
desc: 'accessKeyId',
attrs: { attrs: {
placeholder: 'accessKeyId', placeholder: 'accessKeyId',
rules: [{ required: true, message: '必填项' }] rules: [{ required: true, message: '必填项' }]
@ -19,7 +18,6 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
}, },
accessKeySecret: { accessKeySecret: {
type: String, type: String,
desc: 'accessKeySecret',
attrs: { attrs: {
placeholder: 'accessKeySecret', placeholder: 'accessKeySecret',
rules: [{ required: true, message: '必填项' }] rules: [{ required: true, message: '必填项' }]

View File

@ -10,12 +10,18 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
input: { input: {
id: { id: {
type: String, type: String,
desc: 'dnspod的账户id' attrs: {
placeholder: 'dnspod接口账户id',
rules: [{ required: true, message: '该项必填' }]
}
}, },
token: { token: {
type: String, type: String,
label: 'token', label: 'token',
desc: 'dnspod的开放接口token' attrs: {
placeholder: '开放接口token',
rules: [{ required: true, message: '该项必填' }]
}
} }
} }
} }