1.为什么要使用组合式函数进行封装
应用中页面很多方法都能在多个页面公用,如果每个页面都重新写一遍所有方法,如表格数据的增删改查、分页、数据的条件查询、重置等,那么会重现很多冗余并且十分繁琐。
官网:在 Vue 应用的概念中,“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。组合式函数约定用驼峰命名法命名,并以“use”作为开头。如果组合式函数接收 ref 参数,则返回数据仍具有响应性。
如果你的组合式函数在接收 ref 为参数时会产生响应式 effect,请确保使用 watch()
显式地监听此 ref,或者在 watchEffect()
中调用 unref()
来进行正确的追踪。
抽取组合式函数不仅是为了复用,也是为了代码组织。
所谓的有状态,我的理解就是有可能会变化的数据。
2.封装模板
//组合函数
export function func(param){
const pageNum,page....
func1(){ ....}
func2(){ ....}
func3(){ ....}
return {
pageNum,page,
func1,func2,func3
}
}
//使用:
const {
pageNum,page,func1,func2,func3
} = func({ param });
3.封装实例
import { ref, unref } from 'vue'
import { useRouter } from 'vue-router'
import { setTableData } from "@/composables/transferToTableV2"
/**
* loading状态
* @param {*} request url请求
* @param {*} form 不同页面传入的表单
* @returns
*/
export function useDataGrid({ request, form, tableConfig }) {
const router = useRouter();
//分页
const pageNum = ref(Number.parseInt(router.currentRoute.value.query.pageNum) || 1);
const total = ref(0); //总页数
const pageSize = ref(16); //每页多少条
const pages = ref(0);
// 加载状态
const loading = ref(true);
//表格数据(el-table-v2)
const columns = ref([]);
const data = ref([]);
const selectedId = ref([]);
const tableData = ref([]); //列表数据
const code = ref(500);
//所有下拉框
const allType = ref({});
// 添加资源更新记录Form打开标识
const dialogVisible = ref(false);
// 获取列表数据
const getDataGrid = async(params) => {
loading.value = true;
await request(params).then(res => {
code.value = res.data.code;
if (res.data && res.data.code === 200) {
total.value = res.data.data.total;
pageNum.value = res.data.data.pageNum;
pageSize.value = res.data.data.pageSize;
pages.value = res.data.data.pages;
tableData.value = res.data.data.list ? res.data.data.list : res.data.data;
} else if (res.data.code === 400 && res.data.errorMsg === "暂无记录!") {
total.value = 0; //没有数据时分页页数置为1
} else {
ElMessage.error("内部服务器错误:" + res.data.errorMsg);
}
// 监听到data.value值改变后需要重新设置
function setTableStyleAndData() {
const { columnsVal, dataVal } = setTableData({
code: code.value,
cnColumnNameArr: tableConfig.cnColumnNameArr,
hiddenIndex: tableConfig.hiddenIndex,
columnWidth: tableConfig.columnWidth,
selectedId: selectedId.value,
cellRenderer: tableConfig.cellRenderer,
tableData: tableData.value
});
columns.value = unref(columnsVal);
data.value = unref(dataVal);
}
setTableStyleAndData();
// 数据设置完后再设置loading为false
loading.value = false;
}).catch(error => {
console.log(error);
loading.value = false;
ElMessage.error("列表数据显示失败");
});
}
getDataGrid(form);
function loadDataGrid() {
if (form) form.pageNum = pageNum.value;
getDataGrid(form);
router.push({ path: router.currentRoute.value.path, query: { pageNum: pageNum.value } });
}
// 获取下拉框
async function getSelectOptions(request) {
let result = await request();
if (result.data.code === 200) {
loading.value = false;
allType.value = result.data.data;
} else {
ElMessage.error("获取下拉框数据类型失败:服务器内部错误");
}
}
const deleteOneById = (id, action, request) => {
ElMessageBox.confirm('请确认是否删除?', '警告!', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
async function deleteOne(param) {
let result = await request(param);
if (result.data.code === 200) {
ElMessage.success(action + "成功");
loadDataGrid();
selectedId.value = [];
} else {
ElMessage.error(action + "失败:" + result.data.errorMsg);
}
}
if (typeof id === 'number') {
deleteOne(id);
} else {
id.forEach(item => {
deleteOne(item);
});
}
}).catch(() => {});
}
/**
* 删除一条:可以以数组形式删除
* @param {*} id
*/
const deleteById = (id, request) => {
let ids = [];
ids.push(id);
batchDelByIds(ids, request);
}
/**
* 批量删除 参数:ids List集合类型 RquestParam
* @param {*} ids
*/
const batchDelByIds = (ids, request) => {
// 判断ids没有值,不能删除
if (ids.length === 0) {
ElMessage.warning("请选择要删除的数据");
return;
}
ElMessageBox.confirm('请确认是否删除?', '警告!', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(async() => {
// 区分单个删除和批量删除
await request({ ids: ids.join(",") }).then((res) => {
if (res.data && res.data.code === 200) {
ElMessage.success("删除成功");
// 删除成功后,重新显示当前页面,且清空ids
loadDataGrid();
selectedId.value = [];
} else {
ElMessage.error("删除失败");
}
}).catch(error => {
ElMessage.error("服务器内部错误");
});
}).catch(() => {});
}
/**
* 根据id更新记录
* @param {*} id
* @param {*} action
* @param {*} request 请求api方法
*/
const updateById = async(id, action, request) => {
let result = await request({ id });
if (result.data.code === 200) {
ElMessage.success(action + "成功");
loadDataGrid();
} else {
ElMessage.error(action + "失败:" + result.data.errorMsg);
}
}
/**
* 批量更新
* @param {*} ids
* @param {*} action 操作类型
* @param {*} request 请求api方法
*/
const batchUpdateById = async(ids, action, request) => {
let result = await request({ ids: ids.join(",") });
if (result.data.code === 200) {
ElMessage.success(action + "成功");
// 删除成功后,重新显示当前页面,且清空ids
loadDataGrid();
selectedId.value = [];
} else {
ElMessage.error(action + "失败:" + result.data.errorMsg);
}
}
const dialogAddOrEdit = async(visible, action, param, request) => {
dialogVisible.value = visible;
let result = await request(param);
if (result.data.code === 200) {
ElMessage.success(action + "成功");
loadDataGrid();
} else {
ElMessage.error(action + "失败:" + result.data.errorMsg);
}
}
return {
pageNum,
total,
pageSize,
pages,
loading,
tableData,
selectedId,
columns,
data,
allType,
dialogVisible,
loadDataGrid,
getSelectOptions, //下拉框
deleteOneById, //普通删除一条
deleteById, //删除一条,可以ids 数组形式删除
batchDelByIds, //批量删除 ids数组形式删除
updateById, //更新一条
batchUpdateById, //批量更新
dialogAddOrEdit, //弹框(添加或编辑)
}
}
4.使用
// 通过组合函数获取和声明页面所需要的变量及方法
const {
pageNum,
total,
pageSize,
pages,
loading,
selectedId,
tableData,
columns,
data,
allType,
dialogVisible,
loadDataGrid,
getSelectOptions,
deleteById, //删除一条
batchDelByIds, //批量删除
updateById, //更新一条
batchUpdateById, //批量更新
dialogAddOrEdit, //弹框(添加或编辑)
} = useDataGrid({
request: dataGrid, form: resourceForm, tableConfig: {
cnColumnNameArr, hiddenIndex, columnWidth, cellRenderer
}
});
5.问题:获取的响应性数据columns,data等,但columns.value后无值
解决:使用watchEffect(()=>{})即可监听在数据有值后再进行数据操作
watchEffect(() => {
if (columns.value.length > 0) {
columns.value[0].fixed = true
columns.value[1].fixed = TableV2FixedDir.LEFT
columns.value[2].fixed = TableV2FixedDir.LEFT
columns.value[3].fixed = TableV2FixedDir.LEFT
}
data.value.map(item => {
item['状态'] = transferStatus(item['状态']);
});
tableData.value.length>0 && tableData.value.map(item => {
if(typeof item['setted'] === "number") item['setted'] = transferStatus(item['setted']);
});
// 设置类型
if (JSON.stringify(allType.value) == "{}") return;
// 动态设置options
let tempFileType = addFormOptions(allType.value.file_type);
let tempProductionVersion = addFormOptions(allType.value.production_version);
let tempServer = addFormOptions(allType.value.server);
let tempSubmitter = addFormOptions(allType.value.submitter);
let tempSetted = allType.value.setted;
tempSetted = addFormOptions(tempSetted);
tempSetted = tempSetted.map(item => {
item['label'] = transferStatus(item['label']);
return item;
});
configData.value.formItems[1].options = tempFileType;
configData.value.formItems[2].options = tempSubmitter;
configData.value.formItems[3].options = tempServer;
configData.value.formItems[4].options = tempProductionVersion;
configData.value.formItems[5].options = tempSetted;
});