refactor: ♻️ pageContent组件,工具栏按钮适配移动端,提取按钮公共方法

This commit is contained in:
超凡
2025-04-18 08:07:11 +08:00
parent c3aefbb2df
commit 80ea3a0262
4 changed files with 129 additions and 205 deletions

View File

@@ -1,142 +1,39 @@
<template>
<el-card shadow="never">
<!-- 表格工具栏 -->
<div class="flex-x-between mb-[10px]">
<div class="flex flex-col md:flex-row justify-between gap-y-2.5 mb-2.5">
<!-- 左侧工具栏 -->
<div>
<template v-for="item in toolbar" :key="item">
<template v-if="typeof item === 'string'">
<!-- 新增 -->
<template v-if="item === 'add'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
type="success"
icon="plus"
@click="handleToolbar(item)"
>
新增
</el-button>
</template>
<!-- 删除 -->
<template v-else-if="item === 'delete'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
type="danger"
icon="delete"
:disabled="removeIds.length === 0"
@click="handleToolbar(item)"
>
删除
</el-button>
</template>
<!-- 导入 -->
<template v-else-if="item === 'import'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
type="default"
icon="upload"
@click="handleToolbar(item)"
>
导入
</el-button>
</template>
<!-- 导出 -->
<template v-else-if="item === 'export'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
type="default"
icon="download"
@click="handleToolbar(item)"
>
导出
</el-button>
</template>
</template>
<!-- 其他 -->
<template v-else-if="typeof item === 'object'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
:icon="item.icon"
:type="item.type ?? 'default'"
@click="handleToolbar(item.name)"
>
{{ item.text }}
</el-button>
</template>
<div class="toolbar-left flex gap-y-2.5 gap-x-2 md:gap-x-3 flex-wrap">
<template v-for="btn in toolbarLeftBtn">
<el-button
v-hasPerm="authName ? `${authName}:${btn.auth}` : '*:*:*'"
:disabled="btn.name === 'delete' && removeIds.length === 0"
v-bind="btn.attrs"
@click="handleToolbar(btn.name)"
>
{{ btn.text }}
</el-button>
</template>
</div>
<!-- 右侧工具栏 -->
<div>
<template v-for="item in defaultToolbar" :key="item">
<template v-if="typeof item === 'string'">
<!-- 刷新 -->
<template v-if="item === 'refresh'">
<el-button icon="refresh" circle title="刷新" @click="handleToolbar(item)" />
<div class="toolbar-right flex gap-y-2.5 gap-x-2 md:gap-x-3 flex-wrap">
<template v-for="btn in toolbarRightBtn">
<el-popover v-if="btn.name === 'filter'" placement="bottom" trigger="click">
<template #reference>
<el-button v-bind="btn.attrs"></el-button>
</template>
<!-- 筛选列 -->
<template v-else-if="item === 'filter'">
<el-popover placement="bottom" trigger="click">
<template #reference>
<el-button icon="Operation" circle title="筛选列" />
</template>
<el-scrollbar max-height="350px">
<template v-for="col in cols" :key="col">
<el-checkbox v-if="col.prop" v-model="col.show" :label="col.label" />
</template>
</el-scrollbar>
</el-popover>
</template>
<!-- 导出 -->
<template v-else-if="item === 'exports'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:export`]"
icon="download"
circle
title="导出"
@click="handleToolbar(item)"
/>
</template>
<!-- 导入 -->
<template v-else-if="item === 'imports'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:import`]"
icon="upload"
circle
title="导入"
@click="handleToolbar(item)"
/>
</template>
<!-- 搜索 -->
<template v-else-if="item === 'search'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:query`]"
icon="search"
circle
title="搜索"
@click="handleToolbar(item)"
/>
</template>
</template>
<!-- 其他 -->
<template v-else-if="typeof item === 'object'">
<template v-if="item.auth">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
:icon="item.icon"
circle
:title="item.title"
@click="handleToolbar(item.name)"
/>
</template>
<template v-else>
<el-button
:icon="item.icon"
circle
:title="item.title"
@click="handleToolbar(item.name)"
/>
</template>
</template>
<el-scrollbar max-height="350px">
<template v-for="col in cols" :key="col">
<el-checkbox v-if="col.prop" v-model="col.show" :label="col.label" />
</template>
</el-scrollbar>
</el-popover>
<el-button
v-else
v-hasPerm="authName ? `${authName}:${btn.auth}` : '*:*:*'"
v-bind="btn.attrs"
@click="handleToolbar(btn.name)"
></el-button>
</template>
</div>
</div>
@@ -257,52 +154,22 @@
</template>
<!-- 列操作栏 -->
<template v-else-if="col.templet === 'tool'">
<template v-for="item in col.operat ?? ['edit', 'delete']" :key="item">
<template v-if="typeof item === 'string'">
<!-- 编辑/删除 -->
<template v-if="item === 'edit' || item === 'delete'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
:type="item === 'edit' ? 'primary' : 'danger'"
:icon="item"
size="small"
link
@click="
handleOperat({
name: item,
row: scope.row,
column: scope.column,
$index: scope.$index,
})
"
>
{{ item === "edit" ? "编辑" : "删除" }}
</el-button>
</template>
</template>
<!-- 其他 -->
<template v-else-if="typeof item === 'object'">
<el-button
v-if="item.render === undefined || item.render(scope.row)"
v-bind="
item.auth ? { 'v-hasPerm': [`${contentConfig.pageName}:${item.auth}`] } : {}
"
:icon="item.icon"
:type="item.type ?? 'primary'"
size="small"
link
@click="
handleOperat({
name: item.name,
row: scope.row,
column: scope.column,
$index: scope.$index,
})
"
>
{{ item.text }}
</el-button>
</template>
<template v-for="btn in tableToolbarBtn">
<el-button
v-if="btn.render === undefined || btn.render(scope.row)"
v-hasPerm="authName ? `${authName}:${btn.auth}` : '*:*:*'"
v-bind="btn.attrs"
@click="
handleOperat({
name: btn.name,
row: scope.row,
column: scope.column,
$index: scope.$index,
})
"
>
{{ btn.text }}
</el-button>
</template>
</template>
<!-- 自定义 -->
@@ -464,12 +331,10 @@ import {
} from "element-plus";
import ExcelJS from "exceljs";
import { reactive, ref } from "vue";
import type { IContentConfig, IObject, IOperatData } from "./types";
import type { IContentConfig, IObject, IOperatData, IToolsDefault } from "./types";
// 定义接收的属性
const props = defineProps<{
contentConfig: IContentConfig;
}>();
const props = defineProps<{ contentConfig: IContentConfig }>();
// 定义自定义事件
const emit = defineEmits<{
addClick: [];
@@ -483,10 +348,50 @@ const emit = defineEmits<{
// 主键
const pk = props.contentConfig.pk ?? "id";
// 表格工具栏按钮配置
const config = computed(() => props.contentConfig);
const authName = computed(() => props.contentConfig.pageName);
const buttonConfig = reactive<Record<string, IObject>>({
add: { text: "新增", attrs: { icon: "plus", type: "success" }, auth: "add" },
delete: { text: "删除", attrs: { icon: "delete", type: "danger" }, auth: "delete" },
import: { text: "导入", attrs: { icon: "upload", type: "" }, auth: "import" },
export: { text: "导出", attrs: { icon: "download", type: "" }, auth: "export" },
refresh: { text: "刷新", attrs: { icon: "refresh", type: "" }, auth: "*:*:*" },
filter: { text: "筛选列", attrs: { icon: "operation", type: "" }, auth: "*:*:*" },
search: { text: "搜索", attrs: { icon: "search", type: "" }, auth: "search" },
imports: { text: "批量导入", attrs: { icon: "upload", type: "" }, auth: "imports" },
exports: { text: "批量导出", attrs: { icon: "download", type: "" }, auth: "exports" },
view: { text: "查看", attrs: { icon: "view", type: "primary" }, auth: "view" },
edit: { text: "编辑", attrs: { icon: "edit", type: "primary" }, auth: "edit" },
});
const createToolbar = (toolbar: Array<IToolsDefault>, attr = {}) => {
return toolbar.map((item) => {
const isString = typeof item === "string";
return {
name: isString ? item : item?.name || "",
text: isString ? buttonConfig[item].text : item?.text,
attrs: {
type: isString ? buttonConfig[item].type : "",
icon: isString ? buttonConfig[item].icon : "",
title: isString ? buttonConfig[item].text : item?.text,
...attr,
...(isString ? buttonConfig[item].attrs : item?.attrs),
},
render: isString ? undefined : (item?.render ?? undefined),
auth: isString ? buttonConfig[item].auth : (item?.auth ?? "*:*:*"),
};
});
};
// 表格左侧工具栏
const toolbar = props.contentConfig.toolbar ?? ["add", "delete"];
const toolbarLeft = props.contentConfig?.toolbar ?? ["add", "delete"];
const toolbarLeftBtn = createToolbar(toolbarLeft, {});
// 表格右侧工具栏
const defaultToolbar = props.contentConfig.defaultToolbar ?? ["refresh", "filter"];
const toolbarRight = props.contentConfig?.defaultToolbar ?? ["refresh", "filter"];
const toolbarRightBtn = createToolbar(toolbarRight, { circle: true });
// 表格操作工具栏
const tableToolbar = config.value.cols[config.value.cols.length - 1].operat ?? ["edit", "delete"];
const tableToolbarBtn = createToolbar(tableToolbar, { link: true, size: "small" });
// 表格列
const cols = ref(
props.contentConfig.cols.map((col) => {
@@ -515,7 +420,7 @@ const pageData = ref<IObject[]>([]);
// 显示分页
const showPagination = props.contentConfig.pagination !== false;
// 分页配置
const defalutPagination = {
const defaultPagination = {
background: true,
layout: "total, sizes, prev, pager, next, jumper",
pageSize: 20,
@@ -525,8 +430,8 @@ const defalutPagination = {
};
const pagination = reactive(
typeof props.contentConfig.pagination === "object"
? { ...defalutPagination, ...props.contentConfig.pagination }
: defalutPagination
? { ...defaultPagination, ...props.contentConfig.pagination }
: defaultPagination
);
// 分页相关的请求参数
const request = props.contentConfig.request ?? {
@@ -649,7 +554,7 @@ function handleExports() {
workbook.xlsx
.writeBuffer()
.then((buffer) => {
saveXlsx(buffer, filename);
saveXlsx(buffer, filename as string);
})
.catch((error) => console.log(error));
});
@@ -663,7 +568,7 @@ function handleExports() {
workbook.xlsx
.writeBuffer()
.then((buffer) => {
saveXlsx(buffer, filename);
saveXlsx(buffer, filename as string);
})
.catch((error) => console.log(error));
}
@@ -975,4 +880,12 @@ function saveXlsx(fileData: any, fileName: string) {
defineExpose({ fetchPageData, exportPageData, getFilterParams, getSelectionData });
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.toolbar-left,
.toolbar-right {
.el-button {
margin-right: 0 !important;
margin-left: 0 !important;
}
}
</style>

View File

@@ -1,6 +1,6 @@
import type { DialogProps, DrawerProps, FormItemRule, PaginationProps } from "element-plus";
import type { FormProps, TableProps, ColProps, ButtonProps, CardProps } from "element-plus";
import type { ButtonType, ElTooltipProps } from "element-plus";
import type { ElTooltipProps } from "element-plus";
import type PageContent from "./PageContent.vue";
import type PageForm from "./PageForm.vue";
import type PageModal from "./PageModal.vue";
@@ -24,12 +24,11 @@ type ToolbarLeft = "add" | "delete" | "import" | "export";
type ToolbarRight = "refresh" | "filter" | "imports" | "exports" | "search";
type ToolbarTable = "edit" | "view" | "delete";
type IToolsButton = {
name?: string; // 按钮名称
name: string; // 按钮名称
text?: string; // 按钮文本
icon?: string; // 按钮图标
type?: ButtonType; // 按钮类型
auth?: Array<string> | string; // 按钮权限
attrs?: Partial<ButtonProps>; // 按钮属性
attrs?: Partial<ButtonProps> & { style?: CSSProperties }; // 按钮属性
render?: (row: IObject) => boolean; // 条件渲染
};
export type IToolsDefault = ToolbarLeft | ToolbarRight | ToolbarTable | IToolsButton;