2.3 iHRM人力资源 - 路由、左侧菜单栏、处理token失效、退出登录、修改密码

news2024/11/25 1:04:13

iHRM人力资源 - 处理token失效、退出登录、修改密码

文章目录

  • iHRM人力资源 - 处理token失效、退出登录、修改密码
  • 一、退出登录
    • 1.1 处理token失效
    • 1.2 调整下拉菜单
    • 1.3 退出登录
  • 二、修改密码
    • 2.1 弹出层dialog
    • 2.2 表单结构
    • 2.3 表单校验
    • 2.4 表单提交
  • 三、路由
    • 3.1 清理多余组件和路由
    • 3.2 创建路由与页面
    • 3.3 批量创建路由和组件
  • 四、解析左侧菜单渲染
  • 五、显示项目logo

一、退出登录

1.1 处理token失效

流程图如下所示

image-20240122231649850

拦截器在如下所示的位置

image-20240122232240424

// 创建响应拦截器,并且两个参数都是回调函数
service.interceptors.response.use(
  // 请求成功时响应,此时的响应默认包裹了一层data,即response.data才是后台服务返回的内容
  (response) => {
    // 一次性解析出response.data中的三个属性
    const { data, message, success } = response.data
    if (success) {
      // 此时响应正常
      return data
    } else {
      Message({ type: 'error', message: message })
      return Promise.reject(new Error(message))
    }
  },
  // 请求失败时响应
  async(error) => {
    if (error.response.status === 401) {
      Message({ type: 'warning', message: 'token 超时了,请重新登录' })
      // token超时,调用action退出登录
      // dispatch返回的是一个promise,这里会等dispatch执行完再执行路由跳转
      await store.dispatch('user/logout')
      // 主动跳转到登录页
      router.push('/login')
      return Promise.reject(error)
    }
    // this.$message.warning 不能这么使用,因为此时的this不是组件实例对象
    Message({ type: 'error', message: error.message })
    // 默认支持promise的,下面语句相当于终止了当前promise的执行
    return Promise.reject(error)
  }
)

async…await:

我们store.dispatch(‘user/logout’)中的dispatch其实是一个Promise,这里加一个“async…await”是为了将用户的信息全部删除完成后再跳转到登录页router.push(‘/login’)

加上“async…await”后,就会强制等待把用户信息、token全部删除干净了再跳转到登录页

拦截器中需要调用vuex内容

image-20240122235908561

// Mutations类似java中的数据层,只对数据进行操作,不对业务操作(比如数据加减乘除)
const mutations = {
  // 从浏览器缓存删除token
  removeToken(state) {
    // 删除vuex的token
    state.token = null
    // 删除缓存中的token
    removeToken()
  },
  setUserInfo(state, userInfo) {
    state.userInfo = userInfo
  }
    ..........
}


/**
 * actions似java中的业务逻辑层,对逻辑操作,然后向mutations发送数据,在这个业务逻辑中也可以互相调用
 * actions可以做异步操作
 */
const actions = {
  // 退出登录的action
  logout(context) {
    // 删除用户token
    context.commit('removeToken')
    // 删除用户信息(设置用户信息为空对象)
    context.commit('setUserInfo', {})
  },
    ...................
}

1.2 调整下拉菜单

我们现在菜单的内容是英文的形式,现在调整成中文的形式

image-20240309202917031

其实就是页面这部分的内容:

image-20240309222522752

代码中的位置如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码如下图所示:

