Commit c5c854dcf472ffa480e5dea1fbab58f22019c5fc

Authored by Aaron.Liu
1 parent b34e511a

feat: 新增SchemaSelect组件

examples/router/routes.js
... ... @@ -95,6 +95,12 @@ const _components = [
95 95 meta: { title: 'Schema Page 页面' },
96 96 component: () => import('@/views/docs/component/schema-page.md'),
97 97 },
  98 + {
  99 + path: 'schema-select',
  100 + name: 'schema-select',
  101 + meta: { title: 'Schema Select 选择器' },
  102 + component: () => import('@/views/docs/component/schema-select.md'),
  103 + },
98 104 ],
99 105 },
100 106 ];
... ...
examples/views/docs/component/schema-select.md 0 → 100644
... ... @@ -0,0 +1,55 @@
  1 +# Schema Select 方案选择器
  2 +
  3 +通过配置JSON Schema的方式快速生成一个选择器,与常规选择器不同的是,本选择器是表格型选择器。
  4 +
  5 +## 基础用法
  6 +
  7 +`schema`设置配置项。
  8 +
  9 +::: snippet `schema`设置配置项
  10 +
  11 +```html
  12 +<template>
  13 + <z-schema-select v-model="model" size="small" :schema="schema" :api-search="searchAPI" label-key="name" value-key="id"></z-schema-select>
  14 +</template>
  15 +
  16 +<script>
  17 +export default {
  18 + data() {
  19 + return {
  20 + model: '',
  21 + schema: {
  22 + table: {
  23 + props: {
  24 + border: false
  25 + },
  26 + items: [
  27 + { label: '姓名', prop: 'name', minWidth: 120 },
  28 + { label: '年龄', prop: 'age', minWidth: 120 },
  29 + ]
  30 + }
  31 + }
  32 + }
  33 + },
  34 + methods: {
  35 + searchAPI(params) {
  36 + console.log('search', params);
  37 + return new Promise(resolve => {
  38 + setTimeout(() => {
  39 + const list = [
  40 + { id: '0', name: '李饼', age: 32, address: '地址0', status: '正常' },
  41 + { id: '1', name: '陈拾', age: 20, address: '地址1', status: '正常' },
  42 + { id: '3', name: '王七', age: 26, address: '地址3', status: '正常' },
  43 + { id: '4', name: '崔倍', age: 27, address: '地址4', status: '正常' },
  44 + { id: '5', name: '孙豹', age: 38, address: '地址5', status: '正常' },
  45 + ];
  46 + resolve([list, 37]);
  47 + }, 500);
  48 + });
  49 + },
  50 + }
  51 +}
  52 +</script>
  53 +```
  54 +
  55 +:::
0 56 \ No newline at end of file
... ...
examples/views/docs/design/table.md
1   -
2 1 # 表格设计规范
3 2  
4 3 ## 一、表头
... ... @@ -103,7 +102,7 @@
103 102  
104 103 设置title对当前功能进行描述
105 104  
106   -## 五、浮动列
  105 +## 五、浮动列
107 106  
108 107 ### 表格选择列
109 108  
... ... @@ -115,7 +114,7 @@
115 114  
116 115 设置快捷操作,跳转入口、快速复制
117 116  
118   -## 六、表格设置
  117 +## 六、表格设置
119 118  
120 119 ### 宽度
121 120  
... ... @@ -139,7 +138,7 @@
139 138  
140 139 包含层级的功能,如菜单、组织等功能,使用树形数据展示
141 140  
142   -## 七、业务数据
  141 +## 七、业务数据
143 142  
144 143 ### 常规数字
145 144  
... ...
packages/schema-page/index.vue
... ... @@ -51,9 +51,9 @@
51 51 </z-schema-table>
52 52 </div>
53 53 <!-- 底部区域 -->
54   - <div class="z-schema-page__footer">
  54 + <div v-if="schema.selection !== false || schema.pagination !== false" class="z-schema-page__footer">
55 55 <slot name="pagination" v-bind="_slotScope">
56   - <div v-if="selection.length > 0" class="selection-info">
  56 + <div v-if="schema.selection !== false && selection.length > 0" class="selection-info">
