Commit 1c0a50268dbe76754e88f9a6d95ff3c7d6bb59ec
1 parent
bfe60bf3
Exists in
master
and in
1 other branch
新增搜索表单
Showing
6 changed files
with
408 additions
and
0 deletions
Show diff stats
examples/router/routes.js
| @@ -25,12 +25,29 @@ const _components = [ | @@ -25,12 +25,29 @@ const _components = [ | ||
| 25 | component: () => import('@/views/docs/form.md'), | 25 | component: () => import('@/views/docs/form.md'), |
| 26 | }, | 26 | }, |
| 27 | { | 27 | { |
| 28 | + path: 'search', | ||
| 29 | + name: 'search', | ||
| 30 | + meta: { title: 'Search 搜索' }, | ||
| 31 | + component: () => import('@/views/docs/search.md'), | ||
| 32 | + }, | ||
| 33 | + { | ||
| 28 | path: 'table', | 34 | path: 'table', |
| 29 | name: 'table', | 35 | name: 'table', |
| 30 | meta: { title: 'Table 表格' }, | 36 | meta: { title: 'Table 表格' }, |
| 31 | component: () => import('@/views/docs/table.md'), | 37 | component: () => import('@/views/docs/table.md'), |
| 32 | }, | 38 | }, |
| 33 | ] | 39 | ] |
| 40 | + }, | ||
| 41 | + { | ||
| 42 | + group: '业务', | ||
| 43 | + children: [ | ||
| 44 | + { | ||
| 45 | + path: 'scheme', | ||
| 46 | + name: 'scheme', | ||
| 47 | + meta: { title: 'Scheme 方案' }, | ||
| 48 | + component: () => import('@/views/docs/scheme.md'), | ||
| 49 | + }, | ||
| 50 | + ] | ||
| 34 | } | 51 | } |
| 35 | ] | 52 | ] |
| 36 | 53 |
| @@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
| 1 | +# Scheme 方案 | ||
| 2 | + | ||
| 3 | +Scheme是一个数据驱动的解决方案,通过既定的业务配置参数,生成可模块化编辑的`CURD`业务视图 | ||
| 4 | + | ||
| 5 | +## 基础用法 | ||
| 6 | + | ||
| 7 | +配置项list中通过type可以配置任意组件,不受框架限制 | ||
| 8 | + | ||
| 9 | +:::snippet 使用`list`属性设置数据源,列表项中的`type`指定组件类型,每一项都已设置为**el-form-item**的子组件,通过`rules`配置校验规则 | ||
| 10 | + | ||
| 11 | +```html | ||
| 12 | +<template> | ||
| 13 | + <eagle-scheme :list="schemeList"></eagle-scheme> | ||
| 14 | +</template> | ||
| 15 | + | ||
| 16 | +<script> | ||
| 17 | +export default { | ||
| 18 | + data() { | ||
| 19 | + return { | ||
| 20 | + schemeList: [ | ||
| 21 | + { type: 'el-input', key: 'name', label: '名称', rules: [{ required: true, message: '请输入名称' }] }, | ||
| 22 | + { type: 'eagle-select', key: 'address', label: '住址', props: { dataSource: [{ label: '大街上', value: 'S' }, { label: '小区里', value: 'H' }] } }, | ||
| 23 | + { type: 'el-input', key: 'postcode', label: '邮编', span: 12, tip: { content: '邮政编码', placement: "left" } }, | ||
| 24 | + { type: 'el-input', key: 'number', label: '楼栋号', span: 12, visible: (model) => model.address === 'H' }, | ||
| 25 | + ] | ||
| 26 | + } | ||
| 27 | + }, | ||
| 28 | +} | ||
| 29 | +</script> | ||
| 30 | +``` | ||
| 31 | + | ||
| 32 | +::: | ||
| 33 | + | ||
| 34 | +## Attribute 属性 | ||
| 35 | + | ||
| 36 | +参数|说明|类型|可选值|默认值 | ||
| 37 | +-|-|-|-|- | ||
| 38 | +list | 表单项配置列表 | Array | - | [] | ||
| 0 | \ No newline at end of file | 39 | \ No newline at end of file |
| @@ -0,0 +1,130 @@ | @@ -0,0 +1,130 @@ | ||
| 1 | +# Search 搜索 | ||
| 2 | + | ||
| 3 | +Search 搜索组件是一个使用`list`来配置生成的搜索表单 | ||
| 4 | + | ||
| 5 | +## 基础用法 | ||
| 6 | + | ||
| 7 | +配置项list中通过type可以配置任意组件,不受框架限制 | ||
| 8 | + | ||
| 9 | +:::snippet 使用`list`属性设置数据源,列表项中的`type`指定组件类型,支持通过`rules`配置校验规则 | ||
| 10 | + | ||
| 11 | +```html | ||
| 12 | +<template> | ||
| 13 | + <eagle-search :list="searchList" @search="handleSearch" @reset="handleReset" :span="8" :searching="searching"></eagle-search> | ||
| 14 | +</template> | ||
| 15 | + | ||
| 16 | +<script> | ||
| 17 | +export default { | ||
| 18 | + data() { | ||
| 19 | + return { | ||
| 20 | + searching: false, | ||
| 21 | + searchList: [ | ||
| 22 | + { type: 'el-input', key: 'name', label: '名称', rules: [{ required: true, message: '请输入名称' }] }, | ||
| 23 | + { type: 'eagle-select', key: 'type', label: '类型', props: { dataSource: [{ label: '呆萌', value: '1' }, { label: '二货', value: '2' }] } }, | ||
| 24 | + { type: 'el-input', key: 'postcode', label: '邮编', tip: { content: '邮政编码', placement: "left" } }, | ||
| 25 | + { type: 'el-input', key: 'number', label: '楼栋号' }, | ||
| 26 | + { type: 'el-input', key: 'not', label: '我不是', visible: (model) => model.type === '2' }, | ||
| 27 | + { type: 'el-input', key: 'no', label: '我没有', visible: (model) => model.type === '2' }, | ||
| 28 | + { type: 'el-input', key: 'never', label: '别瞎说啊', visible: (model) => model.type === '2' }, | ||
| 29 | + ] | ||
| 30 | + } | ||
| 31 | + }, | ||
| 32 | + mounted() { | ||
| 33 | + this.searching = true; | ||
| 34 | + setTimeout(() => { | ||
| 35 | + this.searching = false; | ||
| 36 | + }, 3000); | ||
| 37 | + }, | ||
| 38 | + methods: { | ||
| 39 | + handleSearch(value) { | ||
| 40 | + console.log(value); | ||
| 41 | + }, | ||
| 42 | + handleReset() { | ||
| 43 | + console.log('reset'); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | +} | ||
| 47 | +</script> | ||
| 48 | +``` | ||
| 49 | + | ||
| 50 | +::: | ||
| 51 | + | ||
| 52 | +## 自定义组件 | ||
| 53 | + | ||
| 54 | +在使用`list`的同时,也支持通过`slot`传入组件,以满足不同的业务需求 | ||
| 55 | + | ||
| 56 | +:::snippet 使用`list`属性设置数据源,列表项中的`type`指定组件类型,支持通过`rules`配置校验规则 | ||
| 57 | + | ||
| 58 | +```html | ||
| 59 | +<template> | ||
| 60 | + <eagle-search :list="searchList"> | ||
| 61 | + <template #type="{ model }"> | ||
| 62 | + <eagle-select v-model="model.type" :dataSource="dataSource"></eagle-select> | ||
| 63 | + </template> | ||
| 64 | + <template #button-group="{ model, collapse, doSearch, doReset, doCollapse }"> | ||
| 65 | + <el-button size="mini" type="primary" round @click="handleQuery(model, doSearch)">搜索</el-button> | ||
| 66 | + <el-button size="mini" type="success" round @click="doReset">恢复</el-button> | ||
| 67 | + <el-button size="mini" type="info" round @click="doCollapse">{{ collapse ? '打开' : '关闭' }}</el-button> | ||
| 68 | + </template> | ||
| 69 | + </eagle-search> | ||
| 70 | +</template> | ||
| 71 | + | ||
| 72 | +<script> | ||
| 73 | +export default { | ||
| 74 | + data() { | ||
| 75 | + return { | ||
| 76 | + searchList: [ | ||
| 77 | + { type: 'el-input', key: 'name', label: '名称' }, | ||
| 78 | + { key: 'type', label: '类型' }, | ||
| 79 | + { type: 'el-input', key: 'yes', label: '是的' }, | ||
| 80 | + { type: 'el-input', key: 'yeah', label: '没错' }, | ||
| 81 | + { type: 'el-input', key: 'absolutely', label: '就这样' }, | ||
| 82 | + ], | ||
| 83 | + dataSource: [ | ||
| 84 | + { label: '选项A', value: 'A' }, | ||
| 85 | + { label: '选项B', value: 'B' }, | ||
| 86 | + ] | ||
| 87 | + } | ||
| 88 | + }, | ||
| 89 | + methods: { | ||
| 90 | + handleQuery(model, action) { | ||
| 91 | + if (action) { | ||
| 92 | + action(); | ||
| 93 | + console.log(model); | ||
| 94 | + } | ||
| 95 | + }, | ||
| 96 | + } | ||
| 97 | +} | ||
| 98 | +</script> | ||
| 99 | +``` | ||
| 100 | + | ||
| 101 | +::: | ||
| 102 | + | ||
| 103 | +## Attribute 属性 | ||
| 104 | + | ||
| 105 | +参数|说明|类型|可选值|默认值 | ||
| 106 | +-|-|-|-|- | ||
| 107 | +value / v-model | 绑定值 | Object | - | - | ||
| 108 | +list | 表单项配置列表 | Array | - | [] | ||
| 109 | + | ||
| 110 | +## Events 事件 | ||
| 111 | + | ||
| 112 | +事件名称|说明|回调参数 | ||
| 113 | +-|-|- | ||
| 114 | +change | 表单model发生变化时触发 | model对象 | ||
| 115 | +search | 点击查询按钮时触发 | model对象 | ||
| 116 | +reset | 点击重置按钮时触发 | - | ||
| 117 | + | ||
| 118 | +## List 表单项配置列表 | ||
| 119 | + | ||
| 120 | +参数|说明|类型|可选值|默认值 | ||
| 121 | +-|-|-|-|- | ||
| 122 | +type | 组件类型 | String | - | el-input | ||
| 123 | +key | 参数名 | String | - | - | ||
| 124 | +label | 参数标签 | String | - | - | ||
| 125 | +props | 组件参数 | Object,Function(model: object)) | - | {} | ||
| 126 | +style | 组件样式 | Object | - | { width: "100%" } | ||
| 127 | +on | 组件事件 | Object,Function(model: object) | - | {} | ||
| 128 | +visible | 组件v-if状态 | Boolean,Function(model: object) | - | true | ||
| 129 | +rules | 组件校验规则 | Object,Array | - | - | ||
| 130 | +tip | 组件提示框 | Object,String | - | {} | ||
| 0 | \ No newline at end of file | 131 | \ No newline at end of file |
packages/index.js
| @@ -8,6 +8,8 @@ import ImageUpload from './Image-upload' | @@ -8,6 +8,8 @@ import ImageUpload from './Image-upload' | ||
| 8 | import ImageUploadMultiple from './Image-upload/multiple' | 8 | import ImageUploadMultiple from './Image-upload/multiple' |
| 9 | import ImageView from './image-view' | 9 | import ImageView from './image-view' |
| 10 | import RadioGroup from './radio-group' | 10 | import RadioGroup from './radio-group' |
| 11 | +import Scheme from './scheme' | ||
| 12 | +import Search from './search' | ||
| 11 | import Select from './select' | 13 | import Select from './select' |
| 12 | import StatusIndicator from './status-indicator' | 14 | import StatusIndicator from './status-indicator' |
| 13 | import SwitchButton from './switch-button' | 15 | import SwitchButton from './switch-button' |
| @@ -25,6 +27,8 @@ const components = { | @@ -25,6 +27,8 @@ const components = { | ||
| 25 | ImageUploadMultiple, | 27 | ImageUploadMultiple, |
| 26 | ImageView, | 28 | ImageView, |
| 27 | RadioGroup, | 29 | RadioGroup, |
| 30 | + Scheme, | ||
| 31 | + Search, | ||
| 28 | Select, | 32 | Select, |
| 29 | StatusIndicator, | 33 | StatusIndicator, |
| 30 | SwitchButton, | 34 | SwitchButton, |
| @@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
| 1 | +<style> | ||
| 2 | +.eagle-scheme { | ||
| 3 | + padding: 0px; | ||
| 4 | +} | ||
| 5 | +</style> | ||
| 6 | + | ||
| 7 | +<template> | ||
| 8 | + <div class="eagle-scheme"> | ||
| 9 | + Scheme | ||
| 10 | + </div> | ||
| 11 | +</template> | ||
| 12 | + | ||
| 13 | +<script> | ||
| 14 | +export default { | ||
| 15 | + name: 'Scheme', | ||
| 16 | + props: { | ||
| 17 | + // 配置列表 | ||
| 18 | + list: { | ||
| 19 | + type: Array, | ||
| 20 | + required: true | ||
| 21 | + }, | ||
| 22 | + }, | ||
| 23 | + data() { | ||
| 24 | + return { | ||
| 25 | + // 编辑器表单模型 | ||
| 26 | + model: {} | ||
| 27 | + }; | ||
| 28 | + }, | ||
| 29 | +}; | ||
| 30 | +</script> | ||
| 0 | \ No newline at end of file | 31 | \ No newline at end of file |
| @@ -0,0 +1,189 @@ | @@ -0,0 +1,189 @@ | ||
| 1 | +<style> | ||
| 2 | +.eagle-search { | ||
| 3 | + padding: 0px; | ||
| 4 | +} | ||
| 5 | +.eagle-search__btn-col { | ||
| 6 | + text-align: right; | ||
| 7 | +} | ||
| 8 | +</style> | ||
| 9 | + | ||
| 10 | +<template> | ||
| 11 | + <el-form class="eagle-search" ref="search" :model="model" v-bind="formProps"> | ||
| 12 | + <el-row :gutter="15"> | ||
| 13 | + <template v-for="(item, index) in list"> | ||
| 14 | + <el-col v-if="bindItemVisible(item.visible)" v-show="!(collapse && index > visibleColNum - 2)" :key="index + 'data'" :span="!item.span ? span : item.span"> | ||
| 15 | + <el-form-item :label="item.label" :label-width="item.label ? undefined : item.labelWidth || '0px'" :prop="item.key" :rules="item.rules"> | ||
| 16 | + <el-tooltip :disabled="!item.tip" v-bind="bindItemTip(item.tip)"> | ||
| 17 | + <slot v-if="$scopedSlots[item.key] || $slots[item.key]" :name="item.key" :model="model" v-bind="item"></slot> | ||
| 18 | + <component v-else :is="item.type || 'el-input'" v-model="model[item.key]" v-bind="bindItemProps(item)" v-on="bindItemEvent(item)" :style="bindItemStyle(item.style)"></component> | ||
| 19 | + </el-tooltip> | ||
| 20 | + </el-form-item> | ||
| 21 | + </el-col> | ||
| 22 | + </template> | ||
| 23 | + <el-col :span="list.length > visibleColNum ? collapse ? span : 24 : span" class="eagle-search__btn-col"> | ||
| 24 | + <slot v-if="$scopedSlots['button-group'] || $slots['button-group']" name="button-group" | ||
| 25 | + :model="model" :collapse="collapse" :doSearch="handleSearch" :doReset="handleReset" :doCollapse="handleCollapse" | ||
| 26 | + ></slot> | ||
| 27 | + <el-button-group v-else> | ||
| 28 | + <el-button size="small" type="primary" :loading="searching" @click="handleSearch" icon="el-icon-search">查询</el-button> | ||
| 29 | + <el-button size="small" @click="handleReset">重置</el-button> | ||
| 30 | + <el-button size="small" v-if="list.length > visibleColNum" :icon="collapse ? 'ios-arrow-down' : 'ios-arrow-up'" @click="handleCollapse"> | ||
| 31 | + {{ collapse ? '展开' : '收起' }} | ||
| 32 | + </el-button> | ||
| 33 | + </el-button-group> | ||
| 34 | + </el-col> | ||
| 35 | + </el-row> | ||
| 36 | + </el-form> | ||
| 37 | +</template> | ||
| 38 | + | ||
| 39 | +<script> | ||
| 40 | +export default { | ||
| 41 | + name: 'Search', | ||
| 42 | + props: { | ||
| 43 | + // 用于实例化本组件绑定v-model的值 | ||
| 44 | + value: { | ||
| 45 | + type: Object, | ||
| 46 | + default: () => { | ||
| 47 | + return {}; | ||
| 48 | + } | ||
| 49 | + }, | ||
| 50 | + // 配置列表 | ||
| 51 | + list: { | ||
| 52 | + type: Array, | ||
| 53 | + required: true | ||
| 54 | + }, | ||
| 55 | + // 提交加载状态 | ||
| 56 | + searching: Boolean, | ||
| 57 | + // 表单参数 | ||
| 58 | + formProps: { | ||
| 59 | + type: Object, | ||
| 60 | + default() { | ||
| 61 | + return { | ||
| 62 | + size: 'small', | ||
| 63 | + 'label-width': '70px' | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | + }, | ||
| 67 | + // 表单项占位 | ||
| 68 | + span: { | ||
| 69 | + type: Number, | ||
| 70 | + default: 6 | ||
| 71 | + } | ||
| 72 | + }, | ||
| 73 | + data() { | ||
| 74 | + return { | ||
| 75 | + // 编辑器表单模型 | ||
| 76 | + model: {}, | ||
| 77 | + // 表单折叠状态 | ||
| 78 | + collapse: false, | ||
| 79 | + }; | ||
| 80 | + }, | ||
| 81 | + computed: { | ||
| 82 | + visibleColNum() { | ||
| 83 | + return 24 / this.span; | ||
| 84 | + } | ||
| 85 | + }, | ||
| 86 | + watch: { | ||
| 87 | + // 组件外部v-model值更新后同步刷新model | ||
| 88 | + value(val) { | ||
| 89 | + Object.keys(this.model).forEach(key => { | ||
| 90 | + this.model[key] = val ? val[key] : undefined; | ||
| 91 | + }); | ||
| 92 | + }, | ||
| 93 | + // 配置列表有改动时初始化表单模型 | ||
| 94 | + list(value) { | ||
| 95 | + this.initModel(value); | ||
| 96 | + }, | ||
| 97 | + model: { | ||
| 98 | + handler(val) { | ||
| 99 | + this.$emit("input", val); | ||
| 100 | + this.$emit("change", val); | ||
| 101 | + }, | ||
| 102 | + deep: true | ||
| 103 | + } | ||
| 104 | + }, | ||
| 105 | + created() { | ||
| 106 | + // 初始化表单模型 | ||
| 107 | + this.initModel(this.list); | ||
| 108 | + }, | ||
| 109 | + methods: { | ||
| 110 | + // 绑定提示组件参数 | ||
| 111 | + bindItemTip(tip) { | ||
| 112 | + if (typeof tip === 'string') { | ||
| 113 | + return { content: tip, effect: 'light' }; | ||
| 114 | + } else if (typeof tip === 'object') { | ||
| 115 | + return tip; | ||
| 116 | + } else { | ||
| 117 | + return {}; | ||
| 118 | + } | ||
| 119 | + }, | ||
| 120 | + // 绑定组件事件 | ||
| 121 | + bindItemEvent(item) { | ||
| 122 | + if (item.on) { | ||
| 123 | + if (typeof item.on === 'function') { | ||
| 124 | + return item.on(this.model); | ||
| 125 | + } else { | ||
| 126 | + return item.on | ||
| 127 | + } | ||
| 128 | + } else { | ||
| 129 | + return undefined | ||
| 130 | + } | ||
| 131 | + }, | ||
| 132 | + // 初始化表单模型 | ||
| 133 | + initModel(list) { | ||
| 134 | + list.forEach(item => { | ||
| 135 | + this.$set(this.model, item.key, item.default || undefined) | ||
| 136 | + }); | ||
| 137 | + }, | ||
| 138 | + // 绑定组件v-if状态 | ||
| 139 | + bindItemVisible(visible = true) { | ||
| 140 | + let result = visible; | ||
| 141 | + if (typeof visible === 'function') { | ||
| 142 | + result = visible(this.model); | ||
| 143 | + } | ||
| 144 | + return result; | ||
| 145 | + }, | ||
| 146 | + // 绑定组件参数 | ||
| 147 | + bindItemProps(item) { | ||
| 148 | + const { props = {} } = item; | ||
| 149 | + let result = { ...props }; | ||
| 150 | + Object.keys(result).forEach(key => { | ||
| 151 | + if (typeof result[key] === 'function') { | ||
| 152 | + result[key] = result[key](this.model); | ||
| 153 | + } | ||
| 154 | + }); | ||
| 155 | + return result; | ||
| 156 | + }, | ||
| 157 | + // 绑定组件样式 | ||
| 158 | + bindItemStyle(style = {}) { | ||
| 159 | + return { | ||
| 160 | + width: "100%", | ||
| 161 | + ...style | ||
| 162 | + }; | ||
| 163 | + }, | ||
| 164 | + // 点击确定提交表单的操作 | ||
| 165 | + handleSearch() { | ||
| 166 | + this.$refs.search.validate(valid => { | ||
| 167 | + if (valid) { | ||
| 168 | + const result = JSON.parse(JSON.stringify(this.model)); | ||
| 169 | + this.$emit("search", result); | ||
| 170 | + } | ||
| 171 | + }); | ||
| 172 | + }, | ||
| 173 | + // 重置表单 | ||
| 174 | + handleReset() { | ||
| 175 | + Object.keys(this.model).forEach(key => { | ||
| 176 | + this.model[key] = this.list[key] ? this.list[key].default : undefined; | ||
| 177 | + }); | ||
| 178 | + this.$nextTick(() => { | ||
| 179 | + this.$refs.search.clearValidate(); | ||
| 180 | + }); | ||
| 181 | + this.$emit('reset'); | ||
| 182 | + }, | ||
| 183 | + // 折叠表单 | ||
| 184 | + handleCollapse() { | ||
| 185 | + this.collapse = !this.collapse; | ||
| 186 | + } | ||
| 187 | + } | ||
| 188 | +}; | ||
| 189 | +</script> | ||
| 0 | \ No newline at end of file | 190 | \ No newline at end of file |