<template>
  <div class="navbar">
    <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar"/>

    <breadcrumb class="breadcrumb-container"/>

    <div class="right-menu">
      <el-dropdown class="avatar-container" trigger="click">
        <div class="avatar-wrapper">
          <!--用户头像,v-if判断用户头像是否存在-->
          <img v-if="avatar" :src="avatar" class="user-avatar">
          <!--如果用户头像不存在的时候执行下面的v-else,显示用户名的第一个字-->
          <!--当name时null或者undefined时name.charAt(0)会报错,但是当在name之后加上“?”后,如果name为null或者undefined,就不会执行charAt(0),也不会报错了-->
          <!-- "name?" 可选操作符,表示验证name是否一定有值。 此语法需要vue2.7.0之后的版本-->
          <span v-else class="username">{{ name?.charAt(0) }}</span>
          <!--用户名称-->
          <span class="name">{{ name }}</span>
          <!--图标(设置图标,是一个齿轮的样式)-->
          <i class="el-icon-setting"/>
        </div>
        <el-dropdown-menu slot="dropdown" class="user-dropdown">
          <router-link to="/">
            <el-dropdown-item>
              <!--Home-->
              首页
            </el-dropdown-item>
          </router-link>
          <a target="_blank" href="https://github.com/PanJiaChen/vue-admin-template/">
            <el-dropdown-item>
              <!--Github-->
              项目地址
            </el-dropdown-item>
          </a>
          <a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/">
            <el-dropdown-item>
              <!--Docs-->
              修改密码
            </el-dropdown-item>
          </a>
          <!--divided 属性是在列的上面有个分割线,我们去掉-->
          <!--<el-dropdown-item divided @click.native="logout">-->
          <el-dropdown-item @click.native="logout">
            <span style="display:block;">
             <!--Log Out-->
              退出登录
            </span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

1.3 退出登录

实现退出登录功能

image-20240309223116817

我们之前在处理token失效的时候写过退出的Action,我们直接调用就好了,调用完Action,直接将页面跳转到登录页面

<el-dropdown-item @click.native="logout">
  <span style="display:block;">
   <!--Log Out-->
    退出登录
  </span>
</el-dropdown-item>

native:事件的修饰符,此时是修饰@click点击事件,目的是注册组件的根元素的原生事件(也就是H5事件)

因为el-dropdown-item的标签并不是H5的标签,@click.native表示el-dropdown-item标签最终形成的H5的标签去注册H5标签的点击事件

如果不写“.native”表示注册的这个组件的自定义事件,而这个组件本身并没有click这个自定义事件,所以我们需要native触发click点击事件

对于某个标签有没有点击事件,el开头的标签我们开element-ui文档即可,通过下面的文档发现,el-dropdown-item并没有点击事件

image-20240309223851432

  methods: {
    async logout() {
      // 清除用户信息
      await this.$store.dispatch('user/logout')
      // await表示等待上面的代码执行完毕后,执行下面的代码,跳转页面到登录界面
      this.$router.push('/login')
    }
  }

点击“退出登录”后,其实就跳转到了http://localhost:9528/#/login页面

二、修改密码

实现下面的一个效果

说明:超级管理员的密码不可修改,修改密码的时候要有校验功能

image-20240309224505993

修改密码的整体流程

image-20240309225036204

依然是下面这个位置

image-20240309223116817

2.1 弹出层dialog

解释修饰符sync

可以接收子组件传过来的事件和值

我们点击弹出层dialog的“×”号后,所以“showDialog”接收到了el-dialog传过来的false值

这些事情是在el-dialog源码中写的

<template>
  <div>
    <!--放置dialog-->
    <!--title是dialog的标题; :visible.sync用来控制是否显示弹出层 sync作用是点击“×”号时能把弹出层关闭掉-->
    <el-dialog title="修改密码" :visible.sync="showDialog" width="450px">
      <!--放置dialog表单-->
    </el-dialog>
  </div>
</template>
data() {
  return {
    // 控制弹层的显示和隐藏
    showDialog: false
  }
},
  methods: {
    updatePassword() {
      // 弹出层显示
      this.showDialog = true
    }
}

2.2 表单结构

如下图所示的结构

image-20240309231143181

<!--放置dialog-->
<!--title是dialog的标题; :visible.sync用来控制是否显示弹出层 sync作用是点击“×”号时能把弹出层关闭掉-->
<el-dialog title="修改密码" :visible.sync="showDialog" width="450px">
  <!--放置dialog表单-->
  <!--设置完成label-width="120px"后,提示信息就和输入框在同一行了-->
  <el-form label-width="120px">
    <!--label属性其实就是此item的提示信息-->
    <el-form-item label="旧密码">
      <el-input show-password size="small"></el-input>
    </el-form-item>
    <!--show-password 属性表示输入的内容是密文-->
    <el-form-item label="新密码">
      <el-input show-password size="small"></el-input>
    </el-form-item>
    <el-form-item label="重复密码">
      <el-input show-password size="small"></el-input>
    </el-form-item>
    <!--按钮-->
    <el-form-item>
      <el-button size="mini" type="primary">确认修改</el-button>
      <el-button size="mini">取消修改</el-button>
    </el-form-item>
  </el-form>
