最近码代码的时候遇到一些问题,这里自己记录总结一下,供大家参考,说得不对的地方希望大家指出
- 用HbuilderX新建一个uni-app项目 ,vue版本选的2,爬坑轻松一点
- 移动端ui框架选了uView(全量引入),打包的时候没用到的不会打包(可以查看 sourcemap),vant Weapp在微信小程序上面表现不佳,不推荐
- css预处理语言scss,比较接近css写法,小程序样式隔离比较严格,css代码使用样式穿透::v-deep .u-button(框架组件的类名)
- 图标都采用ali-icon加载,代码包可以做一下分包,主包最大2M,最大20M
1、开发环境代理配置无效
代理配置:
H5中代理有效:
微信小程序中异常:
解决办法:后端配置允许跨域,把服务地址直接指定到自己封装的request.js文件中,api.js中的接口配置要把/api前缀去掉,不然404。这里要对uni.request进行封装,使用axios封装在小程序中会报一个不支持使用XMLHttpRequest的错误。
2、scroll-view封装左侧导航,也可以自定义监听oncsrcll事件拿到scrollTop,微信不支持ref
首次渲染时需要计算右侧列表元素的高度(包括类别的高度),uniapp提供了操作dom的方法
//这样的方式存在一些问题,加载的列表不能为空,而且首次加载的数据可能会庞大。后面改造成单次只加载一个门类,做分页上拉加载
//uniapp提供了操作dom的api(微信小程序/H5均支持)
this.$nextTick(()=>{
this.query = uni.createSelectorQuery().in(this);
this.query.select('.scroll-con .cata-name').boundingClientRect();
this.query.select('.scroll-con .item').boundingClientRect().exec((data)=>{
this.headHeihgt=data[0].height;
this.contentHeight=data[1].height;
this.listcata.forEach((item,index)=>{
if(index==0){
this.areaList.push({
startY:0,
endY:this.headHeihgt+this.contentHeight*item.list.length
})
}else{
//这里计算每个滚动区间,与左侧联动
this.areaList.push({
startY:this.areaList[index-1].endY,
endY:this.areaList[index-1].endY+this.headHeihgt+this.contentHeight*item.list.length
})
}
this.areaList.push();
this.orderList.push(item.list.length);
})
});
})
实现效果,左侧点击和右侧滚动联动(双向)
3、自定义组件的使用,不要在子组件修改父组件的值(子组件改数据,在H5中能渲染,但是有逻辑问题,不推荐),通过子组件emit事件在父组件中修改数据,对于通过v-for遍历要传递给子组件的值如果使用v-model语法糖写法,数据需要写成arr[index]的形式,选中的商品存入Vuex,不会丢失响应式,其它页面数据变更,分类列表中的数据也会收到通知
4、错位瀑布流布局,基于grid布局的gridRow属性实现,没有强迫症完全没必要做这样的布局,就像底部的TabBar一样
//这段代码用于计算每个元素的位置,错位布局的重点感觉在于数据的筛选,否则没有足够的内容填充容器
gridLayout(){
this.gridInfo=[];
let flag=true;
//可以调整左右的高度,当前数据会表现成一个回字结构
let arr=[3,4];
for(let i=0;i<this.prefGoods.length;i++){
if(flag){
this.gridInfo.push({
startRow:this.gridInfo[i-2]?this.gridInfo[i-2].endRow:1,
endRow:this.gridInfo[i-2]?this.gridInfo[i-2].endRow+arr[0]:4,
offset:arr[0],
className:arr[0]>arr[1]?'largeSty':'normalSty'
})
}else{
this.gridInfo.push({
startRow:this.gridInfo[i-2]?this.gridInfo[i-2].endRow:1,
endRow:this.gridInfo[i-2]?this.gridInfo[i-2].endRow+arr[1]:5,
offSet:arr[1],
className:arr[1]>arr[0]?'largeSty':'normalSty'
})
}
if((i+1)%2==0){
//反转数组,切换左右的高度变化
arr.reverse();
}
flag=!flag;
}
效果:
PC端准备用Vue3写,后面有问题也接在这里.
书接上回接着写,vue3代码都是在setup语法糖中写的,还是老配方js,后续用ts改一下,先上个效果图
5、多层级别的菜单(理论上可以无限层级),原理主要是组件的递归,直接贴代码了,参考了一下别人vue2的玩法,一开始用vuex做状态管理,感觉太重了,后面用了pinia,直接配置实现持久化,真香~
<template>
<template v-for="(v, index) in props.siderbarMenu">
<el-sub-menu :index="v.name" v-if="v.children && v.children.length > 0" :key="v.name">
<template #title>
<el-icon :size="20">
<span class="iconfont" :class="v.meta.icon"></span>
</el-icon>
<span>{{ v.meta.name }}</span>
</template>
<my-nav :siderbarMenu="v.children">
</my-nav>
</el-sub-menu>
<el-menu-item v-else :key="index" :index="v.name" @click="gotoRoute(v)">
<el-icon :size="20">
<span class="iconfont" :class="v.meta.icon"></span>
</el-icon>
<template #title>{{ v.meta.name }}</template>
</el-menu-item>
</template>
</template>
<script>
export default {
name: 'myNav' //给组件命名
}
</script>
<script setup >
import { onMounted } from 'vue';
import { useRouter } from "vue-router"
import usePermission from '~/store/permission.js'
const permissionStore = usePermission();
const router = useRouter();
const props = defineProps({
siderbarMenu: {
type: Array,
default () {
return new Array();
}
}
})
onMounted(() => {
})
const gotoRoute = function (route) {
permissionStore.currentMenu = route.name;
if (permissionStore.selectMenu.every(item => item.name != route.name)) {
permissionStore.selectMenu.push({
name: route.name,
title: route.meta.name
})
}
router.push({ name: route.name })
}
</script>
里面需要注意一些问题,
1.不要在setup以外的地方使用useStore、useRouter以及useRoute,原因就是无法保证对应的实例已经创建
2.动态路由加载中一定要配置404路由,当用户刷新页面时路由时匹配不到的,会报一个警告,设置临时路由的方式不推荐,太绕了,直接通配解决,路由守卫没有放行用户是看不到404页面的(3g慢速会看到一个白屏)
3.路由拦截中使用pinia需要注意时机
问题1:(引用对应的实例就可以了)
问题2:(配置正则通配,位置可以随意放,不像vue2中“”必须放最后)*
目前差不多就这样,用户菜单数据目前是写死的,接下来撸java,等接口撸完回来搞一搞echarts美化一下。
书接上回,后端的权限设计做完了,基于RBAC模型,一开始想基于用户做权限绑定,但是权限表的数据会十分庞大,因为要做成菜单树返回,因为涉及到递归效率也十分低下,下面是所有的微服务
权限相关的表设计,需要注意的会在图中指出,提一下就是用户和角色是多对多的关系(看到关联表应该明白)
下面是后端鉴权的流程,认证鉴权服务就是上述中的auth-service,主要是生成token,将用户具备的api接口集合存到redis中,用于gateway网关服务放行的参照,这样就控制到了按钮级别。组装前端的菜单树需要一个方法进行迭代,一开始我把按钮权限绑定到了每个路由下(注意不是api),但是vue3中useRoute没办法用,在每个页面写v-if又有点low,最后还是把按钮权限全部收集成一个集合返给前端,前端存pinia(配置了持久化),通过v-directive做按钮权限控制。
下面这段代码用于生成菜单树,我只测试到了四级菜单,理论上可以无限层级,但是没必要哈
//迭代菜单树,从level=1开始
private List<Menu> assemble(List<Menu> routes,List<Menu> menus,Integer level){
//当前遍历的菜单层级
Integer finalLevel = level;
List<Menu> currentLevel=new ArrayList<>();
List<Menu> childrens=new ArrayList<>();
currentLevel=routes.stream()
.filter(m->m.getLevel()== finalLevel).collect(Collectors.toList());
if(currentLevel.stream().count()==0)
return null;
//每个层级下面的子菜单
for (Menu menu : currentLevel) {
level=finalLevel;
//遍历子菜单
childrens=routes.stream().filter(m->m.getParentid()==menu.getId())
.collect(Collectors.toList());
menu.setChildren(childrens);
assemble(routes,menus,++level);
}
return currentLevel;
}
前端代码以及实现效果,当前用户没有sys:order:edit权限
后续完善一下功能,结合业务接入一些高大上一点的功能,目前就这样了,撒花撒花~