chore: optimize code of AdvanceTable.vue;

This commit is contained in:
chenghongxing 2020-10-05 17:11:33 +08:00
parent 5297aec510
commit 8c46e74640
6 changed files with 160 additions and 87 deletions

View File

@ -22,15 +22,16 @@
"core-js": "^3.6.5",
"date-fns": "^2.14.0",
"enquire.js": "^2.1.6",
"highlight.js": "^10.2.1",
"js-cookie": "^2.2.1",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"viser-vue": "^2.4.8",
"vue": "^2.6.11",
"vue-i18n": "^8.18.2",
"vue-router": "^3.3.4",
"vuedraggable": "^2.23.2",
"vuex": "^3.4.0",
"nprogress": "^0.2.0"
"vuex": "^3.4.0"
},
"devDependencies": {
"@ant-design/colors": "^4.0.1",
@ -39,7 +40,9 @@
"@vue/cli-service": "^4.4.0",
"@vuepress/plugin-back-to-top": "^1.5.2",
"babel-eslint": "^10.1.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"babel-polyfill": "^6.26.0",
"compression-webpack-plugin": "^2.0.0",
"deepmerge": "^4.2.2",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
@ -51,9 +54,7 @@
"vue-template-compiler": "^2.6.11",
"vuepress": "^1.5.2",
"webpack-theme-color-replacer": "^1.3.12",
"whatwg-fetch": "^3.0.0",
"compression-webpack-plugin": "^2.0.0",
"babel-plugin-transform-remove-console": "^6.9.4"
"whatwg-fetch": "^3.0.0"
},
"eslintConfig": {
"root": true,

View File

@ -1,6 +1,5 @@
<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" />列展示
@ -14,13 +13,13 @@
</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-tooltip title="固定在列头" :mouseEnterDelay="0.5" :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-tooltip title="固定在列尾" :mouseEnterDelay="0.5" :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-tooltip title="添加搜索" :mouseEnterDelay="0.5" :get-popup-container="() => $refs.root">
<a-icon :class="{active: col.searchAble}" @click="setSearch(col)" type="search" />
</a-tooltip>
</template>
@ -28,7 +27,6 @@
</a-list>
<a-icon class="action" type="setting" />
</a-popover>
</a-tooltip>
</div>
</template>
@ -87,12 +85,14 @@
},
setSearch(col) {
this.$set(col, 'searchAble', !col.searchAble)
console.log(col)
if (!col.searchAble && col.search) {
this.resetSearch(col)
}
},
resetSearch(col) {
col.search.value = col.dataType === 'boolean' ? false : undefined
// col.search.value = col.dataType === 'boolean' ? false : undefined
col.search.value = undefined
col.search.backup = undefined
},
resetColumns() {
@ -114,7 +114,8 @@
} else {
this.$set(column, 'fixed', undefined)
}
column.searchAble = back.searchAble
this.$set(column, 'searchAble', back.searchAble)
// column.searchAble = back.searchAble
this.resetSearch(column)
})
this.checkedCounts = counts

View File