</el-dialog>

2.3 表单校验

其实就是实现下图所示的功能

image-20240310202632467

<!--放置dialog-->
<!--title是dialog的标题; :visible.sync用来控制是否显示弹出层 sync作用是点击“×”号时能把弹出层关闭掉-->
<el-dialog title="修改密码" :visible.sync="showDialog" width="450px">
  <!--放置dialog表单-->
  <!--设置完成label-width="120px"后,提示信息就和输入框在同一行了-->
  <!--ref属性是为了获取整个表单的属性-->
  <el-form label-width="120px" :model="passForm" :rules="rules" ref="passForm">
    <!--label属性其实就是此item的提示信息-->
    <el-form-item label="旧密码" prop="oldPassword">
      <el-input show-password v-model="passForm.oldPassword" size="small"></el-input>
    </el-form-item>
    <!--show-password 属性表示输入的内容是密文-->
    <el-form-item label="新密码" prop="newPassword">
      <el-input show-password v-model="passForm.newPassword" size="small"></el-input>
    </el-form-item>
    <el-form-item label="重复密码" prop="confirmPassword">
      <el-input show-password size="small" v-model="passForm.confirmPassword"></el-input>
    </el-form-item>
    <!--按钮-->
    <el-form-item>
      <el-button size="mini" type="primary">确认修改</el-button>
      <el-button size="mini">取消修改</el-button>
    </el-form-item>
  </el-form>
</el-dialog>
data() {
  return {
    // 控制弹层的显示和隐藏
    showDialog: false,
    // 修改密码功能表单内容
    passForm: {
      // 旧密码
      oldPassword: '',
      // 新密码
      newPassword: '',
      // 确认密码
      confirmPassword: ''
    },
    // 修改密码功能的表单校验内容
    rules: {
      // 旧密码
      oldPassword: [
        // trigger: 'blur' 表示失去焦点的时候再触发校验功能
        { required: true, message: '旧密码不能为空', trigger: 'blur' },
        {}
      ],
      // 新密码
      newPassword: [
        { required: true, message: '新密码不能为空', trigger: 'blur' },
        { min: 6, max: 16, message: '新密码长度6-16', trigger: 'blur' }
      ],
      // 确认密码
      confirmPassword: [
        { required: true, message: '重复密码不能为空', trigger: 'blur' },
        // 当满足第一个required: true触发规则后,才会触发下面的这个规则
        // 自定义校验规则validator,参数1:rule规则,参数2:value参数值,也是就是重复密码的值参数3:callback必须执行的回调函数
        {
          trigger: 'blur', validator: (rule, value, callback) => {
            // 只有当此方法是牵头函数的时候,此处的this才指代组件实例对象
            if (this.passForm.newPassword === value) {
              // 用户输入的新密码和重复密码是相等的,我们执行一下callback回调函数
              callback()
            } else {
              // 否则就放入一个错误对象
              callback(new Error('重复密码和新密码输入不一致'))
            }
          }
        }
      ]
    }
  }
}

2.4 表单提交

image-20240310210025070

如果调用接口失败的话,我们可以不用处理,我们在拦截器中配置了失败时候的提示信息

image-20240310210127225

接口可以写在下面这个问题里

image-20240310211037761

<!--按钮-->
<el-form-item>
  <el-button @click="btnOK" size="mini" type="primary">确认修改</el-button>
  <el-button @click="btnCancel" size="mini">取消修改</el-button>
