feat: 优化表格组件

master
luoer 2024-02-29 17:36:20 +08:00
parent 2a27f67b85
commit fd7b437102
13 changed files with 1114 additions and 38 deletions

View File

@ -0,0 +1,367 @@
import { AnForm, AnFormInstance, AnFormModal, AnFormModalInstance, AnFormModalProps, AnFormProps, getModel } from '@/components/AnForm';
import AnEmpty from '@/components/AnEmpty/AnEmpty.vue';
import { Button, PaginationProps, Table, TableColumnData, TableData, TableInstance } from '@arco-design/web-vue';
import { isArray, merge } from 'lodash-es';
import { InjectionKey, PropType, Ref, VNodeChild, defineComponent, ref } from 'vue';
type DataFn = (params: { page: number; size: number; [key: string]: any }) => any | Promise<TableData[] | { data: TableData[]; total: number }>;
export type ArcoTableProps = Omit<TableInstance['$props'], 'ref' | 'pagination' | 'loading' | 'data'>;
export const AnTableContextKey = Symbol('AnTableContextKey') as InjectionKey<AnTableContext>;
export type TableColumnRender = (data: { record: TableData; column: TableColumnData; rowIndex: number }) => VNodeChild;
export type ArcoTableSlots = {
/**
* th
*/
th?: (column: TableColumnData) => any;
/**
* thead
*/
thead?: () => any;
/**
*
*/
empty?: () => any;
/**
*
*/
'summary-cell'?: (column: TableColumnData, record: TableData, rowIndex: number) => any;
/**
*
*/
'pagination-right'?: () => any;
/**
*
*/
'pagination-left'?: () => any;
/**
* td
*/
td?: (column: TableColumnData, record: TableData, rowIndex: number) => any;
/**
* tr
*/
tr?: (column: TableColumnData, record: TableData, rowIndex: number) => any;
/**
* tbody
*/
tbody?: () => any;
/**
*
*/
'drag-handle-icon'?: () => any;
/**
*
*/
footer?: () => any;
/**
*
*/
'expand-row'?: () => any;
/**
*
*/
'expand-icon'?: () => any;
/**
* columns
*/
columns?: () => any;
};
/**
*
*/
export const AnTable = defineComponent({
name: 'AnTable',
props: {
/**
*
* @description `{ data, total }`
* @example
* ```ts
* async data(params) {
* const res = await api.xxx(params);
* const { data, total } = res;
* return { data, total }
* }
* ```
*/
data: {
type: [Array, Function] as PropType<TableData[] | DataFn>,
},
/**
*
* @example
* ```ts
* [
* {
* title: "名字",
* dataIndex: "name"
* }
* ]
* ```
*/
columns: {
type: Array as PropType<TableColumnData[]>,
default: () => [],
},
/**
*
* @example
* ```ts
* {
* showTotal: true
* }
* ```
*/
paging: {
type: Object as PropType<PaginationProps & { hide?: boolean }>,
},
/**
*
* @example
* ```ts
* [
* {
* label: "姓名关键字",
* setter: "input",
* }
* ]
* ```
*/
search: {
type: Object as PropType<AnFormProps>,
},
/**
*
*/
create: {
type: Object as PropType<AnFormModalProps>,
},
/**
*
*/
modify: {
type: Object as PropType<AnFormModalProps>,
},
/**
*
*/
actions: {
type: Array as PropType<any[]>,
},
/**
*
*/
widgets: {
type: Array as PropType<any[]>,
},
/**
* Table
*/
tableProps: {
type: Object as PropType<ArcoTableProps>,
},
/**
* Table
*/
tableSlots: {
type: Object as PropType<ArcoTableSlots>,
},
},
setup(props) {
const loading = ref(false);
const renderData = ref<TableData[]>([]);
const tableRef = ref<TableInstance | null>(null);
const searchRef = ref<AnFormInstance | null>(null);
const createRef = ref<AnFormModalInstance | null>(null);
const modifyRef = ref<AnFormModalInstance | null>(null);
const selected = ref<TableData[]>([]);
const selectedKeys = computed(() => selected.value.map(i => i[props.tableProps?.rowKey ?? 'id']));
const setPaging = (paging: PaginationProps) => {
if (props.paging) {
merge(props.paging, paging);
}
};
const resetPaging = () => {
setPaging({ current: 1, pageSize: 10 });
};
const loadData = async () => {
if (!props.data || Array.isArray(props.data)) {
return;
}
if (await searchRef.value?.validate()) {
return;
}
const page = props.paging?.current ?? 1;
const size = props.paging?.pageSize ?? 10;
const search = getModel(props.search?.model ?? {});
loading.value = true;
try {
const params = { ...search, page, size };
const resData = await props.data(params);
if (resData) {
let data: TableData[] = [];
let total = 0;
if (isArray(resData)) {
data = resData;
total = resData.length;
} else {
data = resData.data ?? [];
total = resData.total ?? 0;
}
renderData.value = data;
props.paging?.showTotal && (props.paging.total = total);
}
} catch (e) {
console.log('AnTable load fail: ', e);
}
loading.value = false;
};
const load = (page?: number, size?: number) => {
if (props.paging) {
page && (props.paging.current = page);
size && (props.paging.pageSize = size);
}
loadData();
};
const reload = () => load(1, 10);
watchEffect(() => {
if (Array.isArray(props.data)) {
renderData.value = props.data;
resetPaging();
}
});
onMounted(() => {
loadData();
});
return {
loading,
renderData,
selected,
selectedKeys,
tableRef,
searchRef,
createRef,
modifyRef,
load,
reload,
refresh: loadData,
};
},
render() {
return (
<div class="an-table table w-full">
{(this.create || this.actions || this.search || this.widgets) && (
<div class={`mb-3 flex gap-2 toolbar justify-between`}>
{this.create && <AnFormModal {...this.create} ref="createRef" onSubmited={this.reload}></AnFormModal>}
{this.actions && <div class={`flex-1 flex gap-2 items-center`}>{this.actions.map(action => action())}</div>}
{this.search && (
<div>
<AnForm ref="searchRef" v-model:model={this.search.model} items={this.search.items} formProps={this.search.formProps}></AnForm>
</div>
)}
{this.widgets && <div class="flex gap-2">{this.widgets.map(widget => widget())}</div>}
</div>
)}
<Table
row-key="id"
bordered={false}
{...this.tableProps}
ref="tableRef"
loading={this.loading}
pagination={this.paging?.hide ? false : this.paging}
data={this.renderData}
columns={this.columns}
>
{{
empty: () => <AnEmpty />,
...this.tableSlots,
}}
</Table>
{this.modify && <AnFormModal {...this.modify} trigger={false} ref="modifyRef" onSubmited={this.refresh}></AnFormModal>}
</div>
);
},
});
/**
*
*/
export type AnTableInstance = InstanceType<typeof AnTable>;
/**
*
*/
export type AnTableProps = Pick<AnTableInstance['$props'], 'data' | 'columns' | 'search' | 'paging' | 'create' | 'modify' | 'tableProps' | 'tableSlots' | 'actions' | 'widgets'>;
export interface AnTableContext {
/**
*
*/
loading: Ref<boolean>;
/**
*
*/
tableRef: Ref<TableInstance | null>;
/**
*
*/
searchRef: Ref<AnFormInstance | null>;
/**
*
*/
createRef: Ref<AnFormModalInstance | null>;
/**
*
*/
modifyRef: Ref<AnFormModalInstance | null>;
/**
*
*/
renderData: Ref<TableData[]>;
/**
*
*/
loadData: () => Promise<void>;
/**
*
*/
reload: () => Promise<void>;
/**
*
*/
refresh: () => Promise<void>;
/**
*
*/
props: AnTableProps;
onPageChange: any;
onPageSizeChange: any;
}

