mirror of
https://github.com/fugary/simple-element-plus-template.git
synced 2025-11-12 14:27: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>
|
||||
import { computed } from 'vue'
|
||||
import { $i18nBundle } from '@/messages'
|
||||
import ControlChild from '@/components/common-form-control/control-child.vue'
|
||||
|
||||
/**
|
||||
* 定义一些注释属性,方便代码提示
|
||||
* @typedef {Object} CommonFormOption
|
||||
* @property {'input'|'input-number'|'cascader'|'radio'
|
||||
* |'radio-group'|'checkbox'|'checkbox-group'|'date-picker'
|
||||
* |'time-picker'|'switch'|'select'|'option'|'slider'|'transfer'|'upload'} type 类型
|
||||
* @property {any} value
|
||||
* @property {any} config
|
||||
* @property {string} prop
|
||||
* @property {string|[string]} prop
|
||||
* @property {string} label
|
||||
* @property {string} labelKey 用于国际化的label
|
||||
* @property {boolean} required
|
||||
* @property {string} placeholder
|
||||
* @property {{clearable:boolean,disabled:boolean}} attrs
|
||||
* @property {{clearable:boolean,disabled:boolean,showPassword:boolean}} attrs
|
||||
* @property {[CommonFormOption]} children 子节点
|
||||
* @property {Array<RuleItem>} rules 子节点
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {CommonFormOption}
|
||||
* @type {{option:CommonFormOption}}
|
||||
*/
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'input'
|
||||
},
|
||||
value: {
|
||||
/**
|
||||
* @type {CommonFormOption}
|
||||
*/
|
||||
option: {
|
||||
type: Object,
|
||||
default: null
|
||||
required: true
|
||||
},
|
||||
prop: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
children: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
rules: { type: Array, default: () => [] },
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
attrs: {
|
||||
type: Object,
|
||||
default: null
|
||||
model: {
|
||||
type: Object
|
||||
}
|
||||
})
|
||||
|
||||
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>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="inputType"
|
||||
:prop="prop"
|
||||
v-bind="attrs"
|
||||
:placeholder="placeholder"
|
||||
<el-form-item
|
||||
:label="label"
|
||||
:prop="option.prop"
|
||||
>
|
||||
<template v-if="children&&children.length">
|
||||
<common-form-control
|
||||
v-for="(childItem, index) in children"
|
||||
:key="index"
|
||||
:type="childItem.type"
|
||||
/>
|
||||
</template>
|
||||
</component>
|
||||
<component
|
||||
:is="inputType"
|
||||
v-model="modelValue"
|
||||
v-bind="modelAttrs"
|
||||
:placeholder="option.placeholder"
|
||||
@change="option.change"
|
||||
>
|
||||
<template v-if="option.children&&option.children.length">
|
||||
<control-child
|
||||
v-for="(childItem, index) in option.children"
|
||||
:key="index"
|
||||
:option="childItem"
|
||||
/>
|
||||
</template>
|
||||
</component>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<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 CommonForm from '@/components/common-form/index.vue'
|
||||
import CommonFormControl from '@/components/common-form-control/index.vue'
|
||||
import CommonMenu from '@/components/common-menu/index.vue'
|
||||
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
||||
@@ -13,6 +14,7 @@ export default {
|
||||
*/
|
||||
install (Vue) {
|
||||
Vue.component('CommonIcon', CommonIcon)
|
||||
Vue.component('CommonForm', CommonForm)
|
||||
Vue.component('CommonFormControl', CommonFormControl)
|
||||
Vue.component('CommonMenu', CommonMenu)
|
||||
Vue.component('CommonMenuItem', CommonMenuItem)
|
||||
|
||||
@@ -15,9 +15,7 @@ export const MENU_INFO_LIST = ref({})
|
||||
export const useMenuInfo = item => {
|
||||
const path = item.path
|
||||
if (path !== '/') {
|
||||
const menuInfo = MENU_INFO_LIST.value[path]
|
||||
console.info('config menu:', menuInfo)
|
||||
return menuInfo
|
||||
return MENU_INFO_LIST.value[path]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,22 +40,6 @@ export const useBaseTopMenus = () => {
|
||||
icon: 'Setting',
|
||||
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',
|
||||
isDropdown: true,
|
||||
|
||||
@@ -94,21 +94,18 @@ export const useTabsViewStore = defineStore('tabsView', () => {
|
||||
isShowTabIcon,
|
||||
historyTabs,
|
||||
cachedTabs,
|
||||
changeTabMode () {
|
||||
isTabMode.value = !isTabMode.value
|
||||
changeTabMode (val) {
|
||||
isTabMode.value = val
|
||||
if (!isTabMode.value) {
|
||||
clearHistoryTabs()
|
||||
}
|
||||
},
|
||||
changeCachedTabMode () {
|
||||
isCachedTabMode.value = !isCachedTabMode.value
|
||||
changeCachedTabMode (val) {
|
||||
isCachedTabMode.value = val
|
||||
if (!isCachedTabMode.value) {
|
||||
cachedTabs.value = []
|
||||
}
|
||||
},
|
||||
changeShowTabIcon () {
|
||||
isShowTabIcon.value = !isShowTabIcon.value
|
||||
},
|
||||
removeHistoryTab,
|
||||
removeOtherHistoryTabs,
|
||||
clearHistoryTabs,
|
||||
|
||||
@@ -1,22 +1,109 @@
|
||||
<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 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>
|
||||
|
||||
<template>
|
||||
<el-drawer v-model="globalConfigStore.isShowSettings" direction="rtl" :size="350">
|
||||
<el-drawer
|
||||
v-model="globalConfigStore.isShowSettings"
|
||||
direction="rtl"
|
||||
:size="350"
|
||||
>
|
||||
<template #header>
|
||||
<strong>{{ $t('common.label.settings') }}</strong>
|
||||
</template>
|
||||
<template #default>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<common-form
|
||||
:options="options"
|
||||
label-position="left"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<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>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
@@ -1,10 +1,65 @@
|
||||
<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>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<strong>表单测试</strong>
|
||||
<common-form
|
||||
:model="userDto"
|
||||
:options="formOptions"
|
||||
label-width="120px"
|
||||
/>
|
||||
<div>
|
||||
{{ userDto }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user