更新表格组件,新增过滤功能,调整数据获取逻辑,优化界面布局
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# backend service base url, test environment
|
||||
VITE_SERVICE_BASE_URL=http://192.168.1.36:9527
|
||||
VITE_SERVICE_BASE_URL=http://192.168.1.36:9528
|
||||
|
||||
# other backend service base url, test environment
|
||||
VITE_OTHER_SERVICE_BASE_URL= `{}`
|
||||
|
||||
@@ -39,34 +39,32 @@ function refresh() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSpace :align="itemAlign" wrap justify="end" class="lt-sm:w-200px">
|
||||
<NSpace :align="itemAlign" justify="space-between" wrap class="lt-sm:w-200px">
|
||||
<slot name="prefix"></slot>
|
||||
<slot name="default">
|
||||
<NButton size="small" ghost type="primary" @click="add">
|
||||
<div class="space-x-5">
|
||||
<slot name="default">
|
||||
<NButton size="small" ghost type="primary" @click="add">
|
||||
<template #icon>
|
||||
<icon-ic-round-plus class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.add') }}
|
||||
</NButton>
|
||||
|
||||
<NButton size="small" ghost type="error" :disabled="disabledDelete" @click="batchDelete">
|
||||
<template #icon>
|
||||
<icon-ic-round-delete class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.batchDelete') }}
|
||||
</NButton>
|
||||
</slot>
|
||||
<NButton size="small" @click="refresh">
|
||||
<template #icon>
|
||||
<icon-ic-round-plus class="text-icon" />
|
||||
<icon-mdi-refresh class="text-icon" :class="{ 'animate-spin': loading }" />
|
||||
</template>
|
||||
{{ $t('common.add') }}
|
||||
{{ $t('common.refresh') }}
|
||||
</NButton>
|
||||
<NPopconfirm @positive-click="batchDelete">
|
||||
<template #trigger>
|
||||
<NButton size="small" ghost type="error" :disabled="disabledDelete">
|
||||
<template #icon>
|
||||
<icon-ic-round-delete class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.batchDelete') }}
|
||||
</NButton>
|
||||
</template>
|
||||
{{ $t('common.confirmDelete') }}
|
||||
</NPopconfirm>
|
||||
</slot>
|
||||
<NButton size="small" @click="refresh">
|
||||
<template #icon>
|
||||
<icon-mdi-refresh class="text-icon" :class="{ 'animate-spin': loading }" />
|
||||
</template>
|
||||
{{ $t('common.refresh') }}
|
||||
</NButton>
|
||||
<TableColumnSetting v-model:columns="columns" />
|
||||
<TableColumnSetting v-model:columns="columns" />
|
||||
</div>
|
||||
<slot name="suffix"></slot>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Component, VNode } from 'vue';
|
||||
import { h } from 'vue';
|
||||
import type { ButtonProps, DataTableColumn, DataTableColumns } from 'naive-ui';
|
||||
import { NButton, NSpace } from 'naive-ui';
|
||||
@@ -24,12 +25,27 @@ export interface Pagination {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type TableFetchData = (page: Pagination) => ReturnType<typeof safeClient>;
|
||||
export type TableFetchData = (query: {
|
||||
pagination: Pagination;
|
||||
filter?: Record<string, any>;
|
||||
}) => ReturnType<typeof safeClient>;
|
||||
|
||||
export type TableFilterColumn = {
|
||||
key: string;
|
||||
title: string;
|
||||
component?: Component | VNode;
|
||||
componentProps?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type TableFilterColumns = Array<TableFilterColumn>;
|
||||
|
||||
export function transformColumns<T = InternalRowData>(columns: TableBaseColumns<T>): DataTableColumns<T> {
|
||||
return columns.map(col => {
|
||||
return {
|
||||
...col,
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
},
|
||||
render: col.operations
|
||||
? (row: T) =>
|
||||
h(NSpace, null, {
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import type { PaginationProps } from 'naive-ui';
|
||||
import { type TableBaseColumns, type TableFetchData, transformColumns, transformHeaderColumns } from '.';
|
||||
import { useRoute } from 'vue-router';
|
||||
import type { DataTableColumns, PaginationProps } from 'naive-ui';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {
|
||||
type TableBaseColumns,
|
||||
type TableFetchData,
|
||||
type TableFilterColumns,
|
||||
transformColumns,
|
||||
transformHeaderColumns
|
||||
} from '.';
|
||||
import type { TableColumnCheck } from '~/packages/hooks/src';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const title = t(route.meta.i18nKey as string);
|
||||
const props = defineProps<{
|
||||
fetchData: TableFetchData;
|
||||
columns: TableBaseColumns;
|
||||
showHeaderOperation?: boolean;
|
||||
filterColumns?: TableFilterColumns;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'add'): void;
|
||||
@@ -15,8 +28,8 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const tableData = ref<any[]>([]);
|
||||
const dataTableColumns = transformColumns(props.columns);
|
||||
const headerTableColumns = transformHeaderColumns(props.columns);
|
||||
const dataTableColumns = ref<DataTableColumns>(transformColumns(props.columns));
|
||||
const headerTableColumns = ref<NaiveUI.TableColumnCheck[]>(transformHeaderColumns(props.columns));
|
||||
const pagination = ref<PaginationProps>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
@@ -25,12 +38,15 @@ const pagination = ref<PaginationProps>({
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
async function loadData(query?: Record<string, any>) {
|
||||
const page = pagination.value.page || 1;
|
||||
const pageSize = pagination.value.pageSize || 10;
|
||||
const { data } = await props.fetchData({
|
||||
pageIndex: page,
|
||||
pageSize
|
||||
pagination: {
|
||||
pageIndex: page,
|
||||
pageSize
|
||||
},
|
||||
filter: query
|
||||
});
|
||||
|
||||
tableData.value = (data.value as any).data;
|
||||
@@ -45,6 +61,19 @@ function handlePageSizeChange(curPageSize: number) {
|
||||
pagination.value.pageSize = curPageSize;
|
||||
loadData();
|
||||
}
|
||||
function handleSearch(form: Record<string, any>) {
|
||||
pagination.value.page = 1;
|
||||
loadData(form);
|
||||
}
|
||||
function handleUpdateColumns(columns: TableColumnCheck[]) {
|
||||
headerTableColumns.value = columns;
|
||||
const sortKeys = columns.map(col => col.key);
|
||||
|
||||
const currentColumns: DataTableColumns = [...dataTableColumns.value];
|
||||
const sortedColumns = sortKeys.map(key => currentColumns.find(col => (col as any).key === key)).filter(Boolean);
|
||||
|
||||
dataTableColumns.value = sortedColumns as DataTableColumns;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
@@ -57,21 +86,32 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<div class="space-y-5">
|
||||
<TableHeaderOperation
|
||||
v-if="showHeaderOperation"
|
||||
:columns="headerTableColumns"
|
||||
@add="emit('add')"
|
||||
@refresh="emit('refresh')"
|
||||
@delete="emit('delete')"
|
||||
/>
|
||||
<NDataTable
|
||||
:columns="dataTableColumns"
|
||||
:data="tableData"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:on-update:page="handlePageChange"
|
||||
:on-update:page-size="handlePageSizeChange"
|
||||
/>
|
||||
<TableFilter :columns="filterColumns" @confirm="handleSearch" />
|
||||
|
||||
<div class="rounded-lg bg-white p-5 space-y-5">
|
||||
<TableHeaderOperation
|
||||
v-if="showHeaderOperation"
|
||||
:columns="headerTableColumns"
|
||||
@update:columns="handleUpdateColumns"
|
||||
@add="emit('add')"
|
||||
@refresh="loadData()"
|
||||
@delete="emit('delete')"
|
||||
>
|
||||
<template #prefix>
|
||||
<div class="text-lg font-bold">{{ title }}</div>
|
||||
</template>
|
||||
</TableHeaderOperation>
|
||||
|
||||
<NDataTable
|
||||
:scroll-x="2000"
|
||||
:columns="dataTableColumns"
|
||||
:data="tableData"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:on-update:page="handlePageChange"
|
||||
:on-update:page-size="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
47
src/components/table/table-filter.vue
Normal file
47
src/components/table/table-filter.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { NInput } from 'naive-ui';
|
||||
import type { TableFilterColumns } from '.';
|
||||
|
||||
defineProps<{
|
||||
columns?: TableFilterColumns;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'confirm', form: Record<string, any>): void;
|
||||
}>();
|
||||
|
||||
const form = ref<Record<string, any>>({});
|
||||
|
||||
function handleReset() {
|
||||
form.value = {};
|
||||
emit('confirm', form.value);
|
||||
}
|
||||
|
||||
function handleConfirm() {
|
||||
emit('confirm', form.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rounded-lg bg-white p-5">
|
||||
<div class="mb-5">搜索</div>
|
||||
|
||||
<NForm :label-width="80" label-align="left" label-placement="left">
|
||||
<NGrid x-gap="20" :cols="4">
|
||||
<NGi v-for="col in columns" :key="col.key">
|
||||
<NFormItem :label="col.title" :path="col.key">
|
||||
<component :is="col.component || NInput" v-model:value="form[col.key]" v-bind="col.componentProps" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<NSpace>
|
||||
<NButton type="primary" ghost @click="handleReset">重置</NButton>
|
||||
<NButton type="primary" @click="handleConfirm">搜索</NButton>
|
||||
</NSpace>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</NForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
||||
2
src/typings/components.d.ts
vendored
2
src/typings/components.d.ts
vendored
@@ -91,6 +91,7 @@ declare module 'vue' {
|
||||
SystemLogo: typeof import('./../components/common/system-logo.vue')['default']
|
||||
TableBase: typeof import('./../components/table/table-base.vue')['default']
|
||||
TableColumnSetting: typeof import('./../components/advanced/table-column-setting.vue')['default']
|
||||
TableFilter: typeof import('./../components/table/table-filter.vue')['default']
|
||||
TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
||||
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||
@@ -178,6 +179,7 @@ declare global {
|
||||
const SystemLogo: typeof import('./../components/common/system-logo.vue')['default']
|
||||
const TableBase: typeof import('./../components/table/table-base.vue')['default']
|
||||
const TableColumnSetting: typeof import('./../components/advanced/table-column-setting.vue')['default']
|
||||
const TableFilter: typeof import('./../components/table/table-filter.vue')['default']
|
||||
const TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
||||
const ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||
const WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import { h, ref, useTemplateRef } from 'vue';
|
||||
import { NInputNumber, useDialog, useMessage } from 'naive-ui';
|
||||
import { useTemplateRef } from 'vue';
|
||||
import { NDatePicker, useDialog, useMessage } from 'naive-ui';
|
||||
import { client, safeClient } from '@/service/api';
|
||||
import { DepositTypeEnum } from '@/enum';
|
||||
import type { TableBaseColumns, TableFetchData, TableInst } from '@/components/table';
|
||||
import type { TableBaseColumns, TableFetchData, TableFilterColumns, TableInst } from '@/components/table';
|
||||
|
||||
const dialog = useDialog();
|
||||
const message = useMessage();
|
||||
const tableInst = useTemplateRef<TableInst>('tableInst');
|
||||
|
||||
const fetchData: TableFetchData = () => {
|
||||
const fetchData: TableFetchData = ({ pagination, filter }) => {
|
||||
return safeClient(() =>
|
||||
client.api.admin.rwa.issuance.products.get({
|
||||
query: {}
|
||||
query: {
|
||||
...pagination,
|
||||
...filter
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -23,7 +25,7 @@ const columns: TableBaseColumns = [
|
||||
key: 'id'
|
||||
},
|
||||
{
|
||||
title: 'code',
|
||||
title: 'Code',
|
||||
key: 'code'
|
||||
},
|
||||
{
|
||||
@@ -31,64 +33,99 @@ const columns: TableBaseColumns = [
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'description',
|
||||
key: 'description'
|
||||
},
|
||||
{
|
||||
title: 'estimatedValue',
|
||||
title: '估值',
|
||||
key: 'estimatedValue'
|
||||
},
|
||||
{
|
||||
title: 'categoryId',
|
||||
title: '产品分类',
|
||||
key: 'categoryId'
|
||||
},
|
||||
{
|
||||
title: 'createdBy',
|
||||
title: '创建人',
|
||||
key: 'createdBy'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createdAt',
|
||||
render: (row: any) => {
|
||||
return new Date(row.createdAt).toLocaleDateString();
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status'
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
key: 'description'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
key: 'operation',
|
||||
width: 160,
|
||||
width: 230,
|
||||
operations: (row: any) => [
|
||||
{
|
||||
contentText: '编辑',
|
||||
type: 'primary',
|
||||
onClick: () => {
|
||||
contentText: '批准',
|
||||
size: 'small',
|
||||
onClick: async () => {
|
||||
safeClient(() =>
|
||||
client.api.admin.rwa.issuance.approve.post({
|
||||
productId: row.id as string
|
||||
})
|
||||
);
|
||||
tableInst.value?.reload();
|
||||
}
|
||||
},
|
||||
{
|
||||
contentText: '删除',
|
||||
type: 'error',
|
||||
ghost: true,
|
||||
contentText: '拒绝',
|
||||
size: 'small',
|
||||
onClick: async () => {
|
||||
dialog.create({
|
||||
title: '提示',
|
||||
positiveText: '是',
|
||||
negativeText: '否',
|
||||
content: '确认删除该银行信息?',
|
||||
onPositiveClick: async () => {
|
||||
safeClient(() =>
|
||||
client.api.admin.deposit.reject({ orderId: row.id as string }).post({
|
||||
reviewNote: '管理员拒绝充值'
|
||||
})
|
||||
);
|
||||
// tableInst.value?.reload();
|
||||
message.success('删除成功');
|
||||
}
|
||||
});
|
||||
safeClient(() =>
|
||||
client.api.admin.rwa.issuance.reject.post({
|
||||
productId: row.id as string,
|
||||
rejectionReason: '不符合要求'
|
||||
})
|
||||
);
|
||||
tableInst.value?.reload();
|
||||
}
|
||||
},
|
||||
{
|
||||
contentText: '编辑',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
tableInst.value?.reload();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const filterColumns: TableFilterColumns = [
|
||||
{
|
||||
title: '产品名称',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: '产品Code',
|
||||
key: 'Code'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createdAt',
|
||||
component: NDatePicker
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TableBase ref="tableInst" :columns="columns" :fetch-data="fetchData" />
|
||||
<TableBase
|
||||
ref="tableInst"
|
||||
show-header-operation
|
||||
:columns="columns"
|
||||
:filter-columns="filterColumns"
|
||||
:fetch-data="fetchData"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user