View File

@ -0,0 +1,5 @@
export * from './Table';
export * from './useTable';
export * from './useTableColumns';
export * from './useSearchForm';
export * from './useModiyForm';

View File

@ -0,0 +1,21 @@
import { FormModalUseOptions, useFormModalProps } from '@/components/AnForm';
export type UseCreateFormOptions = FormModalUseOptions & {};
export function useCreateForm(options: UseCreateFormOptions) {
if (options.width) {
if (!options.modalProps) {
(options as any).modalProps = {};
}
(options.modalProps as any).width = options.width;
delete options.width;
}
if (options.formClass) {
if (!options.formProps) {
(options as any).formProps = {};
}
options.formProps!.class = options.formClass;
delete options.formClass;
}
return useFormModalProps(options);
}

View File

@ -0,0 +1,88 @@
import { FormItem, FormModalUseOptions, useFormModalProps, AnFormModalProps } from '@/components/AnForm';
import { cloneDeep, merge } from 'lodash-es';
import { ExtendFormItem } from './useSearchForm';
import { TableUseOptions } from './useTable';
import { AnTableInstance } from './Table';
export type ModifyForm = Omit<FormModalUseOptions, 'items' | 'trigger'> & {
/**
*
* @default
* ```ts
* false
* ```
*/
extend?: boolean;
/**
*
* ```tsx
* [{
* extend: 'name', // 从 create.items 中继承
* }]
* ```
*/
items?: ExtendFormItem[];
};
export function useModifyForm(options: TableUseOptions, createModel: Recordable, tableRef: Ref<AnTableInstance | null>): AnFormModalProps | undefined {
const { create, modify, columns } = options;
if (!modify) {
return undefined;
}
for(const column of columns ?? []) {
if(column.type === 'button') {
const btn = column.buttons.find(i => i.type === 'modify')
if(!btn) {
column.buttons.unshift({
text: '修改',
type: 'modify',
onClick({ record }) {
tableRef.value?.modifyRef?.open(record);
}
})
}
break;
}
}
let result: FormModalUseOptions = { items: [], model: cloneDeep(createModel) };
if (modify.extend && create) {
result = merge({}, create);
}
result = merge(result, modify);
if (modify.items) {
const items: FormItem[] = [];
const createItemMap: Record<string, FormItem> = {};
for (const item of create?.items ?? []) {
createItemMap[item.field] = item;
}
for (let item of modify.items ?? []) {
if (item.extend) {
item = merge({}, createItemMap[item.field!] ?? {}, item);
}
items.push(item as any);
}
if (items.length) {
result.items = items;
}
}
if (modify.width || create?.width) {
if (!result.modalProps) {
(result as any).modalProps = {};
}
(result.modalProps as any).width = modify.width || create?.width;
}
if (modify.formClass || create?.formClass) {
if (!result.formProps) {
(result as any).formProps = {};
}
result.formProps!.class = modify.formClass || create?.formClass;
}
return useFormModalProps(result);
}

