perf(过滤器): 文本下拉自定义排序

This commit is contained in:
fit2cloud-chenyw 2022-11-15 16:36:05 +08:00
parent a8dbe2e297
commit e325ef75a1
8 changed files with 435 additions and 349 deletions

View File

@ -1,115 +1,13 @@
<template>
<span>
<el-dropdown
trigger="click"
size="mini"
@command="clickItem"
<span class="">
<el-tag
size="small"
closable
class="item-axis"
@close="removeItem"
>
<span class="el-dropdown-link">
<el-tag
size="small"
class="item-axis"
>
{{ item.name }}<i class="el-icon-arrow-down el-icon--right" />
</el-tag>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-if="isSortWidget"
:disabled="disabledSort"
:command="beforeClickItem('none')"
>
<span
class="de-sort-menu"
:class="!disabledSort && (!sortNode || sortNode.sort === 'none') ? 'de-active-li': ''"
>{{
$t('chart.none')
}}</span>
</el-dropdown-item>
<el-dropdown-item
v-if="isSortWidget"
:disabled="disabledSort"
:command="beforeClickItem('asc')"
>
<span
v-popover:popoverasc
class="el-dropdown-link inner-dropdown-menu de-sort-menu"
:class="!disabledSort && sortNode.sort === 'asc' ? 'de-active-li': ''"
>
<span>
<span>{{ $t('chart.asc') }}</span>
</span>
<i class="el-icon-arrow-right el-icon--right" />
</span>
<el-popover
ref="popoverasc"
v-model="ascFieldsShow"
placement="right-start"
width="120"
:close-delay="500"
trigger="hover"
>
<ul class="de-ul">
<li
v-for="(node, i) in allFields"
:key="node.id"
:index="i"
class="de-sort-field-span"
:class="sortNode.sort === 'asc' && sortNode.id === node.id ? 'de-active-li': ''"
@click="saveAscField(node)"
>
<span>{{ node.name }}</span>
</li>
</ul>
</el-popover>
</el-dropdown-item>
<el-dropdown-item
v-if="isSortWidget"
:disabled="disabledSort"
:command="beforeClickItem('desc')"
>
<span
v-popover:popoverdesc
class="el-dropdown-link inner-dropdown-menu de-sort-menu"
:class="!disabledSort && sortNode.sort === 'desc' ? 'de-active-li': ''"
>
<span>
<span>{{ $t('chart.desc') }}</span>
</span>
<i class="el-icon-arrow-right el-icon--right" />
</span>
<el-popover
ref="popoverdesc"
v-model="descFieldsShow"
placement="right-start"
width="120"
:close-delay="500"
trigger="hover"
>
<ul class="de-ul">
<li
v-for="(node, i) in allFields"
:key="node.id"
:index="i"
class="de-sort-field-span"
:class="sortNode.sort === 'desc' && sortNode.id === node.id ? 'de-active-li': ''"
@click="saveDescField(node)"
>
<span>{{ node.name }}</span>
</li>
</ul>
</el-popover>
</el-dropdown-item>
<el-dropdown-item
:divided="isSortWidget"
icon="el-icon-delete"
:command="beforeClickItem('remove')"
>
<span class="de-delete-field">{{ $t('chart.delete') }}</span>
</el-dropdown-item>
<slot />
</el-dropdown-menu>
</span>
</el-dropdown>
{{ item.name }}
</el-tag>
</span>
</template>
@ -124,103 +22,34 @@ export default {
index: {
type: Number,
required: true
},
allFields: {
type: Array,
default: () => []
},
sort: {
type: Object,
default: () => null
},
isSortWidget: {
type: Boolean,
default: false
}
},
data() {
return {
radio: 0,
ascFieldsShow: false,
descFieldsShow: false,
defaultSortProp: {
sort: 'none'
},
sortNode: null
}
},
computed: {
disabledSort() {
return this.index > 0
}
},
watch: {
index(val, old) {
/* index(val, old) {
if (val !== old) {
this.sortChange('none')
}
}
} */
},
mounted() {
},
created() {
if (!this.sortNode) {
this.sortNode = this.sort && this.sort.id ? JSON.parse(JSON.stringify(this.sort)) : JSON.parse(JSON.stringify(this.defaultSortProp))
}
},
methods: {
clickItem(param) {
if (!param) {
return
}
switch (param.type) {
case 'none':
this.sortChange('none')
break
case 'asc':
this.sortChange('asc')
break
case 'desc':
this.sortChange('desc')
break
case 'remove':
this.removeItem()
break
default:
break
}
},
beforeClickItem(type) {
return {
type: type
}
},
removeItem() {
this.item.index = this.index
this.$emit('closeItem', this.item)
},
saveAscField({ id, name }) {
this.ascFieldsShow = false
const sort = 'asc'
this.sortNode = { id, name, sort }
this.$emit('sort-change', this.sortNode)
},
saveDescField({ id, name }) {
this.descFieldsShow = false
const sort = 'desc'
this.sortNode = { id, name, sort }
this.$emit('sort-change', this.sortNode)
},
sortChange(type) {
this.sortNode.sort = type
if (type === 'none') {
this.sortNode = { sort: 'none' }
}
this.$emit('sort-change', this.sortNode)
}
}
@ -249,51 +78,4 @@ span {
font-size: 12px;
}
.de-ul li {
margin: 5px 2px;
cursor: pointer;
&:hover {
color: #409EFF;
border-color: rgb(198, 226, 255);
background-color: rgb(236, 245, 255);
}
&:before {
content: "";
width: 6px;
height: 6px;
display: inline-block;
border-radius: 50%;
vertical-align: middle;
margin-right: 5px;
}
}
.de-active-li {
&:before {
background: #409EFF;
}
}
.de-sort-menu::before {
content: "";
width: 6px;
height: 6px;
display: inline-block;
border-radius: 50%;
vertical-align: middle;
margin-right: 5px;
}
.de-delete-field {
margin-left: 4px;
}
.de-sort-field-span {
display: inline-flexbox;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -43,11 +43,10 @@
import ElVisualSelect from '@/components/elVisualSelect'
import { linkMultFieldValues, multFieldValues } from '@/api/dataset/dataset'
import bus from '@/utils/bus'
import { isSameVueObj } from '@/utils'
import { isSameVueObj, mergeCustomSortOption } from '@/utils'
import { getLinkToken, getToken } from '@/utils/auth'
import customInput from '@/components/widget/deWidget/customInput'
import { textSelectWidget } from '@/components/widget/deWidget/serviceNameFn.js'
export default {
components: { ElVisualSelect },
mixins: [customInput],
@ -123,6 +122,9 @@ export default {
const i18nKey = this.element.options.attrs.multiple ? 'panel.multiple_choice' : 'panel.single_choice'
const i18nValue = this.$t(i18nKey)
return '(' + i18nValue + ')'
},
isCustomSortWidget() {
return this.element.serviceName === 'textSelectWidget'
}
},
@ -347,7 +349,11 @@ export default {
},
optionData(data) {
if (!data) return null
return data.filter(item => !!item).map(item => {
let tempData = data.filter(item => !!item)
if (this.isCustomSortWidget && this.element.options.attrs?.sort?.sort === 'custom') {
tempData = mergeCustomSortOption(this.element.options.attrs.sort.list, tempData)
}
return tempData.map(item => {
return {
id: item,
text: item

View File

@ -325,3 +325,12 @@ export const changeFavicon = link => {
document.head.appendChild($favicon)
}
}
export const mergeCustomSortOption = (customSortList, sourceList) => {
if (!customSortList?.length) return sourceList?.length ? sourceList : []
if (!sourceList?.length) return customSortList?.length ? customSortList : []
const result = [...customSortList, ...sourceList]
return [...new Set(result)]
}

View File

@ -267,7 +267,6 @@
<div v-if="currentElement.options && currentElement.options.attrs">
<filter-head
:element="currentElement"
:widget="widget"
/>
<filter-control
@ -413,6 +412,10 @@ export default {
this.myAttrs.fieldId = null
this.myAttrs.activeName = null
}
if (this.myAttrs.sort?.sort === 'custom') {
this.myAttrs.sort.list = []
}
this.enableSureButton()
},
@ -857,7 +860,6 @@ export default {
.de-dialog-container {
height: 50vh !important;
}
.ms-aside-container {

View File

@ -55,46 +55,13 @@
v-if="widget.isCustomSortWidget && widget.isCustomSortWidget()"
style="padding-left: 10px;"
>
<el-checkbox
v-model="enableCustomSort"
@change="enableCustomSortChange"
>
<span>{{ $t('chart.sort') }}</span>
</el-checkbox>
<el-popover
v-model="customSortPopovervisible"
placement="bottom-end"
:disabled="!enableCustomSort"
width="180"
>
<div style="width: 100%;overflow-y: auto;overflow-x: hidden;word-break: break-all;position: relative;">
<filter-custom-sort
:field-id="fieldIds"
@on-filter-sort-change="customSortChange"
/>
<div
slot="footer"
class="dialog-footer filter-custom-sort-footer"
>
<el-button
size="mini"
@click="cancelCustomSort"
>{{ $t('chart.cancel') }}</el-button>
<el-button
type="primary"
size="mini"
@click="saveCustomSort"
>{{ $t('chart.confirm') }}</el-button>
</div>
</div>
<filter-sort
:widget="widget"
:element="element"
@sort-change="sortChange"
/>
<i
slot="reference"
:class="{'i-filter-active': enableCustomSort, 'i-filter-inactive': !enableCustomSort}"
class="el-icon-sort i-filter"
/>
</el-popover>
</span>
</div>
@ -243,10 +210,10 @@
</template>
<script>
import FilterCustomSort from './FilterCustomSort'
import FilterSort from './FilterSort'
export default {
name: 'FilterControl',
components: { FilterCustomSort },
components: { FilterSort },
props: {
widget: {
type: Object,
@ -279,9 +246,7 @@ export default {
{ id: 'HH', name: 'HH' },
{ id: 'HH:mm', name: 'HH:mm' },
{ id: 'HH:mm:ss', name: 'HH:mm:ss' }
],
enableCustomSort: false,
customSortPopovervisible: false
]
}
},
computed: {
@ -318,24 +283,10 @@ export default {
}
},
methods: {
enableCustomSortChange(val) {
this.enableCustomSort = val
this.element.options.attrs.sort = {
sort: 'custom'
}
sortChange(param) {
this.element.options.attrs.sort = param
},
customSortChange(list) {
},
saveCustomSort() {
},
cancelCustomSort() {
this.customSortPopovervisible = false
},
initCustomParam() {
this.enableCustomSort = this.element.options.attrs.sort?.sort === 'custom'
},
multipleChange(value) {
this.fillAttrs2Filter()
},
@ -466,11 +417,4 @@ export default {
text-overflow: ellipsis;
}
.filter-custom-sort-footer {
margin-top: 5px;
padding-top: 5px;
border-top: solid 1px #eee;
text-align: end;
}
</style>

View File

@ -31,13 +31,18 @@
<script>
import { linkMultFieldValues, multFieldValues } from '@/api/dataset/dataset'
import { getLinkToken, getToken } from '@/utils/auth'
import { mergeCustomSortOption } from '@/utils'
export default {
name: 'FilterCustomSort',
props: {
fieldId: {
type: String,
required: true
default: null
},
customSortList: {
type: Array,
default: null
}
},
data() {
@ -77,7 +82,7 @@ export default {
},
optionData(data) {
if (!data) return null
return data.filter(item => !!item)
return mergeCustomSortOption(this.customSortList, data.filter(item => !!item))
},
onMove() {
},

View File

@ -23,13 +23,10 @@
>
<drag-item
:key="item.id"
:is-sort-widget="isSortWidget"
:item="item"
:index="index"
:sort="element.options.attrs.sort"
:all-fields="index ? [] : tableFields"
@closeItem="closeItem"
@sort-change="sortChange"
/>
</v-flex>
@ -46,7 +43,6 @@
<script>
import draggable from 'vuedraggable'
import DragItem from '@/components/dragItem'
import { fieldListWithPermission } from '@/api/dataset/dataset'
export default {
name: 'FilterHead',
@ -59,55 +55,22 @@ export default {
type: Object,
default: () => {
}
},
widget: {
type: Object,
default: null
}
},
data() {
return {
targets: [],
tableFields: []
targets: []
}
},
computed: {
isSortWidget() {
return this.widget && this.widget.isSortWidget && this.widget.isSortWidget()
},
firstTableId() {
if (!this.isSortWidget) return null
if (this.element.options.attrs.dragItems && this.element.options.attrs.dragItems.length) {
return this.element.options.attrs.dragItems[0].tableId
}
return null
}
},
watch: {
firstTableId(val, old) {
if (val !== old) {
this.loadFields()
}
}
},
created() {
if (this.isSortWidget && this.element.options.attrs.dragItems && this.element.options.attrs.dragItems.length) {
this.loadFields()
}
},
methods: {
loadFields() {
if (this.firstTableId) {
fieldListWithPermission(this.firstTableId).then(res => {
this.tableFields = JSON.parse(JSON.stringify(res.data))
})
} else {
this.tableFields = []
}
},
onMove(e, originalEvent) {
return true
},
@ -120,9 +83,6 @@ export default {
if (!index) {
this.element.options.attrs.sort = null
}
},
sortChange(param) {
this.element.options.attrs.sort = param
}
}
}

View File

@ -0,0 +1,378 @@
<template>
<div>
<el-dropdown
trigger="click"
size="mini"
@command="clickItem"
>
<span class="el-dropdown-link filter-sort-span">
{{ $t('chart.sort') }}
<i class="el-icon-sort i-filter i-filter-active" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
:command="beforeClickItem('none')"
>
<span
class="de-sort-menu"
:class="(!sortNode || sortNode.sort === 'none') ? 'de-active-li': ''"
>{{
$t('chart.none')
}}</span>
</el-dropdown-item>
<el-dropdown-item
:command="beforeClickItem('asc')"
>
<span
v-popover:popoverasc
class="el-dropdown-link inner-dropdown-menu de-sort-menu"
:class="sortNode.sort === 'asc' ? 'de-active-li': ''"
>
<span>
<span>{{ $t('chart.asc') }}</span>
</span>
<i class="el-icon-arrow-right el-icon--right" />
</span>
<el-popover
ref="popoverasc"
v-model="ascFieldsShow"
placement="right-start"
width="120"
:close-delay="500"
trigger="hover"
>
<ul class="de-ul">
<li
v-for="(node, i) in tableFields"
:key="node.id"
:index="i"
class="de-sort-field-span"
:class="sortNode.sort === 'asc' && sortNode.id === node.id ? 'de-active-li': ''"
@click="saveAscField(node)"
>
<span>{{ node.name }}</span>
</li>
</ul>
</el-popover>
</el-dropdown-item>
<el-dropdown-item
:command="beforeClickItem('desc')"
>
<span
v-popover:popoverdesc
class="el-dropdown-link inner-dropdown-menu de-sort-menu"
:class="sortNode.sort === 'desc' ? 'de-active-li': ''"
>
<span>
<span>{{ $t('chart.desc') }}</span>
</span>
<i class="el-icon-arrow-right el-icon--right" />
</span>
<el-popover
ref="popoverdesc"
v-model="descFieldsShow"
placement="right-start"
width="120"
:close-delay="500"
trigger="hover"
>
<ul class="de-ul">
<li
v-for="(node, i) in tableFields"
:key="node.id"
:index="i"
class="de-sort-field-span"
:class="sortNode.sort === 'desc' && sortNode.id === node.id ? 'de-active-li': ''"
@click="saveDescField(node)"
>
<span>{{ node.name }}</span>
</li>
</ul>
</el-popover>
</el-dropdown-item>
<el-dropdown-item
:command="beforeClickItem('custom')"
>
<span
class="de-sort-menu"
:class="(sortNode && sortNode.sort === 'custom') ? 'de-active-li': ''"
>{{ $t('chart.custom_sort') }}...</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dialog
v-if="isCustomSortWidget && showCustomSort"
append-to-body
:title="$t('chart.custom_sort')"
:visible="showCustomSort"
:show-close="false"
width="300px"
class="dialog-css"
>
<div style="width: 100%;overflow-y: auto;overflow-x: hidden;word-break: break-all;position: relative;">
<filter-custom-sort
:field-id="fieldIds"
:custom-sort-list="customSortList"
@on-filter-sort-change="customSortChange"
/>
</div>
<div
slot="footer"
class="dialog-footer filter-custom-sort-footer"
>
<el-button
size="mini"
@click="cancelCustomSort"
>{{ $t('chart.cancel') }}</el-button>
<el-button
type="primary"
size="mini"
@click="saveCustomSort"
>{{ $t('chart.confirm') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { fieldListWithPermission } from '@/api/dataset/dataset'
import FilterCustomSort from './FilterCustomSort'
export default {
name: 'FilterSort',
components: { FilterCustomSort },
props: {
widget: {
type: Object,
default: null
},
element: {
type: Object,
default: null
}
},
data() {
return {
ascFieldsShow: false,
descFieldsShow: false,
defaultSortProp: {
sort: 'none'
},
tableFields: [],
sortNode: null,
showCustomSort: false,
customSortList: []
}
},
computed: {
fieldIds() {
return this.element.options.attrs.fieldId || []
},
isSortWidget() {
return this.widget && this.widget.isSortWidget && this.widget.isSortWidget()
},
isCustomSortWidget() {
return this.widget && this.widget.isCustomSortWidget && this.widget.isCustomSortWidget()
},
firstTableId() {
if (!this.isSortWidget) return null
if (this.element.options.attrs.dragItems && this.element.options.attrs.dragItems.length) {
return this.element.options.attrs.dragItems[0].tableId
}
return null
}
},
watch: {
firstTableId(val, old) {
if (val !== old) {
this.loadFields()
}
}
},
mounted() {
},
created() {
if (this.isSortWidget && this.element.options.attrs.dragItems && this.element.options.attrs.dragItems.length) {
if (this.element.options.attrs.sort) {
this.sortNode = JSON.parse(JSON.stringify(this.element.options.attrs.sort))
if (this.isCustomSortWidget && this.sortNode.sort === 'custom' && this.sortNode.list) {
this.customSortList = JSON.parse(JSON.stringify(this.sortNode.list))
}
}
if (!this.sortNode) {
this.sortNode = JSON.parse(JSON.stringify(this.defaultSortProp))
}
this.loadFields()
}
},
methods: {
customSortChange(list) {
this.customSortList = list
},
cancelCustomSort() {
this.customSortList = this.sortNode.list?.length ? JSON.parse(JSON.stringify(this.sortNode.list)) : []
this.showCustomSort = false
},
saveCustomSort() {
this.sortNode = {
sort: 'custom',
list: this.customSortList
}
this.$emit('sort-change', this.sortNode)
this.showCustomSort = false
},
loadFields() {
if (this.firstTableId) {
fieldListWithPermission(this.firstTableId).then(res => {
this.tableFields = JSON.parse(JSON.stringify(res.data))
})
} else {
this.tableFields = []
}
},
clickItem(param) {
if (!param) {
return
}
switch (param.type) {
case 'none':
this.sortChange('none')
break
case 'asc':
this.sortChange('asc')
break
case 'desc':
this.sortChange('desc')
break
case 'custom':
this.sortChange('custom')
break
default:
break
}
},
beforeClickItem(type) {
return {
type: type
}
},
saveAscField({ id, name }) {
this.ascFieldsShow = false
const sort = 'asc'
this.sortNode = { id, name, sort }
this.$emit('sort-change', this.sortNode)
},
saveDescField({ id, name }) {
this.descFieldsShow = false
const sort = 'desc'
this.sortNode = { id, name, sort }
this.$emit('sort-change', this.sortNode)
},
sortChange(type) {
if (type === 'custom') {
this.showCustomSort = true
return
}
this.sortNode.sort = type
if (type === 'none') {
this.sortNode = { sort: 'none' }
}
this.$emit('sort-change', this.sortNode)
}
}
}
</script>
<style lang="scss" scoped>
.de-ul li {
margin: 5px 2px;
cursor: pointer;
&:hover {
color: #409EFF;
border-color: rgb(198, 226, 255);
background-color: rgb(236, 245, 255);
}
&:before {
content: "";
width: 6px;
height: 6px;
display: inline-block;
border-radius: 50%;
vertical-align: middle;
margin-right: 5px;
}
}
.de-active-li {
&:before {
background: #409EFF;
}
}
.de-sort-menu::before {
content: "";
width: 6px;
height: 6px;
display: inline-block;
border-radius: 50%;
vertical-align: middle;
margin-right: 5px;
}
.de-delete-field {
margin-left: 4px;
}
.de-sort-field-span {
display: inline-flexbox;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
::v-deep .filter-custom-sort-footer {
padding-top: 5px !important;
border-top: solid 1px #eee;
text-align: end;
}
.filter-sort-span {
color: #303133;
font-weight: 500;
cursor: pointer;
margin-left: 10px;
i {
margin-left: 1px;
}
}
.dialog-css ::v-deep .el-dialog__title {
font-size: 14px;
}
.dialog-css ::v-deep .el-dialog__header {
padding: 20px 20px 0;
}
.dialog-css ::v-deep .el-dialog__body {
padding: 10px 20px 5px;
}
</style>