@ -8,7 +8,7 @@
<template v-else>高级表格</template>
</div>
<div class="search">
<search-area @change="onSearchChange" :columns="columns" >
<search-area :format-conditions="formatConditions" @change="onSearchChange" :columns="columns" >
<template :slot="slot" v-for="slot in slots">
<slot :name="slot"></slot>
</template>
@ -19,11 +19,13 @@
<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="列配置">
<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>
<a-tooltip title="全屏">
<a-icon @click="toggleScreen" class="action" :type="fullScreen ? 'fullscreen-exit' : 'fullscreen'" />
</a-tooltip>
@ -85,7 +87,8 @@
customHeaderRow: Function,
customRow: Function,
getPopupContainer: Function,
transformCellText: Function
transformCellText: Function,
formatConditions: Boolean
},
provide() {
return {
@ -97,7 +100,7 @@
id: `${new Date().getTime()}-${Math.floor(Math.random() * 10)}`,
sSize: this.size || 'default',
fullScreen: false,
conditions: []
conditions: {}
}
},
computed: {
@ -118,9 +121,9 @@
refresh() {
this.$emit('refresh', this.conditions)
},
onSearchChange(conditions) {
onSearchChange(conditions, searchOptions) {
this.conditions = conditions
this.$emit('search', conditions)
this.$emit('search', conditions, searchOptions)
},
toggleScreen() {
if (this.fullScreen) {
@ -131,6 +134,7 @@
},
inFullScreen() {
const el = this.$refs.table
el.classList.add('beauty-scroll')
if (el.requestFullscreen) {
el.requestFullscreen()
return true
@ -145,6 +149,7 @@
return true
}
this.$message.warn('对不起,您的浏览器不支持全屏模式')
el.classList.remove('beauty-scroll')
return false
},
outFullScreen() {
@ -157,6 +162,7 @@
} else if (document.msExitFullscreen) {
document.msExiFullscreen()
}
this.$refs.table.classList.remove('beauty-scroll')
},
onColumnsReset(conditions) {
this.$emit('reset', conditions)
@ -193,6 +199,7 @@
<style scoped lang="less">
.advanced-table{
overflow-y: auto;
background-color: @component-background;
.header-bar{
padding: 16px 24px;
@ -236,4 +243,4 @@
}
}
}
</style>
</style>

View File

@ -2,35 +2,36 @@
<div class="search-area" ref="root">
<div class="select-root" ref="selectRoot"></div>
<div class="search-item" :key="index" v-for="(col, index) in searchCols">
<div v-if="col.dataType === 'boolean'" class="title active">
<div v-if="col.dataType === 'boolean'" :class="['title', {active: col.search.value !== undefined}]">
<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="否" />
<a-switch @change="onSwitchChange(col)" class="switch" v-model="col.search.value" size="small" checked-children="是" un-checked-children="否" />
<a-icon v-if="col.search.value !== undefined" class="close" @click="e => onCloseClick(e, col)" type="close-circle" theme="filled" />
</div>
<div v-else-if="col.dataType === 'time'" class="title active">
<div v-else-if="col.dataType === 'time'" :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>
<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" :get-popup-container="() => $refs.root"/>
<a-time-picker :format="col.search.format" v-model="col.search.value" placeholder="选择时间" @change="(time, timeStr) => onCalendarChange(time, timeStr, col)" @openChange="open => onCalendarOpenChange(open, col)" class="time-picker" size="small" :get-popup-container="() => $refs.root"/>
</div>
<div v-else-if="col.dataType === 'date'" class="title active">
<div v-else-if="col.dataType === 'date'" :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>
<a-date-picker v-model="col.search.value" @change="onDateChange(col)" class="date-picker" size="small" :getCalendarContainer="() => $refs.root"/>
<a-date-picker :format="col.search.format" v-model="col.search.value" @change="onDateChange(col)" class="date-picker" size="small" :getCalendarContainer="() => $refs.root"/>
</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" :get-clalendar-container="() => $refs.root"/>
<a-date-picker :format="col.search.format" v-model="col.search.value" @change="(date, dateStr) => onCalendarChange(date, dateStr, col)" @openChange="open => onCalendarOpenChange(open, col)" class="datetime-picker" size="small" show-time :getCalendarContainer="() => $refs.root"/>
</div>
<div v-else-if="col.dataType === 'select'" class="title active" >
<div v-else-if="col.dataType === 'select'" :class="['title', {active: col.search.value !== undefined}]">
<template v-if="col.title">
{{col.title}}:
</template>
@ -38,23 +39,24 @@
<a-select :allowClear="true" :options="col.search.selectOptions" v-model="col.search.value" placeholder="请选择..." @change="onSelectChange(col)" class="select" slot="content" size="small" :get-popup-container="() => $refs.selectRoot">
</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}]">
<div v-else :class="['title', {active: col.search.value}]">
<a-popover @visibleChange="onVisibleChange(col, index)" v-model="col.search.visible" placement="bottom" :trigger="['click']" :get-popup-container="() => $refs.root">
<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">:&nbsp;&nbsp;{{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 class="value " v-if="col.search.value">:&nbsp;&nbsp;{{col.search.format && typeof col.search.format === 'function' ? col.search.format(col.search.value) : col.search.value}}</div>
<a-icon v-if="!col.search.value" class="icon-down" type="down"/>
<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="small" />
</div>
</a-popover>
<a-icon v-if="col.search.value" @click="e => onCloseClick(e, col)" class="close" type="close-circle" theme="filled"/>
</div>
</div>
</div>
</template>
@ -65,35 +67,31 @@
export default {
name: 'SearchArea',
props: ['columns'],
props: ['columns', 'formatConditions'],
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)})
this.$set(item, 'search', {...item.search, visible: false, value: undefined, format: this.getFormat(item)})
})
},
filters: {
searchValue(col) {
if (col.dataType === 'time' && col.search.value) {
return col.search.value.format('HH:mm:ss')
}
return col.search.value
}
console.log(this.columns)
},
watch: {
searchCols(newVal, oldVal) {
if (newVal.length != oldVal.length) {
const newConditions = this.getConditions(newVal)
const newSearchOptions = this.getSearchOptions(newVal)
if (!fastEqual(newConditions, this.conditions)) {
this.conditions = newConditions
this.$emit('change', this.conditions)
this.searchOptions = newSearchOptions
this.$emit('change', this.conditions, this.searchOptions)
}
}
}
},
data() {
return {
conditions: []
conditions: {},
searchOptions: []
}
},
computed: {
@ -105,72 +103,99 @@
}
},
methods: {
onCloseClick(e, col) {
e.preventDefault()
e.stopPropagation()
col.search.value = undefined
const {backup, value} = col.search
if (backup !== value) {
this.backupAndEmitChange(col)
}
},
onCancel(col) {
col.search.value = col.search.backup
col.search.visible = false
},
onConfirm(col) {
col.search.backup = col.search.value
const {backup, value} = col.search
col.search.visible = false
const conditions = this.getConditions(this.searchCols)
if (!fastEqual(conditions, this.conditions)) {
this.conditions = conditions
this.$emit('change', this.conditions)
if (backup !== value) {
this.backupAndEmitChange(col)
}
},
onSwitchChange() {
this.conditions = this.getConditions(this.searchCols)
this.$emit('change', this.conditions)
onSwitchChange(col) {
const {backup, value} = col.search
if (backup !== value) {
this.backupAndEmitChange(col)
}
},
onSelectChange() {
this.conditions = this.getConditions(this.searchCols)
this.$emit('change', this.conditions)
onSelectChange(col) {
this.backupAndEmitChange(col)
},
onCalendarOpenChange(open, col) {
col.search.visible = open
const {momentEqual, getConditions} = this
const {momentEqual, backupAndEmitChange} = 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)
backupAndEmitChange(col, moment(value))
}
},
onCalendarChange(date, dateStr, col) {
const {momentEqual, getConditions} = this
const {momentEqual, backupAndEmitChange} = 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)
backupAndEmitChange(col, moment(value))
}
},
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)
const {momentEqual, backupAndEmitChange} = this
const {value, backup, format} = col.search
if (!momentEqual(value, backup, format)) {
backupAndEmitChange(col, moment(value))
}
},
getCalendarFormat(col) {
getFormat(col) {
if (col.search && col.search.format) {
return col.search.format
}
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
default: return undefined
}
},
backupAndEmitChange(col, backValue = col.search.value) {
const {getConditions, getSearchOptions} = this
col.search.backup = backValue
this.conditions = getConditions(this.searchCols)
this.searchOptions = getSearchOptions(this.searchCols)
this.$emit('change', this.conditions, this.searchOptions)
},
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
const {value, format} = col.search
if (this.formatConditions && format) {
if (typeof format === 'function') {
conditions[col.dataIndex] = format(col.search.value)
} else if (typeof format === 'string' && value.constructor.name === 'Moment') {
conditions[col.dataIndex] = value.format(format)
} else {
conditions[col.dataIndex] = value
}
} else {
conditions[col.dataIndex] = value
}
})
return conditions
},
getSearchOptions(columns) {
return columns.filter(item => item.search.value !== undefined && item.search.value !== '' && item.search.value !== null)
.map(({dataIndex, search}) => ({field: dataIndex, value: search.value, format: search.format}))
},
onVisibleChange(col, index) {
if (!col.search.visible) {
col.search.value = col.search.backup
@ -214,6 +239,15 @@
user-select: none;
display: inline-flex;
align-items: center;
.close{
color: @text-color-second;
margin-left: 4px;
font-size: 12px;
vertical-align: middle;
:hover{
color: @text-color;
}
}
.switch{
margin-left: 4px;
}
@ -233,6 +267,7 @@
display: inline-block;
overflow: hidden;
flex:1;
vertical-align: middle;
max-width: 144px;
text-overflow: ellipsis;
word-break: break-all;
@ -243,7 +278,7 @@
}
}
.icon-down{
margin-left: 4px;
vertical-align: middle;
font-size: 12px;
}
}
@ -259,9 +294,10 @@
}
.operations{
display: flex;
margin: -6px 0;
justify-content: space-between;
.btn{
}
}
}
</style>
</style>

