工作两年,从自己造轮子,变成使用开源项目!

文章目录

为什么从想自己造轮子,变成使用开源项目!

菜鸟在之前是非常喜欢自己从头开始搭建项目的,原因如下:

  1. 用开源的东西,需要看懂大部分代码,才能知道如何使用,很多情况下,作者的思维和我们的思维是不一样的,所以很难接受或者看懂全部代码!

  2. 菜鸟之前在学校或者学习时,都是从头开始搭建的,可能也是一种习惯,但是感觉更重要的是,菜鸟感觉从头搭建,菜鸟才感觉这个项目由自己做主的感觉!

但是菜鸟在上个项目中发现,其实很多东西确实用别人的会更好一点!例如:

  1. 项目的权限管理、角色管理等,菜鸟在上个项目时,权限管理、角色管理都是自己手写的,但是后来发现,菜鸟实现的其实只是简单的功能,很多都没有开源的项目那么 复杂好用

  2. 开源项目会自带日志管理等功能,这种前端和后端都可以复用,避免前后端都麻烦!

  3. 菜单权限、按钮权限,这些开源项目比较成熟,且解决办法比较正统。菜鸟自己实现的需要我亲自去改数据库(整理出对应的结构),然后请求后端接口之后还要在前端进行处理,耦合度有点高,管理度不够!

  4. 看别人的代码并非全是缺点,其实是可以学到大佬的思维方式,以及遇见一些问题如何处理!虽然过程有点难,但是相信菜鸟,当你熟练使用后,你会有所提升!如果你还能自己思考怎么用自己的方法解决一些作者没处理好的问题、或者优化一些地方,那么你会发现这更是一种质的飞跃!

注意

底下的内容主要讲菜鸟如何实现一些功能,以及为什么要用开源框架,并没有涉及具体开源框架好在什么地方,可以酌情观看!

菜鸟感觉只有公司之前开发了现成的框架模板 或者有需求自己开发一套模板,否则其实都更推荐使用开源框架,当然也可以直接将开源框架,改成公司的一套模板也行,按照自己的需求进行修改即可。

有知名度的开源框架的作者都是大佬,肯定比我们这些只工作几年的要强多了,而且我们可能也接触不到基层建设,更多的都是业务逻辑,所以这些方面肯定不如大佬!

用户管理、日志管理

这部分比较简单,其实自己实现简单的功能也问题不大,但是用开源项目可以方便前端和后端,避免重复劳动。我们自己实现不仅需要开发时间,还需要时间去实践,判断是否完善,而且还不一定有开源的项目那么完整的功能!

注意

这里菜鸟没有实现日志管理,因为没有这个功能需求!

菜鸟实现的界面截图

用户管理/分配角色

角色管理 -- 权限问题

角色管理/菜单权限

到这里问题就慢慢出现了!

权限的选取

js 复制代码
<script setup>
import { updateRoleApi, getMenuListApi } from '@/network/roleApi'
import _ from 'lodash'

const props = defineProps({
  dialogVisible: {
    type: Boolean,
    default: false
  },
  detailData: {
    type: Object,
    default: () => ({})
  }
})

const emit = defineEmits(['closeEvent'])

// 关闭弹窗
function handleClose() {
  emit('closeEvent', false)
}
const dialogBox = ref()
function closeDialog() {
  dialogBox.value.resetFields()
}

// 新建
let roleForm = ref()
let detailDataSelf = ref(_.cloneDeep(props.detailData))
const rules = reactive({
  roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
  role: [{ required: true, message: '权限字符不能为空', trigger: 'blur' }],
  menuPaths: [{ required: true, message: '请选择菜单权限', trigger: 'change' }]
})

// 判断是否修改过
let isChange = ref(true)
// 选择节点
// eslint-disable-next-line
const getTreeNode = (a, b, c, d) => {
  console.log(a, b, c, d)
  // 循环修改display的值
  for (let i of menuData.value) {
    if (b.checkedKeys.includes(i.id)) {
      i.display = true
    } else {
      i.display = false
    }

    if (i.children.length > 0) {
      for (let j of i.children) {
        if (b.checkedKeys.includes(j.id)) {
          j.display = true
        } else {
          j.display = false
        }

        if (j.children.length > 0) {
          for (let k of j.children) {
            if (b.checkedKeys.includes(k.id)) {
              k.display = true
            } else {
              k.display = false
            }
          }
        }
      }
    }
  }
  detailDataSelf.value.menuPaths = menuData.value
  if (detailDataSelf.value.menuPaths) {
    isChange.value = false
  }
}

