当前位置: 首页 > news >正文

基于 vuestic-ui 实战教程 - 登录篇

1. 简介

登录做为一个系统的门面,也是阻挡外界的一道防线,那在vuestic-ui中如何做登录功能呢。在这里就之间沿用初始版本的Login页面,作为一个演示模板,后续需要改进的读者可以在此篇文章的基础上修改。

在这里插入图片描述

2. 登录接口相关api 与 type编写

在上一篇获取动态数据中 我们已经定义好了与ts整合的axios,实现发送异步请求与远程服务器交互(对于ts语法像是函数定义、基本数据类型还是有不懂的读者可以跳转到上一篇的2.1再学习学习)这里就直接引入登录接口的api编写, 具体位置如下我个人习惯创建一个api文件夹,里面专门存放一些与后端交互的api方法和类型定义(初始quickstart版本是写在上面的page中的,就看个人的编写习惯吧😁只要功能实现了就没问题)

在这里插入图片描述

对于index.ts中主要实现了三个基本的方法,登录登出和获取用户信息

这里没有实现注册功能,因为我实现该网站主要是做一个流量监控的系统,注册功能对于用户不多的情况下其实不太需要,管理员可以直接操作加入数据库中,要是对这快感兴趣的读者也可以自己尝试尝试注册模块的功能实现
本质就是add一个user到数据库中,不过需要注意的是添加验证码等防护措施,防止有不法分子大量注册短时间内打爆服务器!!

import { http } from '../../../utils/request'
import type { LoginData , UserInfoRes} from './types'const requestContent = '/simple/cloud/access'
/*** 登录*/
export function login(loginVo: LoginData) {return http.post<UserInfoRes>(`${requestContent}/login`, loginVo);
}/*** 获取登录用户信息*/
export function getUserInfo() {return http.post<UserInfoRes>(`${requestContent}/info`)
}  /*** 退出登录*/
export function logout() {return http.post<string>(`${requestContent}/logout`)
}  

假设访问的后端服务器使用URL - http:localhost:9001/simple/cloud/access/login 这里由于uri前缀是一样的都是 ‘/simple/cloud/access’ 所以把它提取出来做为一个常量简化编写。只需要在使用的地方通过变量占位符引入就好啦(注意不是 ‘’ ,刚开始也踩过这个坑在vscode中看到上面的requestContent 由灰色变成高亮则说明引用成功)

`${key}`

而对于api中引入的数据类型定义在types.ts中,其中的返回值类型就根据后端提供的接口方法来写,像是我后端返回的类型为一个Map<String,Object> 类型的对象如下图所示,那我就根据这个map中的key和value一一对应写出如下的接口UserInfoRes,然后使用export导出给外部使用
在这里插入图片描述