</el-form-item>
btnOK() {
  this.$refs.passForm.validate(async isOK => {
    if (isOK) {
      // 表示校验通过,下一步调用接口
      await updatePassword(this.passForm)
      // 只要执行到这里,说明一定是执行成功
      this.$message.success('修改密码成功')
      this.btnCancel()
      // // 关闭Dialog
      // this.showDialog = false
      // // 重置表单
      // this.$refs.passForm.resetFields()
    }
  })
},
btnCancel() {
  // 关闭Dialog
  this.showDialog = false
  // 重置表单
  this.$refs.passForm.resetFields()
}

api请求内容

// 更改用户密码
export function updatePassword(data) {
  return request({
    url: '/sys/user/updatePass',
    method: 'PUT',
    // 下面这行参数可以简写成 data
    data: data
  })
}

修改下面的bug

当我们点击右上角的叉号后,再打开此页面,会出现下面这个情况,还会有表单验证的提示

image-20240310212724782

按理说我们这是重新打开的表单,不能用表单验证提示,所以改一下

其实就是加了一个@close属性

<!--放置dialog-->
<!--title是dialog的标题; :visible.sync用来控制是否显示弹出层 sync作用是点击“×”号时能把弹出层关闭掉-->
<!--除此之外我们还要添加@close="btnCancel,因为我们只添加sync,当关闭dialog再打开后,表单验证的内容还会存在,所以再加一个@close,当dialog关闭后会执行@close-->
<el-dialog title="修改密码" @close="btnCancel" :visible.sync="showDialog" width="450px">
</el-dialog>   

三、路由

3.1 清理多余组件和路由

  1. 我们现在只保留登录页面、主页、404页面,其他的全部删除

image-20240310213219563

下面选中的全部删除

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 删除页面后,我们对应的路由配置也要删除

image-20240310213238532

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/**
 *  Layout @/在vue中代表路径别名
 *  @ 符号表示当前目录的src
 *  @/ 表示src下的layout,而layout又是一个目录,所以会拉取index.vue文件
 *  即index.vue组件就是我们的路由组件,会实现二级路由
 * */
import Layout from '@/layout'

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: 'Dashboard', icon: 'dashboard' }
    }]
  },

  // 404 page must be placed at the end !!!
  // 下面这行路由是兜底的方案,如果找不到页面,就会匹配最后的*,然后跳转到404页面
  { path: '*', redirect: '/404', hidden: true }
]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

目前的首页内容如下图所示

image-20240310213738742

  1. 请求模块多余的内容也删除

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

选中的内容删除

image-20240310213823084

3.2 创建路由与页面

人力资源项目的业务模块如下图所示

image-20240310214152327

建立对应的路由组件-路由配置

image-20240310214337965

其实相当于把上面的八个功能都模块化了,组件模块化、路由模块化

创建department组织架构模块

image-20240310220111172

创建department的路由信息

image-20240310220042378

// 这个相当于一级路由
import layout from '@/layout/index.vue'

// 默认导出
export default {
  // 路由信息
  path: '/department',
  // 一级路由
  component: layout,
  // 二级路由
  children: [
    {
      // 二级路由path为空,表示'/department'路径时显示一级路由+二级路由
      // 并且按需导入department文件下的组件
      path: '',
      component: () => import('@/views/department'),
      // name属性在这里可以用来跳转,也可以用来标记路由
      // 为什么要标记路由?因为我们后面要做权限的控制,对权限做细分化,
      name: 'department',
      // 路由的元信息,其实就是用来存储数据的,比如说图标信息
      // 在我们的基础模板里面读取了meta的icon和title,并显示在了页面左侧菜单上
      meta: {
        icon: 'tree', // 菜单的图标
        title: '组织' // 菜单的标题
      }
    }
  ]
}

在总路由配置中引用department路由

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/**
 *  Layout @/在vue中代表路径别名
 *  @ 符号表示当前目录的src
 *  @/ 表示src下的layout,而layout又是一个目录,所以会拉取index.vue文件
 *  即index.vue组件就是我们的路由组件,会实现二级路由
 * */
import Layout from '@/layout'
import departmentRouter from '@/router/modules/department'

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: 'Dashboard', icon: 'dashboard' }
    }]
  },
  departmentRouter,

  // 404 page must be placed at the end !!!
  // 下面这行路由是兜底的方案,如果找不到页面,就会匹配最后的*,然后跳转到404页面
  { path: '*', redirect: '/404', hidden: true }
]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

