forked from iod/cpnode-front
根据业务需要封装公共表格
This commit is contained in:
parent
0ffa5598ff
commit
6926fbd9b7
39
src/components/ProTable/ProTableColumn.vue
Normal file
39
src/components/ProTable/ProTableColumn.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<el-table-column v-bind="$attrs" class="ProTableColumn">
|
||||
<template #default="{ row, column, $index }">
|
||||
<slot
|
||||
:row="row"
|
||||
:column="column"
|
||||
:$index="$index"
|
||||
:pageIndex="calcPageIndex($index)"
|
||||
:deleteRow="deleteRow.bind(null, calcPageIndex($index))"
|
||||
:updatedRow="updatedRow.bind(null, calcPageIndex($index))"
|
||||
>
|
||||
{{ row[props?.prop] }}
|
||||
</slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, withDefaults, inject, Ref } from 'vue'
|
||||
import { DELETE_ROW, UPDATE_ROW, PAGINATION_INFO } from '~/constants/proTable'
|
||||
import { IPaginationInfo } from '~/interfaces/proTable'
|
||||
|
||||
interface Props {
|
||||
prop: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {})
|
||||
const paginationInfo = inject<Ref<IPaginationInfo>>(PAGINATION_INFO)
|
||||
const updatedRow = inject(UPDATE_ROW)
|
||||
const deleteRow = inject(DELETE_ROW)
|
||||
|
||||
// 根据分页信息动态计算当前数据
|
||||
const calcPageIndex = (index: number) => {
|
||||
const { currentPage, pageSize } = paginationInfo!.value
|
||||
return (currentPage - 1) * pageSize + index
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--<style scoped lang="scss"></style>-->
|
171
src/components/ProTable/index.vue
Normal file
171
src/components/ProTable/index.vue
Normal file
@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div v-loading="loading" class="pro-table">
|
||||
<div class="header">
|
||||
<div class="action-container">
|
||||
<el-button
|
||||
v-if="props.createText"
|
||||
type="primary"
|
||||
size="mini"
|
||||
:icon="Plus"
|
||||
@click="onCreate"
|
||||
>
|
||||
{{ props.createText }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="displayTableData" v-bind="$attrs">
|
||||
<slot></slot>
|
||||
</el-table>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<el-pagination
|
||||
v-if="paginationInfo.showPagination"
|
||||
class="pagination"
|
||||
:current-page="paginationInfo.currentPage"
|
||||
layout="total, jumper, prev, pager, next, sizes"
|
||||
:page-size="paginationInfo.pageSize"
|
||||
:total="paginationInfo.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</el-config-provider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, withDefaults, provide, ref } from 'vue'
|
||||
|
||||
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
|
||||
import {
|
||||
ITableDataItem,
|
||||
IPaginationInfo,
|
||||
IOnCreated,
|
||||
} from '~/interfaces/proTable'
|
||||
import {
|
||||
DELETE_ROW,
|
||||
UPDATE_ROW,
|
||||
PAGINATION_INFO,
|
||||
CREATE_ROW,
|
||||
} from '~/constants/proTable'
|
||||
import { HandleProTableData } from '~/tools/proTable'
|
||||
|
||||
interface Props {
|
||||
request: () => Promise<{ data: ITableDataItem[] }>
|
||||
// 分页
|
||||
showPagination?: boolean
|
||||
currentPage?: number
|
||||
pageSize?: number
|
||||
pageSizes?: number[]
|
||||
// header
|
||||
createText?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
pageSizes: () => [10, 20, 30, 40, 50, 100],
|
||||
showPagination: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['onCreated'])
|
||||
|
||||
// 分页
|
||||
const paginationInfo = ref<IPaginationInfo>({
|
||||
currentPage: props.currentPage,
|
||||
pageSize: props.pageSize,
|
||||
pageSizes: props.pageSizes,
|
||||
showPagination: props.showPagination,
|
||||
total: 0,
|
||||
})
|
||||
const handleSizeChange = (val: number) => {
|
||||
paginationInfo.value.pageSize = val
|
||||
updateTheView()
|
||||
}
|
||||
const handleCurrentChange = (val: number) => {
|
||||
paginationInfo.value.currentPage = val
|
||||
updateTheView()
|
||||
}
|
||||
// 数据获取
|
||||
const tableData = ref<ITableDataItem[]>([])
|
||||
const displayTableData = ref<ITableDataItem[]>(tableData.value)
|
||||
const loading = ref(false)
|
||||
async function getData() {
|
||||
try {
|
||||
loading.value = true
|
||||
const { data } = await props.request()
|
||||
tableData.value = data
|
||||
paginationInfo.value.total = tableData.value?.length
|
||||
updateTheView()
|
||||
loading.value = false
|
||||
} catch (e) {
|
||||
loading.value = false
|
||||
console.log((e as Error).message)
|
||||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
await getData()
|
||||
})
|
||||
|
||||
const updateTheView = () => {
|
||||
if (!paginationInfo.value?.showPagination) {
|
||||
displayTableData.value = tableData.value
|
||||
}
|
||||
const { currentPage, pageSize } = paginationInfo.value
|
||||
const pageStart: number = (currentPage - 1) * pageSize
|
||||
displayTableData.value = tableData.value.slice(
|
||||
pageStart,
|
||||
pageStart + pageSize,
|
||||
)
|
||||
paginationInfo.value.total = tableData.value.length
|
||||
}
|
||||
|
||||
const handleTable = computed(() => {
|
||||
return new HandleProTableData(updateTheView, tableData)
|
||||
})
|
||||
|
||||
const onCreate = () => {
|
||||
emit('onCreated', handleTable.value.createRow)
|
||||
}
|
||||
|
||||
// 暴漏给给父组件用的方法
|
||||
defineExpose({
|
||||
paginationInfo,
|
||||
...handleTable.value,
|
||||
})
|
||||
|
||||
// 发送给后代组件使用
|
||||
provide(PAGINATION_INFO, paginationInfo)
|
||||
provide(UPDATE_ROW, handleTable.value.updatedRow)
|
||||
provide(DELETE_ROW, handleTable.value.deleteRow)
|
||||
provide(CREATE_ROW, handleTable.value.createRow)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pro-table {
|
||||
width: 100%;
|
||||
|
||||
.header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 15px;
|
||||
$marginLeft: 15px;
|
||||
|
||||
::v-deep(.btn-prev) {
|
||||
margin-left: $marginLeft !important;
|
||||
}
|
||||
|
||||
::v-deep(.el-input) {
|
||||
margin-left: $marginLeft !important;
|
||||
}
|
||||
|
||||
::v-deep(.el-pager li) {
|
||||
margin: 0 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
8
src/constants/proTable.ts
Normal file
8
src/constants/proTable.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// 分页信息
|
||||
export const PAGINATION_INFO = 'PAGINATION_INFO'
|
||||
// 修改表格数据
|
||||
export const UPDATE_ROW = 'UPDATE_ROW'
|
||||
// 删除表格数据
|
||||
export const DELETE_ROW = 'DELETE_ROW'
|
||||
// 添加数据
|
||||
export const CREATE_ROW = 'CREATE_ROW'
|
33
src/interfaces/proTable.ts
Normal file
33
src/interfaces/proTable.ts
Normal file
@ -0,0 +1,33 @@
|
||||
export interface ITableDataItem {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export type IPaginationInfo = {
|
||||
showPagination: boolean
|
||||
currentPage: number
|
||||
pageSize: number
|
||||
pageSizes: number[]
|
||||
total: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @param record 创建的新数据
|
||||
*/
|
||||
export type ICreateRow = (record: ITableDataItem) => void
|
||||
/**
|
||||
* @param index 修改的索引(如果是由插槽获取的该数据则会自动绑定该数据,直接传入修改完后的新数据即可)
|
||||
* @param newRecord 修改完后的新数据
|
||||
*/
|
||||
export type IUpdateRow = (index: number, newRecord: ITableDataItem) => void
|
||||
/**
|
||||
* @param index 同修改的索引
|
||||
*/
|
||||
export type IDeleteRow = (index: number) => void
|
||||
|
||||
export type IOnCreated = (callback: ICreateRow) => void
|
||||
|
||||
export interface IHandleProTableData {
|
||||
createRow: ICreateRow
|
||||
updatedRow: IUpdateRow
|
||||
deleteRow: IDeleteRow
|
||||
}
|
@ -1,35 +1,71 @@
|
||||
<config>
|
||||
{
|
||||
"name": "index",
|
||||
"title": "首页"
|
||||
"name": "index",
|
||||
"title": "首页"
|
||||
}
|
||||
</config>
|
||||
|
||||
<template>
|
||||
<div style="padding: 32px">
|
||||
<h3>fes & 拉夫德鲁</h3>
|
||||
<h4>数据字典</h4>
|
||||
<div v-for="item in enumsGet('status')" :key="item.key">
|
||||
{{ item.value }}:{{ item.key }}
|
||||
</div>
|
||||
|
||||
<section>
|
||||
计数器
|
||||
<el-button @click="increment">click me:{{ count }}</el-button>
|
||||
</section>
|
||||
</div>
|
||||
<pro-table
|
||||
ref="proTableRef"
|
||||
:request="request"
|
||||
create-text="添加"
|
||||
@on-created="onCreated"
|
||||
>
|
||||
<pro-table-column prop="date" label="Date" width="180" />
|
||||
<pro-table-column prop="name" label="Name" width="180" />
|
||||
<pro-table-column prop="address" label="Address" />
|
||||
<pro-table-column prop="action">
|
||||
<template #default="{ deleteRow, updatedRow }">
|
||||
<el-button type="text" size="small" @click.prevent="deleteRow">
|
||||
Remove
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click.prevent="
|
||||
updatedRow({
|
||||
name: 'update',
|
||||
date: 'wiwiiw',
|
||||
address: 'rtrtrttrtr',
|
||||
})
|
||||
"
|
||||
>
|
||||
update
|
||||
</el-button>
|
||||
</template>
|
||||
</pro-table-column>
|
||||
</pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { enums } from '@fesjs/fes'
|
||||
import useCounterStore from '~/stores/counter'
|
||||
import ProTable from '~/components/ProTable'
|
||||
import ProTableColumn from '~/components/ProTable/ProTableColumn'
|
||||
|
||||
const store = useCounterStore()
|
||||
const count = computed(() => store.clicked)
|
||||
const proTableRef = ref(null)
|
||||
|
||||
const increment = () => {
|
||||
store.clicked++
|
||||
onMounted(() => {
|
||||
console.log({ ...proTableRef.value }, 'ppppp')
|
||||
console.log(proTableRef.value.paginationInfo)
|
||||
})
|
||||
|
||||
const request = () => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
data: new Array(10)
|
||||
.join(',')
|
||||
.split(',')
|
||||
.map((i, index) => ({
|
||||
date: `2016-05-03${index}`,
|
||||
name: `Tom${index}`,
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
})),
|
||||
})
|
||||
}, 1500)
|
||||
})
|
||||
}
|
||||
const onCreated = (callback) => {
|
||||
callback({ name: 'add', date: 'wiwiiw', address: 'rtrtrttrtr' })
|
||||
}
|
||||
|
||||
const enumsGet = enums.get
|
||||
</script>
|
||||
|
49
src/tools/proTable.ts
Normal file
49
src/tools/proTable.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { ITableDataItem, IHandleProTableData } from '~/interfaces/proTable'
|
||||
import { ElNotification } from 'element-plus/es'
|
||||
import { Ref } from 'vue'
|
||||
|
||||
export class HandleProTableData implements IHandleProTableData {
|
||||
updateTheView: () => void
|
||||
tableData: Ref<ITableDataItem[]>
|
||||
|
||||
constructor(updateTheView: () => void, tableData: Ref<ITableDataItem[]>) {
|
||||
this.updateTheView = updateTheView
|
||||
this.tableData = tableData
|
||||
}
|
||||
|
||||
handleError(e: unknown) {
|
||||
ElNotification({
|
||||
title: '提示',
|
||||
message: (e as Error).message,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
createRow = (record: ITableDataItem) => {
|
||||
try {
|
||||
this.tableData.value.push(record)
|
||||
this.updateTheView()
|
||||
} catch (e) {
|
||||
this.handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
updatedRow = (index: number, newRecord: ITableDataItem) => {
|
||||
try {
|
||||
this.tableData.value.splice(index, 1, newRecord)
|
||||
this.updateTheView()
|
||||
} catch (e) {
|
||||
this.handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
deleteRow = (index: number) => {
|
||||
try {
|
||||
this.tableData.value.splice(index, 1)
|
||||
this.updateTheView()
|
||||
} catch (e) {
|
||||
console.log(this)
|
||||
this.handleError(e)
|
||||
}
|
||||
}
|
||||
}
|
11
src/types/auto-imports.d.ts
vendored
11
src/types/auto-imports.d.ts
vendored
@ -9,9 +9,20 @@ declare global {
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const ElButton: typeof import('element-plus/es')['ElButton']
|
||||
const ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
const ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
const ElForm: typeof import('element-plus/es')['ElForm']
|
||||
const ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
const ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
const ElInput: typeof import('element-plus/es')['ElInput']
|
||||
const ElLoadingDirective: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
const ElOption: typeof import('element-plus/es')['ElOption']
|
||||
const ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
const ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
const ElTable: typeof import('element-plus/es')['ElTable']
|
||||
const ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
const ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
const ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const h: typeof import('vue')['h']
|
||||
|
14
src/types/components.d.ts
vendored
14
src/types/components.d.ts
vendored
@ -5,10 +5,24 @@
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
'IBi:questionCircle': typeof import('~icons/bi/question-circle')['default']
|
||||
Loading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
PageLoading: typeof import('./../components/PageLoading.vue')['default']
|
||||
ProTable: typeof import('./../components/ProTable/index.vue')['default']
|
||||
ProTableColumn: typeof import('./../components/ProTable/ProTableColumn.vue')['default']
|
||||
UserCenter: typeof import('./../components/UserCenter.vue')['default']
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user