feat: add excel copy toggle beside the list refresh icon
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
52d94b94dd
commit
bf0099c2ad
1 file changed
+169
-78
@@ -1,6 +1,38 @@
|
||||
<!-- 列表工具栏右侧:刷新 + 列显隐(勾选持久化由 usePersistedListColumns 负责) -->
|
||||
<template>
|
||||
<div class="list-toolbar-end">
|
||||
<div ref="rootEl" class="list-toolbar-end">
|
||||
<el-tooltip
|
||||
v-if="showExcelCopy"
|
||||
:content="excelActive ? '退出 Excel 复制' : 'Excel 复制'"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
class="list-toolbar-end-btn"
|
||||
:class="{ 'is-excel-active': excelActive }"
|
||||
:aria-pressed="excelActive"
|
||||
aria-label="Excel 复制"
|
||||
@click="toggleExcelCopy"
|
||||
>
|
||||
<svg class="list-excel-icon" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<rect
|
||||
x="1.5"
|
||||
y="2"
|
||||
width="13"
|
||||
height="12"
|
||||
rx="1.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.4"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 6.2h13M1.5 10h13M6.2 2v12M10.4 2v12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.1"
|
||||
/>
|
||||
</svg>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="刷新列表" placement="top">
|
||||
<el-button
|
||||
class="list-toolbar-end-btn"
|
||||
@@ -61,96 +93,155 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Refresh, Setting } from '@element-plus/icons-vue'
|
||||
import type { ListColumnDef } from '@/composables/usePersistedListColumns'
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { Refresh, Setting } from '@element-plus/icons-vue'
|
||||
import type { ListColumnDef } from '@/composables/usePersistedListColumns'
|
||||
|
||||
defineOptions({ name: 'ListToolbarEndActions' })
|
||||
defineOptions({ name: 'ListToolbarEndActions' })
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
loading?: boolean
|
||||
columnDefs?: ListColumnDef[]
|
||||
visibleColumns?: string[]
|
||||
checkAll?: boolean
|
||||
isIndeterminate?: boolean
|
||||
showColumns?: boolean
|
||||
}>(),
|
||||
{
|
||||
loading: false,
|
||||
columnDefs: () => [],
|
||||
visibleColumns: () => [],
|
||||
checkAll: false,
|
||||
isIndeterminate: false,
|
||||
showColumns: undefined
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
loading?: boolean
|
||||
columnDefs?: ListColumnDef[]
|
||||
visibleColumns?: string[]
|
||||
checkAll?: boolean
|
||||
isIndeterminate?: boolean
|
||||
showColumns?: boolean
|
||||
showExcelCopy?: boolean
|
||||
}>(),
|
||||
{
|
||||
loading: false,
|
||||
columnDefs: () => [],
|
||||
visibleColumns: () => [],
|
||||
checkAll: false,
|
||||
isIndeterminate: false,
|
||||
showColumns: undefined,
|
||||
showExcelCopy: true
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
refresh: []
|
||||
'update:visibleColumns': [value: string[]]
|
||||
'update:checkAll': [value: boolean]
|
||||
resetColumns: []
|
||||
}>()
|
||||
|
||||
const showColumns = computed(() => {
|
||||
if (props.showColumns !== undefined) return props.showColumns
|
||||
return props.columnDefs.length > 0
|
||||
})
|
||||
|
||||
const checkAllProxy = computed({
|
||||
get: () => props.checkAll,
|
||||
set: (value: boolean) => emit('update:checkAll', value)
|
||||
})
|
||||
|
||||
const handleColumnToggle = (prop: string, visible: boolean) => {
|
||||
const next = new Set(props.visibleColumns)
|
||||
if (visible) {
|
||||
next.add(prop)
|
||||
} else {
|
||||
next.delete(prop)
|
||||
}
|
||||
const ordered = props.columnDefs
|
||||
.filter((col) => col.disabled || next.has(col.prop))
|
||||
.map((col) => col.prop)
|
||||
emit('update:visibleColumns', ordered)
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
refresh: []
|
||||
'update:visibleColumns': [value: string[]]
|
||||
'update:checkAll': [value: boolean]
|
||||
resetColumns: []
|
||||
}>()
|
||||
|
||||
const showColumns = computed(() => {
|
||||
if (props.showColumns !== undefined) return props.showColumns
|
||||
return props.columnDefs.length > 0
|
||||
})
|
||||
|
||||
const checkAllProxy = computed({
|
||||
get: () => props.checkAll,
|
||||
set: (value: boolean) => emit('update:checkAll', value)
|
||||
})
|
||||
|
||||
const handleColumnToggle = (prop: string, visible: boolean) => {
|
||||
const next = new Set(props.visibleColumns)
|
||||
if (visible) {
|
||||
next.add(prop)
|
||||
} else {
|
||||
next.delete(prop)
|
||||
const handleReset = () => {
|
||||
emit('resetColumns')
|
||||
}
|
||||
const ordered = props.columnDefs
|
||||
.filter((col) => col.disabled || next.has(col.prop))
|
||||
.map((col) => col.prop)
|
||||
emit('update:visibleColumns', ordered)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
emit('resetColumns')
|
||||
}
|
||||
const excelActive = ref(false)
|
||||
const rootEl = ref<HTMLElement | null>(null)
|
||||
let panelEl: HTMLElement | null = null
|
||||
|
||||
function controlledTable(): HTMLElement | null {
|
||||
const panel = rootEl.value?.closest('.panel')
|
||||
const table = panel?.querySelector('[data-list-excel-copy="on"]')
|
||||
return table instanceof HTMLElement ? table : null
|
||||
}
|
||||
|
||||
function toggleExcelCopy() {
|
||||
controlledTable()?.dispatchEvent(new CustomEvent('sx-list-excel-toggle'))
|
||||
}
|
||||
|
||||
function onExcelState(event: Event) {
|
||||
const custom = event as CustomEvent<{ active?: boolean }>
|
||||
if (event.target !== controlledTable()) return
|
||||
excelActive.value = !!custom.detail?.active
|
||||
}
|
||||
|
||||
function attachExcelStateListener() {
|
||||
const panel = rootEl.value?.closest('.panel')
|
||||
if (!(panel instanceof HTMLElement)) return false
|
||||
panel.addEventListener('sx-list-excel-state', onExcelState, true)
|
||||
panelEl = panel
|
||||
return true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!attachExcelStateListener()) {
|
||||
nextTick(() => {
|
||||
attachExcelStateListener()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
panelEl?.removeEventListener('sx-list-excel-state', onExcelState, true)
|
||||
panelEl = null
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.list-toolbar-end-btn.el-button {
|
||||
--el-button-border-color: transparent;
|
||||
--el-button-hover-border-color: transparent;
|
||||
--el-button-active-border-color: transparent;
|
||||
width: 36px;
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
color: var(--mint-text-muted);
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: var(--mint-primary-dark);
|
||||
.list-toolbar-end-btn.el-button {
|
||||
--el-button-border-color: transparent;
|
||||
--el-button-hover-border-color: transparent;
|
||||
--el-button-active-border-color: transparent;
|
||||
width: 36px;
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
color: var(--mint-text-muted);
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: var(--mint-primary-dark);
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
background: var(--mint-primary-bg);
|
||||
}
|
||||
|
||||
&:active {
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.is-excel-active {
|
||||
color: var(--mint-primary-dark);
|
||||
background: var(--mint-primary-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.list-toolbar-end-btn.is-excel-active {
|
||||
color: var(--mint-primary-dark);
|
||||
background: var(--mint-primary-bg);
|
||||
}
|
||||
|
||||
&:active {
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
.list-excel-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user