Files
fe.ems.vue3/src/components/TableColumnsDnd/index.vue
2024-10-28 11:02:51 +08:00

235 lines
6.0 KiB
Vue

<script setup lang="ts">
import { reactive, watch, onMounted, PropType, nextTick } from 'vue';
import { Container, Draggable } from 'vue3-smooth-dnd';
import useI18n from '@/hooks/useI18n';
import { dbGetJSON, dbSetJSON } from '@/utils/cache-db-utils';
import { CACHE_DB_TABLE_DND } from '@/constants/cache-keys-constants';
const { t, currentLocale } = useI18n();
const emit = defineEmits(['update:columns-dnd']);
const props = defineProps({
/**
* 表格字段列非响应式数据,必传
*
* v-model:columns-dnd取变更的数据
*/
columns: {
type: Array<any>,
required: true,
},
/**
* 按钮类型
* text 图标
* ghost 图标按钮带文字
*/
type: {
type: String as PropType<'text' | 'ghost'>,
default: 'text',
},
/**
* 缓存列变更数据标识,不传则不缓存
*/
cacheId: {
type: String,
default: '',
},
});
/**表格字段列 */
const tableColumns = reactive(props.columns);
/**表格字段列勾选状态 */
const state = reactive<{
indeterminate: boolean;
/**是否全选 */
checkAll: boolean;
/**字段标题列表 */
columnsTitleList: string[];
}>({
indeterminate: false,
checkAll: true,
columnsTitleList: [],
});
/**表格字段列全选操作 */
function fnTableColumnsCheckAllChange(e: any) {
const checked = e.target.checked;
state.indeterminate = false;
state.columnsTitleList = checked
? tableColumns.map(s => `${s.title as string}`)
: [];
}
/**表格字段列拖拽操作 */
function fnTableColumnsDrop(dropResult: any) {
const { removedIndex, addedIndex, payload } = dropResult;
if (removedIndex === null || addedIndex === null) {
return;
}
let itemToAdd = payload;
itemToAdd = tableColumns.splice(removedIndex, 1)[0];
tableColumns.splice(addedIndex, 0, itemToAdd);
fnUpdateColumns();
}
/**表格字段列固定操作 */
function fnTableColumnsFixed(row: Record<string, any>) {
if (row.fixed === undefined) {
return;
}
const optTitle = t('common.operate');
if (row.title === optTitle) {
const optFixed = row.fixed === 'right';
row.fixed = optFixed ? false : 'right';
} else {
row.fixed = !row.fixed;
}
fnUpdateColumns();
}
/**表格字段列勾选字段变化 */
function fnUpdateColumns() {
let list = tableColumns.filter(s =>
state.columnsTitleList.includes(`${s.title}`)
);
// 取消全选时留第一个
if (list.length === 0) {
list = [tableColumns[0]];
}
if (props.cacheId) {
// 存入数据
dbSetJSON(
CACHE_DB_TABLE_DND,
`${props.cacheId}#${currentLocale.value}`,
list
);
}
nextTick(() => {
emit('update:columns-dnd', list);
});
}
/**表格字段列勾选监听 */
watch(
() => state.columnsTitleList,
val => {
const len = val.length;
state.indeterminate = !!len && len < tableColumns.length;
state.checkAll = len === tableColumns.length;
fnUpdateColumns();
}
);
onMounted(() => {
if (props.cacheId) {
// 读取数据后响应
dbGetJSON(CACHE_DB_TABLE_DND, `${props.cacheId}#${currentLocale.value}`)
.then(data => {
if (data) {
const titleList: string[] = [];
for (const item of data) {
titleList.push(`${item.title}`);
// 固定标记还原
if (item.fixed !== undefined) {
const fixedItem = tableColumns.find(s => s.title === item.title);
if (fixedItem) {
fixedItem.fixed = item.fixed;
}
}
}
state.columnsTitleList = titleList;
} else {
state.columnsTitleList = tableColumns.map(s => `${s.title}`);
}
})
.finally(() => {
fnUpdateColumns();
});
} else {
state.columnsTitleList = tableColumns.map(s => `${s.title}`);
fnUpdateColumns();
}
});
</script>
<template>
<a-tooltip>
<template #title>
{{ t('common.columnSetText') }}
</template>
<a-popover trigger="click" placement="bottomRight">
<template #title>
<div class="table-column-setting-title">
<a-checkbox
v-model:checked="state.checkAll"
:indeterminate="state.indeterminate"
@change="fnTableColumnsCheckAllChange"
>
{{ t('common.columnSetTitle') }}
</a-checkbox>
</div>
</template>
<template #content>
<a-checkbox-group
v-model:value="state.columnsTitleList"
style="width: 100%; max-height: 450px; overflow-y: auto"
>
<Container orientation="vertical" @drop="fnTableColumnsDrop">
<Draggable v-for="c in tableColumns" :key="c.title">
<div class="table-column-setting-list-item">
<HolderOutlined class="anticon" />
<a-checkbox :value="c.title">
{{ c.title }}
</a-checkbox>
<a-button
v-if="c.fixed !== undefined"
size="small"
:title="c.fixed ? `Fixed ${c.fixed} side` : ''"
:type="c.fixed ? 'primary' : 'dashed'"
@click="fnTableColumnsFixed(c)"
>
<template #icon><FlagOutlined /></template>
</a-button>
</div>
</Draggable>
</Container>
</a-checkbox-group>
</template>
<a-button :type="props.type" size="small">
<template #icon><TableOutlined /></template>
<span v-if="props.type === 'ghost'">
{{ t('common.columnSetText') }}
</span>
</a-button>
</a-popover>
</a-tooltip>
</template>
<style lang="less" scoped>
.table-column-setting-title {
display: flex;
align-items: center;
justify-content: space-between;
height: 32px;
margin-left: 4px;
}
.table-column-setting-list-item {
display: flex;
align-items: center;
width: 100%;
padding: 4px;
}
.table-column-setting-list-item > .anticon {
padding-right: 8px;
cursor: move;
}
.table-column-setting-list-item .ant-checkbox-wrapper {
flex: 1;
margin: 0;
}
</style>