feat: 表格行增加下拉菜单功能
parent
8a2b29ef01
commit
0c9e19dc10
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="mt-4 mx-5">
|
||||
<div class="mt-3 mx-5">
|
||||
<a-breadcrumb>
|
||||
<a-breadcrumb-item>
|
||||
<i class="icon-park-outline-all-application text-gray-400"></i>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div>
|
||||
<BreadCrumb></BreadCrumb>
|
||||
<slot name="content">
|
||||
<div class="mx-4 mt-4 p-4 bg-white">
|
||||
<div class="mx-4 mt-3 p-4 bg-white">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</slot>
|
||||
|
|
|
|||
|
|
@ -48,19 +48,19 @@ const form = useForm({
|
|||
| formProps | 传递给`AForm`组件的参数(可选),具体可参考`Arco-Design`的`Form`组件,部分参数不可用,如`model`等。 | `FormInstance['$props']` |
|
||||
|
||||
### 表单数据
|
||||
`model`表示当前表单的数据,当使用`useForm`时,将从`items`中每一项的`field`和`initialValue`生成。如果`model`中的属性与`field`值同名,且`initialValue`值不为空,则原`model`中的同名属性值将被覆盖。
|
||||
`model`表示当前表单的数据,可为空。当使用`useForm`时,将从`items`中每一项的`field`和`initialValue`生成。如果`model`中的属性与`field`值同名,且`initialValue`值不为空,则原`model`中的同名属性值将被覆盖。
|
||||
|
||||
对于日期范围框、级联选择器等值为数组的组件,提供有一份便捷的语法,请看如下示例:
|
||||
```typescript
|
||||
const form = useForm({
|
||||
items: [
|
||||
{
|
||||
field: `startDate:endDate`,
|
||||
field: `[startDate, endDate]`,
|
||||
label: '日期范围',
|
||||
type: 'dateRange',
|
||||
},
|
||||
{
|
||||
field: 'provice:city:town',
|
||||
field: '[provice: number, city: number, town: number]',
|
||||
label: '省市区',
|
||||
type: 'cascader',
|
||||
options: []
|
||||
|
|
@ -68,14 +68,14 @@ const form = useForm({
|
|||
]
|
||||
})
|
||||
```
|
||||
以上,`field`可通过`:`分隔的语法,指定提交表单时,将数组值划分到指定的属性上,最终提交的数据如下:
|
||||
以上,`field` 使用的是类似Typescript元组的写法,类型目前支持 number 和 boolean,在提交时将得到如下数据:
|
||||
```typescript
|
||||
{
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
province: '',
|
||||
city: '',
|
||||
town: ''
|
||||
startDate: '2023',
|
||||
endDate: '2024',
|
||||
province: 1,
|
||||
city: 2,
|
||||
town: 3
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -229,9 +229,9 @@ const form = useForm({
|
|||
|
||||
### 常见问题
|
||||
- Q:为什么不是模板形式?
|
||||
- A:配置式更易于描述逻辑,模板介入和引入的组件比较多,且对于做typescript类型提示不是很方便。
|
||||
- A:状态驱动,配置式更易于描述逻辑,模板介入和引入的组件比较多,且对于做typescript类型提示不是很方便。
|
||||
- Q:为什么不是JSON形式?
|
||||
- A:对于自定义组件支持、联动等不是非常友好,尽管可以通过解析字符串执行等方式实现,对typescript提示也不是很友好。
|
||||
- A:对于自定义组件支持、联动等不是非常友好,尽管可以通过解析字符串执行等方式实现,对typescript提示不是很友好。
|
||||
|
||||
### 最后
|
||||
尽管看起来是低代码,但其实我更倾向于是业务组件。
|
||||
|
|
@ -79,18 +79,17 @@ export const FormItem = (props: any, { emit }: any) => {
|
|||
|
||||
type FormItemBase = {
|
||||
/**
|
||||
* 字段名,用于表单数据、表单校验和输入框值绑定,支持特殊语法。
|
||||
* 字段名,用于表单、校验和输入框绑定,支持特殊语法。
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 1. 以:分隔的字段名,将用作数组值解构。例如:
|
||||
* {
|
||||
* field: 'v1:v2',
|
||||
* field: '[v1,v2]',
|
||||
* type: 'dateRange',
|
||||
* }
|
||||
* // 将得到
|
||||
* {
|
||||
* v1: '2021-01-01',
|
||||
* v2: '2021-01-02',
|
||||
* v1: '2021',
|
||||
* v2: '2021',
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ const table = useTable({
|
|||
username: '用户A'
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
total: 30
|
||||
}
|
||||
total: 30
|
||||
};
|
||||
},
|
||||
columns: [
|
||||
|
|
@ -32,13 +30,12 @@ const table = useTable({
|
|||
search: {
|
||||
items: [
|
||||
{
|
||||
field: "username",
|
||||
label: "用户名称",
|
||||
type: "input",
|
||||
extend: "username",
|
||||
},
|
||||
],
|
||||
},
|
||||
common: {
|
||||
create: {
|
||||
title: "新建用户",
|
||||
items: [
|
||||
{
|
||||
field: "username",
|
||||
|
|
@ -46,15 +43,13 @@ const table = useTable({
|
|||
type: "input",
|
||||
},
|
||||
],
|
||||
},
|
||||
create: {
|
||||
title: "新建用户",
|
||||
submit: async ({ model }) => {
|
||||
return api.xx(model);
|
||||
},
|
||||
},
|
||||
modify: {
|
||||
title: "修改用户",
|
||||
extend: true,
|
||||
submit: async ({ model }) => {
|
||||
return api.xx(model);
|
||||
},
|
||||
|
|
@ -62,10 +57,11 @@ const table = useTable({
|
|||
});
|
||||
</script>
|
||||
```
|
||||
以上,就是一个CRUD表格的简单用法。参数描述:
|
||||
|
||||
以上,就是一个 CRUD 表格的简单用法。参数描述:
|
||||
| 参数 | 说明 | 类型 |
|
||||
| :--- | :--- | :--- |
|
||||
| data | 表格数据,可为数组或函数(发起HTTP请求) | BaseData[] | ((search, paging) => Promise<any>) |
|
||||
| data | 表格数据,可为数组或函数(发起 HTTP 请求) | BaseData[] | ((search, paging) => Promise<any>) |
|
||||
| columns | 表格列,参见 [TableColumnData](https://arco.design/vue/component/table#TableColumnData) 文档,增加和扩展部分属性,详见下文。 | TableColumnData[] |
|
||||
| pagination | 分页参数,参见 [Pagination](https://arco.design/vue/component/pagination) 文档,默认 15/每页。| Pagination |
|
||||
| search | 搜索表单的配置,参见 [Form]() 说明,其中 `submit` 参数不可用 | FormProps |
|
||||
|
|
@ -75,81 +71,91 @@ const table = useTable({
|
|||
| tableProps | 传递给`Table`组件的参数,参见 [Table](https://arco.design/vue/component/table) 文档,其中`columns`参数不可用。| TableProps |
|
||||
|
||||
### 表格数据
|
||||
|
||||
`data`定义表格数据,可以是数组或函数。
|
||||
|
||||
- 当是数组时,直接用作数据源。
|
||||
- 当是函数时,传入查询参数和分页参数,可返回数组或对象,返回数组作用同上,返回对象时需遵循`{ data: [], meta: { total: number } }`格式,用于分页处理。
|
||||
- 当是函数时,传入查询参数和分页参数,可返回数组或对象,返回数组作用同上,返回对象时需遵循`{ data: [], total: number }`格式,用于分页处理。
|
||||
|
||||
用法示例:
|
||||
|
||||
```typescript
|
||||
const table = useTable({
|
||||
data: async (search, paging) {
|
||||
const { page, size: pageSize } = paging
|
||||
const res = await api.xx({ ...search, page, pageSize });
|
||||
const res = await api.xx({ ...search, ...paging });
|
||||
return {
|
||||
data: res.data,
|
||||
meta: {
|
||||
total: res.total
|
||||
}
|
||||
total: res.total
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 表格列
|
||||
|
||||
`columns`定义表格列,并在原本基础上增加默认值并扩展部分属性。增加和扩展的属性如下:
|
||||
|
||||
| 参数 | 说明 | 类型 |
|
||||
| :--- | :--- | :--- |
|
||||
| type | 特殊类型, 目前支持`index`(表示行数)、`button`(行操作按钮) | 'index' | 'button' |
|
||||
| buttons | 当`type`为`button`时的按钮数组,如果子项是对象则为`Button`组件的参数,如果为函数则为自定义渲染函数。 | Button[]
|
||||
| 参数 | 说明 | 类型 |
|
||||
| :------ | :--------------------------------------------------------------------------------------------------- | :------- | -------- |
|
||||
| type | 特殊类型, 目前支持`index`(表示行数)、`button`(行操作按钮) | 'index' | 'button' |
|
||||
| buttons | 当`type`为`button`时的按钮数组,如果子项是对象则为`Button`组件的参数,如果为函数则为自定义渲染函数。 | Button[] |
|
||||
|
||||
### 表格分页
|
||||
|
||||
`pagination`定义分页行为,具体参数可参考 [Pagination](https://arco.design/vue/component/pagination) 文档。当`data`为数组时,将作为数据源进行分页;当`data`为函数且返回值为对象时,则根据`total`值进行分页。
|
||||
|
||||
### 搜索表单
|
||||
|
||||
参阅
|
||||
|
||||
### 公共参数
|
||||
|
||||
参数为`FormModal`的参数,主要作为新增和修改的公共参数。在大多数情况,新增和修改的配置大多是相似的,没必要写两份,把相同的参数写在这里即可,不同的参数在`create`和`modify`中单独配置。
|
||||
|
||||
注意,这里的`items`也可以被搜索表单复用,搜索表单可通过`extends: <field>`继承`common.items`中对应的字段配置。使用示例如下:
|
||||
|
||||
```typescript
|
||||
const table = useTable({
|
||||
common: {
|
||||
items: [
|
||||
{
|
||||
field: 'username',
|
||||
label: '用户名称',
|
||||
type: 'input',
|
||||
field: "username",
|
||||
label: "用户名称",
|
||||
type: "input",
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
search: {
|
||||
items: [
|
||||
{
|
||||
extend: 'usernam',
|
||||
extend: "usernam",
|
||||
required: false,
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 新增弹窗
|
||||
|
||||
`create`为新增表单弹窗的参数,即`useFormModal`对应的参数。参阅。当指定该参数时,会在表格左上添加新建按钮,如需自定义按钮样式或自定义渲染,可通过`create.trigger`参数配置。
|
||||
|
||||
### 修改弹窗
|
||||
|
||||
`modify`为新增表单弹窗的参数,即`useFormModal`对应的参数。参阅。当指定该参数时,会在表格行添加修改按钮。
|
||||
|
||||
### 表格参数
|
||||
|
||||
`tableProps`为传递给`Table`组件的额外参数,其中部分参数不可用,如`data`和`columns`等。此外,部分参数有默认值,具体参数可查看`src/components/table/table.config.ts`文件。
|
||||
|
||||
### 插槽
|
||||
|
||||
- `Table`组件的插槽可正常使用
|
||||
- `action`插槽用作表格左上方的操作区。
|
||||
|
||||
## 问题
|
||||
|
||||
- 问题:日期范围框值为数组,处理不方便
|
||||
- 解决:字段名使用`v1:v2`格式,提交时会生成`{ v1: '00:00:01', v2: '00:00:02' }`数据
|
||||
- 问题:搜索表单、新增表单和修改表单通常用到同一表单项,如何避免重复定义
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Button } from "@arco-design/web-vue";
|
|||
import { IconRefresh, IconSearch } from "@arco-design/web-vue/es/icon";
|
||||
|
||||
export const config = {
|
||||
searchInlineCount: 3,
|
||||
searchFormProps: {
|
||||
labelAlign: "left",
|
||||
autoLabelWidth: true,
|
||||
|
|
@ -18,7 +19,7 @@ export const config = {
|
|||
const tableRef = inject<any>("ref:table");
|
||||
return (
|
||||
<div class="w-full flex gap-x-2 justify-end">
|
||||
{(tableRef.search?.items?.length || 0) > 3 && (
|
||||
{(tableRef.search?.items?.length || 0) > config.searchInlineCount && (
|
||||
<Button disabled={tableRef?.loading.value} onClick={() => tableRef?.reloadData()}>
|
||||
{{ icon: () => <IconRefresh></IconRefresh>, default: () => "重置" }}
|
||||
</Button>
|
||||
|
|
@ -55,7 +56,7 @@ export const config = {
|
|||
columnButtonBase: {
|
||||
buttonProps: {
|
||||
// type: "text",
|
||||
size: "mini",
|
||||
// size: "mini",
|
||||
},
|
||||
},
|
||||
columnButtonDelete: {
|
||||
|
|
@ -65,6 +66,10 @@ export const config = {
|
|||
hideCancel: false,
|
||||
maskClosable: false,
|
||||
},
|
||||
columnDropdownModify: {
|
||||
text: "修改",
|
||||
icon: "icon-park-outline-edit",
|
||||
},
|
||||
getApiErrorMessage(error: any): string {
|
||||
const message = error?.response?.data?.message || error?.message || "请求失败";
|
||||
return message;
|
||||
|
|
|
|||
|
|
@ -76,9 +76,9 @@ export const Table = defineComponent({
|
|||
const createRef = ref<FormModalInstance>();
|
||||
const modifyRef = ref<FormModalInstance>();
|
||||
const renderData = ref<BaseData[]>([]);
|
||||
const inlined = computed(() => (props.search?.items?.length ?? 0) < 4);
|
||||
const inlined = computed(() => (props.search?.items?.length ?? 0) <= config.searchInlineCount);
|
||||
const reloadData = () => loadData({ current: 1, pageSize: 10 });
|
||||
const openModifyModal = (data: any) => modifyRef.value?.open(data.record);
|
||||
const openModifyModal = (data: any) => modifyRef.value?.open(data);
|
||||
|
||||
const loadData = async (pagination: Partial<any> = {}) => {
|
||||
const merged = { ...props.pagination, ...pagination };
|
||||
|
|
@ -185,7 +185,7 @@ export const Table = defineComponent({
|
|||
|
||||
<BaseTable
|
||||
row-key="id"
|
||||
bordered={false}
|
||||
bordered={true}
|
||||
{...this.tableProps}
|
||||
loading={this.loading}
|
||||
pagination={this.pagination}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Link, TableColumnData, TableData } from "@arco-design/web-vue";
|
||||
import { Doption, Link, TableColumnData, TableData } from "@arco-design/web-vue";
|
||||
import { FormModalProps, FormProps } from "../form";
|
||||
import { IFormItem } from "../form/form-item";
|
||||
import { TableProps } from "./table";
|
||||
import { RenderFunction } from "vue";
|
||||
|
||||
interface UseColumnRenderOptions {
|
||||
/**
|
||||
|
|
@ -46,15 +47,50 @@ export interface TableColumnButton {
|
|||
buttonProps?: Partial<Omit<InstanceType<typeof Link>["$props"], "onClick" | "disabled">>;
|
||||
}
|
||||
|
||||
interface TableColumnDropdown {
|
||||
/**
|
||||
* 特殊类型
|
||||
*/
|
||||
type?: "modify" | "delete";
|
||||
/**
|
||||
* 下拉菜单文本
|
||||
*/
|
||||
text?: string;
|
||||
/**
|
||||
* 下拉菜单图标
|
||||
*/
|
||||
icon?: string | RenderFunction;
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled?: (data: UseColumnRenderOptions) => boolean;
|
||||
/**
|
||||
* 是否显示
|
||||
*/
|
||||
visibled?: (data: UseColumnRenderOptions) => boolean;
|
||||
/**
|
||||
* 处理事件
|
||||
*/
|
||||
onClick?: (data: UseColumnRenderOptions) => any;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
doptionProps?: Partial<InstanceType<typeof Doption> & Record<string, any>>;
|
||||
}
|
||||
|
||||
export interface UseTableColumn extends TableColumnData {
|
||||
/**
|
||||
* 表格列类型
|
||||
*/
|
||||
type?: "index" | "button";
|
||||
type?: "index" | "button" | "dropdown";
|
||||
/**
|
||||
* 按钮配置列表
|
||||
*/
|
||||
buttons?: TableColumnButton[];
|
||||
/**
|
||||
* 下拉菜单配置列表
|
||||
*/
|
||||
dropdowns?: TableColumnDropdown[];
|
||||
}
|
||||
|
||||
type ExtendedFormItem = Partial<IFormItem> & {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Link, Message, TableColumnData } from "@arco-design/web-vue";
|
||||
import { defaultsDeep, isArray, merge } from "lodash-es";
|
||||
import { Doption, Dropdown, Link, Message, TableColumnData } from "@arco-design/web-vue";
|
||||
import { isArray, merge } from "lodash-es";
|
||||
import { reactive } from "vue";
|
||||
import { useFormModal } from "../form";
|
||||
import { TableInstance } from "./table";
|
||||
|
|
@ -7,6 +7,32 @@ import { config } from "./table.config";
|
|||
import { UseTableOptions } from "./use-interface";
|
||||
import { modal } from "@/utils/modal";
|
||||
|
||||
const onClick = async (item: any, columnData: any, getTable: any) => {
|
||||
if (item.type === "modify") {
|
||||
const data = (await item.onClick?.(columnData)) ?? columnData.record;
|
||||
getTable()?.openModifyModal(data);
|
||||
return;
|
||||
}
|
||||
if (item.type === "delete") {
|
||||
await modal.delConfirm();
|
||||
try {
|
||||
const resData: any = await item?.onClick?.(columnData);
|
||||
const message = resData?.data?.message;
|
||||
if (message) {
|
||||
Message.success(`提示:${message}`);
|
||||
}
|
||||
getTable()?.loadData();
|
||||
} catch (error: any) {
|
||||
const message = error.response?.data?.message;
|
||||
if (message) {
|
||||
Message.warning(`提示:${message}`);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
item.onClick?.(columnData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格组件hook
|
||||
* @see `src/components/table/use-table.tsx`
|
||||
|
|
@ -19,60 +45,50 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
|
|||
/**
|
||||
* 表格列处理
|
||||
*/
|
||||
for (const column of options.columns) {
|
||||
for (let column of options.columns) {
|
||||
/**
|
||||
* 索引列处理
|
||||
*/
|
||||
if (column.type === "index") {
|
||||
defaultsDeep(column, config.columnIndex);
|
||||
column = merge({}, config.columnIndex, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按钮列处理
|
||||
*/
|
||||
if (column.type === "button" && isArray(column.buttons)) {
|
||||
if (options.modify) {
|
||||
const modifyAction = column.buttons.find((i) => i.type === "modify");
|
||||
if (modifyAction) {
|
||||
const { onClick } = modifyAction;
|
||||
modifyAction.onClick = async (columnData) => {
|
||||
const result = (await onClick?.(columnData)) || columnData;
|
||||
getTable()?.openModifyModal(result);
|
||||
};
|
||||
} else {
|
||||
column.buttons.unshift({
|
||||
text: "修改",
|
||||
onClick: (data) => getTable()?.openModifyModal(data),
|
||||
});
|
||||
const buttons = column.buttons;
|
||||
let hasModify = false;
|
||||
let hasDelete = false;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
let btn = merge({}, config.columnButtonBase);
|
||||
if (buttons[i].type === "modify") {
|
||||
btn = merge(btn, buttons[i]);
|
||||
hasModify = true;
|
||||
}
|
||||
if (buttons[i].type === "delete") {
|
||||
btn = merge(btn, buttons[i]);
|
||||
hasDelete = true;
|
||||
}
|
||||
buttons[i] = merge(btn, buttons[i]);
|
||||
}
|
||||
if (!hasModify) {
|
||||
buttons.push(merge({}, config.columnButtonBase));
|
||||
}
|
||||
if (!hasDelete) {
|
||||
buttons.push(merge({}, config.columnButtonBase));
|
||||
}
|
||||
|
||||
column.buttons = column.buttons?.map((action) => {
|
||||
let onClick = action?.onClick;
|
||||
if (action.type === "delete") {
|
||||
onClick = async (data) => {
|
||||
await modal.delConfirm();
|
||||
try {
|
||||
const resData: any = await action?.onClick?.(data);
|
||||
const message = resData?.data?.message;
|
||||
if (message) {
|
||||
Message.success(`提示:${message}`);
|
||||
}
|
||||
getTable()?.loadData();
|
||||
} catch (error: any) {
|
||||
const message = error.response?.data?.message;
|
||||
if (message) {
|
||||
Message.warning(`提示:${message}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return { ...config.columnButtonBase, ...action, onClick } as any;
|
||||
});
|
||||
|
||||
column.render = (columnData) => {
|
||||
return column.buttons?.map((btn) => {
|
||||
const onClick = () => btn.onClick?.(columnData);
|
||||
const disabled = () => btn.disabled?.(columnData);
|
||||
if (btn.visible && !btn.visible(columnData)) {
|
||||
if (btn.visible?.(columnData) === false) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Link onClick={onClick} disabled={disabled()} {...btn.buttonProps}>
|
||||
<Link
|
||||
{...btn.buttonProps}
|
||||
onClick={() => onClick(btn, columnData, getTable)}
|
||||
disabled={btn.disabled?.(columnData)}
|
||||
>
|
||||
{btn.text}
|
||||
</Link>
|
||||
);
|
||||
|
|
@ -80,6 +96,53 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单列处理
|
||||
*/
|
||||
if (column.type === "dropdown" && Array.isArray(column.dropdowns)) {
|
||||
if (options.modify) {
|
||||
const index = column.dropdowns?.findIndex((i) => i.type === "modify");
|
||||
if (index !== undefined) {
|
||||
column.dropdowns[index] = merge({}, config.columnDropdownModify, column.dropdowns[index]);
|
||||
} else {
|
||||
column.dropdowns?.unshift(merge({}, config.columnDropdownModify));
|
||||
}
|
||||
}
|
||||
column.render = (columnData) => {
|
||||
const content = column.dropdowns?.map((dropdown) => {
|
||||
const { text, icon, disabled, visibled, doptionProps } = dropdown;
|
||||
if (visibled?.(columnData) === false) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Doption
|
||||
{...doptionProps}
|
||||
onClick={() => onClick(dropdown, columnData, getTable)}
|
||||
disabled={disabled?.(columnData)}
|
||||
>
|
||||
{{
|
||||
icon: typeof icon === "function" ? icon() : () => <i class={icon} />,
|
||||
default: text,
|
||||
}}
|
||||
</Doption>
|
||||
);
|
||||
});
|
||||
const trigger = () => (
|
||||
<span class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer">
|
||||
<i class="icon-park-outline-more"></i>
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<Dropdown position="br">
|
||||
{{
|
||||
default: trigger,
|
||||
content: content,
|
||||
}}
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
columns.push({ ...config.columnBase, ...column });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</div>
|
||||
<div class="flex items-center gap-4 text-gray-500">
|
||||
<ADropdown>
|
||||
<span class="cursor-pointer">
|
||||
<span class="cursor-pointer hover:text-gray-900">
|
||||
上传者
|
||||
<i class="icon-park-outline-down"></i>
|
||||
</span>
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
</template>
|
||||
</ADropdown>
|
||||
<ADropdown>
|
||||
<span class="cursor-pointer">
|
||||
<span class="cursor-pointer hover:text-gray-900">
|
||||
排序:默认
|
||||
<i class="icon-park-outline-down"></i>
|
||||
</span>
|
||||
|
|
@ -67,17 +67,17 @@
|
|||
</ADropdown>
|
||||
<div class="space-x-1">
|
||||
<span
|
||||
class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer text-gray-400 hover:text-gray-700 bg-slate-200 text-slate-700"
|
||||
>
|
||||
<i class="icon-park-outline-list"></i>
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
>
|
||||
<i class="icon-park-outline-insert-table"></i>
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
>
|
||||
<i class="icon-park-outline-refresh"></i>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -34,19 +34,25 @@ const table = useTable({
|
|||
},
|
||||
{
|
||||
title: "操作",
|
||||
type: "button",
|
||||
width: 136,
|
||||
buttons: [
|
||||
type: "dropdown",
|
||||
width: 60,
|
||||
align: "center",
|
||||
dropdowns: [
|
||||
{
|
||||
type: "modify",
|
||||
text: "修改",
|
||||
icon: "icon-park-outline-edit",
|
||||
},
|
||||
{
|
||||
type: "delete",
|
||||
text: "删除",
|
||||
icon: "icon-park-outline-delete",
|
||||
onClick: ({ record }) => {
|
||||
return api.post.delPost(record.id);
|
||||
},
|
||||
doptionProps: {
|
||||
class: "!text-red-500 !hover-bg-red-50",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -22,19 +22,17 @@ const table = useTable({
|
|||
title: "用户昵称",
|
||||
dataIndex: "username",
|
||||
width: 200,
|
||||
render: ({ record }) => {
|
||||
return (
|
||||
<div class="flex items-center">
|
||||
<Avatar size={32}>
|
||||
<img src={record.avatar} alt="" />
|
||||
</Avatar>
|
||||
<span class="ml-2 flex-1 flex flex-col overflow-hidden">
|
||||
<span>{record.nickname}</span>
|
||||
<span class="text-gray-400 text-xs truncate">账号:{record.username}</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render: ({ record }) => (
|
||||
<div class="flex items-center">
|
||||
<Avatar size={32}>
|
||||
<img src={record.avatar} alt="" />
|
||||
</Avatar>
|
||||
<span class="ml-2 flex-1 flex flex-col overflow-hidden">
|
||||
<span>{record.nickname}</span>
|
||||
<span class="text-gray-400 text-xs truncate">账号:{record.username}</span>
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "用户描述",
|
||||
|
|
@ -63,7 +61,7 @@ const table = useTable({
|
|||
type: "delete",
|
||||
text: "删除",
|
||||
onClick: async ({ record }) => {
|
||||
return api.user.delUser(record.id);
|
||||
return api.user.delUser(record.id, { toast: true });
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -72,7 +70,7 @@ const table = useTable({
|
|||
search: {
|
||||
items: [
|
||||
{
|
||||
extend: "username",
|
||||
extend: "nickname",
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
|
|
@ -151,6 +149,11 @@ const table = useTable({
|
|||
"sort": 10301,
|
||||
"title": "用户管理",
|
||||
"icon": "icon-park-outline-user"
|
||||
},
|
||||
"parentMeta": {
|
||||
"title": "系统管理",
|
||||
"icon": "icon-park-outline-setting",
|
||||
"sort": 20000
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
|
|
|||
Loading…
Reference in New Issue