springboot + Vue前后端项目(第十七记)

项目实战第十七记

  • 写在前面
  • 1. 个人信息
    • 1.1 Person.vue
    • 1.2 设置路由并改动Header.vue
    • 1.3 动态刷新头像
      • 1.3.1 在保存个人信息时,触发方法
      • 1.3.2 父组件Manage.vue
      • 1.3.3 再将user以prop方式传递给子组件Header.vue
      • 1.3.4 Header.vue使用user
    • 1.4 效果图
  • 2. 修改密码
    • 2.1 前端页面编写(Password.vue)
    • 2.2 修改密码后退出系统
    • 2.3 路由设置并改动Header.vue
    • 2.4 后端接口编写
      • 2.4.1 UserController
      • 2.4.2 UserServiceImpl
      • 2.4.3 UserMapper
    • 2.5 页面效果
  • 总结
  • 写在最后

写在前面

  • 本篇主要讲解个人信息和修改密码页面,补充篇

1. 个人信息

1.1 Person.vue

<template><el-card style="width: 500px;"><el-form label-width="80px" size="small"><el-uploadclass="avatar-uploader"action="http://localhost:9000/file/upload":show-file-list="false":on-success="handleAvatarSuccess"><img v-if="form.avatarUrl" :src="form.avatarUrl" class="avatar"><i v-else class="el-icon-plus avatar-uploader-icon"></i></el-upload><el-form-item label="用户名"><el-input v-model="form.username" autocomplete="off"></el-input></el-form-item><el-form-item label="昵称"><el-input v-model="form.nickname" autocomplete="off"></el-input></el-form-item><el-form-item label="邮箱"><el-input v-model="form.email" autocomplete="off"></el-input></el-form-item><el-form-item label="电话"><el-input v-model="form.phone" autocomplete="off"></el-input></el-form-item><el-form-item label="地址"><el-input v-model="form.address" autocomplete="off"></el-input></el-form-item><el-form-item><el-button type="primary" @click="save">确 定</el-button></el-form-item></el-form></el-card></template><script>export default {name: "Person",data() {return {form: {},user: localStorage.getItem("loginUser") ? JSON.parse(localStorage.getItem("loginUser")) : {}}},created() {this.load();},methods: {load(){const username = this.user.usernameif(!username){this.$message.error("当前无法获取用户信息!");return false}// 数据库做了唯一性处理this.request.get("/user/username/"+username).then(res => {this.form = res.data})},save() {this.request.post("/user", this.form).then(res => {if (res.code === '200') {this.$message.success("保存成功")//this.dialogFormVisible("保存成功")this.load()//向父组件传递值   触发方法this.$emit('refreshUser')} else {this.$message.error("保存失败")}})},handleAvatarSuccess(res) {console.log('===',res)//res就是文件的路径this.form.avatarUrl = res}}}</script><style>.avatar-uploader {text-align: center;padding-bottom: 10px;}.avatar-uploader .el-upload {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;}.avatar-uploader .el-upload:hover {border-color: #409EFF;}.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 138px;height: 138px;line-height: 138px;text-align: center;}.avatar {width: 138px;height: 138px;display: block;}</style>

1.2 设置路由并改动Header.vue

// 拼装动态路由const manageRoute = { path: '/', name: 'Manage', component: () => import('../views/Manage.vue'), redirect: "/home", children: [{ path: 'person', name: '个人信息', component: () => import('../views/Person.vue'),meta: { title: '个人信息' }},{ path: 'password', name: '修改密码', component: () => import('../views/Password.vue'),meta: { title: '修改密码' }}] }

在Header.vue中添加

<el-dropdown-item style="font-size: 14px; padding: 5px 0;"><router-link to="/person">个人信息</router-link>
</el-dropdown-item>

1.3 动态刷新头像

当更换头像时,点击确定时,实现头像的同步更新

1.3.1 在保存个人信息时,触发方法

save() {this.request.post("/user", this.form).then(res => {if (res.code === '200') {this.$message.success("保存成功")this.load()/*这段Vue模板代码定义了一个方法,当调用这个方法时,会触发名为refreshUser的自定义事件。这个事件可以被父组件监听到,从而在父组件中执行相应的逻辑操作。**/this.$emit('refreshUser')} else {this.$message.error("保存失败")}})},

1.3.2 父组件Manage.vue

// 触发@refreshUser自定义事件
<el-main><!--  表示当前页面的子路由会在 <router-view/> 里面显示     --><router-view @refreshUser="getUser"/>
</el-main>// 获取用户的最新数据
getUser(){const username = localStorage.getItem('loginUser') ?         			 JSON.parse(localStorage.getItem('loginUser')).username : '';this.request.get('/user/username/' + username).then(res => {if(res.code == '200'){// 重新赋值后台的最新User数据this.user = res.data;}})}

1.3.3 再将user以prop方式传递给子组件Header.vue

<el-header style="border-bottom: 1px solid #ccc;"><Header :collapse-btn-class="collapseBtnClass" :collapse="isCollapse" :user="user"/>
</el-header>

完整的Manage.vue代码

<template><el-container style="min-height: 100vh"><el-aside :width="sideWidth + 'px'" style="box-shadow: 2px 0 6px rgb(0 21 41 / 0.35);"><Aside :is-collapse="isCollapse" :logo-text-show="logoTextShow"/></el-aside><el-container><el-header style="border-bottom: 1px solid #ccc;"><Header :collapse-btn-class="collapseBtnClass" :collapse="isCollapse" :user="user"/></el-header><el-main><!--  表示当前页面的子路由会在 <router-view/> 里面显示     --><router-view @refreshUser="getUser"/></el-main></el-container></el-container>
</template><script>import Aside from "@/components/Aside";
import Header from "@/components/Header";export default {name: 'HomeView',components: {Aside,Header},data() {return {collapseBtnClass: 'el-icon-s-fold',isCollapse: false,sideWidth: 200,logoTextShow: true,headerBg: 'headerBg',user: {}}},created() {this.getUser()},methods: {collapse() {  // 点击收缩按钮触发this.isCollapse = !this.isCollapseif (this.isCollapse) {  // 收缩this.sideWidth = 64this.collapseBtnClass = 'el-icon-s-unfold'this.logoTextShow = false} else {   // 展开this.sideWidth = 200this.collapseBtnClass = 'el-icon-s-fold'this.logoTextShow = true}},// 获取用户的最新数据getUser(){const username = localStorage.getItem('loginUser') ? JSON.parse(localStorage.getItem('loginUser')).username : '';this.request.get('/user/username/' + username).then(res => {if(res.code == '200'){// 重新赋值后台的最新User数据this.user = res.data;}})}}
}
</script><style>
.headerBg {background: #eee!important;
}
</style>

1.3.4 Header.vue使用user

// 接收数据
props: {collapseBtnClass: String,collapse: Boolean,// 定义一个user属性接受从Manage.vue传进来的user对象user: Object},// 使用数据
<div style="display: inline-block"><img :src="user.avatarUrl" alt=""style="width: 30px; border-radius: 50%; position: relative; top: 10px; right: 5px;overflow: hidden;"><span>{{ user.nickname }}</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
</div>

完整的Header.vue代码

<template><div style="line-height: 60px; display: flex"><div style="flex: 1;"><span :class="collapseBtnClass" style="cursor: pointer; font-size: 18px" @click="collapse"></span><el-breadcrumb separator=">" style="display: inline-block; margin-left: 10px"><el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
<!--        <el-breadcrumb-item>{{ currentPathName }}</el-breadcrumb-item>--><el-breadcrumb-item v-for="(item, index) in breadCrumbs" :key="item.path"><router-link :to="item.path">{{ item.meta.title }}</router-link></el-breadcrumb-item></el-breadcrumb></div><el-dropdown style="width: 100px; cursor: pointer"><div style="display: inline-block"><img :src="user.avatarUrl" alt=""style="width: 30px; border-radius: 50%; position: relative; top: 10px; right: 5px;overflow: hidden;"><span>{{ user.nickname }}</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i></div><el-dropdown-menu slot="dropdown" style="width: 100px; text-align: center"><el-dropdown-item style="font-size: 14px; padding: 5px 0;"><router-link to="/person">个人信息</router-link></el-dropdown-item><el-dropdown-item style="font-size: 14px; padding: 5px 0;"><router-link to="/password">修改密码</router-link></el-dropdown-item><el-dropdown-item style="font-size: 14px; padding: 5px 0"><span style="text-decoration: none" @click="logout">退出</span></el-dropdown-item></el-dropdown-menu></el-dropdown></div>
</template><script>
export default {name: "Header",props: {collapseBtnClass: String,collapse: Boolean,// 定义一个user属性接受从Manage.vue传进来的user对象user: Object},// 当当前路由发生变化时,调用getBreadcrumb方法来更新面包屑导航的数据watch: {$route() {this.getBreadcrumb();}},data(){return {breadCrumbs: [],//user: localStorage.getItem("loginUser") ? JSON.parse(localStorage.getItem("loginUser")) : ""}},created() {this.getBreadcrumb()},methods: {getBreadcrumb(){// 从当前路由的匹配记录中过滤出具有meta属性且包含title属性的路由记录this.breadCrumbs = this.$route.matched.filter(item => item.meta && item.meta.title);},// 退出登录logout(){this.$router.push("/login");localStorage.removeItem("loginUser")this.$message.success("退出成功")}}// computed: {//   currentPathName () {//     return this.$store.state.currentPathName;  //需要监听的数据//   }// },}
</script><style scoped></style>

1.4 效果图

在这里插入图片描述
在这里插入图片描述
注意:上传的头像最好是正方形的大小,形成的头像才会是规整的

2. 修改密码

2.1 前端页面编写(Password.vue)

<template><el-card style="width: 500px;"><el-form label-width="120px" size="small" :model="form" :rules="rules" ref="pass"><el-form-item label="原密码" prop="password"><el-input v-model="form.password" autocomplete="off" show-password></el-input></el-form-item><el-form-item label="新密码" prop="newPassword"><el-input v-model="form.newPassword" autocomplete="off" show-password></el-input></el-form-item><el-form-item label="确认新密码" prop="confirmPassword"><el-input v-model="form.confirmPassword" autocomplete="off" show-password></el-input></el-form-item><el-form-item><el-button type="primary" @click="save">确 定</el-button></el-form-item></el-form></el-card>
</template><script>
export default {name: "Password",data() {return {form: {},user: localStorage.getItem("loginUser") ? JSON.parse(localStorage.getItem("loginUser")) : {},rules: {password: [{ required: true, message: '请输入原密码', trigger: 'blur' },{ min: 3, message: '长度不少于3位', trigger: 'blur' }],newPassword: [{ required: true, message: '请输入新密码', trigger: 'blur' },{ min: 3, message: '长度不少于3位', trigger: 'blur' }],confirmPassword: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 3, message: '长度不少于3位', trigger: 'blur' }],}}},created() {// 通过用户名和旧密码来唯一标识用户,然后再修改密码this.form.username = this.user.username},methods: {save() {this.$refs.pass.validate((valid) => {if (valid) {if (this.form.newPassword !== this.form.confirmPassword) {this.$message.error("2次输入的新密码不相同")return false}this.request.post("/user/password", this.form).then(res => {if (res.code === '200') {this.$message.success("修改成功")this.$store.commit("logout")} else {this.$message.error(res.msg)}})}})},}
}
</script><style>
.avatar-uploader {text-align: center;padding-bottom: 10px;
}
.avatar-uploader .el-upload {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;
}
.avatar-uploader .el-upload:hover {border-color: #409EFF;
}
.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 138px;height: 138px;line-height: 138px;text-align: center;
}
.avatar {width: 138px;height: 138px;display: block;
}
</style>

2.2 修改密码后退出系统

// 退出系统
this.$store.commit("logout")

store文件下index.js

import Vue from 'vue'
import Vuex from 'vuex'
import router from "@/router";Vue.use(Vuex)export default new Vuex.Store({state: {currentPathName: ''},getters: {},mutations: {// setPath(state){//   state.currentPathName = localStorage.getItem('currentPathName')// },logout() {// 清空缓存localStorage.removeItem("loginUser")localStorage.removeItem("menus")router.push("/login")// 重置路由//resetRouter()}},actions: {},modules: {}
})

2.3 路由设置并改动Header.vue

// 拼装动态路由
const manageRoute = { path: '/', name: 'Manage', component: () => import('../views/Manage.vue'), redirect: "/home", children: [{ path: 'person', name: '个人信息', component: () => import('../views/Person.vue'),meta: { title: '个人信息' }},{ path: 'password', name: '修改密码', component: () => import('../views/Password.vue'),meta: { title: '修改密码' }}] }

完整的index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from '../views/Manage.vue'
import store from "@/store";Vue.use(VueRouter)
//定义一个路由对象数组
const routes = [{path: '/login',name: '登录',component: () => import('../views/Login.vue')},{path: '/register',name: '注册',component: () => import('../views/Register.vue')},{path: '/404',name: '404',component: () => import('../views/404.vue')}]//使用路由对象数组创建路由实例,供main.js引用
const router = new VueRouter({mode: 'history',base: process.env.BASE_URL,routes
})// 注意:刷新页面会导致页面路由重置
export const setRoutes = () => {const storeMenus = localStorage.getItem("menus");if (storeMenus) {// 获取当前的路由对象名称数组const currentRouteNames = router.getRoutes().map(v => v.name)if (!currentRouteNames.includes('Manage')) {// 拼装动态路由const manageRoute = { path: '/', name: 'Manage', component: () => import('../views/Manage.vue'), redirect: "/home", children: [{ path: 'person', name: '个人信息', component: () => import('../views/Person.vue'),meta: { title: '个人信息' }},{ path: 'password', name: '修改密码', component: () => import('../views/Password.vue'),meta: { title: '修改密码' }}] }const menus = JSON.parse(storeMenus)menus.forEach(item => {if (item.path) {  // 当且仅当path不为空的时候才去设置路由let itemMenu = { path: item.path.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue'),meta: { title: item.name }}manageRoute.children.push(itemMenu)} else if(item.children.length) {item.children.forEach(item => {if (item.path) {let itemMenu = { path: item.path.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue'),meta: { title: item.name }}manageRoute.children.push(itemMenu)}})}})// 动态添加到现在的路由对象中去router.addRoute(manageRoute)}}
}// 重置我就再set一次路由
setRoutes()// 路由守卫
router.beforeEach((to, from, next) => {// localStorage.setItem('currentPathName',to.name);   // 设置当前的路由名称,为了在Header组件中去使用// store.commit('setPath')    // 触发store的数据更新// 未找到路由情况if(!to.matched.length){const storeMenus = localStorage.getItem("menus");if(storeMenus){   // 有菜单没有找到路由,跳转至 404页面next("/404")}else {    // // 没有菜单,直接跳转至登录页next("/login")}}next()   // 放行路由
})export default router

Header.vue改动

<el-dropdown-item style="font-size: 14px; padding: 5px 0;"><router-link to="/password">修改密码</router-link>
</el-dropdown-item>

完整的Header.vue

<template><div style="line-height: 60px; display: flex"><div style="flex: 1;"><span :class="collapseBtnClass" style="cursor: pointer; font-size: 18px" @click="collapse"></span><el-breadcrumb separator=">" style="display: inline-block; margin-left: 10px"><el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
<!--        <el-breadcrumb-item>{{ currentPathName }}</el-breadcrumb-item>--><el-breadcrumb-item v-for="(item, index) in breadCrumbs" :key="item.path"><router-link :to="item.path">{{ item.meta.title }}</router-link></el-breadcrumb-item></el-breadcrumb></div><el-dropdown style="width: 100px; cursor: pointer"><div style="display: inline-block"><img :src="user.avatarUrl" alt=""style="width: 30px; border-radius: 50%; position: relative; top: 10px; right: 5px;overflow: hidden;"><span>{{ user.nickname }}</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i></div><el-dropdown-menu slot="dropdown" style="width: 100px; text-align: center"><el-dropdown-item style="font-size: 14px; padding: 5px 0;"><router-link to="/person">个人信息</router-link></el-dropdown-item><el-dropdown-item style="font-size: 14px; padding: 5px 0;"><router-link to="/password">修改密码</router-link></el-dropdown-item><el-dropdown-item style="font-size: 14px; padding: 5px 0"><span style="text-decoration: none" @click="logout">退出</span></el-dropdown-item></el-dropdown-menu></el-dropdown></div>
</template><script>
export default {name: "Header",props: {collapseBtnClass: String,collapse: Boolean,// 定义一个user属性接受从Manage.vue传进来的user对象user: Object},// 当当前路由发生变化时,调用getBreadcrumb方法来更新面包屑导航的数据watch: {$route() {this.getBreadcrumb();}},data(){return {breadCrumbs: [],//user: localStorage.getItem("loginUser") ? JSON.parse(localStorage.getItem("loginUser")) : ""}},created() {this.getBreadcrumb()},methods: {getBreadcrumb(){// 从当前路由的匹配记录中过滤出具有meta属性且包含title属性的路由记录this.breadCrumbs = this.$route.matched.filter(item => item.meta && item.meta.title);},// 退出登录logout(){this.$router.push("/login");localStorage.removeItem("loginUser")this.$message.success("退出成功")}}// computed: {//   currentPathName () {//     return this.$store.state.currentPathName;  //需要监听的数据//   }// },}
</script><style scoped></style>

2.4 后端接口编写

2.4.1 UserController

@PostMapping("/password")
public Result updatePassword(@RequestBody UserPasswordDTO userPasswordDTO){if(StrUtil.isBlank(userPasswordDTO.getUsername()) || StrUtil.isBlank(userPasswordDTO.getPassword())){return Result.error(Constants.CODE_400,"参数错误");}userService.updatePassword(userPasswordDTO);return Result.success();}

用UserPasswordDTO接收前端传过来的表单参数

package com.ppj.entity.dto;import lombok.Data;@Data
public class UserPasswordDTO {private String username;private String password;private String newPassword;
}

2.4.2 UserServiceImpl

@Override
public void updatePassword(UserPasswordDTO userPasswordDTO) {int res = userMapper.updatePassword(userPasswordDTO);if(res<1){throw new ServiceException(Constants.CODE_600,"密码修改失败");}
}

2.4.3 UserMapper

package com.ppj.mapper;import com.ppj.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ppj.entity.dto.UserPasswordDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;/*** <p>*  Mapper 接口* </p>** @author ppj* @since 2024-04-20*/
@Mapper
public interface UserMapper extends BaseMapper<User> {int updatePassword(UserPasswordDTO userPasswordDTO);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ppj.mapper.UserMapper"><update id="updatePassword" parameterType="com.ppj.entity.dto.UserPasswordDTO">update sys_userset password = #{newPassword}where username = #{username} and password = #{password}</update></mapper>

2.5 页面效果

在这里插入图片描述

总结

  1. 这篇主要的难点是头像上传后的同步刷新;
  2. 修改密码其实是更新用户,通过用户名和旧密码确定唯一用户,然后才进行密码更改。

写在最后

如果此文对您有所帮助,请帅戈靓女们务必不要吝啬你们的Zan,感谢!!不懂的可以在评论区评论,有空会及时回复。
文章会一直更新

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

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

相关文章

[RK-Linux] RK3562 I2C驱动TP芯片GSL3680

TP芯片GSL3680 1、简介 GSL3680是一款电容屏多点触摸控制单芯片,其芯片内部框架图如下所示: GSL3680采用了独特的互电容感应技术,可以在1ms内测量多达192个节点,支持广泛的传感器选择,包括单层或双层ITO,玻璃或薄膜,采用了先进的抗电磁干扰信号处理技术,使其对各种干…

C++初学者指南第一步---12.引用

C初学者指南第一步—12.引用 文章目录 C初学者指南第一步---12.引用1. 功能&#xff08;和限制&#xff09;1.1 非常量引用1.2 常量引用1.3 auto引用 2.用法2.1 范围for循环中的引用2.2 常量引用的函数形参2.3 非常量引用的函数形参2.4 函数参数的选择&#xff1a;copy / const…

git拉取gitee项目到本地

git安装等不做赘述。 根据需要选择不同操作 1.只是单纯拉取个项目&#xff0c;没有后续的追踪等操作 不需要使用git init初始化本地文件夹 新建一个文件夹用于存储项目&#xff0c;右键选择 git bash here 会出现命令行窗口 如果像我一样&#xff0c;只是拉取个项目作业&…

若以框架学习(3),echarts结合后端数据展示,暂时完结。

前三天&#xff0c;参加毕业典礼&#xff0c;领毕业证&#xff0c;顿时感到空落落的失去感&#xff0c;没有工作&#xff0c;啥也没有&#xff0c;总感觉一辈子白活了。晚上ktv了一晚上&#xff0c;由于我不咋个唱歌&#xff0c;没心情&#xff0c;听哥几个唱了一晚上周杰伦&am…

Redis实战—Redis分布式锁

本博客为个人学习笔记&#xff0c;学习网站与详细见&#xff1a;黑马程序员Redis入门到实战 P56 - P63 目录 分布式锁介绍 基于Redis的分布式锁 Redis锁代码实现 修改业务代码 分布式锁误删问题 分布式锁原子性问题 Lua脚本 编写脚本 代码优化 总结 分布式锁介绍…

express+vue在线im实现【三】

往期内容 expressvue在线im实现【一】 expressvue在线im实现【二】 本期示例 本期总结 支持各种类型的文件上传&#xff0c;常见文件类型图片&#xff0c;音频&#xff0c;视频等&#xff0c;上传时同步获取音频与视频的时长&#xff0c;以及使用上传文件的缓存路径来作为vi…

WDF驱动开发-DMA(一)

在 Windows 7 及更早版本上&#xff0c;Kernel-Mode Driver Framework (KMDF) 仅支持 (DMA) 设备的总线-主直接内存访问。 此类设备包含其自己的 DMA 控制器。 在片上系统 (SoC) 上运行Windows 8及更高版本的平台上&#xff0c;该框架还支持系统模式 DMA&#xff0c;其中多个设…

视频讲解|基于模型预测算法的含储能微网双层能量管理模型【mpc】

1 主要内容 该讲解视频对应的免费程序链接为【防骗贴】基于模型预测算法的含储能微网双层能量管理模型&#xff0c;主要做的是一个微网双层优化调度模型&#xff0c;微网聚合单元包括风电、光伏、储能以及超级电容器&#xff0c;在微网的运行成本层面考虑了电池的退化成本&…

快捷方式(lnk)--加载HTA-CS上线

免责声明:本文仅做技术交流与学习... 目录 CS: HTA文档 文件托管 借助mshta.exe突破 本地生成lnk快捷方式: 非系统图标路径不同问题: 关于lnk的上线问题: CS: HTA文档 配置监听器 有效载荷---->HTA文档--->选择监听器--->选择powershell模式----> 默认生成一…

政务大厅引导系统:AR、VR技术革新引领政务服务体验升级

一、传统政务大厅面临的普遍痛点 随着城市的发展和政务服务需求的增长&#xff0c;传统的政务大厅面临着诸多挑战和痛点&#xff1a; 信息不对称&#xff1a;政务大厅内各部门信息分散&#xff0c;群众难以快速获取全面准确的服务信息&#xff0c;导致办事效率低下。 办事流…

计算机视觉 | 基于图像处理和边缘检测算法的黄豆计数实验

目录 一、实验原理二、实验步骤1. 图像读取与预处理2. 边缘检测3. 轮廓检测4. 标记轮廓序号 三、实验结果 Hi&#xff0c;大家好&#xff0c;我是半亩花海。 本实验旨在利用 Python 和 OpenCV 库&#xff0c;通过图像处理和边缘检测算法实现黄豆图像的自动识别和计数&#xff0…

JetBrains GoLand 2024 mac/win版:高效开发,Go无止境

JetBrains GoLand 2024是一款专为Go语言开发者设计的集成开发环境(IDE)&#xff0c;为开发者带来了更加高效、智能和便捷的编程体验。 GoLand 2024 mac/win版获取 在代码编辑方面&#xff0c;GoLand 2024提供了全行代码补全功能&#xff0c;通过利用先进的深度学习模型&#x…

力扣85.最大矩形

力扣85.最大矩形 遍历所有行作为底边 做求矩形面积&#xff08;84. class Solution {public:int maximalRectangle(vector<vector<char>>& matrix) {if (matrix.empty()) return 0;int n matrix.size(),m matrix[0].size();int res0;vector<int> li…

适耳贴合的气传导耳机,带来智能生活体验,塞那Z50耳夹耳机上手

现在大家几乎每天都会用到各种AI产品&#xff0c;蓝牙耳机也是我们必不可少的装备&#xff0c;最近我发现一款很好用的分体式气传导蓝牙耳机&#xff0c;它还带有一个具备AI功能的APP端&#xff0c;大大方便了我们日常的使用。这款sanag塞那Z50耳夹耳机我用过一段时间以后&…

开发指南033-数据库兼容

元芳&#xff0c;你怎么看&#xff1f; 单一数据库自身就有一些不同处理之处&#xff0c;如果一个平台要兼容所有数据库&#xff0c;就是难上加难&#xff0c;像isnull函数各数据库就不同。 对于这类问题&#xff0c;平台采用统一自定义函数解决&#xff0c;例如上面的round函…

模式分解的概念(下)-无损连接分解的与保持函数依赖分解的定义和判断、损失分解

一、无损连接分解 1、定义 2、检验一个分解是否是无损连接分解的算法 输入与输出 输入&#xff1a; 关系模式R&#xff08;U&#xff0c;F&#xff09;&#xff0c;F是最小函数依赖集 R上的一个分解 输出&#xff1a; 判断分解是否为无损连接分解 &#xff08;1&#x…

JAVA同城服务场馆门店预约系统支持H5小程序APP源码

&#x1f4f1;一键预约&#xff0c;畅享无忧体验&#x1f3e2; &#x1f680;一、开启预约新纪元 在繁忙的都市生活中&#xff0c;我们常常因为时间紧张而错过心仪的门店或场馆服务。然而&#xff0c;有了“门店场馆预约小程序”&#xff0c;这些问题都将迎刃而解。这款小程序…

群辉NAS中文件下载的三种方案

目录 一、迅雷套件 1、添加套件来源 2、安装套件 3、手机安装迅雷 二、qBittorrent套件 1、添加套件来源 2、改手工安装 3、更新后的问题 4、最后放弃DSM6 (1)上传文件手工安装 (2)添加套件来源 5、解决登陆报错 6、添加tracker 7、修改下载默认位置 8、手机…

c++之说_15|成员函数的const尾缀修饰 ( const const)

我记得我刚接触c的时候 遇到成员函数 右边尾部 写了个const 我当时就很蒙 不过慢慢的也从大佬口中获得一二经验了 class kj{public:void get(){printf("无修饰\n");}void get()const{printf("const 修饰\n");}}; 大概就是这个样子 当时我抓耳挠腮的看…

【yolov8语义分割】跑通:下载yolov8+预测图片+预测视频

1、下载yolov8到autodl上 git clone https://github.com/ultralytics/ultralytics 下载到Yolov8文件夹下面 另外&#xff1a;现在yolov8支持像包一样导入&#xff0c;pip install就可以 2、yolov8 语义分割文档 看官方文档&#xff1a;主页 -Ultralytics YOLO 文档 还能切…