主页面内容

image-20240310220410829

3.3 批量创建路由和组件

总路由配置

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/**
 *  Layout @/在vue中代表路径别名
 *  @ 符号表示当前目录的src
 *  @/ 表示src下的layout,而layout又是一个目录,所以会拉取index.vue文件
 *  即index.vue组件就是我们的路由组件,会实现二级路由
 * */
import Layout from '@/layout'
import departmentRouter from '@/router/modules/department'
import approvalRouter from '@/router/modules/approval'
import attendanceRouter from '@/router/modules/attendance'
import employeeRouter from '@/router/modules/employee'
import permissionRouter from '@/router/modules/permission'
import roleRouter from '@/router/modules/role'
import salaryRouter from '@/router/modules/salary'
import socialRouter from '@/router/modules/social'

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: '首页', icon: 'dashboard' }
    }]
  },
  departmentRouter,
  roleRouter,
  employeeRouter,
  permissionRouter,
  attendanceRouter,
  approvalRouter,
  salaryRouter,
  socialRouter,
  // 404 page must be placed at the end !!!
  // 下面这行路由是兜底的方案,如果找不到页面,就会匹配最后的*,然后跳转到404页面
  { path: '*', redirect: '/404', hidden: true }
]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

页面展示

image-20240310222101712

四、解析左侧菜单渲染

我们上面通过建立路由生成了左侧的菜单,那到底是怎么实现的

我们的左侧菜单栏是一个叫“sidebar”的菜单组件,会根据路由组件渲染出左侧的菜单内容

image-20240310223951555

siderbar组件会读取路由信息并且会遍历,然后生成一个叫做siderbarItem的组件且会生成很多个(有多少个路由就会生成多少个)

并不是有多少个siderbarItem就会显示多少个左侧菜单,我们会针对siderbarItem组件进行条件渲染,来判断会不会显示

比如登录、404页面就没有在侧边栏展示

如果确定某个组件显示,我们就又会用上一个组件叫做Item组件,此Item组件会渲染咱们传进去的标题和图标,也是就一个渲染过程

image-20240310224400152

我们查看一下这个组件的代码

image-20240310224502216

五、显示项目logo

在settings.js文件中会有许多的配置选项,其中sidebarLogo属性表示是否显示logo标志

module.exports = {

  title: '人力资源后台管理系统',

  /**
   * @type {boolean} true | false
   * @description Whether fix the header
   */
  fixedHeader: false,

  /**
   * @type {boolean} true | false
   * @description Whether show the logo in sidebar
   */
  sidebarLogo: true
}

image-20240310225909176

当设置为true后,我们这里就会有一个图标

image-20240310225921463

但是这个图标并不是我们想要的,所以我们要去左侧菜单里那里进行修改

image-20240310230028453

<template>
  <!--当菜单栏缩小的时候会有一个collapse,当这个类collapse存在,则就是在缩小的情况下-->
  <div class="sidebar-logo-container" :class="{'collapse':collapse}">
    <transition name="sidebarLogoFade">
      <router-link key="collapse" class="sidebar-logo-link" to="/">
        <img src="@/assets/common/logo.png" class="sidebar-logo">
      </router-link>
    </transition>
  </div>
</template>

<script>
export default {
  name: 'SidebarLogo',
  props: {
    collapse: {
      type: Boolean,
      required: true
    }
  },
  data() {
    return {
      title: 'Vue Admin Template',
      logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
    }
  }
}
</script>

<style lang="scss" scoped>
.sidebarLogoFade-enter-active {
  transition: opacity 1.5s;
}

.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
  opacity: 0;
}

.sidebar-logo-container {
  position: relative;
  width: 100%;
  height: 50px;
  line-height: 50px;
  //background: #2b2f3a;
  text-align: center;
  overflow: hidden;

  & .sidebar-logo-link {
    height: 100%;
    width: 100%;

    & .sidebar-logo {
      width: 140px;
      //height: 32px; 高度自适应
      vertical-align: middle;
      margin-right: 12px;
    }

    & .sidebar-title {
      display: inline-block;
      margin: 0;
      color: #fff;
      font-weight: 600;
      line-height: 50px;
      font-size: 14px;
      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
      vertical-align: middle;
    }
  }

  &.collapse {
    .sidebar-logo {
      margin-right: 0px;
      width: 32px;
      height: 32px;
    }
  }
}
</style>

