摘要: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)
})
text="新增"
/>
:data="data"
@edit="onShowEditDialog"
@delete="onDelete"
/>
:size="size"
:total="total"
@change="onChange"
/>
@confirm="onConfirmDelete"
@close="onCloseDeleteDialog"
/>
:title="modalTitle"
@confirm="onSave"
@close="handleCloseDialog"
>
v-model="formDataUser.name"
placeholder="请输入姓名"
/>
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'
}
})
: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)
})
text="新增"
/>
:data="data"
:loading="loading"
@edit="onShowEditDialog"
@delete="onDelete"
/>
:size="size"
:total="total"
@change="onChange"
/>
@confirm="onConfirmDelete"
@close="onCloseDeleteDialog"
/>
:title="modalTitle"
@confirm="onSave"
@close="handleCloseDialog"
>
v-model="formDataUser.name"
placeholder="请输入姓名"
/>
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)
})
text="新增"
/>
:data="data"
:loading="loading"
@edit="onShowEditDialog"
@delete="onShowDeleteDialog"
/>
:size="size"
:total="total"
@change="onChange"
/>
@confirm="onDelete"
@close="onHideDeleteDialog"
/>
:title="modalTitle"
@confirm="onSave"
@close="handleCloseDialog"
>
v-model="formDataUser.name"
placeholder="请输入姓名"
/>
type="number"
v-model="formDataUser.age"
placeholder="请输入年龄"
/>
总结
到目前为止, 我们以及实现了数据的新增, 编辑和删除功能, 还对表格的分页功能和删除功能做了组合式API的封装.
另外我们还新增了加载中状态组件, 实现当表格还在加载数据的时候, 显示加载中的动画, 这样更加的人性化.
不过, 我们还可以继续封装表单相关的东西, 所以我们还有很多的事儿要做.
那么, 我们继续往下学习吧!!!
来源:科技迅