View File

@ -0,0 +1,114 @@
import { defaultsDeep, isArray, merge } from 'lodash-es';
import { AnFormProps, FormUseOptions, AnFormItemProps, FormItem, useFormItems } from '@/components/AnForm';
import { AnTableInstance, AnTableProps } from './Table';
export type ExtendFormItem = Partial<
FormItem & {
/**
*
* @example
* ```ts
* 'name'
* ```
*/
extend: string;
}
>;
export type SearchFormItem = ExtendFormItem & {
/**
*
* @description setter: 'search'
* @default
* ```ts
* false
* ```
*/
searchable?: boolean;
/**
*
* @default
* ```ts
* false
* ```
*/
enterable?: boolean;
};
export type SearchForm = Omit<FormUseOptions, 'items' | 'submit'> & {
/**
*
* @example
* ```ts
* [{
* extend: 'name' // 从 create.items 继承
* }]
* ```
*/
items?: SearchFormItem[];
/**
*
* @default
* ```tsx
* false
* ```
*/
hideSearch?: boolean;
};
export function useSearchForm(search: SearchForm | SearchFormItem[] | null, extendItems: AnFormItemProps[] = [], tableRef: Ref<AnTableInstance | null>): AnFormProps | undefined {
if (!search) {
return undefined;
}
if (isArray(search)) {
search = { items: search };
}
const { items: _items = [], hideSearch, model: _model, formProps: _formProps } = search;
const extendMap = extendItems.reduce((m, v) => ((m[v.field] = v), m), {} as Record<string, AnFormItemProps>);
const props = {
items: [] as AnFormItemProps[],
model: _model ?? {},
formProps: defaultsDeep({}, _formProps, { layout: 'inline' }),
};
const defualts: Partial<AnFormItemProps> = {
setter: 'input',
itemProps: {
hideLabel: true,
},
setterProps: {},
};
const items: AnFormItemProps[] = [];
for (const _item of _items) {
const { searchable, enterable, field, extend, ...itemRest } = _item;
if ((field || extend) === 'submit' && hideSearch) {
continue;
}
let item: AnFormItemProps = defaultsDeep({ field }, itemRest, defualts);
if (extend) {
const extendItem = extendMap[extend];
if (extendItem) {
item = merge({}, extendItem, itemRest);
}
}
if (item.setter === 'search') {
Object.assign(item.setterProps!, {
onSearch: () => tableRef.value?.reload(),
onPressEnter: () => tableRef.value?.reload(),
});
}
if (item.setterProps) {
(item.setterProps as any).placeholder = (item.setterProps as any).placeholder ?? item.label;
}
items.push(item);
}
props.items = useFormItems(items, props.model);
return props;
}

