mirror of
https://github.com/fugary/simple-element-plus-template.git
synced 2025-12-31 03:17:49 +00:00
基本表单功能、通用设置界面
This commit is contained in:
43
src/components/common-form-control/control-child.vue
Normal file
43
src/components/common-form-control/control-child.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { $i18nBundle } from '@/messages'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {{option:CommonFormOption}}
|
||||||
|
*/
|
||||||
|
const props = defineProps({
|
||||||
|
/**
|
||||||
|
* @type {CommonFormOption}
|
||||||
|
*/
|
||||||
|
option: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const inputType = computed(() => {
|
||||||
|
return `el-${props.option.type}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const label = computed(() => {
|
||||||
|
const option = props.option
|
||||||
|
if (option.labelKey) {
|
||||||
|
return $i18nBundle(option.labelKey)
|
||||||
|
}
|
||||||
|
return option.label
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="inputType"
|
||||||
|
:value="option.value"
|
||||||
|
:label="label"
|
||||||
|
v-bind="option.attrs"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,76 +1,102 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { $i18nBundle } from '@/messages'
|
||||||
|
import ControlChild from '@/components/common-form-control/control-child.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 定义一些注释属性,方便代码提示
|
||||||
* @typedef {Object} CommonFormOption
|
* @typedef {Object} CommonFormOption
|
||||||
* @property {'input'|'input-number'|'cascader'|'radio'
|
* @property {'input'|'input-number'|'cascader'|'radio'
|
||||||
* |'radio-group'|'checkbox'|'checkbox-group'|'date-picker'
|
* |'radio-group'|'checkbox'|'checkbox-group'|'date-picker'
|
||||||
* |'time-picker'|'switch'|'select'|'option'|'slider'|'transfer'|'upload'} type 类型
|
* |'time-picker'|'switch'|'select'|'option'|'slider'|'transfer'|'upload'} type 类型
|
||||||
* @property {any} value
|
* @property {any} value
|
||||||
* @property {any} config
|
* @property {any} config
|
||||||
* @property {string} prop
|
* @property {string|[string]} prop
|
||||||
* @property {string} label
|
* @property {string} label
|
||||||
|
* @property {string} labelKey 用于国际化的label
|
||||||
|
* @property {boolean} required
|
||||||
* @property {string} placeholder
|
* @property {string} placeholder
|
||||||
* @property {{clearable:boolean,disabled:boolean}} attrs
|
* @property {{clearable:boolean,disabled:boolean,showPassword:boolean}} attrs
|
||||||
* @property {[CommonFormOption]} children 子节点
|
* @property {[CommonFormOption]} children 子节点
|
||||||
* @property {Array<RuleItem>} rules 子节点
|
* @property {Array<RuleItem>} rules 子节点
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {{option:CommonFormOption}}
|
||||||
|
*/
|
||||||
|
const props = defineProps({
|
||||||
/**
|
/**
|
||||||
* @type {CommonFormOption}
|
* @type {CommonFormOption}
|
||||||
*/
|
*/
|
||||||
const props = defineProps({
|
option: {
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: 'input'
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null
|
required: true
|
||||||
},
|
},
|
||||||
prop: {
|
model: {
|
||||||
type: String,
|
type: Object
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
children: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
rules: { type: Array, default: () => [] },
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
attrs: {
|
|
||||||
type: Object,
|
|
||||||
default: null
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputType = computed(() => {
|
const inputType = computed(() => {
|
||||||
return `el-${props.type}`
|
return `el-${props.option.type || 'input'}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const modelAttrs = computed(() => {
|
||||||
|
if (['input', 'select', 'autocomplete', 'cascader'].includes(inputType.value)) {
|
||||||
|
return Object.assign({ clearable: true }, props.option.attrs || {})
|
||||||
|
}
|
||||||
|
return props.option.attrs
|
||||||
|
})
|
||||||
|
|
||||||
|
const label = computed(() => {
|
||||||
|
const option = props.option
|
||||||
|
if (option.labelKey) {
|
||||||
|
return $i18nBundle(option.labelKey)
|
||||||
|
}
|
||||||
|
return option.label
|
||||||
|
})
|
||||||
|
|
||||||
|
const controlModel = computed(() => props.option.model || props.model)
|
||||||
|
|
||||||
|
const modelValue = computed({
|
||||||
|
get () {
|
||||||
|
console.info('=================', controlModel.value)
|
||||||
|
if (controlModel.value && props.option.prop) {
|
||||||
|
return controlModel.value[props.option.prop]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
console.info('set===============', controlModel.value)
|
||||||
|
if (controlModel.value && props.option.prop) {
|
||||||
|
controlModel.value[props.option.prop] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<el-form-item
|
||||||
|
:label="label"
|
||||||
|
:prop="option.prop"
|
||||||
|
>
|
||||||
<component
|
<component
|
||||||
:is="inputType"
|
:is="inputType"
|
||||||
:prop="prop"
|
v-model="modelValue"
|
||||||
v-bind="attrs"
|
v-bind="modelAttrs"
|
||||||
:placeholder="placeholder"
|
:placeholder="option.placeholder"
|
||||||
|
@change="option.change"
|
||||||
>
|
>
|
||||||
<template v-if="children&&children.length">
|
<template v-if="option.children&&option.children.length">
|
||||||
<common-form-control
|
<control-child
|
||||||
v-for="(childItem, index) in children"
|
v-for="(childItem, index) in option.children"
|
||||||
:key="index"
|
:key="index"
|
||||||
:type="childItem.type"
|
:option="childItem"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</component>
|
</component>
|
||||||
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
53
src/components/common-form/index.vue
Normal file
53
src/components/common-form/index.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
/**
|
||||||
|
* @type [CommonFormOption]
|
||||||
|
*/
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
labelWidth: {
|
||||||
|
type: String,
|
||||||
|
default: '100px'
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = computed(() => {
|
||||||
|
const ruleResult = {}
|
||||||
|
props.options.forEach(option => {
|
||||||
|
if (option.prop && option.rules) {
|
||||||
|
ruleResult[option.prop] = cloneDeep(option.rules)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.info(ruleResult)
|
||||||
|
return ruleResult
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-form
|
||||||
|
:model="model"
|
||||||
|
:rules="rules"
|
||||||
|
:label-width="labelWidth"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<common-form-control
|
||||||
|
v-for="(option,index) in options"
|
||||||
|
:key="index"
|
||||||
|
:model="model"
|
||||||
|
:option="option"
|
||||||
|
/>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import CommonIcon from '@/components/common-icon/index.vue'
|
import CommonIcon from '@/components/common-icon/index.vue'
|
||||||
|
import CommonForm from '@/components/common-form/index.vue'
|
||||||
import CommonFormControl from '@/components/common-form-control/index.vue'
|
import CommonFormControl from '@/components/common-form-control/index.vue'
|
||||||
import CommonMenu from '@/components/common-menu/index.vue'
|
import CommonMenu from '@/components/common-menu/index.vue'
|
||||||
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
||||||
@@ -13,6 +14,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
install (Vue) {
|
install (Vue) {
|
||||||
Vue.component('CommonIcon', CommonIcon)
|
Vue.component('CommonIcon', CommonIcon)
|
||||||
|
Vue.component('CommonForm', CommonForm)
|
||||||
Vue.component('CommonFormControl', CommonFormControl)
|
Vue.component('CommonFormControl', CommonFormControl)
|
||||||
Vue.component('CommonMenu', CommonMenu)
|
Vue.component('CommonMenu', CommonMenu)
|
||||||
Vue.component('CommonMenuItem', CommonMenuItem)
|
Vue.component('CommonMenuItem', CommonMenuItem)
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ export const MENU_INFO_LIST = ref({})
|
|||||||
export const useMenuInfo = item => {
|
export const useMenuInfo = item => {
|
||||||
const path = item.path
|
const path = item.path
|
||||||
if (path !== '/') {
|
if (path !== '/') {
|
||||||
const menuInfo = MENU_INFO_LIST.value[path]
|
return MENU_INFO_LIST.value[path]
|
||||||
console.info('config menu:', menuInfo)
|
|
||||||
return menuInfo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,22 +40,6 @@ export const useBaseTopMenus = () => {
|
|||||||
icon: 'Setting',
|
icon: 'Setting',
|
||||||
click: () => globalConfigStore.changeShowSettings(true)
|
click: () => globalConfigStore.changeShowSettings(true)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: 'AutoAwesomeMosaicFilled',
|
|
||||||
isDropdown: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
iconIf: () => globalConfigStore.layoutMode === GlobalLayoutMode.LEFT ? 'check' : '',
|
|
||||||
labelKey: 'common.label.layoutLeft',
|
|
||||||
click: () => globalConfigStore.changeLayout(GlobalLayoutMode.LEFT)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconIf: () => globalConfigStore.layoutMode === GlobalLayoutMode.TOP ? 'check' : '',
|
|
||||||
labelKey: 'common.label.layoutTop',
|
|
||||||
click: () => globalConfigStore.changeLayout(GlobalLayoutMode.TOP)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: 'user',
|
icon: 'user',
|
||||||
isDropdown: true,
|
isDropdown: true,
|
||||||
|
|||||||
@@ -94,21 +94,18 @@ export const useTabsViewStore = defineStore('tabsView', () => {
|
|||||||
isShowTabIcon,
|
isShowTabIcon,
|
||||||
historyTabs,
|
historyTabs,
|
||||||
cachedTabs,
|
cachedTabs,
|
||||||
changeTabMode () {
|
changeTabMode (val) {
|
||||||
isTabMode.value = !isTabMode.value
|
isTabMode.value = val
|
||||||
if (!isTabMode.value) {
|
if (!isTabMode.value) {
|
||||||
clearHistoryTabs()
|
clearHistoryTabs()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeCachedTabMode () {
|
changeCachedTabMode (val) {
|
||||||
isCachedTabMode.value = !isCachedTabMode.value
|
isCachedTabMode.value = val
|
||||||
if (!isCachedTabMode.value) {
|
if (!isCachedTabMode.value) {
|
||||||
cachedTabs.value = []
|
cachedTabs.value = []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeShowTabIcon () {
|
|
||||||
isShowTabIcon.value = !isShowTabIcon.value
|
|
||||||
},
|
|
||||||
removeHistoryTab,
|
removeHistoryTab,
|
||||||
removeOtherHistoryTabs,
|
removeOtherHistoryTabs,
|
||||||
clearHistoryTabs,
|
clearHistoryTabs,
|
||||||
|
|||||||
@@ -1,22 +1,109 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
|
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
|
||||||
|
import { useTabsViewStore } from '@/stores/TabsViewStore'
|
||||||
|
import { GlobalLayoutMode, GlobalLocales } from '@/consts/GlobalConstants'
|
||||||
const globalConfigStore = useGlobalConfigStore()
|
const globalConfigStore = useGlobalConfigStore()
|
||||||
|
const tabsViewStore = useTabsViewStore()
|
||||||
|
/**
|
||||||
|
* @type {[CommonFormOption]}
|
||||||
|
*/
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
labelKey: 'common.label.theme',
|
||||||
|
prop: 'isDarkTheme',
|
||||||
|
type: 'switch',
|
||||||
|
model: globalConfigStore,
|
||||||
|
attrs: {
|
||||||
|
activeActionIcon: 'icon-moon',
|
||||||
|
inactiveActionIcon: 'icon-sunny'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelKey: 'common.label.language',
|
||||||
|
type: 'select',
|
||||||
|
prop: 'currentLocale',
|
||||||
|
model: globalConfigStore,
|
||||||
|
change (val) {
|
||||||
|
globalConfigStore.changeLocale(val)
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
labelKey: 'common.label.langCn',
|
||||||
|
value: GlobalLocales.CN,
|
||||||
|
type: 'option'
|
||||||
|
}, {
|
||||||
|
labelKey: 'common.label.langEn',
|
||||||
|
value: GlobalLocales.EN,
|
||||||
|
type: 'option'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelKey: 'common.label.layout',
|
||||||
|
type: 'select',
|
||||||
|
prop: 'layoutMode',
|
||||||
|
model: globalConfigStore,
|
||||||
|
change (val) {
|
||||||
|
globalConfigStore.changeLayout(val)
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
labelKey: 'common.label.layoutLeft',
|
||||||
|
value: GlobalLayoutMode.LEFT,
|
||||||
|
type: 'option'
|
||||||
|
}, {
|
||||||
|
labelKey: 'common.label.layoutTop',
|
||||||
|
value: GlobalLayoutMode.TOP,
|
||||||
|
type: 'option'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '多标签模式',
|
||||||
|
prop: 'isTabMode',
|
||||||
|
type: 'switch',
|
||||||
|
model: tabsViewStore,
|
||||||
|
change (val) {
|
||||||
|
tabsViewStore.changeTabMode(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '缓存标签',
|
||||||
|
prop: 'isCachedTabMode',
|
||||||
|
type: 'switch',
|
||||||
|
model: tabsViewStore,
|
||||||
|
change (val) {
|
||||||
|
tabsViewStore.changeCachedTabMode(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签图标',
|
||||||
|
prop: 'isShowTabIcon',
|
||||||
|
type: 'switch',
|
||||||
|
model: tabsViewStore
|
||||||
|
}
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-drawer v-model="globalConfigStore.isShowSettings" direction="rtl" :size="350">
|
<el-drawer
|
||||||
|
v-model="globalConfigStore.isShowSettings"
|
||||||
|
direction="rtl"
|
||||||
|
:size="350"
|
||||||
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<strong>{{ $t('common.label.settings') }}</strong>
|
<strong>{{ $t('common.label.settings') }}</strong>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div>
|
<common-form
|
||||||
|
:options="options"
|
||||||
</div>
|
label-position="left"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div style="flex: auto">
|
<div style="flex: auto">
|
||||||
<el-button type="primary" @click="globalConfigStore.changeShowSettings(false)">{{$t('common.label.close')}}</el-button>
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="globalConfigStore.changeShowSettings(false)"
|
||||||
|
>
|
||||||
|
{{ $t('common.label.close') }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|||||||
@@ -1,10 +1,65 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {[CommonFormOption]}
|
||||||
|
*/
|
||||||
|
const formOptions = [{
|
||||||
|
label: '用户名',
|
||||||
|
prop: 'userName',
|
||||||
|
value: '',
|
||||||
|
placeholder: '请输入用户名',
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '用户名不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
min: 2,
|
||||||
|
max: 6,
|
||||||
|
message: '用户名在2-6位之间',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
label: '密码',
|
||||||
|
prop: 'userPassword',
|
||||||
|
value: '',
|
||||||
|
placeholder: '请输入密码',
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '密码不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
min: 2,
|
||||||
|
max: 6,
|
||||||
|
message: '密码在2-6位之间',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
showPassword: true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
const userDto = ref({
|
||||||
|
userName: '',
|
||||||
|
userPassword: ''
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<strong>表单测试</strong>
|
<common-form
|
||||||
|
:model="userDto"
|
||||||
|
:options="formOptions"
|
||||||
|
label-width="120px"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
{{ userDto }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user