Go语言项目实战班04 Go语言课程管理系统项目实战 20240807 课程笔记和上课代码

news2024/9/24 1:16:14

预览

在这里插入图片描述

课程特色

本教程录制于2024年8月8日,使用Go1.22版本,基于Goland2024进行开发,采用的技术栈比较新。

每节课控制在十分钟以内,课时精简,每节课都是一个独立的知识点,如果有遗忘,完全可以当做字典来查询,绝不浪费大家的时间。

整个课程从两行代码实现注册登录API接口讲起,以一个课程系统为实战,结合Vue3开发的前端,实现一个基本的前后端分离的课程管理系统,层层递进,学习路径平缓。

Golang是当前国内越来越多的企业正在全面转的一门系统级别的高性能的编程语言,比C语言写法更加的简单,比Python性能更加的好,是新时代的C语言,建议每个程序员都掌握!

视频课程

最近发现越来越多的公司在用Golang了,所以精心整理了一套视频教程给大家,这个是其中的第15部,后续还会有很多。

视频已经录制完成,完整目录截图如下:

在这里插入图片描述

本套录播课的售价是319元。

本套课程的特色是每节课都是一个核心知识点,每个视频控制在十分钟左右,精简不废话,拒绝浪费大家的时间。

课程目录

  • 01 概述
  • 02 搭建项目环境
  • 03 连接MySQL数据库
  • 04 课程表的设计和创建
  • 05 项目工程化
  • 06 整合gin框架
  • 07 两行代码自动生成注册和登录接口
  • 08 封装路由模块
  • 09 实现新增课程的接口
  • 10 实现分页查询的接口
  • 11 实现根据ID查询课程的接口
  • 12 解决根据ID查询不生效的问题
  • 13 实现根据ID修改课程的接口
  • 14 实现根据ID删除课程的接口
  • 15 前端界面的整体预览和开发思路
  • 16 实现登录的功能
  • 17 实现记录token和跳转首页的功能
  • 18 实现显示登录用户名的功能
  • 19 实现注销的功能
  • 20 解决注销按钮无法自动显示的BUG
  • 21 完善写作页面和双向绑定变量的设计
  • 22 给写作接口添加简单的权限校验
  • 23 实现添加文章的功能
  • 24 实现文章的请求和动态渲染
  • 25 将秒值转换为年月日字符串
  • 26 实现点击文章标题跳转详情页面的功能
  • 27 实现文章详情的渲染
  • 28 渲染课程的价格
  • 29 渲染编辑按钮
  • 30 实现编辑课程的功能
  • 31 给编辑文章的接口添加简单的权限校验
  • 32 总结

完整代码

03 连接MySQL数据库

package g

import (
	"api/model"
	ginLogin "github.com/zhangdapeng520/zdpgo_gin_login"
	gorm "github.com/zhangdapeng520/zdpgo_gorm"
	_ "github.com/zhangdapeng520/zdpgo_mysql"
)

var GDB *gorm.DB

func initMySQL() {
	var err error
	GDB, err = gorm.Open(
		"mysql",
		"root:root@tcp(127.0.0.1:3306)/blog?charset=utf8mb4&parseTime=True&loc=Local",
	)
	if err != nil {
		panic(err)
	}

	GDB.AutoMigrate(&model.CourseArticle{})
	GDB.AutoMigrate(&ginLogin.GinLoginUser{})
}

func closeMySQL() {
	GDB.Close()
}

04 课程表的设计和创建

package model

type CourseArticle struct {
	Id          int     `json:"id"`
	Title       string  `json:"title" gorm:"unique"`          // 标题
	Category    string  `json:"category"`                     // 分类
	Description string  `json:"description"`                  // 描述
	Content     string  `json:"content" gorm:"type:longtext"` // 内容
	Price       float64 `json:"price" gorm:"type:decimal"`    // 价格
	SaleNum     int     `json:"sale_num"`                     // 销量
	GoodNum     int     `json:"good_num"`                     // 点赞数量
	MoneyNum    int     `json:"money_num"`                    // 打赏数量
	ViewNum     int     `json:"view_num"`                     // 浏览量
	AddTime     int     `json:"add_time"`                     // 添加时间
	UpdateTime  int     `json:"update_time"`                  // 修改时间
}

