commit
35acdd97cd
After Width: | Height: | Size: 25 KiB |
@ -0,0 +1,52 @@
|
||||
export const asideMap: Recordable<AsideEntity> = {
|
||||
izupuser: {
|
||||
label: '提报人',
|
||||
defaultValue: null,
|
||||
isDefaultFilter: true,
|
||||
key: 'izupuser',
|
||||
component: ReportUserVue,
|
||||
},
|
||||
izproject: {
|
||||
label: '所属项目',
|
||||
defaultValue: null,
|
||||
isDefaultFilter: true,
|
||||
key: 'izproject',
|
||||
component: IzProjectVue,
|
||||
},
|
||||
izplan: {
|
||||
label: '所属计划',
|
||||
defaultValue: null,
|
||||
isDefaultFilter: true,
|
||||
key: 'izplan',
|
||||
component: PlanVue,
|
||||
},
|
||||
izstatus: {
|
||||
label: '审批状态',
|
||||
defaultValue: null,
|
||||
isDefaultFilter: false,
|
||||
key: 'izstatus',
|
||||
component: PlanVue, // todo
|
||||
},
|
||||
izuptime: {
|
||||
label: '提报时间',
|
||||
defaultValue: null,
|
||||
isDefaultFilter: false,
|
||||
key: 'izuptime',
|
||||
component: TimeVue,
|
||||
},
|
||||
iztaskrrom: {
|
||||
label: '任务来源',
|
||||
defaultValue: null,
|
||||
isDefaultFilter: false,
|
||||
key: 'iztaskrrom',
|
||||
component: IztaskrromVue,
|
||||
},
|
||||
izshowall: {
|
||||
label: '显示全部任务数据',
|
||||
defaultValue: true,
|
||||
isDefaultFilter: false,
|
||||
key: 'izshowall',
|
||||
component: PictureDownloadVue,
|
||||
inFilterList: false,
|
||||
},
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { store } from '@/store'
|
||||
import { getFilter } from '@/api/home/filter'
|
||||
|
||||
export interface ConfigState {
|
||||
customConfig: string[] | null
|
||||
asideValue: any
|
||||
}
|
||||
|
||||
export const useFinalStore = defineStore({
|
||||
id: 'app-final',
|
||||
state: (): ConfigState => ({
|
||||
customConfig: null,
|
||||
asideValue: null,
|
||||
}),
|
||||
getters: {
|
||||
getCustomConfig(): string[] | null {
|
||||
return this.customConfig
|
||||
},
|
||||
getAsideValue(): any {
|
||||
return this.asideValue
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setAsideValue(value) {
|
||||
this.asideValue = value
|
||||
},
|
||||
// 获取终审个性化配置
|
||||
async fetchCustomConfig() {
|
||||
const res = await getFilter(1)
|
||||
const { data } = res
|
||||
const list = data && data.searchcount ? data.searchcount.split(',') : []
|
||||
this.customConfig = list
|
||||
return list
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useFinal() {
|
||||
return useFinalStore(store)
|
||||
}
|
@ -0,0 +1,417 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { VueDraggable } from 'vue-draggable-plus'
|
||||
import { debounce, difference } from 'lodash-es'
|
||||
import { setFilter } from '@/api/home/filter'
|
||||
import { asideMap } from '@/config/final'
|
||||
import { useFinal } from '@/store/modules/final'
|
||||
|
||||
const show = ref(false)
|
||||
const checkAll = ref(false)
|
||||
const selectIds = ref<string[]>([])
|
||||
const finalStore = useFinal()
|
||||
|
||||
function showModal() {
|
||||
show.value = true
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
show.value = false
|
||||
}
|
||||
|
||||
// 左侧隐藏列表
|
||||
const offList = ref<any[]>([])
|
||||
// 右侧显示列表
|
||||
const onList = ref<any[]>([])
|
||||
|
||||
const allCount = computed(() => {
|
||||
return `全部筛选(共${offList.value.length - 1}个)`
|
||||
})
|
||||
|
||||
const selectCount = computed(() => {
|
||||
return `全部筛选(共${onList.value.length}个)`
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
showModal,
|
||||
})
|
||||
|
||||
function generateDefaultList() {
|
||||
return Object.keys(asideMap).reduce((acc, key) => {
|
||||
const { label, isDefaultFilter } = asideMap[key]
|
||||
|
||||
// 默认开启配置
|
||||
if (isDefaultFilter) {
|
||||
const config = {
|
||||
id: key,
|
||||
name: label || '未配置',
|
||||
fix: isDefaultFilter,
|
||||
checked: true,
|
||||
}
|
||||
return [...acc, config]
|
||||
}
|
||||
else {
|
||||
return acc
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
function generatList(customConfig) {
|
||||
const keys = Object.keys(asideMap)
|
||||
let onList: object[] = []
|
||||
const offList: object[] = []
|
||||
const showKeys = customConfig.map((key: string) => key.toLowerCase())
|
||||
|
||||
for (const key of keys) {
|
||||
if (!key.startsWith('iz') || asideMap[key] === undefined)
|
||||
continue
|
||||
|
||||
const name = asideMap[key]?.label
|
||||
const isDefaultFilter = asideMap[key]?.isDefaultFilter
|
||||
|
||||
// 不是默认配置
|
||||
const isChecked = asideMap[key].isDefaultFilter || showKeys.includes(key)
|
||||
|
||||
offList.push({
|
||||
id: key,
|
||||
name: name || '未配置',
|
||||
fix: isDefaultFilter,
|
||||
checked: isChecked,
|
||||
})
|
||||
|
||||
isChecked && selectIds.value.push(key)
|
||||
}
|
||||
|
||||
onList = showKeys.reduce((acc, key) => {
|
||||
const isDefaultFilter = asideMap[key]?.isDefaultFilter
|
||||
|
||||
// 把默认显示的过滤掉(理论上不会出现这种情况)
|
||||
if (isDefaultFilter === false) {
|
||||
const config = {
|
||||
id: key,
|
||||
name: asideMap[key].label || '未配置',
|
||||
fix: asideMap[key].isDefaultFilter,
|
||||
}
|
||||
return [...acc, config]
|
||||
}
|
||||
else {
|
||||
return acc
|
||||
}
|
||||
}, [])
|
||||
|
||||
const fixedList = generateDefaultList()
|
||||
onList.unshift(...fixedList)
|
||||
return { showList: onList, hideList: offList }
|
||||
}
|
||||
|
||||
finalStore.$subscribe(() => {
|
||||
const customConfig = finalStore.getCustomConfig
|
||||
|
||||
if (customConfig === null)
|
||||
return
|
||||
|
||||
const { showList, hideList } = generatList(customConfig)
|
||||
onList.value = showList
|
||||
offList.value = hideList
|
||||
})
|
||||
|
||||
async function handleSumbit(e: MouseEvent) {
|
||||
e.preventDefault()
|
||||
|
||||
const param = onList.value.filter(item => !asideMap[item.id].isDefaultFilter).map((item) => {
|
||||
return item.id
|
||||
}).join(',')
|
||||
|
||||
await setFilter({ searchcount: param, type: 1 })
|
||||
finalStore.fetchCustomConfig()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
function onCheckAllChange(value) {
|
||||
const ids: string[] = []
|
||||
|
||||
for (const item of offList.value) {
|
||||
if (!item.fix) {
|
||||
item.checked = value
|
||||
ids.push(item.id)
|
||||
}
|
||||
}
|
||||
|
||||
selectIds.value = value ? ids : []
|
||||
}
|
||||
|
||||
function onCheckChange(checked: any, item: any) {
|
||||
const index = selectIds.value.indexOf(item.id)
|
||||
|
||||
item.checked = checked
|
||||
|
||||
if (index === -1 && checked)
|
||||
selectIds.value.push(item.id)
|
||||
else
|
||||
selectIds.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const showIds = computed(() => {
|
||||
return onList.value.map((item) => {
|
||||
return item.id
|
||||
})
|
||||
})
|
||||
|
||||
watch(
|
||||
() => selectIds.value.length,
|
||||
(newVal, oldVal) => {
|
||||
if (newVal === oldVal)
|
||||
return
|
||||
|
||||
const action = newVal > oldVal ? 'add' : 'remove'
|
||||
const diff = action === 'add' ? difference(selectIds.value, showIds.value) : difference(showIds.value, selectIds.value)
|
||||
|
||||
if (diff.length === 0)
|
||||
return
|
||||
|
||||
if (action === 'add') {
|
||||
for (const item of offList.value) {
|
||||
if (!item.fix && diff.includes(item.id)) {
|
||||
onList.value.push({
|
||||
id: item.id,
|
||||
name: item.name || '未配置',
|
||||
fix: item.fix || false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const list = onList.value
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const item = list[index]
|
||||
if (!item.fix && diff.includes(item.id)) {
|
||||
list.splice(index, 1)
|
||||
index--
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => showIds.value.length,
|
||||
(newVal, oldVal) => {
|
||||
if (newVal === oldVal)
|
||||
return
|
||||
|
||||
const diff = difference(selectIds.value, showIds.value)
|
||||
|
||||
if (diff.length === 0)
|
||||
return
|
||||
|
||||
for (const item of offList.value) {
|
||||
if (!item.fix && diff.includes(item.id)) {
|
||||
const index = selectIds.value.indexOf(item.id)
|
||||
item.checked = false
|
||||
selectIds.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
function clearDragSource() {
|
||||
onList.value = onList.value.filter((item) => {
|
||||
return item.fix === true
|
||||
})
|
||||
}
|
||||
|
||||
function removeHandler(id: string) {
|
||||
const index = onList.value.findIndex((item) => {
|
||||
return item.id === id
|
||||
})
|
||||
|
||||
if (index !== -1)
|
||||
onList.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const offKeyword = ref('')
|
||||
const onKeyword = ref('')
|
||||
|
||||
const leftInputHandler = debounce((keyword) => {
|
||||
offKeyword.value = keyword
|
||||
}, 300)
|
||||
|
||||
const rightInputHandler = debounce((keyword) => {
|
||||
onKeyword.value = keyword
|
||||
}, 300)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-modal v-model:show="show" transform-origin="center" :mask-closable="false">
|
||||
<n-card class="cardstyle" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<div class="wrapper">
|
||||
<span class="wrapper-title">自定义筛选</span>
|
||||
<div class="wrapper-bar">
|
||||
<div class="wrapper-info">
|
||||
<span :style="{ 'margin-left': '18px' }">筛选项信息</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-grid cols="24" class="mt-4 proCard" responsive="screen" :x-gap="24">
|
||||
<n-grid-item span="11">
|
||||
<NCard
|
||||
:title="allCount" class="dragcardStyle" :segmented="{ content: true, footer: true }" size="small"
|
||||
:bordered="false"
|
||||
>
|
||||
<div>
|
||||
<n-input placeholder="搜索关键字" @input="leftInputHandler">
|
||||
<template #suffix>
|
||||
<SvgIcon size="14px" name="magnifying-1" />
|
||||
</template>
|
||||
</n-input>
|
||||
<n-scrollbar style="max-height: 500px;border: 1px solid #cad2dd;border-radius: 2px;">
|
||||
<div class="draggable-ul">
|
||||
<div class="draggable-li">
|
||||
<n-checkbox v-model:checked="checkAll" label="全部" @update:checked="onCheckAllChange" />
|
||||
</div>
|
||||
<div
|
||||
v-for="item in offList" v-show="item.name.includes(offKeyword)" :key="item.id"
|
||||
:class="{ 'disable-check': item.fix }" class="draggable-li"
|
||||
>
|
||||
<n-checkbox
|
||||
v-model:checked="item.checked" :label="item.name" :disabled="item.fix"
|
||||
@update:checked="onCheckChange($event, item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</NCard>
|
||||
</n-grid-item>
|
||||
<n-grid-item style="display: flex;align-items: center;" span="2">
|
||||
<SvgIcon size="20" name="switchsvg" />
|
||||
</n-grid-item>
|
||||
<n-grid-item span="11">
|
||||
<NCard
|
||||
:title="selectCount" class="dragcardStyle" :segmented="{ content: true, footer: true }" size="small"
|
||||
:bordered="false"
|
||||
>
|
||||
<template #header-extra>
|
||||
<span class="textbtnStyle" @click="clearDragSource">清空</span>
|
||||
</template>
|
||||
<div>
|
||||
<n-input placeholder="搜索关键字" @input="rightInputHandler">
|
||||
<template #suffix>
|
||||
<SvgIcon size="14px" name="magnifying-1" />
|
||||
</template>
|
||||
</n-input>
|
||||
<n-scrollbar style="max-height: 500px;border: 1px solid #cad2dd;border-radius: 2px;" class="scroll">
|
||||
<VueDraggable v-model="onList" class="draggable-ul" filter=".draggable-li[draggable='false']" :animation="150" group="shared">
|
||||
<div
|
||||
v-for="item in onList" v-show="item.name.includes(onKeyword)" :key="item.id" :draggable="!item.fix"
|
||||
:class="{ fix: item.fix }" class="cursor-move draggable-li"
|
||||
>
|
||||
<SvgIcon v-show="!item.fix" name="drag" size="24" />
|
||||
<span class="ml-2">{{ item.name }}</span>
|
||||
<SvgIcon
|
||||
v-if="!item.fix" size="16px" style="display:block;margin-left: auto;cursor: pointer;"
|
||||
name="clear" @click="removeHandler(item.id)"
|
||||
/>
|
||||
</div>
|
||||
</VueDraggable>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</NCard>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="wrapper-footer">
|
||||
<n-button type="info" @click="handleSumbit">
|
||||
确认
|
||||
</n-button>
|
||||
<n-button secondary style="margin-left:15px" @click="closeModal">
|
||||
取消
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&-title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&-bar {
|
||||
background-color: #e8e8e8;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&-info {
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
background-color: #1980FF;
|
||||
content: "";
|
||||
width: 5px;
|
||||
border-radius: 2px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dragcardStyle {
|
||||
--n-padding-bottom: 0px !important;
|
||||
--n-padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.cardstyle {
|
||||
width: 620px;
|
||||
height: 800px;
|
||||
--n-padding-bottom: 20px;
|
||||
--n-padding-left: 24px;
|
||||
}
|
||||
|
||||
.textbtnStyle {
|
||||
cursor: pointer;
|
||||
color: #1980FF;
|
||||
}
|
||||
|
||||
.draggable-ul {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.draggable-li {
|
||||
width: 100%;
|
||||
padding: 10px 16px;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #efeff5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.disable-check {
|
||||
color: gainsboro;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.n-card.n-card--content-segmented > .n-card__content:not(:first-child)) {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
::v-deep(.n-card > .n-card-header) {
|
||||
--n-padding-top: 0px;
|
||||
--n-padding-bottom: 12px
|
||||
}
|
||||
</style>
|
Loading…
Reference in new issue