View File

@ -0,0 +1,184 @@
import { useFormModalProps } from '@/components/AnForm';
import { AnTable, AnTableInstance, AnTableProps } from './Table';
import { ModifyForm, useModifyForm } from './useModiyForm';
import { SearchForm, SearchFormItem, useSearchForm } from './useSearchForm';
import { TableColumn, useTableColumns } from './useTableColumns';
import { UseCreateFormOptions } from './useCreateForm';
import { Component, FunctionalComponent } from 'vue';
import { Button } from '@arco-design/web-vue';
import { isFunction } from 'lodash-es';
export interface TableUseOptions extends Pick<AnTableProps, 'data' | 'tableProps' | 'tableSlots' | 'paging' | 'actions' | 'widgets'> {
/**
* ID
* @example
* ```ts
* 'UserTable'
* ```
*/
id?: string;
/**
*
* @example
* ```ts
* [{
* dataIndex: 'title',
* title: '标题'
* }]
* ```
*/
columns?: TableColumn[];
/**
*
* @example
* ```ts
* [{
* field: 'name',
* label: '用户名称',
* setter: 'input'
* }]
* ```
*/
search?: SearchFormItem[] | SearchForm;
/**
*
* @example
* ```ts
* {
* title: '添加用户',
* items: [],
* submit: (model) => {}
* }
* ```
*/
create?: UseCreateFormOptions;
/**
*
* @example
* ```ts
* {
* extend: true, // 基于新建弹窗扩展
* title: '修改用户',
* submit: (model) => {}
* }
* ```
*/
modify?: ModifyForm;
}
function useButtons(buttons: any[], tableRef: Ref<AnTableInstance | null>) {
const result: Component[] = [];
for (const button of buttons) {
if (button.render) {
result.push(button.render);
continue;
}
result.push(() => {
if (button.visible && !button.visible()) {
return null;
}
return (
<Button onClick={() => button.onClick?.()} disabled={button.disable?.()} {...button.buttonProps}>
{{
icon: button.icon ? () => <i class={button.icon}></i> : null,
default: button.text ? () => button.text : null,
}}
</Button>
);
});
}
return result;
}
export function useTableProps(options: TableUseOptions, tableRef: Ref<AnTableInstance | null>): AnTableProps {
const { data, tableProps = {}, tableSlots } = options;
const paging = { hide: false, showTotal: true, showPageSize: true, ...(options.paging ?? {}) };
const search = options.search && useSearchForm(options.search, [], tableRef);
const create = options.create && useFormModalProps(options.create);
const modify = options.modify && useModifyForm(options, create?.model ?? {}, tableRef);
const columns = useTableColumns(options.columns ?? [], tableRef);
const actions = options.actions && useButtons(options.actions, tableRef);
const widgets = options.widgets && useButtons(options.widgets, tableRef);
const onPageChange = tableProps.onPageChange;
const onPageSizeChange = tableProps.onPageSizeChange;
tableProps.onPageChange = (page: number) => {
onPageChange?.(page);
tableRef.value?.load(page);
};
tableProps.onPageSizeChange = (size: number) => {
onPageSizeChange?.(size);
tableRef.value?.load(1, size);
};
const props = {
tableProps,
tableSlots,
columns,
data,
search,
paging,
create,
modify,
actions,
widgets,
};
return props;
}
/**
*
* @param options
* @example
* ```html
* <template>
* <UserTable></UserTable>
* </template>
* <script lang="ts" setup>
* const UserTable = useTable({
* data() {
* },
* columns: []
* })
* <script>
* ```
*/
export function useTable(options: TableUseOptions | ((tableRef: Ref<AnTableInstance | null>) => TableUseOptions)) {
const tableRef = ref<AnTableInstance | null>(null);
const refresh = () => tableRef.value?.refresh();
const reload = () => tableRef.value?.reload();
const option: TableUseOptions = isFunction(options) ? options(tableRef) : options;
const rawProps = useTableProps(option, tableRef);
const props = reactive(rawProps);
const component = {
name: 'AnTableWrapper',
tableRef,
refresh,
reload,
render() {
return (
<AnTable
ref={el => (tableRef.value = el)}
data={props.data}
columns={props.columns}
paging={props.paging}
search={props.search}
create={props.create}
modify={props.modify}
actions={props.actions}
widgets={props.widgets}
tableProps={props.tableProps}
tableSlots={props.tableSlots}
></AnTable>
);
},
};
return component;
}

