| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- <template>
- <div class="table-container">
- <el-table
- :data="data"
- :border="setBorder"
- v-bind="$attrs"
- row-key="id"
- stripe
- style="width: 100%"
- v-loading="config.loading"
- @selection-change="onSelectionChange"
- >
- <el-table-column type="selection" :reserve-selection="true" width="30" v-if="config.isSelection" />
- <el-table-column type="index" label="序号" width="60" v-if="config.isSerialNo" />
- <el-table-column
- v-for="(item, index) in setHeader"
- :key="index"
- show-overflow-tooltip
- :prop="item.key"
- :width="item.colWidth"
- :label="item.title"
- >
- <template v-slot="scope">
- <template v-if="item.type === 'image'">
- <el-image
- :style="{ width: `${item.width}px`, height: `${item.height}px` }"
- :src="scope.row[item.key]"
- :zoom-rate="1.2"
- :preview-src-list="[scope.row[item.key]]"
- preview-teleported
- fit="cover"
- />
- </template>
- <template v-else>
- {{ scope.row[item.key] }}
- </template>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="100" v-if="config.isOperate">
- <template v-slot="scope">
- <el-popconfirm title="确定删除吗?" @confirm="onDelRow(scope.row)">
- <template #reference>
- <el-button text type="primary">删除</el-button>
- </template>
- </el-popconfirm>
- </template>
- </el-table-column>
- <template #empty>
- <el-empty description="暂无数据" />
- </template>
- </el-table>
- <div class="table-footer mt15">
- <el-pagination
- v-model:current-page="state.page.pageNum"
- v-model:page-size="state.page.pageSize"
- :pager-count="5"
- :page-sizes="[10, 20, 30]"
- :total="config.total"
- layout="total, sizes, prev, pager, next, jumper"
- background
- @size-change="onHandleSizeChange"
- @current-change="onHandleCurrentChange"
- >
- </el-pagination>
- <div class="table-footer-tool">
- <SvgIcon name="iconfont icon-dayin" :size="19" title="打印" @click="onPrintTable" />
- <SvgIcon name="iconfont icon-yunxiazai_o" :size="22" title="导出" @click="onImportTable" />
- <SvgIcon name="iconfont icon-shuaxin" :size="22" title="刷新" @click="onRefreshTable" />
- <el-popover
- placement="top-end"
- trigger="click"
- transition="el-zoom-in-top"
- popper-class="table-tool-popper"
- :width="300"
- :persistent="false"
- @show="onSetTable"
- >
- <template #reference>
- <SvgIcon name="iconfont icon-quanjushezhi_o" :size="22" title="设置" />
- </template>
- <template #default>
- <div class="tool-box">
- <el-tooltip content="拖动进行排序" placement="top-start">
- <SvgIcon name="fa fa-question-circle-o" :size="17" class="ml11" color="#909399" />
- </el-tooltip>
- <el-checkbox
- v-model="state.checkListAll"
- :indeterminate="state.checkListIndeterminate"
- class="ml10 mr1"
- label="列显示"
- @change="onCheckAllChange"
- />
- <el-checkbox v-model="getConfig.isSerialNo" class="ml12 mr1" label="序号" />
- <el-checkbox v-model="getConfig.isSelection" class="ml12 mr1" label="多选" />
- </div>
- <el-scrollbar>
- <div ref="toolSetRef" class="tool-sortable">
- <div class="tool-sortable-item" v-for="v in header" :key="v.key" :data-key="v.key">
- <i class="fa fa-arrows-alt handle cursor-pointer"></i>
- <el-checkbox v-model="v.isCheck" size="default" class="ml12 mr8" :label="v.title" @change="onCheckChange" />
- </div>
- </div>
- </el-scrollbar>
- </template>
- </el-popover>
- </div>
- </div>
- </div>
- </template>
- <script setup lang="ts" name="netxTable">
- import { reactive, computed, nextTick, ref } from 'vue'
- import { ElMessage } from 'element-plus'
- import printJs from 'print-js'
- import table2excel from 'js-table2excel'
- import Sortable from 'sortablejs'
- import { storeToRefs } from 'pinia'
- import { useThemeConfig } from '/@/stores/themeConfig'
- import '/@/theme/tableTool.scss'
- // 定义父组件传过来的值
- const props = defineProps({
- // 列表内容
- data: {
- type: Array<EmptyObjectType>,
- default: () => [],
- },
- // 表头内容
- header: {
- type: Array<EmptyObjectType>,
- default: () => [],
- },
- // 配置项
- config: {
- type: Object,
- default: () => {},
- },
- // 打印标题
- printName: {
- type: String,
- default: () => '',
- },
- })
- // 定义子组件向父组件传值/事件
- const emit = defineEmits(['delRow', 'pageChange', 'sortHeader'])
- // 定义变量内容
- const toolSetRef = ref()
- const storesThemeConfig = useThemeConfig()
- const { themeConfig } = storeToRefs(storesThemeConfig)
- const state = reactive({
- page: {
- pageNum: 1,
- pageSize: 10,
- },
- selectlist: [] as EmptyObjectType[],
- checkListAll: true,
- checkListIndeterminate: false,
- })
- // 设置边框显示/隐藏
- const setBorder = computed(() => {
- return props.config.isBorder ? true : false
- })
- // 获取父组件 配置项(必传)
- const getConfig = computed(() => {
- return props.config
- })
- // 设置 tool header 数据
- const setHeader = computed(() => {
- return props.header.filter((v) => v.isCheck)
- })
- // tool 列显示全选改变时
- const onCheckAllChange = <T>(val: T) => {
- if (val) props.header.forEach((v) => (v.isCheck = true))
- else props.header.forEach((v) => (v.isCheck = false))
- state.checkListIndeterminate = false
- }
- // tool 列显示当前项改变时
- const onCheckChange = () => {
- const headers = props.header.filter((v) => v.isCheck).length
- state.checkListAll = headers === props.header.length
- state.checkListIndeterminate = headers > 0 && headers < props.header.length
- }
- // 表格多选改变时,用于导出
- const onSelectionChange = (val: EmptyObjectType[]) => {
- state.selectlist = val
- }
- // 删除当前项
- const onDelRow = (row: EmptyObjectType) => {
- emit('delRow', row)
- }
- // 分页改变
- const onHandleSizeChange = (val: number) => {
- state.page.pageSize = val
- emit('pageChange', state.page)
- }
- // 分页改变
- const onHandleCurrentChange = (val: number) => {
- state.page.pageNum = val
- emit('pageChange', state.page)
- }
- // 搜索时,分页还原成默认
- const pageReset = () => {
- state.page.pageNum = 1
- state.page.pageSize = 10
- emit('pageChange', state.page)
- }
- // 打印
- const onPrintTable = () => {
- // https://printjs.crabbly.com/#documentation
- // 自定义打印
- let tableTh = ''
- let tableTrTd = ''
- let tableTd: any = {}
- // 表头
- props.header.forEach((v) => {
- tableTh += `<th class="table-th">${v.title}</th>`
- })
- // 表格内容
- props.data.forEach((val, key) => {
- if (!tableTd[key]) tableTd[key] = []
- props.header.forEach((v) => {
- if (v.type === 'text') {
- tableTd[key].push(`<td class="table-th table-center">${val[v.key]}</td>`)
- } else if (v.type === 'image') {
- tableTd[key].push(`<td class="table-th table-center"><img src="${val[v.key]}" style="width:${v.width}px;height:${v.height}px;"/></td>`)
- }
- })
- tableTrTd += `<tr>${tableTd[key].join('')}</tr>`
- })
- // 打印
- printJs({
- printable: `<div style=display:flex;flex-direction:column;text-align:center><h3>${props.printName}</h3></div><table border=1 cellspacing=0><tr>${tableTh}${tableTrTd}</table>`,
- type: 'raw-html',
- css: ['//at.alicdn.com/t/c/font_2298093_rnp72ifj3ba.css', '//unpkg.com/element-plus/dist/index.css'],
- style: `@media print{.mb15{margin-bottom:15px;}.el-button--small i.iconfont{font-size: 12px !important;margin-right: 5px;}}; .table-th{word-break: break-all;white-space: pre-wrap;}.table-center{text-align: center;}`,
- })
- }
- // 导出
- const onImportTable = () => {
- if (state.selectlist.length <= 0) return ElMessage.warning('请先选择要导出的数据')
- table2excel(props.header, state.selectlist, `${themeConfig.value.globalTitle} ${new Date().toLocaleString()}`)
- }
- // 刷新
- const onRefreshTable = () => {
- emit('pageChange', state.page)
- }
- // 设置
- const onSetTable = () => {
- nextTick(() => {
- const sortable = Sortable.create(toolSetRef.value, {
- handle: '.handle',
- dataIdAttr: 'data-key',
- animation: 150,
- onEnd: () => {
- const headerList: EmptyObjectType[] = []
- sortable.toArray().forEach((val: string) => {
- props.header.forEach((v) => {
- if (v.key === val) headerList.push({ ...v })
- })
- })
- emit('sortHeader', headerList)
- },
- })
- })
- }
- // 暴露变量
- defineExpose({
- pageReset,
- })
- </script>
- <style scoped lang="scss">
- .table-container {
- display: flex;
- flex-direction: column;
- .el-table {
- flex: 1;
- }
- .table-footer {
- display: flex;
- .table-footer-tool {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: flex-end;
- i {
- margin-right: 10px;
- cursor: pointer;
- color: var(--el-text-color-regular);
- &:last-of-type {
- margin-right: 0;
- }
- }
- }
- }
- }
- </style>
|