refactor: form input

master
xiaojunnuo 2021-01-31 02:09:54 +08:00
parent b4ee3d0dfc
commit eab0c3be60
14 changed files with 6826 additions and 3090 deletions

View File

@ -1,6 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: ['@babel/plugin-proposal-optional-chaining']
]
}

File diff suppressed because it is too large Load Diff

View File

@ -9,36 +9,43 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
"@certd/plugins": "^0.1.11",
"@certd/providers": "^0.1.11",
"ant-design-vue": "^2.0.0-rc.8",
"core-js": "^3.6.5",
"lodash-es": "^4.17.20",
"vue": "^3.0.0",
"vue-i18n": "^9.0.0-rc.2",
"vue-router": "^4.0.0-0"
"core-js": "^3.8.1",
"vue": "^3.0.4",
"vue-router": "^4.0.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-unit-mocha": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-standard": "^5.1.2",
"@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.12.1",
"@vue/cli-plugin-babel": "~5.0.0-alpha.3",
"@vue/cli-plugin-eslint": "~5.0.0-alpha.3",
"@vue/cli-plugin-router": "~5.0.0-alpha.3",
"@vue/cli-plugin-unit-mocha": "~5.0.0-alpha.3",
"@vue/cli-plugin-webpack-4": "^5.0.0-alpha.3",
"@vue/cli-service": "~5.0.0-alpha.3",
"@vue/compiler-sfc": "^3.0.4",
"@vue/eslint-config-standard": "^6.0.0",
"@vue/test-utils": "^2.0.0-0",
"babel-eslint": "^10.1.0",
"chai": "^4.1.2",
"eslint": "^6.7.2",
"chai": "^4.2.0",
"eslint": "^7.15.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^7.0.0-0",
"eslint-plugin-standard": "^5.0.0",
"eslint-plugin-vue": "^7.2.0",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"lint-staged": "^9.5.0"
"lint-staged": "^9.5.0",
"postcss": "^8.2.4",
"webpack": "^4.0.0"
},
"resolutions": {
"@vue/cli-*/webpack": "^4.0.0"
},
"gitHooks": {
"pre-commit": "lint-staged"

View File

@ -14,7 +14,7 @@
<a-list
class="list"
item-layout="horizontal"
:data-source="providerList"
:data-source="getProviders()"
>
<template #renderItem="{ item ,index }">
<a-list-item>
@ -23,7 +23,7 @@
<a-button type="danger" @click="remove(item,index)"><template #icon ><DeleteOutlined /></template></a-button>
</template>
<a-radio :checked="item.key===selectedKey" @update:checked="selectedKey = item.key">
<a-radio :disabled="isDisabled(item)" :checked="item.key===selectedKey" @update:checked="selectedKey = item.key" >
{{ item.name }} ({{item.type}})
</a-radio>
@ -39,10 +39,12 @@
<a-modal v-model:visible="editVisible" dialogClass="d-dialog" width="700px" title="编辑授权" @ok="onSubmit">
<a-alert v-if="currentProvider?.desc" :message="currentProvider.desc" type="success" />
<a-form ref="formRef" class="domain-form" :model="formData" labelWidth="150px" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="类型" name="type" :rules="rules.type">
<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" :disabled="isDisabled(option,'name')" :key="option.name" :value="option.name">
{{option.label}}
</a-radio-button>
</a-radio-group>
@ -58,7 +60,9 @@
:label="item.label || key"
:name="key">
<component-render v-model:value="formData[key]" v-bind="item.component || {}"></component-render>
<div class="helper">{{item.desc}}</div>
<template #extra >
<div v-if="item.desc" class="helper">{{item.desc}}</div>
</template>
</a-form-item>
</template>
@ -68,12 +72,12 @@
</template>
<script>
import { ref, reactive, nextTick, watch } from 'vue'
import { ref, reactive, nextTick, watch, inject } from 'vue'
// eslint-disable-next-line no-unused-vars
import { useForm } from '@ant-design-vue/use'
import _ from 'lodash-es'
import providerApi from '@/api/api.providers'
function useEdit (props, context, providerList, onEditSave) {
function useEdit (props, context, onEditSave) {
const formData = reactive({
key: '',
name: '',
@ -155,6 +159,13 @@ function useEdit (props, context, providerList, onEditSave) {
}
}
const isDisabled = (item, keyName = 'type') => {
if (!props.filter) {
return false
}
return item[keyName
] !== props.filter
}
return {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
@ -168,7 +179,8 @@ function useEdit (props, context, providerList, onEditSave) {
editIndex,
openEdit,
onTypeChanged,
add
add,
isDisabled
}
}
@ -192,9 +204,9 @@ export default {
name: 'provider-manager',
props: {
value: {},
providers: {}
filter: {}
},
emits: ['update:value', 'update:providers'],
emits: ['update:value'],
setup (props, context) {
const visible = ref(false)
@ -205,47 +217,55 @@ export default {
const onAfterVisibleChange = () => {
}
const providerList = ref([])
const getProviders = inject('get:accessProviders')
// const providerList = ref([])
const selectedKey = ref(null)
watch(() => props.providers, () => {
providerList.value = _.cloneDeep(props.providers || [])
}, { immediate: true })
watch(() => props.value, () => {
selectedKey.value = props.value
}, { immediate: true })
const onEditSave = (newProvider, editIndex) => {
const providerList = getProviders()
if (editIndex == null) {
// add key
newProvider.key = generateNewKey(providerList.value)
providerList.value.push(newProvider)
newProvider.key = generateNewKey(providerList)
providerList.push(newProvider)
} else {
_.merge(providerList.value[editIndex], newProvider)
_.merge(providerList[editIndex], newProvider)
}
}
const editModule = useEdit(props, context, providerList, onEditSave)
const editModule = useEdit(props, context, onEditSave)
const open = () => {
visible.value = true
if (providerList.value.length === 0) {
const providerList = getProviders()
if (providerList.length === 0) {
nextTick(() => {
editModule.add()
})
}
}
const remove = (item, index) => {
providerList.value.splice(index, 1)
const providerList = getProviders()
providerList.splice(index, 1)
}
const updateProviders = inject('update:accessProviders')
// watch(() => providers, () => {
// providerList.value = _.cloneDeep(props.providers || [])
// }, { immediate: true })
const onProviderSelectSubmit = () => {
context.emit('update:providers', providerList.value)
const providerList = getProviders()
updateProviders(providerList)
context.emit('update:value', selectedKey.value)
close()
}
return {
providerList,
visible,
open,
close,
@ -253,6 +273,7 @@ export default {
remove,
selectedKey,
onProviderSelectSubmit,
getProviders,
...editModule
}
}

View File

@ -4,7 +4,7 @@
:value="value"
@update:value="valueUpdate"
>
<a-select-option v-for="item of providers" :key="item.key" :value="item.key" :disabled="isDisabled(item)">
<a-select-option v-for="item of getProviders()" :key="item.key" :value="item.key" :disabled="isDisabled(item)">
{{ item.name }}
</a-select-option>
</a-select>
@ -13,15 +13,14 @@
</a-button>
</div>
<provider-manager ref="providerManagerRef"
:providers="providers"
:value="value"
:filter="filter"
@update:value="valueUpdate"
@update:providers="providersUpdate"
></provider-manager>
</template>
<script>
import { ref } from 'vue'
import { ref, inject } from 'vue'
import ProviderManager from './provider-manager'
export default {
@ -33,9 +32,6 @@ export default {
value: {
type: String
},
providers: {
type: Object
},
filter: {}
},
setup (props, context) {
@ -58,14 +54,18 @@ export default {
if (!props.filter) {
return false
}
return item.type === props.filter
return item.type !== props.filter
}
const getProviders = inject('get:accessProviders')
return {
providersUpdate,
valueUpdate,
providerManagerOpen,
providerManagerRef,
isDisabled
isDisabled,
getProviders
}
}
}

View File

@ -36,6 +36,25 @@ h1, h2, h3, h4, h5, h6 {
height: 100%
}
.ant-form{
.ant-form-item-children {
&>{
min-height: 40px;
display: flex;
flex-direction: column;
justify-content: center;
}
}
.ant-form-explain, .ant-form-extra{
font-size:12px;
line-height: 1.4;
min-height: 20px;
margin:0px;
padding:0px;
}
}
.ant-drawer-body{
padding:0px;

View File

@ -19,7 +19,9 @@
v-model:value="formData.domains"
:open="false"
></a-select>
<div class="helper">例如*.yourdomain.com</div>
<template #extra >
例如*.yourdomain.com输入完成后回车支持多个
</template>
</a-form-item>
<a-form-item :label="$t('email')" v-bind="validateInfos.email">
@ -27,10 +29,14 @@
</a-form-item>
<a-form-item label="dns验证" v-bind="validateInfos.dnsProvider">
<provider-selector v-model:value="formData.dnsProvider"
:providers="accessProviders"
@update:providers="accessProvidersUpdate"
></provider-selector>
<provider-selector v-model:value="formData.dnsProvider"></provider-selector>
</a-form-item>
<a-form-item label="CA" v-bind="validateInfos.ca">
<a-radio-group v-model:value="formData.ca" >
<a-radio value="LetEncrypt">
LetEncrypt
</a-radio>
</a-radio-group>
</a-form-item>
<h3>CSR <span>必须全英文</span></h3>
@ -110,6 +116,7 @@ export default {
domains: [],
email: undefined,
dnsProvider: '',
ca: 'LetEncrypt',
csr: {
country: '',
state: 'GuangDong',

View File

@ -31,16 +31,19 @@
</a-button>
</d-container>
<d-container v-else class="d-container" >
<a-form class="task-form" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="任务名称">
<a-form ref="taskFormRef" class="task-form" :model="currentTask" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="任务名称" name="taskName" :rules="rules.name">
<a-input
placeholder="请输入任务名称"
v-model:value="currentTask.taskName"
></a-input>
</a-form-item>
<a-form-item v-for="(item,key) in currentPlugin.input" :key="key" :label="item.label">
<a-form-item v-for="(item,key) in currentPlugin.input" v-bind="item.component || {}" :key="key" :label="item.label" :name="key">
<component-render v-model:value="currentTask[key]" v-bind="item.component || {}"></component-render>
<template #extra v-if="item.desc" >
{{item.desc}}
</template>
</a-form-item>
</a-form>
@ -78,13 +81,19 @@ function useTaskForm (context) {
onCreated()
const currentTask = ref()
const currentTask = ref({ taskName: undefined })
const currentTaskIndex = ref()
const currentDeploy = ref()
const currentPlugin = ref(null)
const taskFormRef = ref(null)
const taskDrawerVisible = ref(false)
const rules = ref({
name: [{
type: 'string',
required: true,
message: '请输入名称'
}]
})
const taskAdd = (deploy) => {
const task = { taskName: '新任务', type: undefined, _isAdd: true }
currentDeploy.value = deploy
@ -142,8 +151,11 @@ function useTaskForm (context) {
currentPlugin.value = currentPlugins[0]
}
const taskSave = () => {
const taskSave = async (e) => {
console.log('currentTask', currentTask)
e.preventDefault()
await taskFormRef.value.validate()
// context.emit('update', currentTask.value)
taskDrawerClose()
}
@ -161,7 +173,8 @@ function useTaskForm (context) {
currentTask,
currentTaskIndex,
currentPlugin,
taskSave
taskSave,
rules
}
}

View File

@ -37,38 +37,47 @@
</div>
</div>
<div class="title">CSR
<span>必须全英文</span></div>
<br/>
<div class="title">
CSR
<span>必须全英文</span>
</div>
<div class="label-item">
<label>country:</label>
<label>国家:</label>
<div>
{{ options.cert.csr.country }}
</div>
</div>
<div class="label-item">
<label>state:</label>
<label>省份:</label>
<div>
{{ options.cert.csr.state }}
</div>
</div>
<div class="label-item">
<label>locality:</label>
<label>市区:</label>
<div>
{{ options.cert.csr.locality }}
</div>
</div>
<div class="label-item">
<label>org:</label>
<label>组织:</label>
<div>
{{ options.cert.csr.organization }}
</div>
</div>
<div class="label-item">
<label>orgUnit:</label>
<label>部门:</label>
<div>
{{ options.cert.csr.organizationUnit }}
</div>
</div>
<div class="label-item">
<label>邮箱:</label>
<div>
{{ options.cert.csr.emailAddress }}
</div>
</div>
</div>
</div>
@ -136,7 +145,7 @@
<script>
import { message } from 'ant-design-vue'
// eslint-disable-next-line no-unused-vars
import { reactive, ref, toRef } from 'vue'
import { reactive, ref, toRef, provide, readonly } from 'vue'
// eslint-disable-next-line no-unused-vars
import { useRoute } from 'vue-router'
import CertForm from '@/views/detail/components/cert-form'
@ -169,6 +178,15 @@ function useDeploy (options) {
}
}
function useProvideAccessProviders (options) {
provide('get:accessProviders', () => {
return options.accessProviders
})
provide('update:accessProviders', (providers) => {
options.accessProviders = providers
})
}
export default {
components: { CertForm, TaskForm },
setup () {
@ -189,6 +207,7 @@ export default {
deploy: []
}
_.merge(optionsDefault, optionParams)
// optionsDefault.accessProviders = reactive(optionsDefault.accessProviders)
const options = reactive(optionsDefault)
const certFormChanged = (value) => {
@ -209,6 +228,8 @@ export default {
taskFormRef.value.taskEdit(deploy, task, index)
}
useProvideAccessProviders(options)
return {
options,
certFormChanged,

View File

@ -3,6 +3,9 @@
"env": {
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2020
},
"overrides": [
{
"files": ["*.test.js", "*.spec.js"],

View File

@ -8,23 +8,31 @@ const define = {
input: {
domainName: {
label: 'cdn加速域名',
required: true,
attrs: {
component: {
placeholder: 'cdn加速域名',
rules: [{ required: true, message: '该项必填' }]
}
},
certName: {
label: '证书名称'
label: '证书名称',
component: {
placeholder: '上传后将以此名称作为前缀'
}
},
from: {
value: 'upload',
label: '证书来源',
options: [
{ value: 'upload', label: '直接上传' },
{ value: 'cas', label: '从证书库', desc: '需要uploadCertToAliyun作为前置任务' }
],
required: true
component: {
placeholder: '证书来源',
required: true,
name: 'a-select',
options: [
{ value: 'upload', label: '直接上传' },
{ value: 'cas', label: '从证书库', title: '需要uploadCertToAliyun作为前置任务' }
]
},
desc: '如果选择cas类型则需要以《上传证书到阿里云》作为前置任务'
},
// serverCertificateStatus: {
// label: '启用https',

View File

@ -3,6 +3,10 @@
"env": {
"mocha": true
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2020
},
"overrides": [
{
"files": ["*.test.js", "*.spec.js"],

View File

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

View File

@ -5,12 +5,12 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
static define () {
return {
name: 'dnspod',
label: 'dnspod',
label: 'dnspod(腾讯云)',
desc: '腾讯云的域名解析接口已迁移到dnspod',
input: {
id: {
type: String,
attrs: {
component: {
placeholder: 'dnspod接口账户id',
rules: [{ required: true, message: '该项必填' }]
}
@ -18,7 +18,7 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
token: {
type: String,
label: 'token',
attrs: {
component: {
placeholder: '开放接口token',
rules: [{ required: true, message: '该项必填' }]
}