// 提交
const onSubmit = async (formEl) => {
  if (!formEl) return
  await formEl.validate((valid) => {
    if (valid) {
      updateRoleApi(detailDataSelf.value)
        .then((res) => {
          console.log(res)
          if (res.code == 200) {
            ElMessage({
              message: '添加成功!',
              type: 'success'
            })
            handleClose()
          } else {
            ElMessage({
              message: res.message,
              type: 'error'
            })
          }
        })
        .catch((err) => {
          console.log(err)
        })
    }
  })
}

// 从后端获取菜单列表
let menuData = ref([])
// 获取被选中的menu
const cheackMenu = ref([])
async function getMenuList() {
  const res = await getMenuListApi(detailDataSelf.value.id)
  console.log(res)
  if (res.code === 200) {
    menuData.value = res.data
    // 获取被选中的menu
    for (let i of menuData.value) {
      if (i.display == true && i.children.length === 0) {
        cheackMenu.value.push(i.id)
      }
      if (i.children.length > 0) {
        let index_j = 0
        for (let j of i.children) {
          if (j.display == true && j.children.length === 0) {
            cheackMenu.value.push(j.id)
          }
          if (j.children.length > 0) {
            let index_k = 0
            for (let k of j.children) {
              if (k.display == true) {
                cheackMenu.value.push(k.id)
                index_k++
              }
            }
            if (index_k === j.children.length) {
              cheackMenu.value.push(j.id)
              index_j++
            }
          }
        }
        if (index_j === i.children.length) {
          cheackMenu.value.push(i.id)
        }
      }
    }
    console.log(cheackMenu.value)
  } else {
    ElMessage({ message: res.message, type: 'error' })
  }
}
getMenuList()

// 树形结构配置
const defaultProps = {
  children: 'children',
  label: 'name'
}
</script>

<template>
  <el-dialog
    title="编辑角色"
    ref="dialogBox"
    :modelValue="dialogVisible"
    :before-close="handleClose"
    @close="closeDialog"
    :close-on-click-modal="false"
    :destroy-on-close="true"
  >
    <el-form
      ref="roleForm"
      style="max-width: 600px"
      label-width="auto"
      :model="detailDataSelf"
      :rules="rules"
    >
      <el-form-item label="角色名称" prop="roleName">
        <el-input v-model="detailDataSelf.roleName" />
      </el-form-item>
      <el-form-item label="权限字符" prop="role">
        <el-input v-model="detailDataSelf.role" />
      </el-form-item>
      <el-form-item label="菜单权限" prop="menuIds">
        <el-tree
          style="max-width: 600px"
          :data="menuData"
          show-checkbox
          :props="defaultProps"
          node-key="id"
          default-expand-all
          :default-checked-keys="cheackMenu"
          @check="getTreeNode"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit(roleForm)" :disabled="isChange">提交</el-button>
        <el-button @click="handleClose">关闭</el-button>
      </el-form-item>
    </el-form>
  </el-dialog>
</template>

这里只有配置权限的界面,但是每个权限是什么东西,都是我去数据库里面加的,如果变动,只能我自己去修改数据库,就没有开源项目那样可以全部都进行管理,该功能自己写的话,也是不小的工作量!

