feat(vxetable): ✨ 适配 vxetable 4.7+

This commit is contained in:
cshaptx4869
2026-09-11 12:03:25 +08:00
parent 085764e6b2
commit a2f9e5e5ac
5 changed files with 669 additions and 378 deletions

View File

@@ -1,34 +1,76 @@
/**
* VxeTable 全局配置
* @see https://vxetable.cn/v4.6/#/table/start/install
*/
// 引入样式
import "vxe-pc-ui/lib/style.css";
import "vxe-table/lib/style.css";
// 导入主题变量,也可以重写主题变量
import "vxe-pc-ui/styles/cssvar.scss";
import "vxe-table/styles/cssvar.scss";
// 导入默认的语言
import enUS from "vxe-pc-ui/lib/language/en-US";
import zhCN from "vxe-pc-ui/lib/language/zh-CN";
import VxeUIPluginExportXLSX from "@vxe-ui/plugin-export-xlsx";
import ExcelJS from "exceljs";
import { VxeUI } from "vxe-pc-ui";
import VXETable from "vxe-table";
import { useAppStore } from "@/stores/app";
import { DeviceEnum } from "@/enums";
export function configureVxeTable() {
VXETable.setConfig({
export function configureVxeUI() {
const appStore = useAppStore();
const isMobile = appStore.device === DeviceEnum.MOBILE;
// 导入导出xlsx插件 https://vxetable.cn/other4/#/table/plugin/exportXLSX
VxeUI.use(VxeUIPluginExportXLSX, {
ExcelJS,
});
// 注册语言
VxeUI.setI18n("zh-CN", zhCN);
VxeUI.setI18n("en-US", enUS);
// 切换指定语言
VxeUI.setLanguage("zh-CN");
// 全局默认参数
VxeUI.setConfig({
// 全局尺寸
size: "medium",
// 全局 zIndex 起始值,默认是 999,如果项目的的 z-index 样式值过大时就需要跟随设置更大,避免被遮挡
zIndex: 9999,
// 版本号,对于某些带数据缓存的功能有用到,上升版本号可以用于重置数据
version: 0,
// 全局 loading 提示内容,如果为 null 则不显示文本
loading: {
showText: false,
text: "",
},
table: {
minHeight: 46,
showHeader: true,
showOverflow: "tooltip",
showHeaderOverflow: "tooltip",
showHeaderOverflow: isMobile ? false : "tooltip",
autoResize: true,
// stripe: false,
border: "inner",
// round: false,
emptyText: "暂无数据",
rowConfig: {
isHover: true,
isCurrent: true,
// 行数据的唯一主键字段名
keyField: "_VXE_ID",
},
columnConfig: {
resizable: false,
minWidth: isMobile ? "auto" : undefined,
},
align: "center",
headerAlign: "center",
validConfig: {
theme: "normal",
},
},
pager: {
// size: "medium",
// 配套的样式
perfect: false,
pageSize: 10,
pagerCount: 7,
@@ -44,11 +86,22 @@ export function configureVxeTable() {
"FullJump",
],
},
form: {
span: isMobile ? 24 : 6,
validConfig: {
theme: "normal",
},
},
modal: {
minWidth: 500,
minHeight: 400,
fullscreen: isMobile,
remember: isMobile,
width: isMobile ? "95%" : undefined,
// minWidth: 500,
// minHeight: 400,
lockView: true,
mask: true,
// duration: 3000,
// marginSize: 20,
dblclickZoom: false,
showTitleOverflow: true,
transfer: true,

View File

@@ -7,96 +7,6 @@
background-color: var(--el-color-primary);
}
// VxeTable 变量覆写
:root {
--vxe-font-color: var(--el-text-color-regular);
--vxe-primary-color: var(--el-color-primary);
--vxe-success-color: var(--el-color-success);
--vxe-info-color: var(--el-color-info);
--vxe-warning-color: var(--el-color-warning);
--vxe-danger-color: var(--el-color-danger);
--vxe-font-lighten-color: var(--el-text-color-primary);
--vxe-primary-lighten-color: var(--el-color-primary-light-3);
--vxe-success-lighten-color: var(--el-color-success-light-3);
--vxe-info-lighten-color: var(--el-color-info-light-3);
--vxe-warning-lighten-color: var(--el-color-warning-light-3);
--vxe-danger-lighten-color: var(--el-color-danger-light-3);
--vxe-font-darken-color: var(--el-text-color-secondary);
--vxe-primary-darken-color: var(--el-color-primary-dark-2);
--vxe-success-darken-color: var(--el-color-success-dark-2);
--vxe-info-darken-color: var(--el-color-info-dark-2);
--vxe-warning-darken-color: var(--el-color-warning-dark-2);
--vxe-danger-darken-color: var(--el-color-danger-dark-2);
--vxe-font-disabled-color: var(--el-text-color-disabled);
--vxe-primary-disabled-color: var(--el-color-primary-light-5);
--vxe-success-disabled-color: var(--el-color-success-light-5);
--vxe-info-disabled-color: var(--el-color-info-light-5);
--vxe-warning-disabled-color: var(--el-color-warning-light-5);
--vxe-danger-disabled-color: var(--el-color-danger-light-5);
--vxe-input-border-color: var(--el-border-color);
--vxe-input-disabled-color: var(--el-text-color-disabled);
--vxe-input-disabled-background-color: var(--el-fill-color-light);
--vxe-input-placeholder-color: var(--el-text-color-placeholder);
--vxe-table-popup-border-color: var(--el-border-color);
--vxe-table-header-font-color: var(--el-text-color-regular);
--vxe-table-footer-font-color: var(--el-text-color-regular);
--vxe-table-border-color: var(--el-border-color-lighter);
--vxe-table-header-background-color: var(--el-bg-color);
--vxe-table-body-background-color: var(--el-bg-color);
--vxe-table-footer-background-color: var(--el-bg-color);
--vxe-table-row-hover-background-color: var(--el-fill-color-light);
--vxe-table-row-current-background-color: var(--el-fill-color-light);
--vxe-table-row-hover-current-background-color: var(--el-fill-color-light);
--vxe-table-checkbox-range-background-color: var(--el-fill-color-light);
--vxe-table-menu-background-color: var(--el-bg-color-overlay);
--vxe-loading-color: var(--el-color-primary);
--vxe-loading-background-color: var(--el-mask-color);
--vxe-table-validate-error-color: var(--el-color-danger);
--vxe-toolbar-background-color: var(--el-bg-color);
--vxe-toolbar-custom-active-background-color: var(--el-bg-color-overlay);
--vxe-toolbar-panel-background-color: var(--el-bg-color-overlay);
--vxe-pager-background-color: var(--el-bg-color);
--vxe-modal-header-background-color: var(--el-bg-color);
--vxe-modal-body-background-color: var(--el-bg-color);
--vxe-modal-border-color: var(--el-border-color);
--vxe-button-default-background-color: var(--el-bg-color-overlay);
--vxe-input-background-color: var(--el-fill-color-blank);
--vxe-input-panel-background-color: var(--el-fill-color-blank);
--vxe-form-background-color: var(--el-bg-color);
--vxe-form-validate-error-color: var(--el-color-danger);
--vxe-select-option-hover-background-color: var(--el-bg-color-overlay);
--vxe-select-panel-background-color: var(--el-bg-color);
}
// VxeTable 自定义样式
.vxe-grid {
&--form-wrapper .vxe-form {
padding: 10px 20px;
margin-bottom: 20px;
}
&--toolbar-wrapper .vxe-toolbar {
padding: 20px;
}
&--pager-wrapper .vxe-pager {
height: 70px;
padding: 0 20px;
&--wrapper {
@media screen and (width <= 768px) {
.vxe-pager--total,
.vxe-pager--sizes,
.vxe-pager--jump,
.vxe-pager--jump-prev,
.vxe-pager--jump-next {
display: none;
}
}
}
}
}
// WangEditor 暗黑模式
html.dark {
--w-e-textarea-bg-color: var(--el-bg-color);

View File

@@ -1,32 +1,26 @@
<template>
<div class="page-container">
<!-- 表格 -->
<VxeGrid ref="xGrid" v-bind="gridOptions" v-on="gridEvents">
<VxeGrid ref="xGridDom" v-bind="xGridOpt" v-on="xGridEvents">
<!-- 搜索 -->
<!-- <template #form-roles="{ data }">
<el-select
v-model="data.roles"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="请选择角色"
filterable
<template #search-createTime="{ data, field }">
<el-date-picker
v-model="data[field]"
class="w-full"
type="daterange"
start-placeholder="开始日期"
end-placeholder="截止日期"
value-format="YYYY-MM-DD"
:editable="false"
clearable
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template> -->
/>
</template>
<!-- 左侧按钮列表 -->
<template #toolbar-btns>
<VxeButton status="primary" icon="vxe-icon-add" @click="curd.onShowModal()">
<VxeButton status="primary" icon="vxe-icon-add" @click="crudStore.onShowModal()">
新增用户
</VxeButton>
<VxeButton status="danger" icon="vxe-icon-delete" @click="curd.onDelete()">
<VxeButton status="danger" icon="vxe-icon-delete" @click="crudStore.onDelete()">
批量删除
</VxeButton>
</template>
@@ -62,33 +56,45 @@
</template>
<!-- 操作列 -->
<template #column-operate="{ row }">
<el-button link type="primary" @click="curd.onShowModal(row)">修改</el-button>
<el-button link type="danger" @click="curd.onDelete(row)">删除</el-button>
<el-button link type="primary" @click="crudStore.onShowModal(row)">修改</el-button>
<el-button link type="danger" @click="crudStore.onDelete(row)">删除</el-button>
</template>
</VxeGrid>
<!-- 弹窗 -->
<VxeModal ref="xModal" v-bind="modalOptions">
<VxeModal ref="xModalDom" v-bind="xModalOpt" v-on="xModalEvent">
<!-- 表单 -->
<VxeForm ref="xForm" v-bind="formOptions" />
<VxeForm ref="xFormDom" v-bind="xFormOpt" />
<!-- 底部按钮 -->
<template #footer>
<VxeButton content="取消" @click="xModalDom?.close()" />
<VxeButton status="primary" content="确定" @click="crudStore.onSubmitForm()" />
</template>
</VxeModal>
</div>
</template>
<script setup lang="ts">
import "vxe-table/lib/style.css";
import { ref, reactive, onMounted } from "vue";
import { reactive, onMounted, useTemplateRef } from "vue";
import { configureVxeUI } from "@/plugins/vxe-table";
import type { VxeGridInstance, VxeGridProps, VxeGridListeners } from "vxe-table";
import { VxeGrid } from "vxe-table";
import type {
VxeFormInstance,
VxeFormProps,
VxeModalInstance,
VxeModalProps,
VxeModalListeners,
} from "vxe-pc-ui";
import {
VXETable,
VxeUI,
VxeButton,
VxeButtonGroup,
VxeForm,
VxeGrid,
VxeInput,
VxeModal,
VxeSelect,
} from "vxe-table";
import { configureVxeTable } from "@/plugins/vxe-table";
} from "vxe-pc-ui";
defineOptions({
components: {
@@ -102,24 +108,24 @@ defineOptions({
},
});
configureVxeTable();
configureVxeUI();
const options = [
{ label: "管理", value: "admin" },
{ label: "用户", value: "user" },
{ label: "访客", value: "guest" },
];
onMounted(() => {
// 模拟异步加载数据
setTimeout(() => {
gridOptions!.formConfig!.items!.forEach((item) => {
if (item.field === "roles") {
item!.itemRender!.props!.options = options;
}
});
const rolesItem = xGridDom.value
?.getFormItems()
.find((item) => item.field === "roles" && item.folding !== true);
if (rolesItem) {
rolesItem.itemRender!.props!.options = [
{ label: "管理", value: "admin" },
{ label: "用户", value: "user" },
{ label: "访客", value: "guest" },
];
}
}, 500);
});
// #region vxe-grid
interface RowMeta {
id: number;
username: string;
@@ -129,113 +135,27 @@ interface RowMeta {
status: boolean;
createTime: string;
}
const xGrid = ref<VxeGridInstance<RowMeta>>();
const gridOptions = reactive<VxeGridProps<RowMeta>>({
// #region vxe-grid
const xGridDom = useTemplateRef<VxeGridInstance<RowMeta>>("xGridDom");
const xGridOpt = reactive<VxeGridProps<RowMeta>>({
// 唯一标识(被某些特定的功能所依赖 比如storage)
id: "user",
// 保持原始值的状态,被某些功能所依赖,比如编辑状态、还原数据等(开启后影响性能,具体取决于数据量)
keepSource: true,
// 自动监听父元素的变化去重新计算表格
autoResize: true,
// 是否显示表尾
showFooter: true,
// 表尾数据(优先级比 footerMethod 高)
// footerData: [
// {
// username: "-",
// roles: "-",
// phone: "-",
// email: "-",
// status: "启用/禁用",
// createTime: "-",
// },
// ],
// 表尾的数据获取方法,返回一个二维数组
footerMethod({ columns, data }) {
return [
columns.map((column, columnIndex) => {
if (columnIndex === 0 || column.field === undefined) {
return "";
} else if (column.field === "status") {
return `启用 ${data.reduce((sum, row) => sum + (row.status ? 1 : 0), 0)} 条`;
}
return "-";
}),
];
},
// 列配置
columns: [
{ type: "checkbox", width: 60 },
{
type: "expand",
width: 60,
slots: {
// 只对 type=expand 有效,自定义展开后的内容模板
content: "column-expand",
},
},
{ type: "seq", width: 60 },
{ field: "id", title: "ID", visible: false },
{ field: "username", title: "用户名" },
{ field: "roles", title: "角色", slots: { default: "column-roles" } },
{ field: "phone", title: "手机号" },
{ field: "email", title: "邮箱" },
{
field: "status",
title: "状态",
sortable: true,
filters: [
{ label: "启用", value: true },
{ label: "禁用", value: false },
],
// 数据筛选,只对 filters 有效,筛选是否允许多选
filterMultiple: false,
formatter({ cellValue }) {
return cellValue === true ? "启用" : "禁用";
},
},
{ field: "createTime", title: "创建时间", sortable: true },
{
title: "操作",
width: "150px",
fixed: "right",
showOverflow: false,
slots: {
default: "column-operate",
},
},
],
// 列配置信息
columnConfig: {
// 每一列是否启用列宽拖动
resizable: true,
},
// 自定义列配置项
customConfig: {
// 是否允许列选中
checkMethod: ({ column }) => !["username"].includes(column.field),
},
// 复选框配置项
checkboxConfig: {
// 是否保留勾选状态(需要有 row-config.keyField)
// reserve: true,
},
// 展开行配置项(不支持虚拟滚动)
expandConfig: {
// 展开列显示的字段名,可以直接显示在单元格中
// labelField: "username",
// 每次只能展开一行
accordion: true,
},
// 行配置信息
rowConfig: {
// 自定义行数据唯一主键的字段名
keyField: "id",
// 当鼠标点击行时,是否要高亮当前行
isCurrent: true,
},
// 表单配置项
formConfig: {
// 所有项的标题宽度
titleWidth: 100,
// 所有项的标题对齐方式
titleAlign: "right",
// NOTE: 表单数据 (不能和代理同时存在)
// data: {},
// 项配置
items: [
{
span: 4,
field: "username",
title: "用户名",
// 前缀配置项
@@ -255,14 +175,13 @@ const gridOptions = reactive<VxeGridProps<RowMeta>>({
clearable: true,
placeholder: "请输入用户名",
},
defaultValue: "",
},
resetValue: "",
},
{
span: 4,
field: "roles",
title: "角色",
// 默认收起
folding: true,
itemRender: {
name: "VxeSelect",
props: {
@@ -273,33 +192,42 @@ const gridOptions = reactive<VxeGridProps<RowMeta>>({
options: [],
placeholder: "请选择角色",
},
defaultValue: null,
},
resetValue: null,
},
{
field: "createTime",
title: "创建时间",
// 默认收起
folding: true,
itemRender: {
defaultValue: [],
},
resetValue: [],
// 插槽
slots: {
// 自定义表单项
default: "search-createTime",
},
},
// {
// span: 4,
// field: "roles",
// title: "角色",
// // 默认收起
// folding: true,
// // 插槽
// slots: {
// // 自定义表单项
// default: "form-roles",
// },
// },
{
span: 24,
align: "left",
collapseNode: true,
itemRender: {
name: "VxeButtonGroup",
options: [
{
type: "submit",
status: "primary",
icon: "vxe-icon-search",
content: "搜索",
},
{ type: "reset", icon: "vxe-icon-refresh", content: "重置" },
],
props: {
options: [
{
type: "submit",
status: "primary",
icon: "vxe-icon-search",
content: "搜索",
},
{ type: "reset", icon: "vxe-icon-refresh", content: "重置" },
],
},
},
},
],
@@ -328,30 +256,212 @@ const gridOptions = reactive<VxeGridProps<RowMeta>>({
importConfig: {},
// 导出配置项
exportConfig: {
// 指定列
// 默认选中文件类型
type: "xlsx",
// 可选文件类型列表
types: ["xlsx", "csv", "html", "xml", "txt"],
// 输出数据的方式
mode: "current",
// 输出数据的方式列表,如果为 all,则会通过 proxy-config.ajax.queryAll 获取数据之后进行导出
modes: ["current", "selected"],
// 指定列(列必须在表中显示,否则被忽略)
columns: [{ field: "phone" }, { field: "email" }, { field: "status" }, { field: "createTime" }],
// 列过滤方法,该函数的返回值用来决定是否过滤掉列
columnFilterMethod({ column }) {
return column.field !== undefined;
},
},
// 打印配置项
printConfig: {},
printConfig: {
// 自定义文档的 css 样式信息
style: `
.my-title {
text-align: center;
}`,
// 在打印之前触发,可以通过返回自定义打印的内容
beforePrintMethod: ({ content, options }) => {
console.debug({ options });
return `<h1 class="my-title">用户列表</h1>${content}`;
},
},
// 缩放配置项
zoomConfig: {
// 是否允许通过按下 ESC 键还原
escRestore: true,
},
// 自定义列配置项
customConfig: {
// NOTE: 是否启用 localStorage 本地保存,会将列操作状态保留在本地(需要有 id)
storage: {
// 启用显示/隐藏列状态缓存
visible: true,
// 启用冻结列状态缓存
fixed: true,
// 启用列宽状态缓存
resizable: true,
// 启用列顺序缓存(popup操作模式允许列排序)
sort: false,
},
// 操作模式
mode: "simple",
// 列勾选之后是否实时同步
immediate: false,
// 是否允许自定义冻结列
allowFixed: true,
// 是否显示底部操作按钮
showFooter: true,
// NOTE: 计算公式绑定的单元格的插槽列不允许隐藏,否则公式会不生效
// 是否允许列选中 返回值用来决定这一列的 checkbox 是否可以选中
checkMethod: ({ column }) => !["username"].includes(column.field),
// 是否显示列 返回值用来决定这一列的 checkbox 是否显示
visibleMethod: ({ column }) => column.type === undefined,
},
// 列配置
columns: [
{ type: "checkbox", width: 60 },
{
type: "expand",
width: 60,
slots: {
// 只对 type=expand 有效,自定义展开后的内容模板
content: "column-expand",
},
},
{ type: "seq", width: 60 },
{ field: "id", title: "ID", visible: false },
{ field: "username", title: "用户名" },
{ field: "roles", title: "角色", slots: { default: "column-roles" } },
{ field: "phone", title: "手机号" },
{ field: "email", title: "邮箱" },
{
field: "status",
title: "状态",
filters: [
{ label: "启用", value: true },
{ label: "禁用", value: false },
],
// 数据筛选,只对 filters 有效,筛选是否允许多选
filterMultiple: false,
// 默认的渲染器配置项
cellRender: {
name: "VxeSwitch",
props: {
openLabel: "启用",
closeLabel: "禁用",
openValue: true,
closeValue: false,
},
events: {
change: ({ row, column }, { value }) => {
crudStore.onModify(row.id, column.field, value);
},
},
},
},
{ field: "createTime", title: "创建时间", sortable: true },
{
field: "operate",
title: "操作",
width: "150px",
fixed: "right",
showOverflow: false,
slots: {
default: "column-operate",
},
},
],
// 行配置信息
rowConfig: {
// 自定义行数据唯一主键的字段名(默认自动生成 _X_ROW_KEY,刷新后值会变)
keyField: "id",
// 当鼠标点击行时,是否要高亮当前行
isCurrent: true,
// 当鼠标移到行时,是否要高亮当前行
isHover: false,
},
// 列配置信息
columnConfig: {
// 每一列是否启用列宽调整
resizable: true,
// 每一列的最小宽度
minWidth: 100,
// 冻结列允许设置的最大数量(如果是分组,则一个分组算一个)
maxFixedSize: 4,
},
// 复选框配置项
checkboxConfig: {
// 是否保留勾选状态(需要有 row-config.keyField)仅对分页切换时有效
// reserve: true,
// 高亮勾选行
highlight: true,
// 是否允许勾选 返回值用来决定这一行 checkbox 是否可以勾选
checkMethod: ({ row }) => row.status === false,
// 是否显示勾选 返回值用来决定这一行的 checkbox 是否显示
visibleMethod: ({ row }) => row.status === false,
},
// 展开行配置项(不支持虚拟滚动)
expandConfig: {
// 展开列显示的字段名,可以直接显示在单元格中
// labelField: "username",
// 每次只能展开一行
accordion: true,
},
// 设置所有内容过长时显示为省略号(如果是固定列建议设置该值,提升渲染速度)
showOverflow: "tooltip",
// 是否显示表尾
showFooter: true,
// 表尾数据(优先级比 footerMethod 高)
// footerData: [
// {
// username: "-",
// roles: "-",
// phone: "-",
// email: "-",
// status: "启用/禁用",
// createTime: "-",
// },
// ],
// 表尾的数据获取方法,返回一个二维数组
footerMethod({ columns, data }) {
return [
columns.map((column, columnIndex) => {
if (columnIndex === 0 || column.field === undefined) {
return "";
} else if (column.field === "status") {
return `启用 ${data.reduce((sum, row) => sum + (row.status ? 1 : 0), 0)} 条`;
}
return "-";
}),
];
},
// NOTE: 分页配置项(含 pager-config 才会启用分页)
pagerConfig: {
// 对齐方式
align: "right",
// 每页大小
pageSize: 10,
},
// 筛选配置项
filterConfig: {
// 所有列是否使用服务端筛选
remote: false,
remote: true,
},
// 排序配置项
sortConfig: {
// 默认排序(只会在初始化时被触发一次)
defaultSort: {
// 列字段名
field: "id",
// 排序方式(asc升序 desc降序)
order: "desc",
},
// 所有列是否使用服务端排序
remote: false,
remote: true,
// 是否启用多列组合筛选
multiple: false,
// 只对 multiple 有效,是否按照先后触发顺序进行排序
chronological: true,
},
// 分页配置项
pagerConfig: {
enabled: true,
pageSize: 10,
},
// 数据代理配置项"
proxyConfig: {
// 是否自动加载查询数据
@@ -373,8 +483,19 @@ const gridOptions = reactive<VxeGridProps<RowMeta>>({
},
ajax: {
// 接收 Promise
query: ({ page: { currentPage, pageSize } }) => {
query: ({ page: { currentPage, pageSize }, form, filters, sort, sorts }) => {
console.debug({ currentPage, pageSize, form, filters, sort, sorts });
xGridOpt.loading = true;
return new Promise<{ total: number; result: RowMeta[] }>((resolve) => {
// 接口需要的参数
// const params = {
// page: currentPage,
// limit: pageSize,
// username: form.username,
// roles: form.roles === null ? undefined : form.roles.join(","),
// createTime: form.createTime.length > 0 ? form.createTime.join(",") : undefined,
// };
// 模拟异步加载数据
setTimeout(() => {
const list = [
{
@@ -477,6 +598,7 @@ const gridOptions = reactive<VxeGridProps<RowMeta>>({
id: 640000197005230000,
},
];
xGridOpt.loading = false;
resolve({
result: list.slice((currentPage - 1) * pageSize, currentPage * pageSize),
total: list.length,
@@ -487,32 +609,16 @@ const gridOptions = reactive<VxeGridProps<RowMeta>>({
},
},
});
const gridEvents: VxeGridListeners<RowMeta> = {
// 只对 form-config 配置时有效,表单重置时会触发该事件
formReset() {},
const xGridEvents: VxeGridListeners<RowMeta> = {
toggleRowExpand: ({ row, expanded }) => {
console.debug("Toggle Row Expand", { row, expanded });
},
};
// #endregion
// #region vxe-modal
const xModal = ref();
const modalOptions = reactive({
// 窗口的标题
title: "",
// 是否允许点击遮罩层关闭窗口
maskClosable: true,
// 是否允许按 Esc 键关闭窗口
escClosable: true,
// 在窗口隐藏之前执行
beforeHideMethod: () => {
xForm.value?.clearValidate();
return Promise.resolve();
},
});
// #endregion
// #region vxe-form
const xForm = ref();
const formOptions = reactive({
const xFormDom = useTemplateRef<VxeFormInstance>("xFormDom");
const xFormOpt = reactive<VxeFormProps>({
// 所有项的栅格占据的列数
span: 24,
// 所有项的标题宽度
@@ -533,6 +639,7 @@ const formOptions = reactive({
placeholder: "请输入",
},
},
resetValue: "",
},
{
field: "password",
@@ -543,32 +650,7 @@ const formOptions = reactive({
placeholder: "请输入",
},
},
},
{
align: "right" as const,
itemRender: {
name: "$buttons",
children: [
{
props: {
content: "取消",
},
events: {
click: () => xModal.value?.close(),
},
},
{
props: {
type: "submit",
content: "确定",
status: "primary",
},
events: {
click: () => curd.onSubmitForm(),
},
},
],
},
resetValue: "",
},
],
/** 校验规则 */
@@ -603,30 +685,127 @@ const formOptions = reactive({
});
// #endregion
const curd = {
commitQuery: () => xGrid.value?.commitProxy("query"),
// #region vxe-modal
const xModalDom = useTemplateRef<VxeModalInstance>("xModalDom");
const xModalOpt = reactive<VxeModalProps>({
// 窗口的标题
title: "",
// 窗口的高度
// height: 500,
// 窗口宽度
// width: "60%",
// 窗口打开时自动最大化显示
// fullscreen: true,
// 标题是否标显示最大化与还原按钮
showZoom: true,
// 是否显示关闭按钮
showClose: true,
// 是否允许点击遮罩层关闭窗口
maskClosable: true,
// 是否允许按 Esc 键关闭窗口
escClosable: true,
// // 是否显示底部栏
showFooter: true,
// 在窗口关闭时销毁内容
destroyOnClose: false,
// 在窗口隐藏之前执行,可以返回 Error 阻止关闭,支持异步
beforeHideMethod: ({ type }) => {
console.debug("beforeHideMethod", type);
xFormDom.value?.clearValidate();
return Promise.resolve();
},
});
const xModalEvent: VxeModalListeners = {
// 显示时触发
show: () => {
console.debug("🚀 ~ show");
},
// 隐藏时触发
hide: ({ type }) => {
console.debug("🚀 ~ hide", type);
const $form = xFormDom.value;
if ($form) {
$form.reset();
$form.clearValidate();
// 重置自定义表单项
$form.getItems().forEach((item) => {
if (item.slots?.default) {
xFormOpt.data[item.field] = item.resetValue;
}
});
// 重置表单数据(解决reset后数据不清空的问题)
delete xFormOpt.data.id;
// xFormOpt.data.id = 0
}
},
};
// #endregion
const crudStore = {
/** 表单类型,true 表示修改,false 表示新增 */
isUpdate: true,
/** 加载表格数据 */
commitQuery: () => xGridDom.value?.commitProxy("query"),
/** 清空表格数据 */
clearTable: () => xGridDom.value?.reloadData([]),
/** 新增后是否跳入最后一页 */
afterInsert: () => {
const pager = xGridDom.value?.getProxyInfo()?.pager;
if (pager) {
const currentTotal = pager.currentPage * pager.pageSize;
if (currentTotal === pager.total) {
++pager.currentPage;
}
}
},
/** 删除后是否返回上一页 */
afterDelete: () => {
const tableData: RowMeta[] = xGridDom.value!.getData();
const pager = xGridDom.value?.getProxyInfo()?.pager;
if (pager && pager.currentPage > 1 && tableData.length === 1) {
--pager.currentPage;
}
},
/** 点击显示弹窗 */
onShowModal: (row?: RowMeta) => {
if (row) {
modalOptions.title = "修改用户";
crudStore.isUpdate = true;
xModalOpt.title = "修改用户";
xFormOpt.data = { ...row };
} else {
modalOptions.title = "新增用户";
crudStore.isUpdate = false;
xModalOpt.title = "新增用户";
}
xModal.value?.open();
xModalDom.value?.open();
},
/** 确定并保存 */
onSubmitForm: () => {
VXETable.modal.message({
content: "提交成功",
status: "success",
if (xModalOpt.loading) return;
// 表单校验
xFormDom.value?.validate((errMap) => {
if (errMap) return;
xModalOpt.loading = true;
const callback = () => {
xModalOpt.loading = false;
xModalDom.value?.close();
VxeUI.modal.message({ status: "success", content: "操作成功" });
if (!crudStore.isUpdate) {
crudStore.afterInsert();
}
crudStore.commitQuery();
};
// 模拟异步操作
callback();
});
},
/** 删除 */
onDelete: (row?: RowMeta) => {
let ids = [];
if (row === undefined) {
// 获取当前已选中的行数据
const selected = xGrid.value?.getCheckboxRecords();
const selected = xGridDom.value?.getCheckboxRecords();
if (!selected || selected.length === 0) {
VXETable.modal.message({
VxeUI.modal.message({
content: "请至少选择一条数据",
status: "warning",
});
@@ -636,15 +815,153 @@ const curd = {
} else {
ids = [row.id];
}
VXETable.modal.confirm("确定要删除吗?").then((type) => {
VxeUI.modal.confirm("确定要删除吗?").then((type) => {
if (type === "confirm") {
// 执行删除操作
VXETable.modal.message({
content: `已删除 ${ids.length} 条数据`,
status: "success",
});
const callback = () => {
// 执行删除操作
VxeUI.modal.message({ status: "success", content: `已删除 ${ids.length} 条数据` });
crudStore.afterDelete();
crudStore.commitQuery();
};
// 模拟异步操作
callback();
}
});
},
onModify: (id: number, field: string, value: any) => {
console.debug("🚀 ~ onModify", { id, field, value });
},
/** 更多自定义方法 */
moreFn: () => {},
};
</script>
<style lang="scss">
:root {
/* color */
--vxe-ui-font-color: var(--el-text-color-regular);
--vxe-ui-font-lighten-color: var(--el-text-color-primary);
--vxe-ui-font-darken-color: var(--el-text-color-secondary);
--vxe-ui-font-disabled-color: var(--el-text-color-disabled);
/*font status color*/
--vxe-ui-font-primary-color: var(--el-color-primary);
--vxe-ui-font-primary-lighten-color: var(--el-color-primary-light-3);
--vxe-ui-font-primary-darken-color: var(--el-color-primary-dark-2);
--vxe-ui-font-primary-disabled-color: var(--el-color-primary-light-5);
--vxe-ui-status-success-color: var(--el-color-success);
--vxe-ui-status-info-color: var(--el-color-info);
--vxe-ui-status-warning-color: var(--el-color-warning);
--vxe-ui-status-danger-color: var(--el-color-danger);
--vxe-ui-status-success-lighten-color: var(--el-color-success-light-3);
--vxe-ui-status-info-lighten-color: var(--el-color-info-light-3);
--vxe-ui-status-warning-lighten-color: var(--el-color-warning-light-3);
--vxe-ui-status-danger-lighten-color: var(--el-color-danger-light-3);
--vxe-ui-status-success-darken-color: var(--el-color-success-dark-2);
--vxe-ui-status-info-darken-color: var(--el-color-info-dark-2);
--vxe-ui-status-warning-darken-color: var(--el-color-warning-dark-2);
--vxe-ui-status-danger-darken-color: var(--el-color-danger-dark-2);
--vxe-ui-status-success-disabled-color: var(--el-color-success-light-5);
--vxe-ui-status-info-disabled-color: var(--el-color-info-light-5);
--vxe-ui-status-warning-disabled-color: var(--el-color-warning-light-5);
--vxe-ui-status-danger-disabled-color: var(--el-color-danger-light-5);
/*base*/
--vxe-ui-base-popup-border-color: var(--el-border-color);
/* layout */
--vxe-ui-layout-background-color: var(--el-bg-color);
/* input/radio/checkbox */
--vxe-ui-input-border-color: var(--el-border-color);
--vxe-ui-input-disabled-color: var(--el-text-color-disabled);
--vxe-ui-input-disabled-background-color: var(--el-fill-color-light);
--vxe-ui-input-placeholder-color: var(--el-text-color-placeholder);
/* table */
--vxe-ui-table-border-color: var(--el-border-color-lighter);
--vxe-ui-table-header-background-color: var(--el-bg-color);
--vxe-ui-table-row-hover-background-color: var(--el-fill-color-light);
--vxe-ui-table-row-current-background-color: var(--el-fill-color-light);
--vxe-ui-table-row-hover-current-background-color: var(--el-fill-color-light);
/* loading */
--vxe-ui-loading-color: var(--el-color-primary);
--vxe-ui-loading-background-color: var(--el-mask-color);
/* modal */
--vxe-ui-modal-header-background-color: var(--el-bg-color);
/* form */
--vxe-ui-form-validate-error-color: var(--el-color-danger);
}
// VxeTable 自定义样式
.vxe-grid {
// 表单
&--form-wrapper {
.vxe-form {
padding: 10px 20px;
margin-bottom: 20px;
}
}
// 工具栏
&--toolbar-wrapper {
.vxe-toolbar {
padding: 20px;
}
}
// 分页
&--pager-wrapper {
.vxe-pager {
height: 70px;
padding: 0 20px;
&--wrapper {
// 参考 Bootstrap 的响应式设计 WIDTH = 768
@media screen and (max-width: 768px) {
.vxe-pager--total,
.vxe-pager--sizes,
.vxe-pager--jump,
.vxe-pager--jump-prev,
.vxe-pager--jump-next {
display: none;
}
}
}
}
}
}
.vxe-table {
// 自定义列配置
&-custom {
&--body {
min-height: 80px;
}
}
}
/* 恢复 4.6 版本弹窗 loading 样式 */
.vxe-modal--v46.vxe-modal--wrapper.is--loading {
.vxe-modal--footer,
.vxe-modal--header {
&:before {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
user-select: none;
content: "";
background-color: var(--vxe-ui-loading-background-color);
}
}
}
</style>