自研国产零依赖前端UI框架实战010 表格加载中状态

B站影视 2024-12-31 23:46 2

摘要:functionapiGetPageUser(page = 1, size = 8, total = 888) {returnnewPromise((resolve, reject) => {setTimeout( => {try {resolve(getPa

前言

目前我们已经实现了用户管理系统的基础功能, 接下来就是想办法让它的开发变得更加的简单, 让里面的大部分逻辑能够被后面其他类似的功能进行复用.

这里我主要有两个想法, 一个是封装一个crud组件, 提供基础的增删改查的能力. 另一个是封装组合式API, 让逻辑代码能够被复用.

先封装组合式API

首先就是数据分页相关的逻辑, 按照我之前的开发经验, 这里可以抽离成一个通用的逻辑.

我这里先是创建了一个模拟获取用户后端接口的方法:

functionapiGetPageUser(page = 1, size = 8, total = 888) {
returnnewPromise((resolve, reject) => {
setTimeout( => {
try {
resolve(getPageUser(page, size, total));
} catch (error) {
reject(error);
}
}, 1000); // 模拟 1 秒的延迟
});
}

接着我把分页相关的代码封装到了crud.js文件中.

import {ref} from"vue";

// 用于crud的分页
const useCrudPage = (apiGetPage) => {
// 第几页
const page = ref(1);
// 每页数据
const size = ref(8);
// 共多少条数据
const total = ref(0);
// 表格数据
const data = ref()
// 监听分页变化
const onChange = async (v) => {
page.value = v
await loadData
}
// 加载数据
const loadData = async => {
const newData = await apiGetPage(
page.value,
size.value,
)
data.value = newData.data
total.value = newData.total
}

return {
page,
size,
total,
data,
loadData,
onChange,
}
}

exportdefault {
useCrudPage,
}

最后再修改一下App.vue.


import zdp_table1 from"./zdpui/components/zdp_table1.vue";
import zdp_page1 from"./zdpui/components/zdp_page1.vue";
import random from"./zdpui/js/random.js";
import {onMounted, reactive, ref} from"vue";
import zdp_confirm1 from"./zdpui/components/zdp_confirm1.vue";
import zdp_modal1 from"./zdpui/components/zdp_modal1.vue";
import Zdp_input1 from"./zdpui/components/zdp_input1.vue";
import Zdp_button1 from"./zdpui/components/zdp_button1.vue";
import array from"./zdpui/js/array.js";
import crud from"./zdpui/js/crud.js";

const columns = [
{
title: "员工编号",
key: "id",
width: 80,
align: "center"
},
{
title: "姓名",
key: "name",
width: 100,
align: "center"
},
{
title: "年龄",
key: "age",
width: 100,
align: "center"
}
]
const {
page,
size,
total,
data,
onChange,
loadData,
} = crud.useCrudPage(random.apiGetPageUser)

const isEdit = ref(false);
const editId = ref(0);
const editIndex = ref(0);

// 点击编辑, 显示编辑对话框
const onShowEditDialog = (index, item) => {
console.log("编辑", index, item)
isEdit.value = true;
isShowUserDialog.value = true;
editId.value = item.id
editIndex.value = index
formDataUser.name = item.name
formDataUser.age = item.age
modalTitle.value = "编辑用户"
}

const onDelete = (index, item) => {
console.log("删除", index, item)
showDeleteDialog.value = true;
}
const showDeleteDialog = ref(false);
const onConfirmDelete = => {
console.log("确认删除");
false;
};
const onCloseDeleteDialog = => {
console.log("取消删除");
false;
};

const isShowUserDialog = ref(false);
const formDataUser = reactive({name: "张三", age: 23});
const modalTitle = ref("编辑用户");

// 点击对话框中的保存按钮
const onSave = => {
console.log("保存用户", formDataUser);
false;

if (isEdit.value) {
data.value[editIndex.value] = {
id: editId.value,
...formDataUser,
};
} else {
let newUser = {
id: random.id1,
name: formDataUser.name,
age: formDataUser.age,
}
array.insertFirst(data.value, newUser)
}

isEdit.value = false;
formDataUser.name = ""
formDataUser.age = 0
};

const handleCloseDialog = => {
false;
};
const onShowAddUserModal = => {
true;
formDataUser.id = 0
formDataUser.name = ""
formDataUser.age = 0
modalTitle.value = "新增用户"
};

onMounted(async =>{
await loadData
console.log("data...", data.value)
})



@click="onShowAddUserModal"
text="新增"
/>
:columns="columns"
:data="data"
@edit="onShowEditDialog"
@delete="onDelete"
/>
:page="page"
:size="size"
:total="total"
@change="onChange"
/>
:show="showDeleteDialog"
@confirm="onConfirmDelete"
@close="onCloseDeleteDialog"
/>
:show="isShowUserDialog"
:title="modalTitle"
@confirm="onSave"
@close="handleCloseDialog"
>
label="姓名"
v-model="formDataUser.name"
placeholder="请输入姓名"
/>
label="年龄"
type="number"
v-model="formDataUser.age"
placeholder="请输入年龄"
/>



页面一切正常, 很完美, 这样我们就得到了第一个需要的组合式API.

在这里插入图片描述

表格加载状态

由于模拟接口有一秒钟的延迟, 在这一秒钟以内, 表格的内容是空的, 不太好.

所以我决定先封装一个通用的全局的加载中状态的组件, 然后把这个组件复用到表格组件中.

加载中组件的效果如:

在这里插入图片描述

组件代码如下:


const props = defineProps({
loading: {
type: Boolean,
default: false
},
text: {
type: String,
default: '加载中...'
}
})





{{ props.text }}




.loading-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(240, 240, 240, 0.8); /* 透明的灰白色背景 */
z-index: 9999; /* 确保在其他元素之上 */
}

.loading-spinner {
border: 16px solid #f3f3f3; /* 浅灰色 */
border-top: 16px solid #3498db; /* 蓝色 */
border-radius: 50%;
width: 88px;
height: 88px;
animation: spin 2s linear infinite;
}

@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

.loading-text {
margin-top: 20px; /* 加载文本在加载图标下面 */
color: #333; /* 文本颜色 */
font-size: 1.5em;
font-family: 'Arial', sans-serif;
}

改造加载中组件

我接着对这个加载中的组件进行了一下改造, 现在, 它即只是普通的加载, 也支持全屏的加载, 通过type来控制.

默认是普通加载, type=full的时候是全屏加载.

完整代码如下:


const props = defineProps({
loading: {
type: Boolean,
default: false
},
text: {
type: String,
default: '加载中...'
},
type: {
type: String,
// full, normal
default: 'normal'
}
})



v-if="props.loading"
:class="type"
>

{{ props.text }}




/*全屏加载*/
.full {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(240, 240, 240, 0.8); /* 透明的灰白色背景 */
z-index: 9999; /* 确保在其他元素之上 */
}

/*普通加载*/
.normal {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(240, 240, 240, 0.8); /* 透明的灰白色背景 */
padding: 88px0;
}

.spinner {
border: 16px solid #f3f3f3; /* 浅灰色 */
border-top: 16px solid #3498db; /* 蓝色 */
border-radius: 50%;
width: 88px;
height: 88px;
animation: spin 2s linear infinite;
}

@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

.text {
margin-top: 20px; /* 加载文本在加载图标下面 */
color: #333; /* 文本颜色 */
font-size: 1.5em;
font-family: 'Arial', sans-serif;
}

普通加载状态如下:

在这里插入图片描述在这里插入图片描述

此时App.vue完整代码如下:


import zdp_table1 from"./zdpui/components/zdp_table1.vue";
import zdp_page1 from"./zdpui/components/zdp_page1.vue";
import random from"./zdpui/js/random.js";
import {onMounted, reactive, ref} from"vue";
import zdp_confirm1 from"./zdpui/components/zdp_confirm1.vue";
import zdp_modal1 from"./zdpui/components/zdp_modal1.vue";
import Zdp_input1 from"./zdpui/components/zdp_input1.vue";
import Zdp_button1 from"./zdpui/components/zdp_button1.vue";
import array from"./zdpui/js/array.js";
import crud from"./zdpui/js/crud.js";
import zdp_loading1 from"./zdpui/components/zdp_loading1.vue";

const columns = [
{
title: "员工编号",
key: "id",
width: 80,
align: "center"
},
{
title: "姓名",
key: "name",
width: 100,
align: "center"
},
{
title: "年龄",
key: "age",
width: 100,
align: "center"
}
]
const {
page,
size,
total,
data,
loading,
onChange,
loadData,
} = crud.useCrudPage(random.apiGetPageUser)

const isEdit = ref(false);
const editId = ref(0);
const editIndex = ref(0);

// 点击编辑, 显示编辑对话框
const onShowEditDialog = (index, item) => {
console.log("编辑", index, item)
isEdit.value = true;
isShowUserDialog.value = true;
editId.value = item.id
editIndex.value = index
formDataUser.name = item.name
formDataUser.age = item.age
modalTitle.value = "编辑用户"
}

const onDelete = (index, item) => {
console.log("删除", index, item)
showDeleteDialog.value = true;
}
const showDeleteDialog = ref(false);
const onConfirmDelete = => {
console.log("确认删除");
false;
};
const onCloseDeleteDialog = => {
console.log("取消删除");
false;
};

const isShowUserDialog = ref(false);
const formDataUser = reactive({name: "张三", age: 23});
const modalTitle = ref("编辑用户");

// 点击对话框中的保存按钮
const onSave = => {
console.log("保存用户", formDataUser);
false;

if (isEdit.value) {
data.value[editIndex.value] = {
id: editId.value,
...formDataUser,
};
} else {
let newUser = {
id: random.id1,
name: formDataUser.name,
age: formDataUser.age,
}
array.insertFirst(data.value, newUser)
}

isEdit.value = false;
formDataUser.name = ""
formDataUser.age = 0
};

const handleCloseDialog = => {
false;
};
const onShowAddUserModal = => {
true;
formDataUser.id = 0
formDataUser.name = ""
formDataUser.age = 0
modalTitle.value = "新增用户"
};

onMounted(async =>{
await loadData
console.log("data...", data.value)
})



@click="onShowAddUserModal"
text="新增"
/>
:columns="columns"
:data="data"
:loading="loading"
@edit="onShowEditDialog"
@delete="onDelete"
/>
:page="page"
:size="size"
:total="total"
@change="onChange"
/>
:show="showDeleteDialog"
@confirm="onConfirmDelete"
@close="onCloseDeleteDialog"
/>
:show="isShowUserDialog"
:title="modalTitle"
@confirm="onSave"
@close="handleCloseDialog"
>
label="姓名"
v-model="formDataUser.name"
placeholder="请输入姓名"
/>
label="年龄"
type="number"
v-model="formDataUser.age"
placeholder="请输入年龄"
/>



这样的话, 我们的表格就支持加载中状态这个功能了, 是不是很强大, 比antd vue还要简单得多, 这个因为笔记里面是跟着一步一步走过来的, 可能稍微复杂点, 我后面出个使用教程, 那个时候就能够体会到这个组件的简单和强大了.

接下来做什么

表格优化完了, 但是我们真正的目标才走了一小步, 也就是封装组合式API, 封装crud组件.

接下来, 我们先继续封装组合式API, 等组合式API封装完毕以后, 我们再封装crud组件.

封装表格相关的操作

表格里面现在的操作也可以封装到一个组合式API中, 主要是表格的数据, 已经删除数据的接口.

当我点击删除的时候, 弹出删除窗口:

在这里插入图片描述

点击确认, 这个数据从表格中移除.

在这里插入图片描述

在表格相关的组合式API中, 主要封装了表格的columns相关的操作, 以及删除相关的操作.

// 用于表格
const useCrudTable = (data, _columns = null, apiDelete = null) => {
// 表格数据
const columns = ref(_columns ?? [
{
title: "编号",
key: "id",
width: 80,
align: "center"
},
{
title: "姓名",
key: "name",
width: 100,
align: "center"
},
{
title: "年龄",
key: "age",
width: 100,
align: "center"
}
])
// 删除索引
const deleteIndex = ref
// 删除数据
const deleteData = ref
// 删除对话框
const isShowDeleteDialog = ref(false);
// 执行删除
const onShowDeleteDialog = async (index, item) => {
isShowDeleteDialog.value = true;
deleteIndex.value = index
deleteData.value = item
}
// 确认删除
const onDelete = async => {
false;
array.remove(data.value, deleteIndex.value)
if (apiDelete && deleteData.value.id) await apiDelete(deleteData.value.id)
};
const onHideDeleteDialog = => {
false;
};
return {
columns,
deleteIndex,
deleteData,
isShowDeleteDialog,
onShowDeleteDialog,
onDelete,
onHideDeleteDialog,
}
}

此时App.vue完整代码如下.


import zdp_table1 from"./zdpui/components/zdp_table1.vue";
import zdp_page1 from"./zdpui/components/zdp_page1.vue";
import random from"./zdpui/js/random.js";
import {onMounted, reactive, ref} from"vue";
import zdp_confirm1 from"./zdpui/components/zdp_confirm1.vue";
import zdp_modal1 from"./zdpui/components/zdp_modal1.vue";
import Zdp_input1 from"./zdpui/components/zdp_input1.vue";
import Zdp_button1 from"./zdpui/components/zdp_button1.vue";
import array from"./zdpui/js/array.js";
import crud from"./zdpui/js/crud.js";

const {
page,
size,
total,
data,
loading,
onChange,
loadData,
} = crud.useCrudPage(random.apiGetPageUser)
const {
columns,
isShowDeleteDialog,
onDelete,
onShowDeleteDialog,
onHideDeleteDialog,
} = crud.useCrudTable(data)

const isEdit = ref(false);
const editId = ref(0);
const editIndex = ref(0);

// 点击编辑, 显示编辑对话框
const onShowEditDialog = (index, item) => {
console.log("编辑", index, item)
isEdit.value = true;
isShowUserDialog.value = true;
editId.value = item.id
editIndex.value = index
formDataUser.name = item.name
formDataUser.age = item.age
modalTitle.value = "编辑用户"
}

const isShowUserDialog = ref(false);
const formDataUser = reactive({name: "张三", age: 23});
const modalTitle = ref("编辑用户");

// 点击对话框中的保存按钮
const onSave = => {
console.log("保存用户", formDataUser);
false;

if (isEdit.value) {
data.value[editIndex.value] = {
id: editId.value,
...formDataUser,
};
} else {
let newUser = {
id: random.id1,
name: formDataUser.name,
age: formDataUser.age,
}
array.insertFirst(data.value, newUser)
}

isEdit.value = false;
formDataUser.name = ""
formDataUser.age = 0
};

const handleCloseDialog = => {
false;
};
const onShowAddUserModal = => {
true;
formDataUser.id = 0
formDataUser.name = ""
formDataUser.age = 0
modalTitle.value = "新增用户"
};

onMounted(async =>{
await loadData
console.log("data...", data.value)
})



@click="onShowAddUserModal"
text="新增"
/>
:columns="columns"
:data="data"
:loading="loading"
@edit="onShowEditDialog"
@delete="onShowDeleteDialog"
/>
:page="page"
:size="size"
:total="total"
@change="onChange"
/>
:show="isShowDeleteDialog"
@confirm="onDelete"
@close="onHideDeleteDialog"
/>
:show="isShowUserDialog"
:title="modalTitle"
@confirm="onSave"
@close="handleCloseDialog"
>
label="姓名"
v-model="formDataUser.name"
placeholder="请输入姓名"
/>
label="年龄"
type="number"
v-model="formDataUser.age"
placeholder="请输入年龄"
/>



总结

到目前为止, 我们以及实现了数据的新增, 编辑和删除功能, 还对表格的分页功能和删除功能做了组合式API的封装.

另外我们还新增了加载中状态组件, 实现当表格还在加载数据的时候, 显示加载中的动画, 这样更加的人性化.

不过, 我们还可以继续封装表单相关的东西, 所以我们还有很多的事儿要做.

那么, 我们继续往下学习吧!!!

来源:科技迅

相关推荐