You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.0 KiB
145 lines
3.0 KiB
<script setup lang="ts">
|
|
import type { VNode } from 'vue'
|
|
import { onMounted, reactive } from 'vue'
|
|
|
|
import Expand from './expand'
|
|
|
|
defineOptions({
|
|
name: 'FtTable',
|
|
})
|
|
const props = withDefaults(defineProps<TableProp>(), {
|
|
columns: () => [],
|
|
mustInit: true,
|
|
hasHeader: true,
|
|
getDataFn: () => Promise.resolve([]),
|
|
})
|
|
const emits = defineEmits([])
|
|
enum ColumnType {
|
|
index = 'index',
|
|
selection = 'selection',
|
|
expand = 'expand',
|
|
}
|
|
interface TableColumn {
|
|
title: string
|
|
key: string
|
|
type?: ColumnType
|
|
width?: number // 列宽
|
|
fixed?: 'left' | 'right' | undefined // 是否固定列
|
|
render?: (row: any) => VNode // 内容自定义
|
|
}
|
|
|
|
interface Btn {
|
|
name: string
|
|
icon?: string
|
|
type?: string
|
|
serverUrl: string
|
|
}
|
|
|
|
interface TableProp {
|
|
columns: TableColumn[]
|
|
getDataFn: (params: any) => Promise<any> // 表格数据的接口
|
|
mustInit?: boolean // 是否在mounted里执行getDataFn
|
|
hasHeader?: boolean
|
|
btnList?: Btn[]
|
|
}
|
|
|
|
// const attrs = useAttrs()
|
|
|
|
async function methodParent(fn: any) {
|
|
const newFn = fn[0] === '/' ? fn.slice(1) : fn
|
|
emits(newFn as never)
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (props.mustInit) {
|
|
initData()
|
|
}
|
|
})
|
|
|
|
const state = reactive({
|
|
loading: false,
|
|
dataTotal: 0,
|
|
tableData: [],
|
|
})
|
|
|
|
function initData() {
|
|
state.loading = true
|
|
props
|
|
.getDataFn({})
|
|
.then((data) => {
|
|
console.log(data)
|
|
state.tableData = data
|
|
state.loading = false
|
|
})
|
|
.finally(() => {
|
|
state.loading = false
|
|
})
|
|
}
|
|
defineExpose({
|
|
initData,
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="hasHeader" class="header">
|
|
<div v-for="btn in btnList" :key="btn.serverUrl">
|
|
<el-button :icon="btn.icon" :type="btn.type" @click="methodParent(btn.serverUrl)">
|
|
{{ btn.name }}
|
|
</el-button>
|
|
</div>
|
|
<div class="search">
|
|
<el-input v-prevent-keyboard>
|
|
<template #suffix>
|
|
<el-icon class="el-input__icon">
|
|
<search />
|
|
</el-icon>
|
|
</template>
|
|
</el-input>
|
|
</div>
|
|
</div>
|
|
<el-table
|
|
v-loading="state.loading"
|
|
:="$attrs"
|
|
:data="state.tableData"
|
|
style="width: 100%"
|
|
height="100%"
|
|
:highlight-current-row="true"
|
|
class="container-table"
|
|
header-row-class-name="header-row-class"
|
|
>
|
|
<template v-for="(column, index) in columns" :key="column.key">
|
|
<el-table-column
|
|
show-overflow-tooltip
|
|
:prop="column.key"
|
|
:label="column.title"
|
|
:width="column.width"
|
|
:type="column.type"
|
|
:fixed="column.fixed"
|
|
>
|
|
<template v-if="column.render" #default="scope">
|
|
<Expand :column="column" :row="scope.row" :render="column.render" :index="index" />
|
|
</template>
|
|
</el-table-column>
|
|
</template>
|
|
</el-table>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
height: 40px;
|
|
.search {
|
|
width: 200px;
|
|
}
|
|
}
|
|
:deep(.header-row-class) {
|
|
th {
|
|
background-color: rgba(0,0,0,0.02 ) !important;
|
|
//font-weight: 500;
|
|
//border-bottom: none;
|
|
color: rgba(0, 0, 0, 0.85)
|
|
}
|
|
}
|
|
</style>
|