57 57 <span>已选中</span>
58 58 <span class="num">{{ selection.length }}</span>
59 59 <span>项</span>
... ...
packages/schema-select/index.vue 0 → 100644
... ... @@ -0,0 +1,226 @@
  1 +<style lang="scss">
  2 +.z-schema-select {
  3 + display: inline-block;
  4 +}
  5 +.z-schema-select__popper {
  6 + padding: 0 !important;
  7 +}
  8 +.z-schema-select__popper-content {
  9 + padding: 4px;
  10 +}
  11 +</style>
  12 +
  13 +<template>
  14 + <el-popover class="z-schema-select" popper-class="z-schema-select__popper" v-model="visible" trigger="click" placement="bottom-start" transition="el-zoom-in-top">
  15 + <template #reference>
  16 + <el-input
  17 + class="el-select__input"
  18 + v-model="model"
  19 + :size="selectSize"
  20 + :disabled="selectDisabled"
  21 + prefix-icon="el-icon-search"
  22 + :placeholder="selectPlaceholder"
  23 + @input="debouncedOnInput"
  24 + @focus="onInputFocus"
  25 + @blur="onInputBlur"
  26 + @mouseenter.native="inputHovering = true"
  27 + @mouseleave.native="inputHovering = false"
  28 + >
  29 + <template #suffix>
  30 + <i v-if="showClose" class="el-input__icon el-icon-circle-close" @click="onClear"></i>
  31 + <i v-else class="el-input__icon"></i>
  32 + </template>
  33 + </el-input>
  34 + </template>
  35 + <div class="z-schema-select__popper-content" v-clickoutside="onClickoutside">
  36 + <z-schema-page
  37 + ref="schema"
  38 + :value-table.sync="tableData"
  39 + :value-filter="valueFilter"
  40 + @update:value-filter="e => $emit('update:value-filter', e)"
  41 + :schema="selectSchema"
  42 + :auto="auto"
  43 + :api-search="apiSearch"
  44 + >
  45 + <template v-for="(item, index) in tableColumns" #[`table-cell-${item.prop}`]="{ value }">
  46 + <cell-highlight v-if="highlight" :value="value" :keyword="query" :key="index"></cell-highlight>
  47 + <template v-else>{{ value }}</template>
  48 + </template>
  49 + </z-schema-page>
  50 + </div>
  51 + </el-popover>
  52 +</template>
  53 +
  54 +<script>
  55 +import debounce from 'throttle-debounce/debounce';
  56 +import Clickoutside from 'element-ui/src/utils/clickoutside';
  57 +
  58 +export default {
  59 + name: 'SchemaSelect',
  60 + directives: { Clickoutside },
  61 + components: {
  62 + CellHighlight: {
  63 + functional: true,
  64 + render(h, context) {
  65 + const props = context.props || {};
  66 + const keyword = props.keyword;
  67 + const value = props.value || '';
  68 + const reg = new RegExp(`(${keyword})`, 'g');
  69 + const result = `${value}`.replace(reg, '<font style="color: red;">$1</font>');
  70 + return h('span', { domProps: { innerHTML: result } });
  71 + },
  72 + },
  73 + },
  74 + props: {
  75 + value: String,
  76 + schema: {
  77 + required: true,
  78 + type: Object,
  79 + default() {
  80 + return {};
  81 + },
  82 + },
  83 + auto: {
  84 + type: Boolean,
  85 + default: true,
  86 + },
  87 + // clearable: Boolean,
  88 + clearable: {
  89 + type: Boolean,
  90 + default: true,
  91 + },
  92 + // highlight: Boolean,
  93 + highlight: {
  94 + type: Boolean,
  95 + default: true,
  96 + },
  97 + disabled: Boolean,
  98 + size: String,
  99 + placeholder: String,
  100 + labelKey: {
  101 + type: String,
  102 + default: 'label',
  103 + },
  104 + valueKey: {
  105 + type: String,
  106 + default: 'value',
  107 + },
  108 + 'value-filter': {
  109 + type: Object,
  110 + default() {
  111 + return {};
  112 + },
  113 + },
  114 + 'api-search': Function,
  115 + },
  116 + inject: {
  117 + elForm: {
  118 + default: undefined,
  119 + },
  120 + elFormItem: {
  121 + default: undefined,
  122 + },
  123 + },
  124 + data() {
  125 + return {
  126 + model: '',
  127 + query: '',
  128 + visible: false,
  129 + inputHovering: false,
  130 + tableData: [],
  131 + };
  132 + },
  133 + created() {
  134 + this.debouncedOnInput = debounce(300, () => {
  135 + this.onInput();
  136 + });
  137 + },
  138 + computed: {
  139 + _elFormItemSize() {
  140 + return (this.elFormItem || {}).elFormItemSize;
  141 + },
  142 + selectSize() {
  143 + return this.size || this._elFormItemSize || (this.elForm || {}).size || (this.$ELEMENT || {}).size;
  144 + },
  145 + selectDisabled() {
  146 + return this.disabled || (this.elForm || {}).disabled;
  147 + },
  148 + selectPlaceholder() {
  149 + return this.selectedLabel || this.placeholder || '请选择';
  150 + },
  151 + selectedLabel() {
  152 + if (this.value) {
  153 + const item = this.tableData.find(item => item[this.valueKey] === this.value) || {};
  154 + return item[this.labelKey];
  155 + }
  156 + return '';
  157 + },
  158 + selectSchema() {
  159 + return {
  160 + filter: false,
  161 + action: false,
  162 + operation: false,
  163 + pagination: false,
  164 + selection: false,
  165 + ...this.schema,
  166 + table: {
  167 + on: {
  168 + 'row-click': this.onTableRowClick,
  169 + },
  170 + ...(this.schema.table || {}),
  171 + },
  172 + };
  173 + },
  174 + showClose() {
  175 + let hasValue = this.value !== undefined && this.value !== null && this.value !== '';
  176 + let criteria = this.clearable && !this.selectDisabled && this.inputHovering && hasValue;
  177 + return criteria;
  178 + },
  179 + tableColumns() {
  180 + if (this.schema) {
  181 + return this.schema.items;
  182 + }
  183 + return [];
  184 + },
  185 + },
  186 + methods: {
  187 + // 输入查询
  188 + onInput() {
  189 + this.query = this.model;
  190 + this.$refs.schema.search();
  191 + },
  192 + onInputFocus() {
  193 + this.model = '';
  194 + },
  195 + onInputBlur() {
  196 + if (!this.visible) {
  197 + this.model = this.selectedLabel;
  198 + }
  199 + },
  200 + onClickoutside() {
  201 + if (this.visible) {
  202 + this.model = this.selectedLabel;
  203 + }
  204 + },
  205 + // 点击表格行
  206 + onTableRowClick(row) {
  207 + this.query = '';
  208 + this.model = row.name;
  209 + this.visible = false;
  210 + this.$emit('input', row[this.valueKey]);
  211 + this.$emit('change', row);
  212 + },
  213 + // 清空
  214 + onClear(event) {
  215 + event.stopPropagation();
  216 + this.query = '';
  217 + this.model = '';
  218 + this.visible = false;
  219 + this.$emit('input', '');
  220 + this.$emit('clear');
  221 + this.$emit('change', '');
  222 + this.onInput();
  223 + },
  224 + },
  225 +};
  226 +</script>
... ...
packages/select/index.vue
... ... @@ -15,7 +15,7 @@
15 15 v-on="bindEvents"
16 16 v-bind="$attrs"
17 17 >
18   - <slot v-if="$scopedSlots.default" :item="item"></slot>
  18 + <slot v-if="$scopedSlots.default"></slot>
19 19 <template v-else>
20 20 <el-option v-for="item in optionsCurrent" :key="item.id" :label="labelFormat ? labelFormat(item) : item[labelKey]" :value="item[valueKey]" :disabled="item.disabled">
21 21 <slot name="option" :item="item" :value="model"></slot>
... ... @@ -63,10 +63,6 @@ export default {
63 63 type: String,
64 64 default: 'value',
65 65 },
66   - searchKey: {
67   - type: String,
68   - default: 'query',
69   - },
70 66 size: String,
71 67 multiple: Boolean,
72 68 sequence: Boolean,
... ...