View File

@ -134,6 +134,28 @@ function loadInterceptors(interceptors, options) {
})
}
/**
* 解析 url 中的参数
* @param url
* @returns {Object}
*/
function parseUrlParams(url) {
const params = {}
if (!url || url === '' || typeof url !== 'string') {
return params
}
const paramsStr = url.split('?')[1]
if (!paramsStr) {
return params
}
const paramsArr = paramsStr.replace(/&|=/g, ' ').split(' ')
for (let i = 0; i < paramsArr.length / 2; i++) {
const value = paramsArr[i * 2 + 1]
params[paramsArr[i * 2]] = value === 'true' ? true : (value === 'false' ? false : value)
}
return params
}
export {
METHOD,
AUTH_TYPE,
@ -141,5 +163,6 @@ export {
setAuthorization,
removeAuthorization,
checkAuthorization,
loadInterceptors
loadInterceptors,
parseUrlParams
}

View File

@ -5302,6 +5302,11 @@ hex-color-regex@^1.1.0:
resolved "https://registry.npm.taobao.org/hex-color-regex/download/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha1-TAb8y0YC/iYCs8k9+C1+fb8aio4=
highlight.js@^10.2.1:
version "10.2.1"
resolved "https://registry.npm.taobao.org/highlight.js/download/highlight.js-10.2.1.tgz#09784fe2e95612abbefd510948945d4fe6fa9668"
integrity sha1-CXhP4ulWEqu+/VEJSJRdT+b6lmg=
highlight.js@^9.6.0:
version "9.18.1"
resolved "https://registry.npm.taobao.org/highlight.js/download/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c"