07 两行代码自动生成注册和登录接口

package router

import (
	"api/g"
	gin "github.com/zhangdapeng520/zdpgo_gin"
	ginLogin "github.com/zhangdapeng520/zdpgo_gin_login"
)

func initUser(app *gin.Engine) {
	group := app.Group("/user")
	group.POST(
		"/register/",
		ginLogin.GetRegisterHandler(g.GDB, g.PasswordSalt),
	)
	group.POST(
		"/login/",
		ginLogin.GetLoginHandler(g.GDB, g.JwtKey, g.PasswordSalt),
	)
}

09 实现新增课程的接口

package course_article

import (
	"api/g"
	"api/model"
	gin "github.com/zhangdapeng520/zdpgo_gin"
	"time"
)

func add(c *gin.Context) {
	// 解析请求
	var req requestCourseArticle
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	// 对作者做简单的限制,只有指定的作者才能拥有写作权限
	if req.Author != "zhangdapeng" {
		c.JSON(402, gin.H{"error": "you have not authorization"})
		return
	}

	// 新增
	now := int(time.Now().Unix())
	g.GDB.Create(&model.CourseArticle{
		Title:       req.Title,
		Category:    req.Category,
		Description: req.Description,
		Content:     req.Content,
		Price:       req.Price,
		SaleNum:     0,
		GoodNum:     0,
		MoneyNum:    0,
		ViewNum:     0,
		AddTime:     now,
		UpdateTime:  now,
	})

	c.JSON(200, nil)
}

10 实现分页查询的接口

package course_article

import (
	"api/g"
	"api/model"
	gin "github.com/zhangdapeng520/zdpgo_gin"
	"strconv"
)

func getAll(c *gin.Context) {
	pageStr := c.DefaultQuery("page", "1")
	sizeStr := c.DefaultQuery("size", "20")

	page, err := strconv.Atoi(pageStr)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	size, err := strconv.Atoi(sizeStr)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	var articles []model.CourseArticle
	g.GDB.
		Limit(size).
		Offset((page - 1) * size).
		Order("update_time desc").
		Find(&articles)
	c.JSON(200, articles)
}