页面样式

image-20240310230717402

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1600350.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink CDC在阿里云DataWorks数据集成应用实践

摘要&#xff1a;本文整理自阿里云 DataWorks 数据集成团队的高级技术专家 王明亚&#xff08;云时&#xff09;老师在 Flink Forward Asia 2023 中数据集成专场的分享。内容主要为以下四部分&#xff1a; 阿里云 DataWorks 数据集成介绍基于 Flink CDC 数据集成新版引擎架构和…

为硬刚小米SU7,华为智界S7整出了「梅开二度」操作

如今国产中大型新能源轿车市场&#xff0c;在小米 SU7 加入后&#xff0c;可算彻底活了过来。 过去几年&#xff0c;咱们自主新能源品牌在 20-30 万元级轿车上发力明显不足&#xff0c;老牌车厂比亚迪汉几乎以一己之力扛起销量担当。 随着新能源汽车消费升级、竞争加剧&#x…

盲人安全导航技巧:科技赋能让出行更自如

作为一名资深记者&#xff0c;长期关注并报道无障碍领域的发展动态。今日&#xff0c;我将聚焦盲人安全导航技巧&#xff0c;探讨这一主题下科技如何赋能视障人士实现更为安全、独立的出行。一款融合了实时避障、拍照识别物体及场景功能的盲人出行辅助应用叫做蝙蝠避障&#xf…

4月16号总结

java学习 网络编程 1.网络分层 网络分层是将网络通信划分为不同的逻辑层次&#xff0c;每一层负责特定的功能&#xff0c;从而实现网络通信的模块化和标准化。常用的网络分层模型包括OSI&#xff08;开放系统互联&#xff09;模型和TCP/IP模型。 特点和作用&#xff1a; 分…

计算机网络的七层模型

序 OSl(Open System Interconnect)&#xff0c;即开放式系统互联。一般都叫OSI参考模型。在网络编程中最重要的模型就是OSI七层网络模型和TCP/IP四层网络模型 一、OSI七层参考模型以及功能概述 二、各层的具体职能以及实际应用 1.应用层&#xff1a; OSI参考模型中最接近用…

MathType安装导致的Word粘贴操作出现运行时错误‘53’:文件未找到:MathPage.WLL

MathType安装导致的Word粘贴操作出现运行时错误‘53’&#xff1a;文件未找到&#xff1a;MathPage.WLL 解决方案 1、确定自己电脑的位数&#xff1b; 2、右击MathType桌面图标&#xff0c;点击“打开文件所在位置”&#xff0c;然后找到MathPage.WLL &#xff0c;复制一份进行…

ES源码二:集群启动过程

命令行参数解析 Elasticsearch&#xff1a;在main里面创建了Elasticsearch实例&#xff0c;然后调用了main方法&#xff0c;这个main方法最终会调用到父类Command的main方法 这里做了几件事&#xff1a; 注册一个 ShutdownHook&#xff0c;其作用就是在系统关闭的时候捕获IO…

配置路由器实现互通

1.实验环境 实验用具包括两台路由器(或交换机)&#xff0c;一根双绞线缆&#xff0c;一台PC&#xff0c;一条Console 线缆。 2.需求描述 如图6.14 所示&#xff0c;将两台路由器的F0/0 接口相连&#xff0c;通过一台PC 连接设备的 Console 端口并配置P地址&#xff08;192.1…

基于栈求解迷宫的单条路径和所有路径

数据结构与算法课的一个实验&#xff0c;记录一下。 单纯想要了解利用栈求解迷宫的算法可以直接跳转到相应的小标题。 完整代码链接code_2024/mazeLab LeePlace_OUC/code - 码云 - 开源中国 (gitee.com) 文章目录 要求栈的实现MazeType类型的组织迷宫的初始化和销毁打印路径…