View File

@ -0,0 +1,216 @@
import { delConfirm, delOptions } from '@/utils';
import { Divider, Link, Message, TableColumnData } from '@arco-design/web-vue';
import { defaultsDeep } from 'lodash-es';
import { AnTableInstance } from './Table';
interface TableBaseColumn {
/**
*
* @example
* ```tsx
* 'delete'
* ```
*/
type?: undefined;
}
interface TableIndexColumn {
/**
*
*/
type: 'index';
}
interface TableColumnButton {
/**
*
* @example
* ```ts
* 'delete'
* ```
*/
type?: 'modify' | 'delete';
/**
*
* @example
* ```ts
* '确定删除吗?'
* ```
*/
confirm?: string;
/**
*
* @example
* ```ts
* '修改'
* ```
*/
text?: string;
/**
*
* @see ALink
*/
buttonProps?: Recordable;
icon?: string;
/**
*
* @example
* ```ts
* (props) => props.record.status === 1
* ```
*/
visible?: (args: Recordable) => boolean;
/**
*
* @example
* ```ts
* (props) => props.record.status === 1
* ```
*/
disable?: (args: Recordable) => boolean;
/**
*
* @example
* ```ts
* (props) => api.user.rmUser(props.record.id)
* ```
*/
onClick?: (props: any) => any | Promise<any>;
}
interface TableButtonColumn {
/**
*
*/
type: 'button';
/**
*
* @example
* ```ts
* [{
* type: 'delete',
* text: '删除',
* onClick: (args) => api.user.rmUser(args.record.id)
* }]
* ```
*/
buttons: TableColumnButton[];
}
interface TableDropdownColumn {
/**
*
*/
type: 'dropdown';
/**
*
*/
dropdowns: any[];
}
export type TableColumn = TableColumnData &
(TableIndexColumn | TableBaseColumn | TableButtonColumn | TableDropdownColumn) & {
/**
*
* @example
* ```ts
* true
* ```
*/
configable?: boolean;
};
function useRowDelete(btn: TableColumnButton, tableRef: Ref<AnTableInstance | null>) {
const onClick = btn.onClick;
let confirm = btn.confirm ?? {};
if (typeof confirm === 'string') {
confirm = { content: confirm };
}
defaultsDeep(btn, {
buttonProps: {
status: 'danger',
},
});
btn.onClick = async props => {
delConfirm({
...delOptions,
...confirm,
async onBeforeOk() {
const res: any = await onClick?.(props);
const msg = res?.data?.message;
msg && Message.success(`提示: ${msg}`);
tableRef.value?.refresh();
},
});
};
return btn;
}
function useRowModify(btn: TableColumnButton, tableRef: Ref<AnTableInstance | null>) {
const onClick = btn.onClick;
btn.onClick = async props => {
const data = (await onClick?.(props)) ?? props.record;
tableRef.value?.modifyRef?.open(data);
};
return btn;
}
export function useTableColumns(data: TableColumn[], tableRef: Ref<AnTableInstance | null>) {
const columns: TableColumnData[] = [];
for (let column of data) {
// if (column.type === "index") {
// column = useTableIndexColumn(column);
// }
if (column.type === 'button') {
column = useTableButtonColumn(column, tableRef);
}
// if (column.type === "dropdown") {
// column = useTableDropdownColumn(column);
// }
columns.push(column);
}
return columns;
}
function useTableIndexColumn() {}
function useTableButtonColumn(column: TableButtonColumn & TableColumnData, tableRef: Ref<AnTableInstance | null>) {
const items: TableColumnButton[] = [];
defaultsDeep(column, { align: 'right' });
for (let button of column.buttons) {
if (button.type === 'delete') {
button = useRowDelete(button, tableRef);
}
if (button.type === 'modify') {
button = useRowModify(button, tableRef);
}
items.push(button);
}
column.render = props => {
return items.map((item, index) => {
if (item.visible && !item.visible(props)) {
return null;
}
return (
<>
{index !== 0 && <Divider direction="vertical" margin={4} />}
<Link {...item.buttonProps} disabled={item.disable?.(props)} onClick={() => item.onClick?.(props)}>
{item.text}
</Link>
</>
);
});
};
return column;
}
function useTableDropdownColumn() {}