func get(c *gin.Context) {
	idStr := c.Param("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	var article model.CourseArticle
	g.GDB.Find(&article, "id=?", id)
	c.JSON(200, article)
}

13 实现根据ID修改课程的接口

package course_article

import (
	"api/g"
	"api/model"
	gin "github.com/zhangdapeng520/zdpgo_gin"
	"strconv"
)

func update(c *gin.Context) {
	idStr := c.Param("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	// 解析请求
	var req requestCourseArticle
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	// 添加简单的权限校验
	if req.Author != "zhangdapeng" {
		c.JSON(402, gin.H{"error": "you have not authorization"})
		return
	}

	// 根据ID查询
	var article model.CourseArticle
	g.GDB.Find(&article, "id=?", id)
	if article.Id == 0 {
		c.JSON(404, gin.H{"error": "article not found"})
		return
	}

	// 修改
	article.Title = req.Title
	article.Category = req.Category
	article.Price = req.Price
	article.Description = req.Description
	article.Content = req.Content
	err = g.GDB.Save(&article).Error
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}

	c.JSON(200, nil)
}

14 实现根据ID删除课程的接口

package course_article

import (
	"api/g"
	"api/model"
	gin "github.com/zhangdapeng520/zdpgo_gin"
	"strconv"
)

func deleteArticle(c *gin.Context) {
	idStr := c.Param("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	err = g.GDB.Delete(&model.CourseArticle{Id: id}).Error
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}

	c.JSON(200, nil)
}

16 实现登录的功能

<template>
  <form class="p-3">
    <div class="form-group">
      <label for="username">账号</label>
      <input type="text" class="form-control" id="username" v-model="username">
    </div>
    <div class="form-group">
      <label for="password">密码</label>
      <input type="password" class="form-control" id="password" v-model="password">
    </div>
    <button class="btn btn-primary" @click="onLogin">立即登录</button>
  </form>

</template>

<script setup lang="ts">
import {ref} from "vue";
import axios from "redaxios";
import {useRouter} from "vue-router";

const username = ref("")
const password = ref("")
const router = useRouter()

const onLogin = () => {
  if (username.value == "") {
    alert("请输入用户名")
    return
  }
  if (username.value.length < 3) {
    alert("用户名长度最小为3")
    return
  }
  if (username.value.length > 36) {
    alert("用户名长度最大为36")
    return
  }
  if (password.value == "") {
    alert("请输入密码")
    return
  }
  if (password.value.length < 6) {
    alert("密码长度最小为6")
    return
  }
  if (password.value.length > 128) {
    alert("密码长度最大为128")
    return
  }

  axios({
    method: "POST",
    url: "/api/user/login/",
    data: {
      username: username.value,
      password: password.value,
    }
  }).then((res) => {
    let data =res.data
    if (data){
      localStorage.setItem("token", data.token)
      localStorage.setItem("username", data.username)
      router.push("/")
    }else{
      alert("登录失败!")
    }
  })
}
</script>

18 实现显示登录用户名的功能

<template>
  <nav
      class="navbar navbar-expand-md navbar-light mb-0"
      :style="`background-color: ${VUE_APP_NAVBAR_BG_CSS_COLOR}; color: ${VUE_APP_NAVBAR_TEXT_CSS_COLOR};`"
  >
    <router-link
        class="navbar-brand"
        :to="'/'"
        :style="`color: ${VUE_APP_NAVBAR_TEXT_CSS_COLOR};`"
    >
      {{ title }}
    </router-link>
    <button
        :class="`navbar-toggler collapsed`"
        type="button"
        aria-label="Toggle navigation"
        @click.prevent="showDropdown = !showDropdown"
    >
      <span
          class="navbar-toggler-icon"
          :style="`background-color: ${VUE_APP_NAVBAR_TEXT_CSS_COLOR};`"
      />
    </button>

    <div
        id="navbarNavDropdown"
        class="navbar-collapse"
    >
      <ul class="navbar-nav ml-auto top-right"
          @focusout="focusOut"
          tabindex="1">
        <li class="category">Python</li>
        <li class="category">Golang</li>
        <li class="nav-item" v-if="username">
          <a
              class="nav-link border rounded py-2 px-3 mr-2"
              style="color: white">
            {{ username }}
          </a>
        </li>
        <li class="nav-item" v-else>
          <router-link
              class="nav-link border rounded py-2 px-3 mr-2"
              :to="'/login'"
              :style="`color: ${VUE_APP_NAVBAR_TEXT_CSS_COLOR};`"
          >
            登录
          </router-link>
        </li>

        <li class="nav-item" v-if="username">
          <a
              class="nav-link border rounded py-2 px-3 mr-2 logout"
              @click.prevent="onLogout"
              style="color: white">
            注销
          </a>
        </li>

        <li v-if="router.currentRoute.value.path !== '/editor'" class="nav-item">
          <router-link
              class="nav-link border rounded py-2 px-3"
              :to="'/editor'"
              :style="`color: ${VUE_APP_NAVBAR_TEXT_CSS_COLOR};`"
          >
            写作
          </router-link>
        </li>
      </ul>
    </div>
  </nav>
</template>

<script setup lang="ts">
import {onMounted, ref} from 'vue';
import blogConfig from '../blog_config'
import router from '../router';
import {useRouter} from "vue-router";

const {VUE_APP_NAVBAR_BG_CSS_COLOR = 'black', VUE_APP_NAVBAR_TEXT_CSS_COLOR = 'white'} = blogConfig

defineProps({
  title: {
    type: String,
    default: ''
  },
  sections: {
    type: Object,
    default: () => ({})
  }
});

const showDropdown = ref(false)

const focusOut = ({relatedTarget}: any) => {
  if (!(Array.from(relatedTarget?.classList ?? []).includes('dropdown-item'))) {
    showDropdown.value = false
  }
}

const username = ref("")
const mrouter = useRouter()
const onLogout = () => {
  localStorage.removeItem("token")
  localStorage.removeItem("username")
  localStorage.removeItem("refresh")
  username.value = ""
  mrouter.push("/login")
}

onMounted(() => {
  username.value = localStorage.getItem("username") || ""
})
</script>

<style scoped>
.top-right {
  display: flex;
  align-items: center;
  justify-content: center;
}

.top-right .category {
  margin-right: 15px;
}

.top-right .category:hover {
  color: cornflowerblue;
  cursor: pointer;
}

.logout:hover {
  color: dodgerblue !important;
  cursor: pointer;
}
</style>

23 实现添加文章的功能

<template>
  <form class="p-3">
    <div class="form-group">
      <label for="title">标题</label>
      <input type="text" class="form-control" id="title" v-model="title">
    </div>
    <div class="form-group">
      <label for="category">分类</label>
      <input type="text" class="form-control" id="category" v-model="category">
    </div>
    <div class="form-group">
      <label for="price">价格</label>
      <input type="number" class="form-control" id="price" v-model="price">
    </div>
    <div class="form-group">
      <label for="description">描述</label>
      <textarea class="form-control" id="description" rows="3" v-model="description"></textarea>
    </div>
    <div class="form-group">
      <label for="content">内容</label>
      <v-md-editor v-model="content" @save="onSave" height="100vh"
                   :right-toolbar="'preview sync-scroll fullscreen'"></v-md-editor>
    </div>
    <button class="btn btn-primary" @click="onSubmit">保存</button>
  </form>

</template>

<script setup>
import {onMounted, ref} from "vue"
import axios from "redaxios";
import {useRouter} from "vue-router";

const router = useRouter()
const editArticle = ref({}) // 编辑的文章
const author = ref("")
const title = ref("")
const category = ref("")
const price = ref(0)
const description = ref("")
const content = ref("支持Markdown语法")

const onSave = (text, html) => {
  alert("保存成功")
}
const onSubmit = () => {
  if (title.value.length < 3) {
    alert("标题最小长度是3")
    return
  }
  if (category.value.length < 3) {
    alert("分类最小长度是3")
    return
  }
  if (price.value < 0) {
    alert("价格不能小于0")
    return
  }
  if (description.value.length < 6) {
    alert("描述最小长度是6")
    return
  }
  if (content.value.length < 6) {
    alert("内容最小长度是6")
    return
  }

  // 提交
  if (editArticle.value && editArticle.value.id){
    axios({
      method: "put",
      url: `/api/course_article/${editArticle.value.id}/`,
      data: {
        author: author.value,
        title: title.value,
        category: category.value,
        price: price.value,
        description: description.value,
        content: content.value,
      }
    }).then(() => {
      localStorage.removeItem("edit_article")
      router.push("/")
    }).catch(err => {
      alert(err)
    })
  }else{
    axios({
      method: "post",
      url: "/api/course_article/",
      data: {
        author: author.value,
        title: title.value,
        category: category.value,
        price: price.value,
        description: description.value,
        content: content.value,
      }
    }).then(() => {
      title.value = ""
      category.value = ""
      price.value = 0
      description.value = ""
      content.value = ""

      router.push("/")
    }).catch(err => {
      alert(err)
    })
  }

}

onMounted(() => {
  // 获取作者信息
  author.value = localStorage.getItem("username") || ""

  // 尝试获取编辑文章信息
  try {
    editArticle.value = JSON.parse(localStorage.getItem("edit_article"))
    title.value = editArticle.value.title
    category.value = editArticle.value.category
    price.value = editArticle.value.price
    description.value = editArticle.value.description
    content.value = editArticle.value.content
  } catch {
  }
})
</script>

24 实现文章的请求和动态渲染

<template>
  <div :style="`background-color: ${VUE_APP_MAIN_BG_CSS_COLOR}; color: ${VUE_APP_MAIN_TEXT_CSS_COLOR};`">
    <div
        v-for="article in articles"
        :key="article.id"
        class="container markdown-body p-3 p-md-4 my-3"
    >
      <!-- 标题 -->
      <a class="text-reset link-title" @click.prevent="onOpenDetail(article)">
        <h3 class="text-left m-0 p-0">
          {{ article.title }}
        </h3>
      </a>

      <!-- 日期 -->
      <p class="font-weight-light  m-0 p-0 text-right">
        {{ secondsToDateStr(article.update_time) }}
      </p>
      <!--分类-->
      <div class="text-right">
        <a class="m-0 p-0 text-right font-weight-bold" style="cursor: pointer">
          #{{ article.category }}
        </a>
        <span class="text-right font-weight-bold" style="margin: 0 5px; color: red;">
          {{ article.price }} 元
        </span>
        <a
            class="m-0 p-0 text-right font-weight-bold"
            style="cursor: pointer"
            @click.prevent="onEdit(article)"
            v-if="username==='zhangdapeng'">
          编辑
        </a>
      </div>

      <p class="font-weight-light mt-1">
        {{ article.description }}
      </p>
    </div>
  </div>
</template>

<script setup>
import {ref, computed, inject, onMounted} from 'vue'
import blogConfig from '../blog_config'
import axios from "redaxios";
import {useRouter} from "vue-router";

const {VUE_APP_POSTS_PER_PAGE, VUE_APP_MAIN_BG_CSS_COLOR, VUE_APP_MAIN_TEXT_CSS_COLOR} = blogConfig


const router = useRouter()
const username = ref("")

// 打开详情页面
const onOpenDetail = (article) => {
  localStorage.setItem("article", JSON.stringify(article))
  router.push("/detail")
}

// 打开编辑页面
const onEdit = (article) => {
  localStorage.setItem("edit_article", JSON.stringify(article))
  router.push("/editor")
}

// 将时间的秒值转化为年月日字符串
const secondsToDateStr = (seconds) => {
  const date = new Date(seconds * 1000)

  // 月份
  const month = date.getMonth() + 1
  let monthStr = month.toString();
  if (month < 10) {
    monthStr = "0" + monthStr
  }

  // 日期
  const mdate = date.getDate()
  let mdateStr = mdate.toString();
  if (mdate < 10) {
    mdateStr = "0" + mdateStr
  }

  // 时
  const hour = date.getHours()
  let hourStr = hour.toString();
  if (hour < 10) {
    hourStr = "0" + hourStr
  }

  // 分
  const minute = date.getMinutes()
  let minuteStr = minute.toString();
  if (minute < 10) {
    minuteStr = "0" + minuteStr
  }

  // 秒
  const second = date.getSeconds()
  let secondStr = second.toString();
  if (second < 10) {
    secondStr = "0" + secondStr
  }

  // 返回
  return `${date.getFullYear()}-${monthStr}-${mdateStr} ${hourStr}:${minuteStr}:${secondStr}`
}
const articles = ref([])
onMounted(() => {
  // 获取当前登录用户
  username.value = localStorage.getItem("username") || ""

  // 解决注销按钮无法自动显示的BUG
  if (!localStorage.getItem("refresh")) {
    window.location.href = "/"
    localStorage.setItem("refresh", "true")
  }
  // 加载数据
  axios({
    method: 'get',
    url: "/api/course_article/",
    params: {
      page: 1,
      size: 20,
    }
  }).then(res => {
    articles.value = res.data
  })
})
</script>

<style scoped>
.link-title {
  cursor: pointer;
}

.link-title:hover {
  color: dodgerblue !important;
}
</style>

27 实现文章详情的渲染

<template>
  <div class="container my-4 my-md-5">
    <h1>{{ article.title}}</h1>
    <!--渲染富文本-->
    <v-md-editor
        mode="preview"
        v-model="article.content"
        :style="`background-color: ${VUE_APP_MAIN_BG_CSS_COLOR}; color: ${VUE_APP_MAIN_TEXT_CSS_COLOR};`"/>
    <!--返回按钮-->
    <button type="button" :style="`color: ${VUE_APP_MAIN_TEXT_CSS_COLOR};`" class="border btn mt-4"
            @click="hasHistory() ? router.go(-1) : router.push('/')">
      &laquo; 返回
    </button>
  </div>
</template>

<script setup lang="ts">
import {inject, onMounted, ref} from 'vue'
import {onBeforeRouteUpdate} from 'vue-router'
import router from '../router'
import axios from 'redaxios'
import {type PostIndex} from '../types/PostIndex'
import blogConfig from '../blog_config'

const {VUE_APP_MAIN_BG_CSS_COLOR, VUE_APP_MAIN_TEXT_CSS_COLOR} = blogConfig

const props = defineProps({
  id: {
    type: String,
    default: ''
  }
})

/* Hacky navigation when a href link is clicked within the compiled html Post */
onBeforeRouteUpdate(() => {
  location.reload()
})

// Fetch Post markdown and compile it to html
const postsIndex: PostIndex[] = inject<PostIndex[]>('postsIndex', [])
const {url = ''} = postsIndex.find(({id}) => id === props.id) || {}
const {data: markDownSource} = await axios.get(url)

// Patch page title
const [, title] = markDownSource.split('#')

// Back button helper
const hasHistory = () => window.history?.length > 2

const article = ref({}) // 文章
onMounted(() => {
  try {
    article.value = JSON.parse(localStorage.getItem("article"))
    console.log(article.value)
  } catch {
    console.error("读取文章信息失败")
  }
})
</script>

总结

整个课程从两行代码实现注册登录API接口讲起,以一个课程系统为实战,结合Vue3开发的前端,实现一个基本的前后端分离的课程管理系统,层层递进,学习路径平缓。

通过本套课程,能帮你入门gin+gorm+vue3开发前后端分离管理系统,积累实际的前后端分离开发经验。

如果您需要完整的源码,打赏20元即可。

人生苦短,我用PyGo,我是您身边的Python私教~

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

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

相关文章

【JavaEE】synchronized原理

目录 前言 synchronized特性 synchronized特点 synchronize的加锁过程 1.无锁-->偏向锁 2.偏向锁->轻量级锁 3.轻量级锁->重量级锁 锁的优化操作 1.锁消除 2.锁粗化 3.自适应自旋锁 相关面试题 1.什么是偏向锁&#xff1f; 2.synchronized的实现原理是什…

LVS原理及实例

目录 LVS原理 LVS概念 lvs集群的类型 lvs-nat 解释 传输过程 lvs-dr 解释 传输过程 特点 lvs-tun LVS&#xff08;Linux Virtual Server&#xff09;常见的调度算法 防火墙标记&#xff08;Firewall Marking&#xff09;结合轮询调度 实战案例 lvs的nat模式配置 …

代码随想录算法刷题训练营day49:LeetCode(42)接雨水、LeetCode(84)柱状图中最大的矩形

代码随想录算法刷题训练营day49&#xff1a;LeetCode(42)接雨水、LeetCode(84)柱状图中最大的矩形 LeetCode(42)接雨水 题目 代码 import java.util.Stack;class Solution {public int trap(int[] height) {//用单调栈进行操作int sum0;Stack<Integer> stacknew Stac…

计算机的错误计算(五十六)

摘要 展示大数的正切函数值的错误计算。 由计算机的错误计算&#xff08;五十五&#xff09;知&#xff0c;国际IEEE 754 标准给出的正切函数的定义域是整个实数域范围。那么&#xff0c;在该范围内&#xff0c;软件的计算效果如何呢&#xff1f; 例1. 计算 . 在 Python下计…

字体识别验证码的介绍!

字体识别验证码 ​是一种安全机制&#xff0c;‌通过要求用户识别特定字体来验证用户的身份或防止自动化攻击。‌这种验证码通常包含一些经过特殊设计的字符&#xff0c;‌需要用户根据这些字符的特定样式&#xff08;‌如字体、‌字形等&#xff09;‌来进行识别和输入。‌字…

html+css网页制作 博云丝网5个页面 无js ui还原度100%

htmlcss网页制作 博云丝网5个页面 无js ui还原度100% 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取…

el-tree限制选中个数

el-tree限制选中个数 <el-treestyle"max-width: 600px":data"Treedata":check-strictly"true"show-checkboxnode-key"id":props"defaultProps":default-expanded-keys"[1, 2]"ref"treeRef"check&quo…

Java数组类型

目录 一维数组 一维数组的声明 动态数组初始化 静态数组的初始化 一维数组的访问 数组长度 数组的遍历操作 数组中的默认值 数组中的两个常见异常 越界访问异常ArrayIndexOutOfBoundsException 空指针异常NullPointerException Java中的内存划分 一维数组的内存分…

pdf怎么加密码怎么设置密码?pdf加密码的几种设置方法

在数字化时代&#xff0c;信息的保密性与安全性日益成为我们不可忽视的重要环节。尤其对于包含敏感信息或个人隐私的PDF文档而言&#xff0c;保护其免受未授权访问的侵扰显得尤为重要。通过为PDF文档设置密码保护&#xff0c;我们能够筑起一道坚实的防线&#xff0c;确保只有拥…

危化品安全生产风险监测预警系统的构建与实施

引言 1、背景与重要性 在现代工业生产中&#xff0c;危险化学品&#xff08;简称“危化品”&#xff09;的使用和管理日益广泛。它们在化工、制药、能源等多个领域中扮演着不可或缺的角色。然而&#xff0c;危化品因其固有的易燃、易爆、腐蚀、有毒等特性&#xff0c;一旦管理…

Git使用错误分析

一.fatal: Pathspec is in submodule 我做了这样的错误操作&#xff0c;在一个仓库下的一个子目录&#xff0c;执行了git init 创建了一个子仓库&#xff0c;然后想删掉这个子仓库&#xff0c;就只删除了该子目录下的.git文件夹&#xff0c;而没有删除缓存&#xff0c;执行如下…

java: Internal error in the mapping processor: java.lang.NullPointerExceptio

java: Internal error in the mapping processor: java.lang.NullPointerExceptio 解决办法&#xff1a;idea里面加参数-Djps.track.ap.dependenciesfalse即可

Java程序设计:Java 网络聊天室客户端

相关网络编程前两篇文章&#xff1a;Java程序设计&#xff1a;Java网络编程实验 服务端部分见上一篇文章&#xff1a;Java程序设计&#xff1a;Java网络聊天室服务端 目录 1 实验名称 2 实验目的 3 实验源代码 4 实验运行结果图 5 总结 1 实验名称 Java 网络聊天室客户端 …

纳米软件的电源模块测试系统有什么功能和优势?

纳米软件电源模块自动化测试系统主要实现针对单路进4路出和单路进2路出的非隔离DCDC电源模块进行测试。本次方案包含对以下15个测试项目的自动化测试&#xff0c;分别为输入电压范围VIN的最大值最小值、输出电压范围VOUT的最大值最小值、输出纹波VOPP、电压调整率SV、负载调整率…

基于SpringBoot+Vue的学院商铺管理系统(带1w+文档)

基于SpringBootVue的学院商铺管理系统(带1w文档) 基于SpringBootVue的学院商铺管理系统(带1w文档) 互联网概念的产生到如今的蓬勃发展&#xff0c;用了短短的几十年时间就风靡全球&#xff0c;使得全球各个行业都进行了互联网的改造升级&#xff0c;标志着互联网浪潮的来临。在…

[VBA]使用VBA在Excel中 操作 形状shape 对象

excel已关闭地图插件,对于想做 地图可视化 的,用形状来操作是一种办法,就是要自行找到合适的 地图形状,修改形状颜色等就可以用于 可视化展示不同省市销量、人口等数据。 引言 在Excel中,通过VBA(Visual Basic for Applications)可以极大地增强数据可视化和报告自动化…

Spring Cache在业务系统中最佳实践教程详解及实现原理

1.概述 接着之前总结的如何保证MySQL与Redis数据同步一致性一文中提到在业务代码中一般采用旁路缓存策略方式实现同步&#xff0c;Spring Cache 就是 Spring Framework 基于该策略方式提供的一种缓存抽象&#xff0c;可以帮助开发者简化缓存的使用过程。它支持多种缓存实现&am…

【STM32】DMA数据转运(存储器到存储器)

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 DMA简介 DMA时钟使能 DMA初始化 转运起始和终止的地址 转运方向 数据宽度 传输次数 转运触发方式 转运模式 通道优先级 开启DMA通道 DMA初始化框架 更改转运次数 DMA应用实例-…

Google Mock 和 Google Test编写单元测试入门(环境配置、简单执行)

文章目录 环境的配置方法1&#xff1a;从源代码构建第一步&#xff1a;克隆库的源代码第二步&#xff1a;构建库 方法 2&#xff1a;使用 CMake 的 FetchContent示例 CMakeLists.txt 项目的创建项目结构CMakeLists.txt (根目录)main.cpp (示例程序)tests/CMakeLists.txt (测试部…

更换CentOS中docker的镜像源

如果docker pull镜像出现&#xff1a; error pulling image configuration: download failed after attempts6: dial tcp 173.236.182.137:443: i/o timeout 如果是阿里云&#xff0c;我们进入阿里云官网&#xff1a; 阿里云开发者社区-云计算社区-阿里云 然后点击产品&#…