死磕GMSSL通信-java/Netty系列(二)

死磕GMSSL通信-java/Netty系列(二) 在上一篇文章中,我们探讨了如何利用C/C++实现国密通信。而本文将聚焦于Java环境下,特别是基于Netty框架,如何实现与国密系统的安全通信。为了确保新项目遵循最新的国密标准,我们将优先推荐使用GB/T 38636-2020(TLCP)协议。对于Java开…

python 如何使用windows系统自带的定时功能运行脚本

亲测此定时可以在电脑重启后&#xff0c;仍会自动运行 1.搜索栏搜索‘任务’&#xff0c;打开任务计划程序&#xff0c;点击创建任务 2.写一下任务名称和描述 3.选择触发器&#xff0c;点新建&#xff0c;可以创建定时计划 4.默认只有一次&#xff0c;每天&#xff0c;每月&am…

BackTrader 中文文档(二十七)

原文&#xff1a;www.backtrader.com/ 数据 - 多个时间框架 原文&#xff1a;www.backtrader.com/blog/posts/2015-08-24-data-multitimeframe/data-multitimeframe/ 有时&#xff0c;使用不同的时间框架进行投资决策&#xff1a; 周线用于评估趋势 每日执行进入 或者 5 分钟…

解决 MSYS2 Qt 6.7 默认 stylesheet 在 windows 11 下的显示故障

项目场景&#xff1a; MSYS2 升级到 Qt6.7.0&#xff0c;发现显示故障&#xff0c;所有Qt6程序以及 QtCreator的SpinBox都显示不全&#xff0c;Combox的底色不对。 问题描述 2024年4月1日&#xff0c;pacman升级MSYS2后&#xff0c;Qt6遇到风格错误。如果使用官方的 Qt onlin…

CSS设置内外边距

目录 内边距&#xff08;paddingj&#xff09;&#xff1a; 前言&#xff1a; 设置内边距&#xff1a; 外边距&#xff08;margin&#xff09;&#xff1a; 前言&#xff1a; 设置外边距&#xff1a; 补充(折叠)&#xff1a; 内边距&#xff08;padding&#xff09;&#…

【程序分享】bopcalc 程序:一种评估分子动力学模拟中键序参数的高效方法(二)

分享一篇使用一种评估键序参数的高效程序&#xff1a;bopcalc 。 感谢论文的原作者&#xff01; 主要内容 “ 我们提出了一种新颖、高效的方法来评估键序参数 (BOP)。我们的方法利用球谐函数和 Wigner 符号的特性来减少 BOP 表达式中的项数&#xff0c;并采用归一化关联勒让德…

APP测试基本流程以及APP测试要点梳理,保证您看了不后悔!

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

使用Pandas解决问题:对比两列数据取最大值的五种方法

目录 一、使用max方法 二、使用apply方法结合lambda函数 三、使用np.maximum函数 四、使用clip方法 五、使用where方法结合条件赋值 总结&#xff1a; 在数据处理和分析中&#xff0c;经常需要比较两个或多个列的值&#xff0c;并取其中的最大值。Pandas库作为Python…

前端大屏项目适配方法

要在F11全屏模式下查看 方法一&#xff0c;rem font-size 动态设置HTML根字体大小 和 body 字体大小&#xff08;lib_flexible.js&#xff09; 将设计稿的宽&#xff08;1920&#xff09;平均分成 24 等份&#xff0c; 每一份为 80px。HTML字体大小就设置为 80 px&#xff…

OSPF大型实验

OSPF大型实验 实验拓扑图 实验思路 1、R4为ISP&#xff0c;其上只配置IP地址&#xff1b;R4与其他所直连设备间均使用公有IP&#xff1b; 2、R3-R5、R6、R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;除了R12有两…

智慧园区可视对讲广播解决方案

智慧园区可视对讲广播解决方案 目前而言智慧园区管理者主要需要解决的问题是&#xff1a; 1.面对庞大的园区小区规模&#xff0c;能源管理全部依赖人工已经无法实现&#xff1b; 2.节能管理工作难度大&#xff0c;面对问题&#xff0c;没有好的解决方案&#xff1b; 3.面临…