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
|
||||||
|
}
|
@ -6,30 +6,66 @@
|
|||||||
</config>
|
</config>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="padding: 32px">
|
<pro-table
|
||||||
<h3>fes & 拉夫德鲁</h3>
|
ref="proTableRef"
|
||||||
<h4>数据字典</h4>
|
:request="request"
|
||||||
<div v-for="item in enumsGet('status')" :key="item.key">
|
create-text="添加"
|
||||||
{{ item.value }}:{{ item.key }}
|
@on-created="onCreated"
|
||||||
</div>
|
>
|
||||||
|
<pro-table-column prop="date" label="Date" width="180" />
|
||||||
<section>
|
<pro-table-column prop="name" label="Name" width="180" />
|
||||||
计数器
|
<pro-table-column prop="address" label="Address" />
|
||||||
<el-button @click="increment">click me:{{ count }}</el-button>
|
<pro-table-column prop="action">
|
||||||
</section>
|
<template #default="{ deleteRow, updatedRow }">
|
||||||
</div>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { enums } from '@fesjs/fes'
|
import ProTable from '~/components/ProTable'
|
||||||
import useCounterStore from '~/stores/counter'
|
import ProTableColumn from '~/components/ProTable/ProTableColumn'
|
||||||
|
|
||||||
const store = useCounterStore()
|
const proTableRef = ref(null)
|
||||||
const count = computed(() => store.clicked)
|
|
||||||
|
|
||||||
const increment = () => {
|
onMounted(() => {
|
||||||
store.clicked++
|
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>
|
</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 EffectScope: typeof import('vue')['EffectScope']
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
const ElButton: typeof import('element-plus/es')['ElButton']
|
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 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 ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
const ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
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 getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
const h: typeof import('vue')['h']
|
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' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
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']
|
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']
|
ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
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']
|
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']
|
UserCenter: typeof import('./../components/UserCenter.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user