笔者一年前做B端项目的时候,用的是Vue框架,开发中发现很多场景的相似性,且Vue语法相对React而言不够简洁,因此提炼出了共性的、高复用的代码片段(仅适用于新手水平),虽然当时只是经验不足的小小实习生一枚,且此后主要做React开发,但总结的这些片段依旧具有一定的意义,故整理出来:
表单验证
formRef.value
.validateFields() // 验证表单中所有字段的值是否符合预期的规则
.then()
.catch(() => {
btnLoading.value = false; // 取消按钮的Loading状态
emits("search", formState.value); // 将表单的值传递给父组件
});
父子组件传值
// 子组件
const props = defineProps({
options: {
type: Array,
required: true,
},
formData: {
type: Object,
default: () => ({}),
},
});
// 父组件
对表格数据作特殊展示
例如对身份证号进行加密展示,只需要定义一个字符串的替换规则(replace()+正则匹配)
将原本的数据替换之后使用{{}}动态展示
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'idCard'">
{{ maskedId(record.idCard) }}
</template>
</template>
const maskedId = (idCard: number | string) => {
return idCard
.toString()
.replace(
/(?<=\d{3})\d{12}(?=\d{2})|(?<=\d{3})\d{15}(?=\d{3})/,
"************"
);
};
又例如对所选择的日期进行Date类型格式转换,
需要将收到的字符串形式通过 new Date(Date.parse(start))转换 —— 日期格式;
如果是时间戳(一串数字)就只需要Date.parse方法;
<a-form-item label="活动时间">
<a-range-picker
v-model:value="activityDuration"
show-time
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
@change="handleRangeChange"
></a-range-picker>
</a-form-item>
const handleRangeChange = function (value: string[]) {
if (value) {
const [start, end] = value;
formState.value.activityStart = new Date(Date.parse(start));
formState.value.activityEnd = new Date(Date.parse(end));
}else {
formState.value.activityStart = null;
formState.value.activityEnd = null;
}
};
限制用户输入类型
可以使用onkeyup事件去替换replace所输入的字符串;然后在提交时再转换为数字;
<a-form-item label="活动编号">
<a-input
v-model:value="formState.activityId"
placeholder="请输入活动编号"
onkeyup="value=value.replace(/[^0-9]/g, '')"
></a-input>
</a-form-item>
优雅的请求处理
项目一般都会拦截请求失败的情况并进行统一处理,因此在调用接口时可以只处理请求成功的逻辑;
await listOrder({
pageIndex: 1,
pageSize: 4,
traceId: uniqueId,
platform: 1,
...params,
}).then(({ data }) => {
console.log("sellerOrderList", data.sellerOrderList);
tableData.value = data.sellerOrderList;
total.value = data.total;
});
// 或者
await listExchangeRules(params).then(({data})=>{
if (data.totalElements) {
formData.value = data.content
total.value = data.totalElements;
console.log("loadTable", "total", total.value);
} else {
formData.value = null;
total.value = 0;
}
})
三级类目懒加载
const loadData = async (selectedOptions: any) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
console.log(targetOption.value);
await querySubCategory({ parentId: targetOption.value }).then(({ data }) => {
targetOption.loading = false;
// 方案一:根据返回的data判断是否显示展开符
// if (data.length == 0) {
// targetOption.isLeaf = true;
// console.log(targetOption);
// }
targetOption.children = data.map((item: any) => {
const { categoryId, categoryName } = item;
console.log(data.length, data.length == 0);
// 方案二:当目前是两级类目,第三级目录则不显示展开符
return selectedOptions.length == 2
? {
value: categoryId,
label: categoryName,
children: [],
}
: {
value: categoryId,
label: categoryName,
children: [],
// 目前为一级类目时,默认显示展开符
isLeaf: false,
};
});
});
};
子组件分页功能
const total = toRef(props, "total");
const pagination = ref({
current: 1,
defaultPageSize: 20,
total: 0,
showTotal: () => `共 ${total.value} 条`,
onChange: (current: number, size: number) => {
pagination.value.current = current;
pagination.value.defaultPageSize = size;
// 把新的页码发给父组件调用接口请求
emits("pageChange", pagination.value.current);
},
});
// 必须watch总数total的变化,否则total永远是0
watch(total, (newValue: number) => {
pagination.value.total = newValue;
// 父组件loadTable后,子组件可以监听到数据变化,此时把当前页码设置为1
pagination.value.current = 1;
console.log("watch-total", total.value, "cur", pagination.value.current);
});
本文的分享就到这里,谢谢观看~