注意编写的过程中只用指定ts基本的类型(java的List对应的就是ts中的数组 - 使用 [ ] 进行初始化 ),而要是需要返回一个User类型的对象,那就需要重新定义一个UserInfoInterface ,或者在user对应的api处定义types.ts 再在该文件下在引入(我是更推荐这种做法👍

/* 登录接口参数类型 */
export interface LoginData {email: string,password: string,
}/* 用户信息接口返回值类型 */
export interface UserInfoRes {routers: [],buttons: [],roles: [],name: string,token: string,
}

3. 修改Login.vue

定义好与后端交互的方法api后,我们就可以回到前面的Login.vue处修改具体登录逻辑啦,由于初始版本使用的全是静态数据,所以很多功能其实都是不用的,具体删除修改后的模板如下(只保留了一个忘记密码的选项,该功能后续再完善😭先把主要的逻辑跑通先,感兴趣的读者可以先占个坑,后续我一定会回来填坑的!)

<template><VaForm ref="form" @submit.prevent="submit"><h1 class="font-semibold text-4xl mb-4">Log in</h1><VaInputv-model="formData.email":rules="[validators.required, validators.email]"class="mb-4"label="Email"type="email"/><VaValue v-slot="isPasswordVisible" :default-value="false"><VaInputv-model="formData.password":rules="[validators.required]":type="isPasswordVisible.value ? 'text' : 'password'"class="mb-4"label="Password"@clickAppendInner.stop="isPasswordVisible.value = !isPasswordVisible.value"><template #appendInner><VaIcon:name="isPasswordVisible.value ? 'mso-visibility_off' : 'mso-visibility'"class="cursor-pointer"color="secondary"/></template></VaInput></VaValue><div class="auth-layout__options flex flex-col sm:flex-row items-start sm:items-center justify-between"><RouterLink :to="{ name: 'recover-password' }" class="mt-2 sm:mt-0 sm:ml-1 font-semibold text-primary">Forgot password?</RouterLink></div><div class="flex justify-center mt-4"><VaButton class="w-full" @click="submit"> Login</VaButton></div></VaForm>
</template>

重写绑定的submit点击事件逻辑

const submit = () => {if (validate()) {login(formData).then((data: UserInfoRes) => {if (data) {// 在这里添加需要执行的操作const token = data.token;// 将token存储到authStore中const authStore = useAuthStore()authStore.setToken(token)authStore.setIsAuthenticated(true)window.sessionStorage.setItem('isAuthenticated', 'true')authStore.setName(data.name)authStore.setButtons(data.buttons)authStore.setRoles(data.roles)authStore.setRouters(data.routers)init({ message: "logged in success", color: 'success' });// 登陆成功后就重定向到主页面dashboardpush({ name: 'dashboard' })}}).catch(() => {init({ message: "logged in fail , please check carefully!", color: '#FF0000' });});}else{Message.error('error submit!!')return false}
}

看到这里我相信你肯定会疑惑,为什么我需要获取到数据又存储到store中,那这个store又在哪里定义的呢,作者也没讲啊😡
别急别急,请听我细细道来

4. store实现

在Vue应用程序中,当需要管理共享状态时,通常会使用Vuex库,而store就是Vuex中用于存储这些状态的地方,而我们登录后自然需要围护当前登录角色的一些关键信息(权限,姓名等等)需要的时候就直接到store中拿去,而不是反复的去数据库中查找,废话不多说下面就来定义一个store ,在初始版本中就已经定义好了store,只不过这个store里面是没东西的,如下图所示

在这里插入图片描述

那我们就可以在原有的基础上添加修改,下面的代码都是在index.ts中实现的,如下代码就是一个模板,对应pinia库的描述如下
Pinia是Vue的另一种状态管理方案,与Vuex类似,但设计上更简洁、更易于上手。以下是关于Pinia的一些详细说明:

  • 简单易用:Pinia的目标是提供一个更简单的状态管理解决方案,它的API设计非常直观,使得开发者可以快速上手并有效地管理状态。
  • 独立模块:与Vuex不同,Pinia中的每个store都是一个独立的模块,它们可以单独导入和导出,这有助于更好地组织和维护代码。
  • 响应式:Pinia中的状态是响应式的,当状态发生变化时,依赖于这些状态的组件会自动更新。
  • Devtools支持:Pinia具有良好的Devtools支持,可以帮助开发者更方便地跟踪和调试状态变化。
  • 插件化:Pinia被设计为一个插件,可以轻松地集成到现有的Vue应用中。
  • 与Vuex兼容:虽然Pinia是一个全新的状态管理库,但它也允许与Vuex共存于同一个项目中,方便开发者逐步迁移。

本次项目中store就基于Pinia实现,首先通过defineStore方法定义一个全局可供调用的store, 其中包括了一些属性像是

  1. id (自己设定,但是要保证全局唯一)
  2. state (定义的所有状态)
  3. getters (获取状态的方法)
  4. actions (有获取肯定就有设置的方法啦)
// store.ts
import { createPinia, defineStore } from 'pinia'export const useAuthStore = defineStore({id: 'auth',state: () => ({}),getters: {},actions: {},
})export default createPinia()

4.1 state

在state中定义的状态就是在一个浏览器会话内需要存储的用户信息(登录后赋值,登出或者会话结束就销毁)根据第2点中types定义的UserInfoRes 可以设计出来, 由于ts不像js一样是弱语言,ts是有类型的上一讲也提到过,所以为了能在后续的get set中拿到指定和设置其中的属性值,我们需要通过as 参数类型的方式来指定

isAuthenticated 本意是为了阻止用户登录前就访问其他的页面(会被驳回,重定向到登录页面)后面发现存到浏览器缓存中也是可以的,这里就做个备选,看读者喜欢哪一种方式

state: () => ({token : '',isAuthenticated : false,routers : [] as RouterVo[],buttons : [] as string[],name : '',roles : [] as RoleData[],}),

这里的RoleDta和RouterVo就分别对应了角色和菜单列表,具体实现如下(编写在types.ts文件中,具体位置看下边4.4的总体代码)


/* sysUser参数类型 */
export interface RoleData {id: number,roleName: string,roleCode: string,description: string
}/* RouterVo参数类型 */
export interface RouterVo {path: string,hidden: boolean,alwaysShow: boolean,meta: MetaVo,children: RouterVo[],
}

4.2 getters

根据如下的指定格式获取存在store中的参数

getters: {getButtons: (state) => state.buttons,getToken: (state) => state.token,getIsAuthenticated: (state) => state.isAuthenticated,getRouters: (state) => state.routers,getName: (state) => state.name,getRoles: (state) => state.roles,
},

4.3 actions

actions中定义了一系列set方法,可以发现这里()内的参数都是指定类型的,如果我们在定义的时候不指定类型这就会报错!!

actions: {setRoles(roles : RoleData[]) {this.roles = roles},setButtons(buttons : string[]) {this.buttons = buttons},setRouters(routers : RouterVo[]) {this.routers = routers},setName(name : string) {this.name = name},setToken(token : string) {this.token = token},setIsAuthenticated(isAuthenticated : boolean){this.isAuthenticated = isAuthenticated},// 登出后的资源重置reset(){this.roles = []this.name = ''this.buttons = []this.routers = []this.isAuthenticated = falsethis.token = ''},
},

4.4 总体代码

// store.ts
import { createPinia, defineStore } from 'pinia'
import { RoleData } from '@/api/system/sysRole/types'
import { RouterVo } from '@/api/system/sysMenu/types'export const useAuthStore = defineStore({id: 'auth',state: () => ({token : '',isAuthenticated : false,routers : [] as RouterVo[],buttons : [] as string[],name : '',roles : [] as RoleData[],}),getters: {getButtons: (state) => state.buttons,getToken: (state) => state.token,getIsAuthenticated: (state) => state.isAuthenticated,getRouters: (state) => state.routers,getName: (state) => state.name,getRoles: (state) => state.roles,},actions: {setRoles(roles : RoleData[]) {this.roles = roles},setButtons(buttons : string[]) {this.buttons = buttons},setRouters(routers : RouterVo[]) {this.routers = routers},setName(name : string) {this.name = name},setToken(token : string) {this.token = token},setIsAuthenticated(isAuthenticated : boolean){this.isAuthenticated = isAuthenticated},reset(){this.roles = []this.name = ''this.buttons = []this.routers = []this.isAuthenticated = falsethis.token = ''},},
})
// 记得要导出,不在就白定义了 外部通过调用createPinia() 获取示例
export default createPinia()

4.5 main.ts中App引入

在Vue中引入App是因为App.vue通常作为项目的主组件和页面入口文件,负责构建定义及页面组件的归集和切换。定义的组件自然要添加到其中,在初始化的时候就一同创建。在文件原有基础上添加如下代码

import stores from './stores'
import { createPinia } from 'pinia'app.use(createPinia)
app.use(stores)

最后保存就好啦,到这里在回看第3点的submit方法是不是就一目了然
这里提炼出使用store的核心代码,有需要的读者可以直接复制使用😁

// 导入刚刚定义的方法
import { useAuthStore } from '@/stores'// 外部调用创建一个示例(唯一的)
const authStore = useAuthStore()
// 在对应的操作方法里面使用我们在getters和actions中定义的方法
// set
authStore.setToken(token)
// get
const token = authStore.getToken

5. vue限制实现不登录无法进入其他页面

这个模块可用的方法有很多网上也是有各种各样的教程,在这里使用的是设置路由守卫的方法,在router/index.ts下修改,具体做三种判断

  1. 防止重复登录: 登录后的用户不能在登录了,只能主动退出或者关闭浏览器(token失效也是一个,这个后面讲)
  2. 白名单直接放行:对于可以供给全部用户访问的一些静态资源、页面(比如登录页面,和一些docs帮助文档是可以直接访问的)
  3. 没有登录:对于没有登录的用户无法访问系统的资源,为了提防有些通过导航栏修改URL的方法访问
// 设置哪些页面是属于白名单的
const witheList = ["/auth/login"];function isWitheRoute(path : string) {return witheList.includes(path);
}// 全局前置守卫
router.beforeEach((to, from, next) => {const isAuthenticated =  window.sessionStorage.getItem('isAuthenticated');//防止重复登录if (isAuthenticated && (to.path === "/auth/login"))  {Message.info("You have successfully logged in. Please avoid logging in repeatedly! (You can log out if you wish)");return next({ path: from.path ? from.path : "/" });}// 判断如果是白名单就直接放行if (isWitheRoute(to.path)) {next();return;}// 没有登录,强制跳转到登录页面if (!isAuthenticated && to.path != "/auth/login") {Message.info("Please logging first");next({ path: "/auth/login" });return;}  next()
});

5.1. 浏览器缓存

上边埋了一个坑,可以使用浏览器缓存的方法实现该功能,上边代码也看到了window.sessionStorage. 那么这到底是嘛玩意,作用范围生命周期又是什么呢?下面将一一解答:

  1. sessionStorage为Web开发者提供了一种在用户的浏览器中临时存储数据的方式。这种存储方式特定于用户打开的特定窗口或标签页,并且数据只在这个特定的窗口或标签页有效。当用户关闭这个窗口或标签页时,存储在sessionStorage中的所有数据将被清除。这就意味着不同的浏览器窗口或标签页,即使是打开相同的网页,它们之间的sessionStorage数据是不共享的。
  2. sessionStorage的生命周期与用户打开的窗口或标签页的持续时间同步。只要窗口或标签页保持打开状态,即便是进行页面刷新或切换到同源的其他页面,sessionStorage中的数据都将持续存在。然而,一旦窗口或标签页被关闭,sessionStorage中的所有数据将立即失效并被清除。

可以见得通过该方法保存用户的登录状态也是不错之选,而且非正常退出时候也不用担心数据泄露(会自动销毁,后端的数据就需要通过勾子函数回调,或者直接设置redis过期时间就等它自动过期)下边就是三个常用的方法:

对于我们的登录功能来说,在登录成功后设置为true,此时路由守卫判断时候就能获取到该值,而在登出的时候就删除掉该数据,这样就能保证统一

//设置对应的key-value
window.sessionStorage.setItem('isAuthenticated', 'true');//通过getItem获取 (取不到时为null)
const isAuthenticated =  window.sessionStorage.getItem('isAuthenticated');//去除浏览器缓存
window.sessionStorage.removeItem('isAuthenticated')

6. 登出功能实现

登出功能本质上是跟登录没什么区别的,就是后端清除存储的数据token , reids中权限数据等,前端清除login获取到的所有数据(回到出厂设置的感觉)在初始版本中是没有登出这个按钮的,经常登录网页的朋友都知道,登出的按钮一般是在右上角,那这里我们就遵循惯例先找找最上边的栏目是在哪一个vue页面里面(最笨的方法就是一个一个去搜索是否有相应的字眼)

那么我就以我的理解来告诉大家如何快速找到相应的模块。首先要知道的是所有的组件都是放在src/components文件夹下的,那我们就去下边找,一展开就很明显看到navbar的字眼(导航栏嘛,也就是我们要找的上边栏所在位置)点开后发现又有个components(根据上面的知识不用我说都知道这是放组件的吧)点开就看到GitHubButton 这不就是我们要找的上边栏上的github按钮吗,说明我们找对地方了,最终就锁定范围在这两个vue文件中,是不是一下子节省很多工作量😁 , 具体示例文件所在处如下图所示
在这里插入图片描述

找到这个文件后我们预期的效果是跟下图这样加一个Logout 按钮 用户点击就可以退出登录
在这里插入图片描述
在AppNavbarActions这个文件中点开就发现其实实现起来很简单,就是依葫芦画瓢,照抄原来有的button组件就好啦,具体代码如下

<VaButtonv-if="!isMobile"preset="secondary"@click="logoutOper" <!--自定义点击事件-->target="_blank"color="textPrimary"class="app-navbar-actions__item flex-shrink-0 mx-0"
>{{ t('Logout') }}
</VaButton>

因为我们绑定了点击事件,自然要实现的啦(如下代码在script中原有的基础上添加)

import { logout } from '@/api/system/auth/index'
import { useAuthStore } from '@/stores'
import { useToast } from 'vuestic-ui'
import { useRouter } from 'vue-router'
const { push } = useRouter()
const { init } = useToast()const logoutOper = () => {logout().then(() => {const store = useAuthStore() // 获取store实例store.reset() // 重置store//去除浏览器缓存window.sessionStorage.removeItem('isAuthenticated')//跳转路由init({ message: "logout success", color: 'success' });push({ name: 'login' })}).catch(() => {init({ message: "logged out fail , please contact administration", color: '#FF0000' });});
}

7. 每次请求时带上token访问服务器

由于加入了权限认证功能,所以登录后的每一次请求都必须携带上token(这里的token就遵循OAuth2的规范以"Bearer "开头),不然会认为没有登录跳转的登录页面重新登录,在每一次请求中添加请求头是不是就是定义一个全局filter,也就是在上一讲中提到的axios请求拦截器,那如下代码就在utils/request.ts下修改(还没有的请看上一讲)

import { useAuthStore } from '@/stores'/* 请求拦截器 */
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {  const authStore = useAuthStore()if (authStore != undefined) {//获取tokenconst token = authStore.getTokenconfig.headers.Authorization = `Bearer ${token}`;} else {// 如果不存在 token,则拒绝请求并跳转到登录页面window.location.href = '/auth/login';//去除浏览器缓存window.sessionStorage.removeItem('isAuthenticated')return Promise.reject('Authenticated fail');}return config;  }, (error: AxiosError) => {Message.error(error.message);return Promise.reject(error)
})

终于讲完啦,这篇内容挺多的,给看到这里的读者点赞👍,希望能够对你们有所帮助(本篇主要实现前端的功能,后续会结合权限管理给出后端认证授权功能实现,敬请期待…)


各位读者我回来填坑啦,对于上面的后端实现我又写了点自己的想法,感兴趣的读者可以点击查阅后端认证授权功能实现

相关文章:

基于 vuestic-ui 实战教程 - 登录篇

1. 简介 登录做为一个系统的门面&#xff0c;也是阻挡外界的一道防线&#xff0c;那在vuestic-ui中如何做登录功能呢。在这里就之间沿用初始版本的Login页面&#xff0c;作为一个演示模板&#xff0c;后续需要改进的读者可以在此篇文章的基础上修改。 2. 登录接口相关api 与 t…...

SAPUI5基础知识2 - 手动创建一个SAPUI5的项目

1. 前言 在本篇文章中&#xff0c;我们将手动一步一步建立出第一个SAPUI5的 ‘Hello World!’ 项目。 2. 步骤详解 2.1 在BAS中建立Dev Space 进入SAP Business Application Studio的Dev Space Manger&#xff0c;选择创建Dev Space。 勾选HTML5 Application Template插件…...

设计模式--访问者模式

访问者模式是一种行为设计模式&#xff0c;它用于将算法与对象结构分离&#xff0c;使得算法可以独立于使用它的数据结构而变化。这种模式在许多应用场景中非常有用&#xff0c;例如在实现图形算法、数据结构遍历、文件格式转换以及代码分析时。 应用场景 图形算法&#xff1…...

onnx模型转换到rknn脚本

from rknn.api import RKNN ONNX_MODEL ./onnx_models/yolov5s_rm_transpose.onnx # platform"rk1808" platform "rv1109" RKNN_MODEL yolov5s_relu_{}_out_opt.rknn.format(platform) if __name__ __main__: add_perm False # 如果设置成True,则将模…...

防御恶意爬虫攻击

数据抓取爬虫 数据抓取爬虫是攻击者使用自动化脚本或工具在移动应用程序中抓取敏感数据的一种方式。这些爬虫可以定向抓取用户信息、产品列表、评论和评级等数据。攻击者可能会将这些数据用于非法目的&#xff0c;例如进行身份盗窃、诈骗活动或者卖给其他恶意方。 对于移动应用…...

【自动驾驶技术栈学习】2-软件《大话自动驾驶》| 综述要点总结 by.Akaxi

----------------------------------------------------------------------------------------------------------------- 致谢&#xff1a;感谢十一号线人老师的《大话自动驾驶》书籍&#xff0c;收获颇丰 链接&#xff1a;大话自动驾驶 (豆瓣) (douban.com) -------------…...

SRS视频服务器应用研究

1.SRS尝试从源码编译启动 1.1.安装ubuntu 下载镜像文件 使用VMWare安装&#xff0c;过程中出现蓝屏&#xff0c;后将VM的软件版本从15.5升级到17&#xff0c;就正常了。 1.2.更新ubuntu依赖...

没有括号的字符串四则运算

目录 问题分析与解答evalsympy消去法逆波兰表达式拓展思考参考资料 问题 用代码实现一个method&#xff0c;这个method的入参是一个字符串&#xff0c;这个字符串是一个四则运算的算式&#xff0c;比如“12*34/2-3”&#xff1b;返回值是这个算式的运算结果&#xff0c;比如“…...

vue2 $set 后期添加响应式数据的问题,使用vm.$set()

文章目录 后期添加数据的问题后期给Vue的实例添加的属性&#xff0c;会有响应式吗&#xff1f;避免在运行时向vm或其根$data添加响应式 对象的响应式处理想给后期追加的属性添加响应式处理的&#xff0c;有以下俩个方法&#xff1a; 数组的响应式处理解决方案一&#xff1a;解决…...

笔记-X86下用Docker运行ARM64编译Libreoffice

初衷 针对恶略环境下的自适应&#xff0c;记个笔记&#xff0c;苦于没有外网的arm架构环境&#xff0c;内网中安装个arm类型的deb&#xff0c;难如登天&#xff0c;突然发现这个好东西。 参考引用 x86架构的Ubuntu上通过Docker运行ARM架构的系统 前提 docker已经安装好 安…...

力扣:92. 反转链表 II(Java)

目录 题目描述&#xff1a;示例 1&#xff1a;示例 2&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的…...

[less配置]vue2引入less

1、终端输入&#xff1a;npm install less less-loader --save-dev 2、在package.json查看是否安装less依赖 3、调用...

物理内存与虚拟内存的区别

物理内存和虚拟内存是计算机系统中重要的概念&#xff0c;它们有着不同的特点和作用。 物理内存&#xff1a; 物理内存是计算机实际存在的内存&#xff0c;通常指的是RAM&#xff08;随机存取存储器&#xff09;。物理内存直接映射到计算机的物理地址空间&#xff0c;可以直接被…...

MySQL数据库案例实战教程:数据类型、语法与高级查询详解

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

操作系统——用户态与内核态、同步与异步、阻塞与阻塞

文章目录 什么是用户态与内核态同步与异步、阻塞与非阻塞四种组合方式 什么是用户态与内核态 计算机系统中&#xff0c;通常 CPU 执行两种不同性质的程序代码&#xff1a;一种是操作系统内核程序&#xff08;管理程序&#xff09;&#xff1b;另一种是用户自编程序&#xff08…...

C# VSTO读取Excel单元格Value、Value2

对单个单元格的值&#xff0c;需要用object 对象去接 object value (object)oneCellRange.Value; object value2 (object)oneCellRange.Value2; 对矩形范围的值&#xff0c;需要用object[,]去接 object[,] matrixValues (object[,])matrixRange.Value; object[,] matrixV…...

如何快速从手动测试转向自动化测试

寻求具有无缝持续集成和持续交付 (CI/CD) 的高效 DevOps 管道比以往任何时候都更加重要。想象一下这样一个场景&#xff1a;您的软件组织显著减少了人工工作量、降低了成本&#xff0c;并更加自信地发布了软件更新。换句话说&#xff0c;通过将 Web UI 和 API 测试结合在一起&a…...

【Linux+Docker】修改Docker容器中的hosts文件

1、进入容器bash docker exec -it <container_id> bash2、安装编辑器 2.1、安装vim apt-get updateapt-get install vim2.2、安装nano apt-get install nano3、编辑hosts文件 3.1、使用vim编辑 vi /etc/hosts3.2、使用nano编辑 nano /etc/hosts4、安装ping apt-get…...

在VS Code中进行Java的单元测试

在VS Code中可以使用 Test Runner for Java扩展进行Java的测试执行和调试。 Test Runner for Java的功能 Test Runner for Java 结合 Language Support for Java by Red Hat 和 Debugger for Java这两个插件提供如下功能&#xff1a; 运行测试&#xff1a; Test Runner for …...

国内信创web中间件生态

国内信创web中间件生态 东方通 官网https://www.tongtech.com/pctype/25.html 宝蓝德 官网https://www.bessystem.com/product/0ad9b8c4d6af462b8d15723a5f25a87d/info?p101 金蝶天燕 官网 https://www.apusic.com/list-117.html 中创 官网http://www.inforbus.com…...

RimWorld模组管理终极指南:3步掌握RimSort智能排序,告别游戏崩溃烦恼

RimWorld模组管理终极指南&#xff1a;3步掌握RimSort智能排序&#xff0c;告别游戏崩溃烦恼 【免费下载链接】RimSort RimSort is an open source mod manager for the video game RimWorld. There is support for Linux, Mac, and Windows, built from the ground up to be a…...

Chrome Regex Search:如何在网页上使用正则表达式快速查找信息

Chrome Regex Search&#xff1a;如何在网页上使用正则表达式快速查找信息 【免费下载链接】chrome-regex-search 项目地址: https://gitcode.com/gh_mirrors/ch/chrome-regex-search 你是否曾经在浏览网页时&#xff0c;需要查找特定格式的信息却无从下手&#xff1f;…...

专业休闲卤味零食包装设计公司排名榜单盘点-哲仕设计上榜

专业休闲卤味零食包装设计公司排名榜单盘点-哲仕设计上榜休闲卤味零食属于大众刚需休闲食品&#xff0c;涵盖肉类卤制熟食、素菜卤味小吃、真空独立卤包、常温即食卤品、麻辣风干肉干、组合卤味礼盒等品类&#xff0c;广泛适用于居家休闲解馋、办公下午茶加餐、追剧娱乐食用、出…...

不是模型不行,是你没做好特征工程(附完整步骤)

来源&#xff1a;DeepHub IMBA 本文约1800字&#xff0c;建议阅读5分钟本文介绍了特征工程全流程&#xff0c;含数据处理、特征构造与选择。Feature engineering 是机器学习 pipeline 里最关键的一环。算法再好&#xff0c;如果输入数据噪声大、不一致或者缺乏有意义的特征&…...

3分钟掌握京东自动抢购神器:告别“手慢无“的终极指南

3分钟掌握京东自动抢购神器&#xff1a;告别"手慢无"的终极指南 【免费下载链接】autobuy-jd 使用python语言的京东平台抢购脚本 项目地址: https://gitcode.com/gh_mirrors/au/autobuy-jd 还在为京东限时秒杀商品总是抢不到而烦恼吗&#xff1f;面对心仪的热…...

RVC-WebUI语音克隆:如何在5分钟内打造你的专属AI声优

RVC-WebUI语音克隆&#xff1a;如何在5分钟内打造你的专属AI声优 【免费下载链接】rvc-webui liujing04/Retrieval-based-Voice-Conversion-WebUI reconstruction project 项目地址: https://gitcode.com/gh_mirrors/rv/rvc-webui 想象一下&#xff0c;用你自己的声音为…...

手把手教你用VHDL在FPGA上解码IMX214 MIPI视频(基于MC20901 D-PHY,含6套工程源码)

基于VHDL的FPGA视频处理系统实战&#xff1a;从IMX214到高清显示 在嵌入式视觉系统开发中&#xff0c;FPGA因其并行处理能力和低延迟特性&#xff0c;成为视频采集与处理的理想平台。本文将深入探讨如何利用VHDL语言在Xilinx FPGA上构建完整的MIPI视频处理流水线&#xff0c;实…...

深入解析RISC-V CLINT:多核中断与定时器编程实战

1. 项目概述&#xff1a;深入理解SiFive U54内核的CLINT如果你正在基于SiFive的Freedom U540 SoC或者类似的RISC-V多核平台进行嵌入式开发&#xff0c;特别是涉及到操作系统移植、多核启动或者中断管理&#xff0c;那么“CLINT”&#xff08;Core-Local Interruptor&#xff0c…...

FreeRTOS移植避坑指南:当你的芯片不在官方支持列表时(以S3C2440为例)

FreeRTOS移植实战&#xff1a;非官方支持芯片的定制化开发方法论 当你的项目需要将FreeRTOS移植到非官方支持芯片时&#xff0c;整个过程就像在未知海域航行——没有现成的海图&#xff0c;但掌握正确的导航方法同样能到达目的地。以经典的ARM9芯片S3C2440为例&#xff0c;这种…...

FRED应用:背散射教程

这个教程描述一个有散射性质的简单plano-plano透镜&#xff0c;这样一条入射光就会散射回发射方向。教程首先&#xff0c;在FRED中创建一个新的系统&#xff0c;在树视图中的Geometry上右击&#xff0c;选择“Create New Lens…”并在出现的对话框上点OK按钮&#xff0c;在全局…...