mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
refactor: update config panels views using useConfigPanels
This commit is contained in:
parent
8af40dd04a
commit
678638534b
4 changed files with 126 additions and 173 deletions
|
@ -140,15 +140,10 @@ const routes = [
|
||||||
breadcrumb: ['domain-list', 'domain-add'],
|
breadcrumb: ['domain-list', 'domain-add'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/domains/:name',
|
|
||||||
component: () => import('@/views/domain/DomainInfo.vue'),
|
|
||||||
props: true,
|
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
name: 'domain-info',
|
name: 'domain-info',
|
||||||
path: ':tabId?',
|
path: '/domains/:name/:tabId?',
|
||||||
component: () => import('@/components/ConfigPanel.vue'),
|
component: () => import('@/views/domain/DomainInfo.vue'),
|
||||||
props: true,
|
props: true,
|
||||||
meta: {
|
meta: {
|
||||||
routerParams: ['name'], // Override router key params to avoid view recreation at tab change.
|
routerParams: ['name'], // Override router key params to avoid view recreation at tab change.
|
||||||
|
@ -156,8 +151,6 @@ const routes = [
|
||||||
breadcrumb: ['domain-list', 'domain-info'],
|
breadcrumb: ['domain-list', 'domain-info'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ───────╮
|
/* ───────╮
|
||||||
│ APPS │
|
│ APPS │
|
||||||
|
@ -201,15 +194,10 @@ const routes = [
|
||||||
breadcrumb: ['app-list', 'app-catalog', 'app-install-custom'],
|
breadcrumb: ['app-list', 'app-catalog', 'app-install-custom'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/apps/:id',
|
|
||||||
component: () => import('@/views/app/AppInfo.vue'),
|
|
||||||
props: true,
|
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
name: 'app-info',
|
name: 'app-info',
|
||||||
path: ':tabId?',
|
path: '/apps/:id/:tabId?',
|
||||||
component: () => import('@/components/ConfigPanel.vue'),
|
component: () => import('@/views/app/AppInfo.vue'),
|
||||||
props: true,
|
props: true,
|
||||||
meta: {
|
meta: {
|
||||||
routerParams: ['id'], // Override router key params to avoid view recreation at tab change.
|
routerParams: ['id'], // Override router key params to avoid view recreation at tab change.
|
||||||
|
@ -217,8 +205,6 @@ const routes = [
|
||||||
breadcrumb: ['app-list', 'app-info'],
|
breadcrumb: ['app-list', 'app-info'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ────────────────╮
|
/* ────────────────╮
|
||||||
│ SYSTEM UPDATE │
|
│ SYSTEM UPDATE │
|
||||||
|
@ -314,14 +300,10 @@ const routes = [
|
||||||
breadcrumb: ['tool-list', 'tool-webadmin'],
|
breadcrumb: ['tool-list', 'tool-webadmin'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/tools/settings',
|
|
||||||
component: () => import('@/views/tool/ToolSettings.vue'),
|
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
name: 'tool-settings',
|
name: 'tool-settings',
|
||||||
path: ':tabId?',
|
path: '/tools/settings/:tabId?',
|
||||||
component: () => import('@/components/ConfigPanel.vue'),
|
component: () => import('@/views/tool/ToolSettings.vue'),
|
||||||
props: true,
|
props: true,
|
||||||
meta: {
|
meta: {
|
||||||
routerParams: [],
|
routerParams: [],
|
||||||
|
@ -329,8 +311,6 @@ const routes = [
|
||||||
breadcrumb: ['tool-list', 'tool-settings'],
|
breadcrumb: ['tool-list', 'tool-settings'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'tool-power',
|
name: 'tool-power',
|
||||||
path: '/tools/power',
|
path: '/tools/power',
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useVuelidate } from '@vuelidate/core'
|
import { useVuelidate } from '@vuelidate/core'
|
||||||
import { computed, reactive, ref } from 'vue'
|
import { computed, reactive, ref, shallowRef } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import api, { objectToParams } from '@/api'
|
import api, { objectToParams } from '@/api'
|
||||||
import { APIBadRequestError, type APIError } from '@/api/errors'
|
import { type APIError } from '@/api/errors'
|
||||||
import ConfigPanels from '@/components/ConfigPanels.vue'
|
import ConfigPanelsComponent from '@/components/ConfigPanels.vue'
|
||||||
|
import type {
|
||||||
|
ConfigPanelsProps,
|
||||||
|
OnPanelApply,
|
||||||
|
} from '@/composables/configPanels'
|
||||||
|
import { formatConfigPanels, useConfigPanels } from '@/composables/configPanels'
|
||||||
import { useAutoModal } from '@/composables/useAutoModal'
|
import { useAutoModal } from '@/composables/useAutoModal'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
import { isEmptyValue } from '@/helpers/commons'
|
import { isEmptyValue } from '@/helpers/commons'
|
||||||
import { humanPermissionName } from '@/helpers/filters/human'
|
import { humanPermissionName } from '@/helpers/filters/human'
|
||||||
import { helpers, required } from '@/helpers/validators'
|
import { helpers, required } from '@/helpers/validators'
|
||||||
import {
|
import { formatI18nField } from '@/helpers/yunohostArguments'
|
||||||
formatFormData,
|
|
||||||
formatI18nField,
|
|
||||||
formatYunoHostConfigPanels,
|
|
||||||
} from '@/helpers/yunohostArguments'
|
|
||||||
import { useStoreGetters } from '@/store/utils'
|
import { useStoreGetters } from '@/store/utils'
|
||||||
|
import type { CoreConfigPanels } from '@/types/core/options'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id: string
|
id: string
|
||||||
|
tabId?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
@ -60,18 +63,8 @@ const { loading, refetch } = useInitialQueries(
|
||||||
|
|
||||||
const app = ref()
|
const app = ref()
|
||||||
const purge = ref(false)
|
const purge = ref(false)
|
||||||
const config_panel_err = ref(null)
|
const configPanelErr = ref('')
|
||||||
const config = ref({
|
const config = shallowRef<ConfigPanelsProps | undefined>()
|
||||||
panels: [
|
|
||||||
// Fake integration of operations in config panels
|
|
||||||
{
|
|
||||||
hasApplyButton: false,
|
|
||||||
id: 'operations',
|
|
||||||
name: t('operations'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
validations: {},
|
|
||||||
})
|
|
||||||
const doc = ref()
|
const doc = ref()
|
||||||
|
|
||||||
const currentTab = computed(() => {
|
const currentTab = computed(() => {
|
||||||
|
@ -215,47 +208,39 @@ async function onQueriesResponse(app_: any) {
|
||||||
await api
|
await api
|
||||||
.get(`apps/${props.id}/config?full`)
|
.get(`apps/${props.id}/config?full`)
|
||||||
.then((cp) => {
|
.then((cp) => {
|
||||||
const config_ = formatYunoHostConfigPanels(cp)
|
const config_ = cp as CoreConfigPanels
|
||||||
// reinject 'operations' fake config tab
|
// Fake integration of operations in config panels
|
||||||
config_.panels.unshift(config.panels[0])
|
config_.panels.unshift({
|
||||||
config.value = config_
|
id: 'operations',
|
||||||
|
name: t('operations'),
|
||||||
|
})
|
||||||
|
config.value = useConfigPanels(
|
||||||
|
formatConfigPanels(config_),
|
||||||
|
() => props.tabId,
|
||||||
|
onPanelApply,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.catch((err: APIError) => {
|
.catch((err: APIError) => {
|
||||||
config_panel_err.value = err.message
|
configPanelErr.value = err.message
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onConfigSubmit({ id, form, action, name }) {
|
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
|
||||||
const args = await formatFormData(form, {
|
|
||||||
removeEmpty: false,
|
|
||||||
removeNull: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
api
|
api
|
||||||
.put(
|
.put(
|
||||||
action
|
action
|
||||||
? `apps/${props.id}/actions/${action}`
|
? `apps/${props.id}/actions/${action}`
|
||||||
: `apps/${props.id}/config/${id}`,
|
: `apps/${props.id}/config/${panelId}`,
|
||||||
isEmptyValue(args) ? {} : { args: objectToParams(args) },
|
isEmptyValue(data) ? {} : { args: objectToParams(data) },
|
||||||
{
|
{
|
||||||
key: `apps.${action ? 'action' : 'update'}_config`,
|
key: `apps.${action ? 'action' : 'update'}_config`,
|
||||||
id,
|
id: panelId,
|
||||||
name: props.id,
|
name: props.id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.then(() => refetch())
|
.then(() => refetch())
|
||||||
.catch((err: APIError) => {
|
.catch(onError)
|
||||||
if (!(err instanceof APIBadRequestError)) throw err
|
|
||||||
const panel = config.value.panels.find((panel) => panel.id === id)!
|
|
||||||
if (err.data.name) {
|
|
||||||
Object.assign(externalResults, {
|
|
||||||
forms: { [panel.id]: { [err.data.name]: [err.data.error] } },
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
panel.serverError = err.message
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeLabel(permName, data) {
|
function changeLabel(permName, data) {
|
||||||
|
@ -439,20 +424,23 @@ async function uninstall() {
|
||||||
<VueShowdown :markdown="app.description" />
|
<VueShowdown :markdown="app.description" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<YAlert v-if="config_panel_err" class="mb-4" variant="danger" icon="bug">
|
<YAlert v-if="configPanelErr" class="mb-4" variant="danger" icon="bug">
|
||||||
<p>{{ $t('app.info.config_panel_error') }}</p>
|
<p>{{ $t('app.info.config_panel_error') }}</p>
|
||||||
<p>{{ config_panel_err }}</p>
|
<p>{{ configPanelErr }}</p>
|
||||||
<p>{{ $t('app.info.config_panel_error_please_report') }}</p>
|
<p>{{ $t('app.info.config_panel_error_please_report') }}</p>
|
||||||
</YAlert>
|
</YAlert>
|
||||||
|
|
||||||
<!-- BASIC INFOS -->
|
<!-- BASIC INFOS -->
|
||||||
<ConfigPanels
|
<ConfigPanelsComponent
|
||||||
v-bind="config"
|
v-if="config"
|
||||||
:external-results="externalResults"
|
v-model="config.form"
|
||||||
@apply="onConfigSubmit"
|
:panel="config.panel.value"
|
||||||
|
:validations="config.v.value"
|
||||||
|
:routes="config.routes"
|
||||||
|
@apply="config.onPanelApply"
|
||||||
>
|
>
|
||||||
<!-- OPERATIONS TAB -->
|
<!-- OPERATIONS TAB -->
|
||||||
<template v-if="currentTab === 'operations'" #tab-top>
|
<template v-if="currentTab === 'operations'" #default>
|
||||||
<!-- CHANGE PERMISSIONS LABEL -->
|
<!-- CHANGE PERMISSIONS LABEL -->
|
||||||
<BFormGroup
|
<BFormGroup
|
||||||
:label="$t('app_manage_label_and_tiles')"
|
:label="$t('app_manage_label_and_tiles')"
|
||||||
|
@ -584,7 +572,7 @@ async function uninstall() {
|
||||||
</template>
|
</template>
|
||||||
</BFormGroup>
|
</BFormGroup>
|
||||||
</template>
|
</template>
|
||||||
</ConfigPanels>
|
</ConfigPanelsComponent>
|
||||||
|
|
||||||
<BCard v-if="app && app.doc.admin.length" no-body>
|
<BCard v-if="app && app.doc.admin.length" no-body>
|
||||||
<BTabs card fill pills>
|
<BTabs card fill pills>
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref } from 'vue'
|
import { computed, ref, shallowRef } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
|
|
||||||
import api, { objectToParams } from '@/api'
|
import api, { objectToParams } from '@/api'
|
||||||
import ConfigPanels from '@/components/ConfigPanels.vue'
|
import ConfigPanelsComponent from '@/components/ConfigPanels.vue'
|
||||||
|
import type {
|
||||||
|
ConfigPanelsProps,
|
||||||
|
OnPanelApply,
|
||||||
|
} from '@/composables/configPanels'
|
||||||
|
import { formatConfigPanels, useConfigPanels } from '@/composables/configPanels'
|
||||||
import { useAutoModal } from '@/composables/useAutoModal'
|
import { useAutoModal } from '@/composables/useAutoModal'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
import {
|
|
||||||
formatFormData,
|
|
||||||
formatYunoHostConfigPanels,
|
|
||||||
} from '@/helpers/yunohostArguments'
|
|
||||||
import { useStoreGetters } from '@/store/utils'
|
import { useStoreGetters } from '@/store/utils'
|
||||||
|
import type { CoreConfigPanels } from '@/types/core/options'
|
||||||
import DomainDns from '@/views/domain/DomainDns.vue'
|
import DomainDns from '@/views/domain/DomainDns.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
name: string
|
name: string
|
||||||
|
tabId?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
@ -34,14 +37,9 @@ const { loading, refetch } = useInitialQueries(
|
||||||
)
|
)
|
||||||
|
|
||||||
const { mainDomain } = useStoreGetters()
|
const { mainDomain } = useStoreGetters()
|
||||||
const config = ref({})
|
const config = shallowRef<ConfigPanelsProps | undefined>()
|
||||||
const externalResults = reactive({})
|
|
||||||
const unsubscribeDomainFromDyndns = ref(false)
|
const unsubscribeDomainFromDyndns = ref(false)
|
||||||
|
|
||||||
const currentTab = computed(() => {
|
|
||||||
return route.params.tabId
|
|
||||||
})
|
|
||||||
|
|
||||||
const domain = computed(() => {
|
const domain = computed(() => {
|
||||||
return store.getters.domain(props.name)
|
return store.getters.domain(props.name)
|
||||||
})
|
})
|
||||||
|
@ -80,40 +78,33 @@ const isMainDynDomain = computed(() => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
function onQueriesResponse(domains: any, domain: any, config_: any) {
|
function onQueriesResponse(
|
||||||
config.value = formatYunoHostConfigPanels(config_)
|
domains: any,
|
||||||
|
domain: any,
|
||||||
|
config_: CoreConfigPanels,
|
||||||
|
) {
|
||||||
|
config.value = useConfigPanels(
|
||||||
|
formatConfigPanels(config_),
|
||||||
|
() => props.tabId,
|
||||||
|
onPanelApply,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onConfigSubmit({ id, form, action, name }) {
|
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
|
||||||
const args = await formatFormData(form, {
|
|
||||||
removeEmpty: false,
|
|
||||||
removeNull: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
api
|
api
|
||||||
.put(
|
.put(
|
||||||
action
|
action
|
||||||
? `domain/${props.name}/actions/${action}`
|
? `domain/${props.name}/actions/${action}`
|
||||||
: `domains/${props.name}/config/${id}`,
|
: `domains/${props.name}/config/${panelId}`,
|
||||||
{ args: objectToParams(args) },
|
{ args: objectToParams(data) },
|
||||||
{
|
{
|
||||||
key: `domains.${action ? 'action' : 'update'}_config`,
|
key: `domains.${action ? 'action' : 'update'}_config`,
|
||||||
id,
|
id: panelId,
|
||||||
name: props.name,
|
name: props.name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.then(() => refetch())
|
.then(() => refetch())
|
||||||
.catch((err) => {
|
.catch(onError)
|
||||||
if (err.name !== 'APIBadRequestError') throw err
|
|
||||||
const panel = config.value.panels.find((panel) => panel.id === id)
|
|
||||||
if (err.data.name) {
|
|
||||||
Object.assign(externalResults, {
|
|
||||||
forms: { [panel.id]: { [err.data.name]: [err.data.error] } },
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
panel.serverError = err.message
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteDomain() {
|
async function deleteDomain() {
|
||||||
|
@ -254,16 +245,18 @@ async function setAsDefaultDomain() {
|
||||||
</DescriptionRow>
|
</DescriptionRow>
|
||||||
</YCard>
|
</YCard>
|
||||||
|
|
||||||
<ConfigPanels
|
<ConfigPanelsComponent
|
||||||
v-if="config.panels"
|
v-if="config"
|
||||||
v-bind="config"
|
v-model="config.form"
|
||||||
:external-results="externalResults"
|
:panel="config.panel.value"
|
||||||
@apply="onConfigSubmit"
|
:validations="config.v.value"
|
||||||
|
:routes="config.routes"
|
||||||
|
@apply="config.onPanelApply"
|
||||||
>
|
>
|
||||||
<template v-if="currentTab === 'dns'" #tab-after>
|
<template v-if="tabId === 'dns'" #tab-after>
|
||||||
<DomainDns :name="name" />
|
<DomainDns :name="name" />
|
||||||
</template>
|
</template>
|
||||||
</ConfigPanels>
|
</ConfigPanelsComponent>
|
||||||
|
|
||||||
<BModal
|
<BModal
|
||||||
v-if="domain"
|
v-if="domain"
|
||||||
|
|
|
@ -1,61 +1,53 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref } from 'vue'
|
import { shallowRef } from 'vue'
|
||||||
|
|
||||||
import api, { objectToParams } from '@/api'
|
import api, { objectToParams } from '@/api'
|
||||||
import { APIBadRequestError, type APIError } from '@/api/errors'
|
import ConfigPanelsComponent from '@/components/ConfigPanels.vue'
|
||||||
import ConfigPanels from '@/components/ConfigPanels.vue'
|
import type {
|
||||||
|
ConfigPanelsProps,
|
||||||
|
OnPanelApply,
|
||||||
|
} from '@/composables/configPanels'
|
||||||
|
import { formatConfigPanels, useConfigPanels } from '@/composables/configPanels'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
import {
|
import type { CoreConfigPanels } from '@/types/core/options'
|
||||||
formatFormData,
|
|
||||||
formatYunoHostConfigPanels,
|
const props = defineProps<{ tabId?: string }>()
|
||||||
} from '@/helpers/yunohostArguments'
|
|
||||||
|
|
||||||
const { loading, refetch } = useInitialQueries([['GET', 'settings?full']], {
|
const { loading, refetch } = useInitialQueries([['GET', 'settings?full']], {
|
||||||
onQueriesResponse,
|
onQueriesResponse,
|
||||||
})
|
})
|
||||||
const config = ref({})
|
const config = shallowRef<ConfigPanelsProps | undefined>()
|
||||||
// FIXME user proper useValidate stuff
|
|
||||||
const externalResults = reactive({})
|
|
||||||
|
|
||||||
function onQueriesResponse(config_: any) {
|
function onQueriesResponse(config_: CoreConfigPanels) {
|
||||||
config.value = formatYunoHostConfigPanels(config_)
|
config.value = useConfigPanels(
|
||||||
|
formatConfigPanels(config_),
|
||||||
|
() => props.tabId,
|
||||||
|
onPanelApply,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onConfigSubmit({ id, form }) {
|
const onPanelApply: OnPanelApply = ({ panelId, data }, onError) => {
|
||||||
const args = await formatFormData(form, {
|
|
||||||
removeEmpty: false,
|
|
||||||
removeNull: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// FIXME no route for potential action
|
// FIXME no route for potential action
|
||||||
api
|
api
|
||||||
.put(
|
.put(
|
||||||
`settings/${id}`,
|
`settings/${panelId}`,
|
||||||
{ args: objectToParams(args) },
|
{ args: objectToParams(data) },
|
||||||
{ key: 'settings.update', panel: id },
|
{ key: 'settings.update', panel: panelId },
|
||||||
)
|
)
|
||||||
.then(() => refetch())
|
.then(() => refetch())
|
||||||
.catch((err: APIError) => {
|
.catch(onError)
|
||||||
if (!(err instanceof APIBadRequestError)) throw err
|
|
||||||
const panel = config.value.panels.find((panel) => panel.id === id)
|
|
||||||
if (err.data.name) {
|
|
||||||
Object.assign(externalResults, {
|
|
||||||
forms: { [panel.id]: { [err.data.name]: [err.data.error] } },
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
panel.serverError = err.message
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewBase :loading="loading" skeleton="CardFormSkeleton">
|
<ViewBase :loading="loading" skeleton="CardFormSkeleton">
|
||||||
<ConfigPanels
|
<ConfigPanelsComponent
|
||||||
v-if="config.panels"
|
v-if="config"
|
||||||
v-bind="config"
|
v-model="config.form"
|
||||||
:external-results="externalResults"
|
:panel="config.panel.value"
|
||||||
@apply="onConfigSubmit"
|
:validations="config.v.value"
|
||||||
|
:routes="config.routes"
|
||||||
|
@apply="config.onPanelApply"
|
||||||
/>
|
/>
|
||||||
</ViewBase>
|
</ViewBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Add table
Reference in a new issue