feat: add AdvanceTable.vue component; :star:
新增:高级表格;
New file |
| | |
| | | <template> |
| | | <div class="action-columns" ref="root"> |
| | | <a-tooltip title="列设置" :get-popup-container="() => $refs.root"> |
| | | <a-popover v-model="visible" placement="bottomRight" trigger="click" :get-popup-container="() => $refs.root"> |
| | | <div slot="title"> |
| | | <a-checkbox :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange" class="check-all" />列展示 |
| | | <a-button @click="resetColumns" style="float: right" type="link" size="small">重置</a-button> |
| | | </div> |
| | | <a-list style="width: 100%" size="small" :key="i" v-for="(col, i) in columns" slot="content"> |
| | | <a-list-item> |
| | | <a-checkbox v-model="col.visible" @change="e => onCheckChange(e, col)"/> |
| | | <template v-if="col.title"> |
| | | {{col.title}}: |
| | | </template> |
| | | <slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot> |
| | | <template slot="actions"> |
| | | <a-tooltip title="固定在列头" :get-popup-container="() => $refs.root"> |
| | | <a-icon :class="['left', {active: col.fixed === 'left'}]" @click="fixColumn('left', col)" type="vertical-align-top" /> |
| | | </a-tooltip> |
| | | <a-tooltip title="固定在列尾" :get-popup-container="() => $refs.root"> |
| | | <a-icon :class="['right', {active: col.fixed === 'right'}]" @click="fixColumn('right', col)" type="vertical-align-bottom" /> |
| | | </a-tooltip> |
| | | <a-tooltip title="添加搜索" :get-popup-container="() => $refs.root"> |
| | | <a-icon :class="{active: col.searchAble}" @click="setSearch(col)" type="search" /> |
| | | </a-tooltip> |
| | | </template> |
| | | </a-list-item> |
| | | </a-list> |
| | | <a-icon class="action" type="setting" /> |
| | | </a-popover> |
| | | </a-tooltip> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import cloneDeep from 'lodash.clonedeep' |
| | | |
| | | export default { |
| | | name: 'ActionColumns', |
| | | props: ['columns', 'visibleColumns'], |
| | | data() { |
| | | return { |
| | | visible: false, |
| | | indeterminate: false, |
| | | checkAll: true, |
| | | checkedCounts: this.columns.length, |
| | | backColumns: cloneDeep(this.columns) |
| | | } |
| | | }, |
| | | watch: { |
| | | checkedCounts(val) { |
| | | this.checkAll = val === this.columns.length |
| | | this.indeterminate = val > 0 && val < this.columns.length |
| | | } |
| | | }, |
| | | created() { |
| | | this.$emit('update:visibleColumns', [...this.columns]) |
| | | for (let col of this.columns) { |
| | | if (col.visible === undefined) { |
| | | this.$set(col, 'visible', true) |
| | | } |
| | | if (!col.visible) { |
| | | this.checkedCounts -= 1 |
| | | this.$set(col, 'colSpan', 0) |
| | | this.$set(col, 'customCell', () => ({style: 'display: none;'})) |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | onCheckChange(e, col) { |
| | | if (!col.visible) { |
| | | this.checkedCounts -= 1 |
| | | this.$set(col, 'colSpan', 0) |
| | | this.$set(col, 'customCell', () => ({style: 'display: none;'})) |
| | | } else { |
| | | this.checkedCounts += 1 |
| | | this.$set(col, 'colSpan', undefined) |
| | | this.$set(col, 'customCell', undefined) |
| | | } |
| | | }, |
| | | fixColumn(fixed, col) { |
| | | if (fixed !== col.fixed) { |
| | | this.$set(col, 'fixed', fixed) |
| | | } else { |
| | | this.$set(col, 'fixed', undefined) |
| | | } |
| | | }, |
| | | setSearch(col) { |
| | | this.$set(col, 'searchAble', !col.searchAble) |
| | | if (!col.searchAble && col.search) { |
| | | this.resetSearch(col) |
| | | } |
| | | }, |
| | | resetSearch(col) { |
| | | col.search.value = col.dataType === 'boolean' ? false : undefined |
| | | col.search.backup = undefined |
| | | }, |
| | | resetColumns() { |
| | | const {columns, backColumns} = this |
| | | let counts = columns.length |
| | | backColumns.forEach((back, index) => { |
| | | const column = columns[index] |
| | | column.visible = back.visible === undefined || back.visible |
| | | if (column.visible) { |
| | | this.$set(column, 'colSpan', undefined) |
| | | this.$set(column, 'customCell', undefined) |
| | | } else { |
| | | counts -= 1 |
| | | this.$set(column, 'colSpan', 0) |
| | | this.$set(column, 'customCell', () => ({style: 'display: none;'})) |
| | | } |
| | | if (back.fixed !== undefined) { |
| | | column.fixed = back.fixed |
| | | } else { |
| | | this.$set(column, 'fixed', undefined) |
| | | } |
| | | column.searchAble = back.searchAble |
| | | this.resetSearch(column) |
| | | }) |
| | | this.checkedCounts = counts |
| | | this.visible = false |
| | | this.$emit('reset', this.getConditions(columns)) |
| | | }, |
| | | onCheckAllChange(e) { |
| | | if (e.target.checked) { |
| | | this.checkedCounts = this.columns.length |
| | | this.columns.forEach(col => { |
| | | col.visible = true |
| | | this.$set(col, 'colSpan', undefined) |
| | | this.$set(col, 'customCell', undefined) |
| | | }) |
| | | } else { |
| | | this.checkedCounts = 0 |
| | | this.columns.forEach(col => { |
| | | col.visible = false |
| | | this.$set(col, 'colSpan', 0) |
| | | this.$set(col, 'customCell', () => ({style: 'display: none;'})) |
| | | }) |
| | | } |
| | | }, |
| | | getConditions(columns) { |
| | | const conditions = {} |
| | | columns.filter(item => item.search.value !== undefined && item.search.value !== '' && item.search.value !== null) |
| | | .forEach(col => { |
| | | conditions[col.dataIndex] = col.search.value |
| | | }) |
| | | return conditions |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="less"> |
| | | .action-columns{ |
| | | display: inline-block; |
| | | .check-all{ |
| | | margin-right: 8px; |
| | | } |
| | | .left,.right{ |
| | | transform: rotate(-90deg); |
| | | } |
| | | .active{ |
| | | color: @primary-color; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="action-size" ref="root"> |
| | | <a-tooltip title="密度"> |
| | | <a-dropdown placement="bottomCenter" :trigger="['click']" :get-popup-container="() => $refs.root"> |
| | | <a-icon class="action" type="column-height" /> |
| | | <a-menu :selected-keys="[value]" slot="overlay" @click="onClick"> |
| | | <a-menu-item key="default"> |
| | | 默认 |
| | | </a-menu-item> |
| | | <a-menu-item key="middle"> |
| | | 中等 |
| | | </a-menu-item> |
| | | <a-menu-item key="small"> |
| | | 紧密 |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | </a-tooltip> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'ActionSize', |
| | | props: ['value'], |
| | | inject: ['table'], |
| | | data() { |
| | | return { |
| | | selectedKeys: ['middle'] |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick({key}) { |
| | | this.$emit('input', key) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="less"> |
| | | .action-size{ |
| | | display: inline-block; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div ref="table" :id="id" class="advanced-table"> |
| | | <a-spin :spinning="loading"> |
| | | <div :class="['header-bar', size]"> |
| | | <div class="title"> |
| | | <template v-if="title">{{title}}</template> |
| | | <slot v-else-if="$slots.title" name="title"></slot> |
| | | <template v-else>高级表格</template> |
| | | </div> |
| | | <div class="search"> |
| | | <search-area @change="onSearchChange" :columns="columns" > |
| | | <template :slot="slot" v-for="slot in slots"> |
| | | <slot :name="slot"></slot> |
| | | </template> |
| | | </search-area> |
| | | </div> |
| | | <div class="actions"> |
| | | <a-tooltip title="刷新"> |
| | | <a-icon @click="refresh" class="action" :type="loading ? 'loading' : 'reload'" /> |
| | | </a-tooltip> |
| | | <action-size v-model="sSize" class="action" /> |
| | | <action-columns :columns="columns" @reset="onColumnsReset" class="action"> |
| | | <template :slot="slot" v-for="slot in slots"> |
| | | <slot :name="slot"></slot> |
| | | </template> |
| | | </action-columns> |
| | | <a-tooltip title="全屏"> |
| | | <a-icon @click="toggleScreen" class="action" :type="fullScreen ? 'fullscreen-exit' : 'fullscreen'" /> |
| | | </a-tooltip> |
| | | </div> |
| | | </div> |
| | | <a-table |
| | | v-bind="{...$options.propsData, title: undefined, loading: false}" |
| | | :size="sSize" |
| | | @expandedRowsChange="onExpandedRowsChange" |
| | | @change="onChange" |
| | | @expand="onExpand" |
| | | > |
| | | <template slot-scope="text, record, index" :slot="slot" v-for="slot in scopedSlots "> |
| | | <slot :name="slot" v-bind="{text, record, index}"></slot> |
| | | </template> |
| | | <template :slot="slot" v-for="slot in slots"> |
| | | <slot :name="slot"></slot> |
| | | </template> |
| | | <template slot-scope="record, index, indent, expanded" :slot="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''"> |
| | | <slot v-bind="{record, index, indent, expanded}" :name="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''"></slot> |
| | | </template> |
| | | </a-table> |
| | | </a-spin> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import ActionSize from '@/components/table/advance/ActionSize' |
| | | import ActionColumns from '@/components/table/advance/ActionColumns' |
| | | import SearchArea from '@/components/table/advance/SearchArea' |
| | | export default { |
| | | name: 'AdvanceTable', |
| | | components: {SearchArea, ActionColumns, ActionSize}, |
| | | props: { |
| | | tableLayout: String, |
| | | bordered: Boolean, |
| | | childrenColumnName: Array[String], |
| | | columns: Array, |
| | | components: Object, |
| | | dataSource: Array, |
| | | defaultExpandAllRows: Array[String], |
| | | expandedRowKeys: Array[String], |
| | | expandedRowRender: Function, |
| | | expandIcon: Function, |
| | | expandRowByClick: Boolean, |
| | | expandIconColumnIndex: Number, |
| | | footer: Function, |
| | | indentSize: Number, |
| | | loading: Boolean, |
| | | locale: Object, |
| | | pagination: Object, |
| | | rowClassName: Function, |
| | | rowKey: [String, Function], |
| | | rowSelection: Object, |
| | | scroll: Object, |
| | | showHeader: Boolean, |
| | | size: String, |
| | | title: String, |
| | | customHeaderRow: Function, |
| | | customRow: Function, |
| | | getPopupContainer: Function, |
| | | transformCellText: Function |
| | | }, |
| | | provide() { |
| | | return { |
| | | table: this |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | id: `${new Date().getTime()}-${Math.floor(Math.random() * 10)}`, |
| | | sSize: this.size || 'default', |
| | | fullScreen: false, |
| | | conditions: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | slots() { |
| | | return Object.keys(this.$slots).filter(slot => slot !== 'title') |
| | | }, |
| | | scopedSlots() { |
| | | return Object.keys(this.$scopedSlots).filter(slot => slot !== 'expandedRowRender' && slot !== 'title') |
| | | } |
| | | }, |
| | | created() { |
| | | this.addListener() |
| | | }, |
| | | beforeDestroy() { |
| | | this.removeListener() |
| | | }, |
| | | methods: { |
| | | refresh() { |
| | | this.$emit('refresh', this.conditions) |
| | | }, |
| | | onSearchChange(conditions) { |
| | | this.conditions = conditions |
| | | this.$emit('search', conditions) |
| | | }, |
| | | toggleScreen() { |
| | | if (this.fullScreen) { |
| | | this.outFullScreen() |
| | | } else { |
| | | this.inFullScreen() |
| | | } |
| | | }, |
| | | inFullScreen() { |
| | | const el = this.$refs.table |
| | | if (el.requestFullscreen) { |
| | | el.requestFullscreen() |
| | | return true |
| | | } else if (el.webkitRequestFullScreen) { |
| | | el.webkitRequestFullScreen() |
| | | return true |
| | | } else if (el.mozRequestFullScreen) { |
| | | el.mozRequestFullScreen() |
| | | return true |
| | | } else if (el.msRequestFullscreen) { |
| | | el.msRequestFullscreen() |
| | | return true |
| | | } |
| | | this.$message.warn('对不起,您的浏览器不支持全屏模式') |
| | | return false |
| | | }, |
| | | outFullScreen() { |
| | | if (document.exitFullscreen) { |
| | | document.exitFullscreen() |
| | | } else if (document.webkitCancelFullScreen) { |
| | | document.webkitCancelFullScreen(); |
| | | } else if (document.mozCancelFullScreen) { |
| | | document.mozCancelFullScreen() |
| | | } else if (document.msExitFullscreen) { |
| | | document.msExiFullscreen() |
| | | } |
| | | }, |
| | | onColumnsReset(conditions) { |
| | | this.$emit('reset', conditions) |
| | | }, |
| | | onExpandedRowsChange(expandedRows) { |
| | | this.$emit('expandedRowsChange', expandedRows) |
| | | }, |
| | | onChange(pagination, filters, sorter, options) { |
| | | this.$emit('expandedRowsChange', pagination, filters, sorter, options) |
| | | }, |
| | | onExpand(expanded, record) { |
| | | this.$emit('expandedRowsChange', expanded, record) |
| | | }, |
| | | addListener() { |
| | | document.addEventListener('fullscreenchange', this.fullScreenListener) |
| | | document.addEventListener('webkitfullscreenchange', this.fullScreenListener) |
| | | document.addEventListener('mozfullscreenchange', this.fullScreenListener) |
| | | document.addEventListener('msfullscreenchange', this.fullScreenListener) |
| | | }, |
| | | removeListener() { |
| | | document.removeEventListener('fullscreenchange', this.fullScreenListener) |
| | | document.removeEventListener('webkitfullscreenchange', this.fullScreenListener) |
| | | document.removeEventListener('mozfullscreenchange', this.fullScreenListener) |
| | | document.removeEventListener('msfullscreenchange', this.fullScreenListener) |
| | | }, |
| | | fullScreenListener(e) { |
| | | if (e.target.id === this.id) { |
| | | this.fullScreen = !this.fullScreen |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="less"> |
| | | .advanced-table{ |
| | | background-color: @component-background; |
| | | .header-bar{ |
| | | padding: 16px 24px; |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 4px; |
| | | transition: all 0.3s; |
| | | &.middle{ |
| | | padding: 12px 16px; |
| | | } |
| | | &.small{ |
| | | padding: 8px 12px; |
| | | border: 1px solid @border-color; |
| | | border-bottom: 0; |
| | | .title{ |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | .title{ |
| | | transition: all 0.3s; |
| | | font-size: 18px; |
| | | color: @title-color; |
| | | font-weight: 700; |
| | | } |
| | | .search{ |
| | | flex: 1; |
| | | text-align: right; |
| | | margin: 0 24px; |
| | | } |
| | | .actions{ |
| | | text-align: right; |
| | | font-size: 17px; |
| | | color: @text-color; |
| | | .action{ |
| | | margin: 0 8px; |
| | | cursor: pointer; |
| | | &:hover{ |
| | | color: @primary-color; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="search-area" ref="root"> |
| | | <div class="search-item" :key="index" v-for="(col, index) in searchCols"> |
| | | <div v-if="col.dataType === 'boolean'" class="title active"> |
| | | <template v-if="col.title"> |
| | | {{col.title}}: |
| | | </template> |
| | | <slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot> |
| | | <a-switch @change="onSwitchChange" class="switch" v-model="col.search.value" size="small" checked-children="是" un-checked-children="否" /> |
| | | </div> |
| | | <div v-else-if="col.dataType === 'time'" class="title active"> |
| | | <template v-if="col.title"> |
| | | {{col.title}}: |
| | | </template> |
| | | <slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot> |
| | | <a-time-picker v-model="col.search.value" placeholder="选择时间" @change="(time, timeStr) => onCalendarChange(time, timeStr, col)" @openChange="open => onCalendarOpenChange(open, col)" class="time-picker" size="small" /> |
| | | </div> |
| | | <div v-else-if="col.dataType === 'date'" class="title active"> |
| | | <template v-if="col.title"> |
| | | {{col.title}}: |
| | | </template> |
| | | <slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot> |
| | | <a-date-picker v-model="col.search.value" @change="onDateChange(col)" class="date-picker" size="small" /> |
| | | </div> |
| | | <div v-else-if="col.dataType === 'datetime'" class="title datetime active"> |
| | | <template v-if="col.title"> |
| | | {{col.title}}: |
| | | </template> |
| | | <slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot> |
| | | <a-date-picker v-model="col.search.value" @change="(date, dateStr) => onCalendarChange(date, dateStr, col)" @openChange="open => onCalendarOpenChange(open, col)" show-time class="datetime-picker" size="small" /> |
| | | </div> |
| | | <div v-else-if="col.dataType === 'select'" class="title active"> |
| | | <template v-if="col.title"> |
| | | {{col.title}}: |
| | | </template> |
| | | <slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot> |
| | | <a-select :allowClear="true" :options="col.search.selectOptions" v-model="col.search.value" placeholder="请选择..." @change="onSelectChange(col)" class="select" slot="content" size="small"> |
| | | </a-select> |
| | | </div> |
| | | <a-popover v-else @visibleChange="onVisibleChange(col, index)" v-model="col.search.visible" placement="bottom" :trigger="['click']" :get-popup-container="() => $refs.root"> |
| | | <div :class="['title', {active: col.search.value}]"> |
| | | <template v-if="col.title"> |
| | | {{col.title}} |
| | | </template> |
| | | <slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot> |
| | | <div class="value " v-if="col.search.value">: {{col | searchValue}}</div> |
| | | <a-icon class="icon-down" type="down"/> |
| | | </div> |
| | | <div class="operations" slot="content"> |
| | | <a-button @click="onCancel(col)" class="btn" size="small" type="link">取消</a-button> |
| | | <a-button @click="onConfirm(col)" class="btn" size="small" type="primary">确认</a-button> |
| | | </div> |
| | | <div class="search-overlay" slot="title"> |
| | | <a-input :id="`${searchIdPrefix}${index}`" :allow-clear="true" @keyup.esc="onCancel(col)" @keyup.enter="onConfirm(col)" v-model="col.search.value" size="default" /> |
| | | </div> |
| | | </a-popover> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import fastEqual from 'fast-deep-equal' |
| | | import moment from 'moment' |
| | | |
| | | export default { |
| | | name: 'SearchArea', |
| | | props: ['columns'], |
| | | inject: ['table'], |
| | | created() { |
| | | this.columns.forEach(item => { |
| | | this.$set(item, 'search', {...item.search, visible: false, value: item.dataType === 'boolean' ? false : undefined, format: this.getCalendarFormat(item)}) |
| | | }) |
| | | }, |
| | | filters: { |
| | | searchValue(col) { |
| | | if (col.dataType === 'time' && col.search.value) { |
| | | return col.search.value.format('HH:mm:ss') |
| | | } |
| | | return col.search.value |
| | | } |
| | | }, |
| | | watch: { |
| | | searchCols(newVal, oldVal) { |
| | | if (newVal.length != oldVal.length) { |
| | | const newConditions = this.getConditions(newVal) |
| | | if (!fastEqual(newConditions, this.conditions)) { |
| | | this.conditions = newConditions |
| | | this.$emit('change', this.conditions) |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | conditions: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | searchCols() { |
| | | return this.columns.filter(item => item.searchAble) |
| | | }, |
| | | searchIdPrefix() { |
| | | return this.table.id + '-ipt-' |
| | | } |
| | | }, |
| | | methods: { |
| | | onCancel(col) { |
| | | col.search.value = col.search.backup |
| | | col.search.visible = false |
| | | }, |
| | | onConfirm(col) { |
| | | col.search.backup = col.search.value |
| | | col.search.visible = false |
| | | const conditions = this.getConditions(this.searchCols) |
| | | if (!fastEqual(conditions, this.conditions)) { |
| | | this.conditions = conditions |
| | | this.$emit('change', this.conditions) |
| | | } |
| | | }, |
| | | onSwitchChange() { |
| | | this.conditions = this.getConditions(this.searchCols) |
| | | this.$emit('change', this.conditions) |
| | | }, |
| | | onSelectChange() { |
| | | this.conditions = this.getConditions(this.searchCols) |
| | | this.$emit('change', this.conditions) |
| | | }, |
| | | onCalendarOpenChange(open, col) { |
| | | col.search.visible = open |
| | | const {momentEqual, getConditions} = this |
| | | const {value, backup, format} = col.search |
| | | if (!open && !momentEqual(value, backup, format)) { |
| | | col.search.backup = moment(value) |
| | | this.conditions = getConditions(this.searchCols) |
| | | this.$emit('change', this.conditions) |
| | | } |
| | | }, |
| | | onCalendarChange(date, dateStr, col) { |
| | | const {momentEqual, getConditions} = this |
| | | const {value, backup, format} = col.search |
| | | if (!col.search.visible && !momentEqual(value, backup, format)) { |
| | | col.search.backup = moment(value) |
| | | this.conditions = getConditions(this.searchCols) |
| | | this.$emit('change', this.conditions) |
| | | } |
| | | }, |
| | | onDateChange(col) { |
| | | const {momentEqual, getConditions} = this |
| | | const {value, backup} = col.search |
| | | if (!momentEqual(value, backup, 'YYYY-MM-DD')) { |
| | | col.search.backup = moment(value) |
| | | this.conditions = getConditions(this.searchCols) |
| | | this.$emit('change', this.conditions) |
| | | } |
| | | }, |
| | | getCalendarFormat(col) { |
| | | const dataType = col.dataType |
| | | switch(dataType) { |
| | | case 'time': return 'HH:mm:ss' |
| | | case 'date': return 'YYYY-MM-DD' |
| | | case 'datetime': return 'YYYY-MM-DD HH:mm:ss' |
| | | default: return col.search && col.search.format |
| | | } |
| | | }, |
| | | getConditions(columns) { |
| | | const conditions = {} |
| | | columns.filter(item => item.search.value !== undefined && item.search.value !== '' && item.search.value !== null) |
| | | .forEach(col => { |
| | | conditions[col.dataIndex] = col.search.value |
| | | }) |
| | | return conditions |
| | | }, |
| | | onVisibleChange(col, index) { |
| | | if (!col.search.visible) { |
| | | col.search.value = col.search.backup |
| | | } else { |
| | | let input = document.getElementById(`${this.searchIdPrefix}${index}`) |
| | | if (input) { |
| | | setTimeout(() => {input.focus()}, 0) |
| | | } else { |
| | | this.$nextTick(() => { |
| | | input = document.getElementById(`${this.searchIdPrefix}${index}`) |
| | | input.focus() |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | momentEqual(target, source, format) { |
| | | if (target === source) { |
| | | return true |
| | | } else if (target && source && target.format(format) === source.format(format)) { |
| | | return true |
| | | } |
| | | return false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="less"> |
| | | .search-area{ |
| | | margin: -4px 0; |
| | | .search-item{ |
| | | margin: 4px 4px; |
| | | display: inline-block; |
| | | .title{ |
| | | padding: 4px 8px; |
| | | cursor: pointer; |
| | | border-radius: 4px; |
| | | user-select: none; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | .switch{ |
| | | margin-left: 4px; |
| | | } |
| | | .time-picker{ |
| | | margin-left: 4px; |
| | | width: 96px; |
| | | } |
| | | .date-picker{ |
| | | margin-left: 4px; |
| | | width: 120px; |
| | | } |
| | | .datetime-picker{ |
| | | margin-left: 4px; |
| | | width: 195px; |
| | | } |
| | | .value{ |
| | | display: inline-block; |
| | | overflow: hidden; |
| | | flex:1; |
| | | max-width: 144px; |
| | | text-overflow: ellipsis; |
| | | word-break: break-all; |
| | | white-space: nowrap; |
| | | } |
| | | &.active{ |
| | | background-color: @layout-bg-color; |
| | | } |
| | | } |
| | | .icon-down{ |
| | | margin-left: 4px; |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | .search-overlay{ |
| | | padding: 8px 0px; |
| | | text-align: center; |
| | | } |
| | | .select{ |
| | | margin-left: 4px; |
| | | max-width: 144px; |
| | | min-width: 96px; |
| | | } |
| | | .operations{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | .btn{ |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import AdvanceTable from './AdvanceTable' |
| | | export default AdvanceTable |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <advance-table |
| | | :columns="columns" |
| | | :data-source="dataSource" |
| | | title="高级表格-Beta" |
| | | :loading="loading" |
| | | rowKey="id" |
| | | @search="onSearch" |
| | | @refresh="onRefresh" |
| | | @reset="onReset" |
| | | > |
| | | <template slot="statusTitle"> |
| | | 状态<a-icon style="margin: 0 4px" type="info-circle" /> |
| | | </template> |
| | | <template slot="send" slot-scope="{text}"> |
| | | {{text ? '是' : '否'}} |
| | | </template> |
| | | <template slot="status" slot-scope="{text}"> |
| | | {{text | statusStr}} |
| | | </template> |
| | | </advance-table> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import AdvanceTable from '@/components/table/advance/AdvanceTable' |
| | | import moment from 'moment' |
| | | |
| | | const goods = ['运动鞋', 'T恤', '长裤', '短裤'] |
| | | const dataSource = [] |
| | | const current = new Date().getTime() |
| | | for (let i = 0; i < 100; i++) { |
| | | dataSource.push({ |
| | | id: i, |
| | | name: goods[Math.floor((Math.random() * 4))], |
| | | orderId: `${new Date().getTime()}-${Math.floor(Math.random() * 10)}`, |
| | | status: Math.floor((Math.random() * 4) + 1), |
| | | send: (i % 2) === 1, |
| | | sendTime: moment(current - Math.floor((Math.random() * 8000000))).format('YYYY-MM-DD HH:mm:ss'), |
| | | orderDate: moment(current - Math.floor((Math.random() * 800000000))).format('YYYY-MM-DD'), |
| | | auditTime: moment(current - Math.floor((Math.random() * 8000000))).format('HH:mm:ss'), |
| | | }) |
| | | } |
| | | export default { |
| | | name: 'Table', |
| | | components: {AdvanceTable}, |
| | | filters: { |
| | | statusStr(val) { |
| | | switch (val) { |
| | | case 1: return '已下单' |
| | | case 2: return '已付款' |
| | | case 3: return '已审核' |
| | | case 4: return '已发货' |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | columns: [ |
| | | { |
| | | title: '商品名称', |
| | | dataIndex: 'name', |
| | | searchAble: true |
| | | }, |
| | | { |
| | | title: '订单号', |
| | | dataIndex: 'orderId' |
| | | }, |
| | | { |
| | | searchAble: true, |
| | | dataIndex: 'status', |
| | | dataType: 'select', |
| | | slots: {title: 'statusTitle'}, |
| | | scopedSlots: {customRender: 'status'}, |
| | | search: { |
| | | selectOptions: [ |
| | | {title: '已下单', value: 1}, |
| | | {title: '已付款', value: 2}, |
| | | {title: '已审核', value: 3}, |
| | | {title: '已发货', value: 4} |
| | | ] |
| | | } |
| | | }, |
| | | { |
| | | title: '发货', |
| | | searchAble: true, |
| | | dataIndex: 'send', |
| | | dataType: 'boolean', |
| | | scopedSlots: {customRender: 'send'} |
| | | }, |
| | | { |
| | | title: '发货时间', |
| | | dataIndex: 'sendTime', |
| | | dataType: 'datetime' |
| | | }, |
| | | { |
| | | title: '下单日期', |
| | | searchAble: true, |
| | | dataIndex: 'orderDate', |
| | | dataType: 'date' |
| | | }, |
| | | { |
| | | title: '审核时间', |
| | | searchAble: true, |
| | | dataIndex: 'auditTime', |
| | | dataType: 'time', |
| | | }, |
| | | ], |
| | | dataSource: dataSource |
| | | } |
| | | }, |
| | | methods: { |
| | | onSearch(conditions) { |
| | | this.loading = true |
| | | this.searchGoods(conditions).then(result => { |
| | | this.dataSource = result |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | onRefresh(conditions) { |
| | | this.loading = true |
| | | this.searchGoods(conditions).then(result => { |
| | | this.dataSource = result |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | onReset(conditions) { |
| | | this.loading = true |
| | | this.searchGoods(conditions).then(result => { |
| | | this.dataSource = result |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | async searchGoods(conditions) { |
| | | const promise = new Promise((resolve, reject) => { |
| | | try { |
| | | const result = dataSource.filter(item => { |
| | | for (let key of Object.keys(conditions)) { |
| | | if (key === 'sendTime') { |
| | | if (conditions[key].format('YYYY-MM-DD HH:mm:ss') !== item[key]) return false |
| | | } else if (key === 'orderDate') { |
| | | if (conditions[key].format('YYYY-MM-DD') !== item[key]) return false |
| | | } else if (key === 'auditTime') { |
| | | if (conditions[key].format('HH:mm:ss') !== item[key]) return false |
| | | } else if (item[key] !== conditions[key]) { |
| | | return false |
| | | } |
| | | } |
| | | return true |
| | | }) |
| | | setTimeout(() => { |
| | | resolve(result) |
| | | }, 300) |
| | | } catch (e) { |
| | | reject(e) |
| | | } |
| | | }) |
| | | return promise |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | |
| | | path: 'palette', |
| | | name: '颜色复选框', |
| | | component: () => import('@/pages/components/Palette') |
| | | }, |
| | | { |
| | | path: 'table', |
| | | name: '高级表格', |
| | | component: () => import('@/pages/components/Table') |
| | | } |
| | | ] |
| | | }, |