View File

@ -3,15 +3,21 @@ import 'nprogress/nprogress.css';
import './nprogress.css';
import { App } from 'vue';
declare module 'nprogress' {
interface NProgress {
install: (app: App) => void;
}
}
/**
* VUE
*/
NProgress.install = function (app: App) {
NProgress.configure({
showSpinner: false,
trickleSpeed: 200,
minimum: 0.3,
});
/**
* VUE
*/
NProgress.install = function (app: App) {};
};
export { NProgress };

View File

@ -1,8 +0,0 @@
import 'nprogress';
import { App } from 'vue';
declare module 'nprogress' {
interface NProgress {
install: (app: App) => void;
}
}

View File

@ -2,18 +2,12 @@
<a-layout class="layout">
<a-layout-header class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700">
<div class="h-13 flex items-center">
<!-- <a-button size="small" @click="isCollapsed = !isCollapsed">
<template #icon>
<i class="icon-park-outline-hamburger-button text-base"></i>
</template>
</a-button> -->
<router-link to="/" class="px-2 flex items-center gap-2 text-slate-700">
<img src="/favicon.ico" alt="" width="24" height="24" class="" />
<h1 class="relative text-[18px] leading-[22px] dark:text-white m-0 p-0 font-normal">
{{ appStore.title }}
<span class="absolute -right-10 -top-1 font-normal text-xs text-gray-400"> v0.0.1 </span>
</h1>
<!-- <span class="text-gray-400">{{ appStore.subtitle }}</span> -->
</router-link>
</div>
<div class="flex items-center gap-2">

View File