其次,登录的时候我又会获取权限( 菜鸟这里想了一下,是后端的问题,后端返回的数据应该要去除display为false的给我,不然别人可以通过改前端缓存的权限来修改权限

js 复制代码
// 获取后端返回的全部的数据
const menuList = JSON.parse(localStorage.getItem('menuList'))
const menuData = _.cloneDeep(menuList)

// 按钮权限 --》 这里就不是很优雅,我这是确定了只有目录-菜单-按钮,如果多几层就要改代码
const authArr = [] // 权限数组
for (let i of menuList) {
  if (i?.children?.length > 0) {
    for (let j of i.children) {
      if (j?.children?.length > 0) {
        for (let k of j.children) {
          // console.log(k)
          if (k.display === true) {
            authArr.push(k.path)
          }
        }
      }
    }
  }
}
localStorage.setItem('Auth', JSON.stringify(authArr))
AuthStore.SET_AUTHARR(authArr)

// 菜单权限 --》 这里的问题同上
for (let i of menuData) {
  if (i?.children?.length > 0) {
    for (let j of i.children) {
      if (j?.children?.length > 0) {
        for (let k of j.children) {
          if (k.display === true) {
            j.display = true
            i.display = true
          }
        }
        j.children = []
      }
    }
  }
}

菜鸟判断是否显示菜单是这样的

menu.vue

js 复制代码
<script setup>
import menuItem from './menuItem.vue'

const props = defineProps({
  // 打开的菜单
  openmenu: {
    type: Array,
    default: () => {
      return []
    }
  },
  // 菜单内容
  menucontent: {
    type: Array,
    default: () => {
      return []
    }
  },
  // 控制是否只打开一个
  openone: {
    type: Boolean,
    default: false
  },
  // 控制横向竖向
  mode: {
    type: String,
    default: 'vertical'
  }
})

const emit = defineEmits(['menuSelect'])
const menuSelect = (e) => {
  emit('menuSelect', e)
}
</script>

<template>
  <el-menu
    class="elMenu"
    ref="elMenu"
    :router="true"
    :unique-opened="openone"
    :default-openeds="openmenu"
    :default-active="openmenu[0]"
    :mode="mode"
    @select="menuSelect"
    :ellipsis="false"
  >
    <!-- 递归动态菜单 -->
    <menuItem :menudata="menucontent"></menuItem>
  </el-menu>
</template>

<style lang="scss" scoped>
.el-menu {
  border: none;
}
</style>

menuItem.vue

js 复制代码
<script setup>
const props = defineProps({
  menudata: {
    type: Array,
    default: () => {
      return []
    }
  }
})
</script>

<template>
  <template v-for="(item, index) in menudata">
    <template v-if="item?.children?.length > 0 && item?.display">
      <el-sub-menu :key="index" :index="item.path">
        <template #title>
          <el-icon v-if="item.icon">
            <span class="iconfont" :class="item.icon"></span>
          </el-icon>
          <span>{{ item.name }}</span>
        </template>
        <menuItem :menudata="item?.children"></menuItem>
      </el-sub-menu>
    </template>

    <template v-else-if="item?.display">
      <el-menu-item :index="item.path" :key="index + '1'">
        <el-icon v-if="item.icon">
          <span class="iconfont" :class="item.icon"></span>
        </el-icon>
        <template #title>
          {{ item.name }}
        </template>
      </el-menu-item>
    </template>
  </template>
</template>

<style lang="scss" scoped>
.hotBox {
  margin-left: 10px;
  width: 20px;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  img {
    width: 100%;
  }
}
</style>
<style>
.el-menu--popup {
  min-width: 100px;
}
</style>

这里菜鸟感觉封装还行,但是对比开源框架还是差了不少!

菜鸟判断按钮是否显示是这样实现的

js 复制代码
/**
 * 判断权限
 * 使用:import { useAuth } from '@/hooks/useAuth.js'
 *       let { btnAuth, examineAuth } = useAuth()
 */
import { useAuthStore } from '@/store/authority.js'
const AuthStore = useAuthStore()

export function useAuth() {
  // 按钮权限判断
  function btnAuth(str) {
    return AuthStore.authArr.indexOf(str) > -1
  }

  // 审核权限判断
  function examineAuth(id) {
    if (id == localStorage.getItem('userid')) {
      return true
    }
    return false
  }

  return { btnAuth, examineAuth }
}

这里大家也可以看出来,没有直接开源框架的自定义指令来得优雅!

推荐:若依框架

菜鸟公司现在使用的是若依框架,感觉若依比较知名,且其含有很多不同类型的开发技术,可以满足大部分前端+后端的技术需求!

基本功能很完善,bug暂时没有遇见,因为这种开源框架一般只需要其基本的权限等功能,其他界面基本上还是要我们自己开发!

若依的项目文档也挺完善,但是我们公司的大佬也指出了一些问题,若依的一些实现也不是很优雅,有很多是按照vue2的想法去用vue3实现的,比如:使用了vue3中没有的this、部分打破了单向数据流......

感觉使用框架,就是可以节约一些基层搭建,让我们更好的关注我们要开发的业务逻辑上!

若依官网:https://doc.ruoyi.vip/ruoyi/

若依的权限管理

这里可以控制更多东西,自己实现不是一时半会可以搞定的!

代码生成功能


这里前端生成的风格偏向vue2,也可以修改生成内容(修改比较费事且生成的只能是单表的增删改查,酌情判断)!

相关推荐
cindershade6 小时前
Vue3 实时音频录制与转写 Composable 技术实现
前端
张风捷特烈6 小时前
Flutter&TolyUI#12 | 树形组件 toly_tree 重磅推出!
android·前端·flutter
柯南二号6 小时前
【大前端】【Android】一文详解Android MVVM 模式详情解析
android·前端
Mintopia6 小时前
⚛️ 深入学习 React Fiber 架构的思路分析
前端·react.js·架构
页面魔术7 小时前
⭐看完vite纪录片才知道尤大有多屌(上)
前端·javascript·vue.js
UpgradeLink7 小时前
Electron 项目使用官方组件 electron-builder 进行跨架构打包
前端·javascript·electron
周杰伦_Jay7 小时前
【Agent智能体】开发流程与开源框架对比(GitHub热门项目分析)
开源·github
蚂蚁不吃土&7 小时前
cmd powershell svm nodejs npm
前端·npm·node.js
Moment7 小时前
别再让 JavaScript 抢 CSS 的活儿了,css原生虚拟化来了
前端·javascript·css