版本:3.4.0
技术栈:vue3,vueX,vite,yarn

该项目勇于尝试新技术,3.5.0的某个版本将状态管理工具由vuex替换为了Pinia
Vue官方也推荐使用Pinia(甚至官网都把VueX的链接删了hhh)
但状态管理工具上手起来很方便,且本质都是使用LocalStorge等本地存储

编写风格:vue3语法糖
<script setup> 里编写代码和组件内容,而不是在export default中编写组件。
这种写法可在vue的官方文档-API-单文件组件中看到,是一种语法糖。
也可以在script标签上声明使用ts。

<script setup lang='ts' name= '组件名'>
interface Tree {
  id: number
  label: string
  children?: Tree[]
</script>

声明const类型的引用(ref),来代替曾经在data()中声明变量的方法:

const deptOptions = ref(undefined);
const title = ref("");
const open= ref(false);
const data = reactive({
  form: {},
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    userName: undefined,
    phonenumber: undefined,
    status: undefined,
    deptId: undefined
  rules: {
    userName: [{ required: true, message: "用户名称不能为空", trigger: "blur" }, { min: 2, max: 20, message: "用户名称长度必须介于 2 和 20 之间", trigger: "blur" }],
    nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
    password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }, { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }],
    email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
    phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
});
const { queryParams, form, rules } = toRefs(data);
    /** 新增按钮操作 */
    function handleAdd(row) {
      if (row != null && row.label) {
        form.value.label = row.label;
      } else {
        form.value.label = '顶级分类';
      open.value = true;
      title.value = "添加商品管理";

ES6:const指向对象时类似指针,指向的内存不变,但内部的数据可以变。然后在js中通过变量名.value.属性名的形式改变值,在template中通过变量名.属性名获取值。

  • 类似单例模式?

获得的其实是真正对象的代理(Proxy对象)
let只有局部的作用域,所以声明全局变量时不用

vue教程-createAPP

使用$refs等功能的时候,如果不在组件内,会报错:未定义。要先获得当前实例的代理,然后使用proxy.$refs

const { proxy } = getCurrentInstance();
console.log(form)
ObjectRefImpl {_object: Proxy, _key: 'form', _defaultValue: undefined, __v_isRef: true}
console.log(form.value)
Proxy {searchValue: null, createBy: null, createTime: '2022-04-27 02:37:03', updateBy: null, updateTime: null,}

ruoyi-cloud-vue3使用了vite、yarn
vite热部署?

  • 哪些变化可以触发热部署?比如改了label,就刷新才行

vite.config.js

import { defineConfig, loadEnv } from 'vite'
import path from 'path'
import createVitePlugins from './vite/plugins'
// https://vitejs.dev/config/
export default defineConfig(({ mode, command }) => {
  const env = loadEnv(mode, process.cwd())
  const { VITE_APP_ENV } = env
  return {
    // 部署生产环境和开发环境下的URL。
    // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
    // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
    base: VITE_APP_ENV === 'production' ? '/ruoyi/' : '/',
    plugins: createVitePlugins(env, command === 'build'),
    resolve: {
      // https://cn.vitejs.dev/config/#resolve-alias
      alias: {
        // 设置路径
        '~': path.resolve(__dirname, './'),
        // 设置别名
        '@': path.resolve(__dirname, './src')
      // https://cn.vitejs.dev/config/#resolve-extensions
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
    // vite 相关配置
    server: {
      port: 80,
      host: true,
      open: true,
      proxy: {
        // https://cn.vitejs.dev/config/#server-proxy
        '/dev-api': {
          target: 'http://localhost:8080',
          changeOrigin: true,
          rewrite: (p) => p.replace(/^\/dev-api/, '')
    //fix:error:stdin>:7356:1: warning: "@charset" must be the first rule in the file
    css: {
      postcss: {
        plugins: [
            postcssPlugin: 'internal:charset-removal',
            AtRule: {
              charset: (atRule) => {
                if (atRule.name === 'charset') {
                  atRule.remove();

环境变量:

生产环境:.env.prop
开发环境:.env.development
环境:
然后通过import 导入(貌似vite.config.js直接用env)

网络请求:封装axios

request.js:axios工厂模式创造实例、设置拦截器等。

// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: import.meta.env.VITE_APP_BASE_API,
  // 超时
  timeout: 10000
export default service

ES6:export default 默认暴漏的对象。和单纯的export相比,当其他文件用import导入的名字不用和export导出时的名字一一对应,也可以自己起别名,如:

import request from '@/utils/request'

导航栏是前端的,但左侧菜单栏是从后端拿到的。拿到后还要根据自己权限来看是否要渲染

  • 权限保存到前端哪儿了?

util/ruoyi.js

* 构造树型结构数据 * @param {*} data 数据源 * @param {*} id id字段 默认 'id' * @param {*} parentId 父节点字段 默认 'parentId' * @param {*} children 孩子节点字段 默认 'children' export function handleTree(data, id, parentId, children) { let config = { id: id || 'id', parentId: parentId || 'parentId', childrenList: children || 'children' var childrenListMap = {}; var nodeIds = {}; var tree = []; for (let d of data) { let parentId = d[config.parentId]; if (childrenListMap[parentId] == null) { childrenListMap[parentId] = []; nodeIds[d[config.id]] = d; childrenListMap[parentId].push(d); for (let d of data) { let parentId = d[config.parentId]; if (nodeIds[parentId] == null) { tree.push(d); for (let t of tree) { adaptToChildrenList(t); function adaptToChildrenList(o) { if (childrenListMap[o[config.id]] !== null) { o[config.childrenList] = childrenListMap[o[config.id]]; if (o[config.childrenList]) { for (let c of o[config.childrenList]) { adaptToChildrenList(c); return tree;
  • //用了||的写法来设定默认值,巧妙。看来默认是左边?
  • nodeIds默认是对象也可以数组,直接用[ ] ?
  • [ ]

前端流程梳理

项目结构:

  • api 为每个功能模块分别封装网络接口
  • components 复用的组件
  • layout 整体的页面框架,如菜单栏、tabbar、主体部分
  • views 主题部份内的页面
    • error
    • system等各个模块
  • utils
  • store
  • router
  • App.vue
  • permission.js
  • main.js
  • setting.js
  • 页面显示流程

    App.vue,内有router-view,在里面渲染router.js中配置的第一个路由,即

    path: '/redirect',
        component: Layout,
        hidden: true,
        children: [
            path: '/redirect/:path(.*)',
            component: () => import('@/views/redirect/index.vue')
    

    layout是前端整体框架,包含导航栏、菜单栏、appmain

    • 这里userouter等都是灰色的,是不需要了嘛?封装到main.js了吗?

    因为菜单栏从后端拿(根据用户动态显示),路由后面只配置了导航栏的

    @/views/redirect/index.vue,好疑惑的写法

    <template>
      <div></div>
    </template>
    <script setup>
    import { useRoute, useRouter } from 'vue-router'
    const route = useRoute();
    const router = useRouter();
    const { params, query } = route
    const { path } = params
    router.replace({ path: '/' + path, query })
    </script>
    

    似乎是自动跳转并且带参数
    默认跳转到ocalhost/login?redirect=/index
    index在后面有配置,也是Layout组件。应该是想跳转到index,没登陆被拦截到了login(看参数redirect=/index猜的)

    Q:useRoute干啥的,他咋知道参数是redirect=/index?
    A:vue自带的拿参数的。 params, query是想用两个名字而已

    index的路由

        path: '',
        component: Layout,
        redirect: '/index',
        children: [
            path: '/index',
            component: () => import('@/views/index'),
            name: 'Index',
            meta: { title: '首页', icon: 'dashboard', affix: true }
    

    Layout的appmain组件放主体内容。
    AppMain.vue组件中用

     <component :is="Component" :key="route.path"/>
    

    通过子路由加载@/views/index
    应该是同通过改变route.path改变主题部分的内容。一开始传入的是index。这里也就是一开始放着若依一大堆简介的地方
    以后路由跳转时,route.path也会动态改变,达到改变appmian中component标签的作用

    登录态判断

    在根目录permisson.js中(好多primission.js),定义router.beforeEach方法,在跳转前判断是否有token。要跳转的路径在router.beforeEach方法的参数next方法里跳转
    如果本地没有token,就看是否白名单,白名单内放行不然跳转到登录

    如果有token,
    设置title

    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
    
    • 如果去login,登录了还去干啥,next给他改成/
    • 如果不去login
       if (store.getters.roles.length === 0) {
            isRelogin.show = true
            // 判断当前用户是否已拉取完user_info信息
            store.dispatch('GetInfo').then(() => {
              isRelogin.show = false
              store.dispatch('GenerateRoutes').then(accessRoutes => {
                // 根据roles权限生成可访问的路由表
                accessRoutes.forEach(route => {
                  if (!isHttp(route.path)) {
                    router.addRoute(route) // 动态添加可访问路由表
                next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
            }).catch(err => {
              store.dispatch('LogOut').then(() => {
                ElMessage.error(err)
                next({ path: '/' })
      
      api里的login、getInfo等方法只负责发请求,用store调用,然后store把数据存在本地。
      用store获取用户信息(角色等)、生成路由,都只有每次路由跳转时做

    auth.js只负责GetToken之类的

    动态渲染侧边栏菜单

    根据用户权限、角色动态渲染,发生在router.beforeEach方法中,拿到用户信息后

    // 匹配views里面所有的.vue文件
    const modules = import.meta.glob('./../../views/**/*.vue')
    

    modules是一个数组。然后export了loadView方法,根据传入的view返回.vue文件

    大致思路:

    修改store中的sidebarRoutes数组
    然后layou页面的sideBar组件会根据sidebarRoutes数组动态渲染标签
    根据sidebarRoutes数组动态渲染多个siderbarItem
    通过siderbarItem的item属性将当前sidebarRoutes的值传入
    siderbarItem如果要渲染子菜单,就在自己里面渲染siderbarItem

    根目录下setting.js和layout界面框架

    其实是对应的

    * 是否显示顶部导航 topNav: true,
    • layout
    • appmain 除去导航栏和侧边导航栏的主体部分,views内容在这里
    • navbar 默认固定上放的导航栏
    • tagsview appmain上方的小标签,类似浏览器新页面的标签的那个
    • sidebar 左侧边栏
      • sidebarItem
      • logo
    • index.vue 包含didebar、navbar、appmain

    navbar

    在这里插入图片描述从左到右建议结构依次是

    <div navbar的div> 
    <hamburger id="hamburger-container" :is-active="getters.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
        <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!$store.state.settings.topNav" />
        <top-nav id="topmenu-container" class="topmenu-container" v-if="$store.state.settings.topNav" />
        <div class="right-menu">
    			个人中心等
    		</div>
    </div>
    

    bug集锦

    表单输入不进内容

    默认生成代码的表单的ref都是form,和保存表单内容的form变量重名。
    要修改ref和下面的proxy.resetForm(“form”);

    Edge有些按钮不显示

          v-hasPermi="['forum:item:edit']",默认的权限不对 
                                        百度地图开发java源码
    RuoYi-Cloud-Ant-Design简介
    RuoYi-Cloud-Ant-Design是一套基于若依Cloud微服务版本为后端开发的一套基于Ant
    Design
    Vue的前端项目
    在线体验往下滑↓
    (本项目采取的后端)。
    拉取项目代码
    clone
    git@gitee.com:xuezipeng/ruoyi-cloud-ant-design.git
    install
    开发模式运行
    serve
    build
    eslint规范自动修复
    api(存放接口)
    assets(存放一些静态资源,图片等)
    components(存放组件,由Ant
    Design
    Pro提供)
    config(项目配置文件如全局样式、路由)
    core(项目核心文件夹,用于配置一些必备的组件/图标)
    layouts(项目布局文件夹,路由以及固定的侧边栏所采用的组件都在这里哦)
    若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
    采用前后端分离的模式,微服务版本前端(基于 )。
    后端采用Spring Boot、Spring Cloud & Alibaba。
    注册中心、配置中心选型Nacos,权限认证使用Redis。
    流量控制框架选型Sentinel,分布式事务选型Seata。
    如需不分离应用,请移步 ,如需分离应用,请移步
    阿里云折扣场:,腾讯云秒杀场:  
    阿里云优惠券:,腾讯云优惠券:  
    友情链接  Ant Design版本。
    com.ruoyi     
    ├── ruoyi-ui              // 前端框架 [80]
    ├── ruoyi-gateway         // 网关模块 [8080]
    ├── ruoyi-auth            // 认证中心 [9200]
    ├── ruoyi-api