@ -12,18 +12,14 @@
</div>
</div>
<div class="flex items-center justify-center w-full overflow-hidden">
<div
class="login-box w-[960px] h-[560px] relative mx-4 grid md:grid-cols-2 rounded-lg overflow-hidden border border-blue-100"
>
<div
class="login-left relative hidden md:block w-full h-full overflow-hidden bg-[rgb(var(--primary-6))] px-4"
></div>
<div class="login-box w-[960px] h-[560px] relative mx-4 grid md:grid-cols-2 rounded-lg overflow-hidden border border-blue-100">
<div class="login-left relative hidden md:block w-full h-full overflow-hidden bg-[rgb(var(--primary-6))] px-4"></div>
<div class="relative p-20 px-8 md:px-14 bg-white shadow-sm">
<div class="text-xl text-brand-500 font-semibold">用户登陆</div>
<div class="text-gray-500 mt-2.5">{{ meridiem }}欢迎访问 {{ appStore.title }} 系统!</div>
<a-form ref="formRef" :model="model" :rules="formRules" layout="vertical" class="mt-6">
<a-form-item field="username" label="账号" :disabled="loading" hide-asterisk>
<a-input v-model="model.username" placeholder="请输入账号/手机号/邮箱" allow-clear>
<a-input v-model="model.username" placeholder="请输入账号" allow-clear>
<template #prefix>
<i class="icon-park-outline-user" />
</template>
@ -77,7 +73,7 @@ const formRules: Record<string, FieldRule[]> = {
username: [
{
required: true,
message: '请输入账号/手机号/邮箱',
message: '请输入账号',
},
],
password: [
@ -101,8 +97,8 @@ const onSubmitForm = async () => {
if (await formRef.value?.validate()) {
return;
}
try {
loading.value = true;
try {
const res = await api.auth.login(model);
userStore.setAccessToken(res.data.data);
Notification.success({
@ -113,9 +109,8 @@ const onSubmitForm = async () => {
} catch (error: any) {
const message = error?.response?.data?.message;
message && Message.warning(`提示:${message}`);
} finally {
loading.value = false;
}
loading.value = false;
};
</script>

View File

@ -1,6 +1,9 @@
<template>
<div class="w-full p-5 flex gap-4">
<div class="flex-1">
<div class="bg-white p-4">
<UserTable></UserTable>
</div>
<div class="bg-white px-5 py-4 rounded-sm">
<div>统计概览</div>
<div class="flex justify-between gap-4 mt-4">
@ -31,9 +34,7 @@
<i class="icon-park-outline-delete text-xs"></i>
</div>
</div>
<div
class="py-3 px-3 border border-dashed rounded-sm border-gray-400 text-gray-500 hover:bg-gray-100 cursor-pointer"
>
<div class="py-3 px-3 border border-dashed rounded-sm border-gray-400 text-gray-500 hover:bg-gray-100 cursor-pointer">
<i class="icon-park-outline-add ml-2"></i>
添加服务1
</div>
@ -59,9 +60,7 @@
<ul class="list-none w-full m-0 p-0">
<li v-for="i in 8" class="w-full h-6 items-center overflow-hidden justify-between flex gap-2 mb-2">
<a-tag>{{ i }}</a-tag>
<span class="flex-1 truncate hover:underline underline-offset-2 hover:text-brand-500 cursor-pointer">
但是预测已加载的数据不足以
</span>
<span class="flex-1 truncate hover:underline underline-offset-2 hover:text-brand-500 cursor-pointer"> 但是预测已加载的数据不足以 </span>
<span class="text-gray-400">3天前</span>
</li>
</ul>
@ -71,8 +70,102 @@
</div>
</template>
<script setup lang="ts">
<script setup lang="tsx">
import { useUserStore } from '@/store/user';
import { useTable } from '@/components/AnTable.1';
const UserTable = useTable(instance => {
return {
data: async params => {
await new Promise(res => setTimeout(res, 2000));
return {
data: Array.from({ length: params.size }, (_, i) => ({
id: i + 1,
name: Math.random(),
})),
total: 200,
};
},
columns: [
{
title: '名字',
dataIndex: 'name',
},
{
title: '操作',
type: 'button',
width: 200,
align: 'right',
buttons: [
{
text: '测试',
onClick() {
console.log(instance);
instance.value?.refresh();
},
},
{
type: 'delete',
text: '删除',
onClick(props) {
instance.value?.renderData.splice(props.rowIndex, 1);
},
},
],
},
],
search: [
{
field: 'name',
label: '请输入名字',
setter: 'search',
},
],
actions: [
{
text: '测试',
icon: 'icon-park-outline-refresh',
disable: () => Boolean(instance.value?.search?.model?.name),
},
],
widgets: [
{
// text: '',
icon: 'icon-park-outline-refresh',
},
],
tableProps: {
rowSelection: {
showCheckedAll: true,
},
onSelect(rowKeys, rowKey, record) {
console.log(rowKeys, rowKey, record);
},
},
tableSlots: {
// 'pagination-left': () => {
// return <a-button>1</a-button>;
// },
// 'pagination-right': () => {
// return <a-button>1</a-button>;
// },
},
create: {
items: [
{
field: 'name',
label: '名字',
setter: 'input',
},
],
submit: () => {},
},
modify: {
extend: true,
title: '修改',
},
};
});
const userStore = useUserStore();

View File

@ -65,6 +65,7 @@ const { component: UserTable } = useTable({
title: '操作',
type: 'button',
width: 200,
align: 'right',
buttons: [
{
text: '重置密码',