个人blog系统 前后端分离 前端js后端go
系统设计:
1.使用语言:前端使用vue,并使用axios向后端发送数据。后端使用的是go的gin框架,并使用grom连接数据库实现数据存储读取。
2.设计结构:
最终展示:仅展示添加模块,其他模块基本相似
前端部分
基础的页面设计部分使用的是flex布局,这里就不过多的讲解了。
由于vue是模块化的,所以这个页面可以分为三大模块
1.就是app.vue这个是最主要的,我们的页面是通过它来显示的
2.就是左侧的导航栏,它对应四个模块,根据选择模块不同来显示不同的页面
3.就是右侧的交互部分,四个交互页面对应四个导航栏的选项
运行端口
运行端口有默认的端口,如果需要自定义端口。我们需要在项目文件中找到vue.config.js文件。然后在里面设置:
原来的文件内容:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,})修改后
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,devServer:{//主要添加这个port: 3000,open: true,}
})
port指定端口,open指定程序运行的时候是否自动打开浏览器true为打开
组件
想要使用组件就需要提前加载组件,也就是导入它(import ...)
加载之后才能挂载(router-view)
我们首先要明确的目标是:1 加载 2对应的组件 2 加载 3对应的组件
我们一步一步来看
大致的文件结构如下图所示,仅做展示,为了方便了解结构
app.vue:
<template>
<div class="father"><div class="one"><h1>blog control center</h1></div><div><index /></div>
</div>
</template><script>
import index from "./views/index.vue"export default {name: "App",components: {index,}
}
</script><style>.father {display: flex;justify-content: center;align-items: center;flex-direction: column;
}
.one{width: 1380px;height: 100px;background: #8e96f1;text-align: center;display: flex;justify-content: center;
}</style>
App.vue这是整个项目的根组件。其他所有的组件都是基于这个组件展开的
我们主要在这个组件内做了:
- 加载 index组件
- 制作了HTML的头部区域,也就是显示blog control center 的部分
- 规定了index的显示区域(为头部区域下方
- 给body设置了一个flex布局,让网页内容居中显示
加载组件就是在组件的script部分通过import导入,加载分为全局加载和组件内加载
在上面的代码中我们使用的是组件内加载,它的特点是,在其组件内加载的组件只能在此组件内使用。而全局加载的特点是只要我们使用全局加载,那么在这个项目内所有组件都可以使用我们加载的组件。
全局加载步骤:首先在main.js里面加载需要用的组件。
import xxx from "相对路径" xxx就相当于我们给这个组件起的别名
然后我们会看到一个语句:const app = createApp(APP)
然后通过 app.conponent("组件名",组件名)
注意:如果没有找到const app = createApp(APP)那就是被集成为了createApp(APP).use(router).mount('#app')放在最下方。这时候我们需要把它拆开
我们来看一个简单的例子:
import index from "./views/index.vue"const app =createApp(App)app.component("index",index)app.use(router).mount('#app')
这就是全局导入的基本步骤了。在这里导入之后就不需要在组件内搞import和components了。直接<index />就可以使用了
这里我们就讲解完了,接下来我们来看index的设置
index:
<template>
<div class="body"><div ><div class="son" style="background-color: #42b983"><router-link to="/add" ><h1>添加博文</h1></router-link></div><div class="son" style="background-color: gold"><router-link to="/change"><h1>修改博文</h1></router-link></div><div class="son" style="background-color: pink"><router-link to="/discover"> <h1>查询博文</h1></router-link></div><div class="son" style="background-color: coral"><router-link to="/del"> <h1>删除博文</h1></router-link></div></div><div style="width: 1200px;height: 1120px;">
<router-view></router-view></div>
</div>
</template><script>export default {setup() {return{}},}
</script><style scoped>.son{width: 180px;height: 280px;display: flex;justify-content: center;align-items: center;
}
.body{display: flex;flex-direction: row;
}
</style>
这里主要要理解的是router-view和router-link
router-view是组件要挂载到哪里的入口,也就是说我的router-view放在div里面,我的子组件展示的时候就只能在这个div里面展示。它起到一个占位符的作用,需要与router-link结合使用
router-link就相当于一个连接,这个连接里面设置的路径是我们在router目录里面提前设置好的。针对于这个项目我们来看一下router里面index.js的设置
import { createRouter, createWebHashHistory } from 'vue-router'
import index from "../views/index.vue"
import add from "../views/add.vue"
import change from "../views/change.vue"
import discover from "../views/discover.vue"
import del from "../views/del.vue"const routes = [{path: '/index',component: index,},{path: '/add',component: add,},{path: '/change',component: change,},{path: '/discover',component: discover,},{path: '/del',component: del,}
]const router = createRouter({history: createWebHashHistory(),routes
})export default router
路径的设置主要是创建一个数组,数组存储对象,对象存储路径信息(path和component)path是我们设置的路径,component是我们导入的组件。由于它是一个.js文件不知道哪些组件被注册为全局组件。所以需要重新加载一次
最后我们再创建一个router,吧createRouter([])赋值给它。在这个函数里面有两个参数。这里暂且略过,因为博主也没有很理解这个。就暂且按照博主的写法来吧(嘻嘻)
最后的export default router这个语句的作用是导出一个router对象。这样问再main里面才可以使用(app.use(router))(注:使用之前需要import导入index.js所在的相对地址哦)
到此,router-view和router-link我们也大致的明白是怎么回事了,简单来说就是router-link连接显示的组件会显示到router-view里面。
然后我们再来看四个不同模块文件
add.vue:
<script>
import {ref} from "vue";
import axios from "axios"
export default{name:"add",setup(props,context) {let name = ref("")let text = ref("")let rese =ref("")function give(e) {e.preventDefault()axios.post("http://localhost:8081/Add",{name: name.value,text: text.value}).then(res => {console.log(res)alert(res.data.Error)rese.value = res.data.Error})}return{give,name,text,}}}
</script><template><div class="rit"><form method="post" @submit="give" ><table><tr class="a"><td> 请输入博文名称: <input name="name" v-model="name" type="text" style="width: 500px"><input type="submit" value="提交" class="submit-button"></td></tr><tr class="b"><td><textarea name="text" v-model="text" /></td></tr></table></form></div>
</template><style scoped>
div{display: flex;align-items: center;justify-content: center;
}
.rit{width: 1200px;height: 1120px;background: #42b882;
}
.a{
width:1200px;height: 100px;
}
.b{width:1200px;height: 1025px;
}
textarea{width:1100px;height:1000px;overflow:auto;
}
</style>
全局css样式
这里我们不怎么酱它的css样式,只简单了解一下:通常来说,组件之间的css样式不会互相影响,也就是说我在哪个组件设置的样式就只能用在哪个组件内。
如果有一个样式会被运用到很多组件内,我们就可以设置全局css样式
全局css样式
设置全局css样式我们需要单独写一个css文件,通常这个文件我们会存放在src/assets目录下。
导入全局组件同样需要在main.js里面导入(import "css样式路径(相对路径)")
这样就可以直接在组件内使用这个文件内的样式了。
axios 传递数据
这里我们传递数据使用到了axios作为传递的工具。
我们首先导入这个axios,同样的。既然是导入就会有全局导入和组件内导入
组件内导入很简单,我们不过多赘述
我们主要来讲一下全局导入
在main.js我们首先 import axios from "axios"导入
然后
axios.defaults.baseURL = 'http://localhost:8081'; // 设置默认的 baseURL
这里设置的作用是在组件内使用的时候就不需要写完整的路径了,只需要补出后面剩余路径即可:.post("http://localhost:8081/Add" 就可以写为.post("/Add"
这样就会方便很多。
然后app.config.globalProperties.$axios = axios; 这一句的作用类似于起别名。我们在组件内导入的时候使用axios直接使用就可以了。但是全局导入就需要this.$axios了 $axios也可以改成别的名字,这个看自己需要。$也可以不写,但是我们通常会写上哦
如果使用全局导入,我们刚才的代码就需要改为:
setup(props,context) {let name = ref("")let text = ref("")let rese =ref("")function give(e) {e.preventDefault()this.$axios.post("/Add",{name: name.value,text: text.value}).then(res => {console.log(res)alert(res.data.Error)rese.value = res.data.Error})}
this.$axios
这里要着重声明的是,在vue3的里面这个方法就不是很适用了。因为setup没有自己的this
我们需要
import {getCurrentInstance} from "vue";事先导入这个
const { proxy } = getCurrentInstance(); // 然后再在setup里面创建这个常量。通过
proxy.$axios进行使用,这里的差异要注意
我们使用this.$axios.post.("url",{json键值对数据})向后端发送请求。然后再.then(res=>{接收回应})接收后端返回的回应(一般都是res.data里面有回应的数据)。然后再.catch捕获错误信息,这个在本系统内并没有使用(不会)
ref变量
这个是vue3里面新出的一个,通过它来创建响应式变量,用于绑定页面内元素。实现动态的变化。如果要在js所属的代码部分访问它的值是不能直接用变量名的,而是需要使用 变量名.value 获取变量的值。
v-model绑定
它的主要作用是把一个变量绑定到某个元素上,这个变量要是响应式变量。它有一个语法糖,就是先读取后渲染。把它综合到了v-model里面。
change.vue:
<script>
import { ref } from "vue";export default {name: "add",setup() {let change_text = ref("");let Blog_body = ref("");let Blog_title = ref("");function change_submit(e) {e.preventDefault();console.log("change_text:", change_text.value); // 检查值this.$axios.post("/Change",{"Title": change_text.value}).then((response) => {console.log(response);Blog_body.value = response.data.Blog_body;Blog_title.value = response.data.Blog_title;alert("i get the blog");});}function change_out(e){e.preventDefault()alert("数据发送回后端",Blog_title,Blog_body)this.$axios.post("/Changeend", {BlogText: Blog_body.value,BlogName:Blog_title.value,}).then((response) => {alert(response.data.return);})}return {change_submit,change_text,Blog_title,Blog_body,change_out,};},
};
</script><template><div class="rit"><form method="post" @submit="change_submit"><table><tr class="a"><td>请输入要修改的博文的名称:<input type="text" style="width: 500px" v-model="change_text" /><input type="submit" value="提交" class="submit-button" /></td></tr></table></form><form @submit="change_out" id="f2"><table><tr class="a"><td>blog_name:<input type="text" style="width: 500px" v-model="Blog_title" /><input type="submit" value="确认提交" class="submit-button"></td></tr><tr class="b"><td><textarea v-model="Blog_body"></textarea></td></tr></table></form></div>
</template><style scoped>
div{display: flex;align-items: center;justify-content: center;
}
.rit{width: 1200px;height: 1120px;background: #fdd600;display: flex;flex-direction: column;justify-content: center;align-items: center;
}.a{width:1200px;height: 50px;text-align: center;
}
.b{width:1200px;height: 1025px;
}
textarea{width:1100px;height:1000px;overflow:auto;
}
</style>
这个里面就没有什么特别新的内容了,我们简单理解一下设计概念即可。在change模块中。主要步骤如下
- 前端输入blog的name
- 发送给后端,后端返还blog的name和内容 这是第一次前后端交流
- 用户修改,再次返回给后端
- 后端接收,修改数据库中的数据(是修改不是新建一个新的数据)这是第二次前后端交流
这里着重讲一下的是使用了两个路由,一个路由用于第一次前后端交流,另一个路由用于第二次前后端交流。
其他的部分都是之前讲过的了。就不过多赘述了
后面的del.vue和discover.vue模块都是重复的内容,我们只把代码贴出来,剩下的就略过了
del.vue
<script>
import {ref} from "vue";export default{name:"add",setup() {let blogs_name = ref("")function del_blog(e){e.preventDefault()console.log("函数被触发")this.$axios.post("/Delete",{"blog_name":blogs_name.value}).then(res=>{alert("blog 删除"+res.data.return)console.log(res)})}return {blogs_name,del_blog,}}}
</script><template><div class="rit"><form method="post" @submit="del_blog" ><table><tr class="a"><td> 请输入要删除的博文名称: <input type="text" style="width: 500px" v-model="blogs_name"><input type="submit" value="提交" class="submit-button" ></td></tr></table></form></div>
</template><style scoped>
div{display: flex;align-items: center;justify-content: center;
}
.rit{width: 1200px;height: 1120px;background: #fd7e50;
}
.a{width:1200px;height: 100px;
}textarea{width:1100px;height:1000px;overflow:auto;
}
</style>
discover.vue
<script>
import {ref} from "vue";export default{name:"add",setup() {let change_text = ref("");let Blog_body = ref("");let Blog_title = ref("");function change_submit(e) {e.preventDefault();console.log("change_text:", change_text.value); // 检查值this.$axios.post("/Discover",{"Title": change_text.value}).then((response) => {console.log(response);Blog_body.value = response.data.Blog_body;alert("i get the blog");});}return {Blog_body,Blog_title,change_text,change_submit,}}}
</script><template><div class="rit"><form method="post" @submit="change_submit"><table><tr class="a"><td> 请输入要查询的博文名称: <input type="text" style="width: 500px" v-model="change_text"><input type="submit" value="提交" class="submit-button"> </td></tr><tr class="b"><td><textarea v-model="Blog_body"/></td></tr></table></form></div>
</template><style scoped>
div{display: flex;align-items: center;justify-content: center;
}
.rit{width: 1200px;height: 1120px;background: #fdbfca;
}
.a{width:1200px;height: 100px;
}
.b{width:1200px;height: 1025px;
}
textarea{width:1100px;height:1000px;overflow:auto;
}
</style>
路由设置
路由的设置我们一般都在router/index.js文件内设置。这个目录我们可以手动创建,也可以在项目创建的时候直接选定创建。
跟随项目一起创建:
这里有一个Router选项,勾选上就可以。
手动创建:
我们创建对应目录以及文件,这个时候是不能使用的。我们还需要在main.js里面设置
-
import router from './router'
-
app.use(router)
设置完这两个就说明我们启用了路由系统。
具体路由的设置我们在index:对应的部分已经理解过了。这里就简单赘述一下
路由的设置是设置一个数组,数组存放对象们,对象里面包含两个参数,一个是path,另一个是component,path设置的是路径,compoent设置的是路径对应跳转哪个界面。
如果有嵌套路径的存在,就需要再在改对象里面添加一个属性
children: []
这个里面存放的数据和外部的是一样的,path,component如果还有嵌套内容就再写一个children。并且嵌套的路径不需要再写 斜杠/ 直接写路径就可以了,这个斜杠会自动补充。
前端部分大概就讲这一点吧。更加深层的还是需要看更权威的专家的讲解或者去看官方文档。
后端部分
后端我们使用的是go语言的gin框架
大致的架构我们讲一下
主要是分为三个模块,一个是路由模块,一个是路由对应处理函数模块,一个是与数据库的连接模块。
我们首先来讲一下最重要的路由模块(其实都很重要)
main.go:
这个文件主要负责路由的设置。
package mainimport ("github.com/gin-contrib/cors""github.com/gin-gonic/gin"
)// 同一目录下,同一包内的.go文件内函数是共享的
func main() {router := gin.Default()router.Use(cors.Default())router.POST("/Add", Addpost) //router.POST("/Change", Change) //这是前端查询并返回查询结果的路径router.POST("/Changeend", Changeend) //这是后端接收前端处理好的结果的路径router.POST("./Discover", Discover)router.POST("/Delete", Delete)router.Run(":8081") //运行指定端口
}
我们既然要使用gin框架就需要实现导入这个框架。也就是:import "github.com/gin-gonic/gin"
,然后我们就可以设置路由了,这里需要注意设置的路由需要和前端对应上,而且要严格区分大小写!!!我们先通过router:= gin.Default()创建了一个默认的gin路由引擎。
然后我们设置了很多条路由,通过 .请求方法("路由",对应的处理函数)
最后使用router.Run(":8081")运行。
这里提到了端口,我们前端运行的时候也是需要端口的。双方端口不能一样,如果一样会导致端口占用错误,但是端口不一样虽然不会导致错误出现。却引发了一个新的问题:跨域
跨域问题的解决在前端后端都可以,但是我们还是默认在后端解决跨域问题。
router.Use(cors.Default())这条语句就是解决跨域问题的,它在"github.com/gin-contrib/cors"包下,所以要事先导入这个包。但是这部分博主并不是很精通,就不讲了。只能说通过router.Use(cors.Default())语句我们可以设置一些默认的设定解决跨域问题。
然后就是路由后面跟着的处理函数,这个函数我们是写在与main.go同级目录下并且都属于包main。所以可以直接使用不用导入对应的包。
至于为什么函数不加()是因为如果加了()就是直接使用这个函数。这并不是我们需要的。我们需要的是在特定情况下跳转到对应的处理函数。所以没有加上()
mysql.go:
package mainimport ("gorm.io/driver/mysql""gorm.io/gorm"
)type blog struct {BlogName string `gorm:"primaryKey type:varchar(255)"`BlogText string `gorm:"not null unique type:text"`
} //创建一个结构体,结构体用于存储博文和博文名称func sqlof() *gorm.DB { //这是一个数据库连接函数,当我们调用它的时候会返回一个数据库对象。我们用这个对象进行数据的增上改查dsn := "root:123456@tcp(127.0.0.1:3306)/web_of_blog"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {panic(err)}db.AutoMigrate(&blog{})return db
}
这个文件的主要作用是连接数据库并提供一个函数,函数返回一个数据库对象。
我们需要创建一个结构体对象,这个对象的字段和数据库里面的字段一一对应。
dsn是一个字符串,字符串的内容是用户:密码@协议(ip,端口)/数据库名称
ip使用的是127.0.0.1是说明这是本机的数据库。
我们使用gorm.Open(mysql.Open(dsn),&gorm.Config{})连接数据库,它会返会一个对象和错误信息。如果连接成功是没有错误信息的,即err==nil
db.AutoMigrate(&blog{})的作用是确认数据库里有对应的表(结构体名称+s)如果没有则自动创建,如果表的结构不同则更新结构。
我们把最后的连接好的对象作为返回值返回给调用者。
controller.go
这个部分可以说是后端的灵魂所在了
package mainimport ("fmt""github.com/gin-gonic/gin"
)var ch = make(chan string, 1)
var db = sqlof() //创建数据库全局对象
type Article struct { //这个结构体用于接收前端数据博文和博文名称Name string `json:"name"`Text string `json:"text"`
}
type change_blog_title struct {Title string `json:"title"`
}func Addpost(c *gin.Context) {var article Articleif err := c.ShouldBindJSON(&article); err != nil {c.JSON(400, gin.H{"error": "解析 JSON 失败"})return}var insert blog //创建一个结构体对象,用于存储从前端读取的数据insert.BlogName = article.Nameinsert.BlogText = article.Textif db.Create(&insert).Error != nil { //判断数据库插入操作是否成功,成功返回200不成功返回500c.JSON(500, gin.H{"Error": "error",})/*这里返回的是json数据格式,所以前端读取的时候就需要使用.Error读取对应的key值*/} else {c.JSON(200, gin.H{"Error": "no error",})}
}// Change /*前端发送一个title作为博文的名字给后端,后端读取这个数据然后查询,并返回查询结果*/
func Change(c *gin.Context) {fmt.Println("change函数被成功调用,说明后端路由设置无误")var title change_blog_title //这是一个博文的表头的结构体实例对象var content blog//通过前端发送的数据来把title赋值给后端的title然后再通过db对象进行查询,并把查询结果的信息返回给后端err := c.ShouldBindJSON(&title)if err != nil {fmt.Println("error")}fmt.Println("jianche", title.Title)ch <- title.Titledb.Table("blogs").Where("blog_name = ?", title.Title).First(&content) //把查询到的第一个结果返回给content进行数据绑定c.JSON(200, gin.H{ //返回查询到的文章和数据"Blog_body": content.BlogText,"Blog_title": content.BlogName,})fmt.Println("数据已返回", content.BlogName, content.BlogText)}
func Changeend(c *gin.Context) { //接收数据并修改,然后返回修改完成还是错误blog_name := <-chdate := blog{}if err := c.ShouldBindJSON(&date); err != nil {c.JSON(400, gin.H{"return": "数据写入错误",})}result := db.Table("blogs").Where("blog_name = ?", blog_name).Update("blog_text", date.BlogText)if result.Error != nil {c.JSON(500, gin.H{"return": "更新博文失败"})return}result = db.Table("blogs").Where("blog_name = ?", blog_name).Update("blog_name", date.BlogName)if result.Error != nil {c.JSON(500, gin.H{"return": "更新博文失败"})return} else {c.JSON(200, gin.H{"return": "修改成功",})}}func Discover(c *gin.Context) {fmt.Println("discover函数被成功调用,说明后端路由设置无误")var title change_blog_title //这是一个博文的表头的结构体实例对象var content blog//通过前端发送的数据来把title赋值给后端的title然后再通过db对象进行查询,并把查询结果的信息返回给后端err := c.ShouldBindJSON(&title)if err != nil {fmt.Println("error")}fmt.Println("jianche", title.Title)db.Table("blogs").Where("blog_name = ?", title.Title).First(&content) //把查询到的第一个结果返回给content进行数据绑定c.JSON(200, gin.H{ //返回查询到的文章和数据"Blog_body": content.BlogText,})fmt.Println("数据已返回", content.BlogName, content.BlogText)}type DeleteRequest struct {BlogName string `json:"blog_name"`
}func Delete(c *gin.Context) {var request DeleteRequesterr := c.ShouldBindJSON(&request)if err != nil {c.JSON(400, gin.H{"return": "出现错误,啦啦啦",})return}blog_name := request.BlogNamefmt.Println("i get it: ", blog_name)resout := db.Where("blog_name = ?", blog_name).Delete(&blog{})if resout.Error != nil {c.JSON(500, gin.H{"return": "error",})} else {c.JSON(200, gin.H{"return": "ok",})}
}
它根据不同路由的设置来执行对应的函数,这一整个文件的核心就是c *gin.Context这个
c *gin.Context
是 Gin 框架中处理 HTTP 请求的核心对象,它的主要作用包括:
-
获取请求信息:如请求头、请求体、查询参数、路径参数等。
-
生成响应:如返回 JSON、HTML、文本等。
-
管理中间件:在请求处理过程中传递数据。
-
控制流程:如中止请求、重定向等。
我们通过它获取前端发送的数据,并且返回给前端数据。
我们使用c.ShouldBindJSON(&结构体)把数据绑定到结构体,这需要我们提前创建一个空的结构体对象。对象的字段名称要与前端通过axios传递过来的json数据的key值相同
也就是我前端传递数据{"Name":"123","age":18}后端的结构体字段就需要是Name和age默认的类型都是string字符串类型。同样的,前端获取后端的回应也是如此。只不过我们前端使用的是
.then(res=>{
我们需要使用res.data.返回json数据的key获取对应的value
})
绑定完成之后我们就获取到了前端发送的请求了。
然后就是与数据库交互的部分了,这部分我们之前在gorm讲过
使用gorm连接数据库
至于为什么后端没有用mvc架构,是因为代码量不大,而且模块也就三个用不上架构。
相关文章:
个人blog系统 前后端分离 前端js后端go
系统设计: 1.使用语言:前端使用vue,并使用axios向后端发送数据。后端使用的是go的gin框架,并使用grom连接数据库实现数据存储读取。 2.设计结构: 最终展示:仅展示添加模块,其他模块基本相似 前…...
OSG简介
OSG OpenSceneGraph (简称 OSG) 是一个开源的高性能3D图形库。 作用 它为开发者提供了一个强大的API,处理和渲染复杂的3D图形。 特点 OSG基于OpenGL构建,提供了对现代图形技术的支持,如着色器、纹理映射、光照模型等高级特性。 跨平台支…...
社区版Uos20.9从源码编译QT5.15.2
主要是在这个文章上学的究极保姆式教你如何在Ubuntu上源码安装Qt5.15.2_ubuntu安装qt5.15.2-CSDN博客 但原文上在环境变量的配置上真用在 uso上好像不行,要加一些引号和$号。原文的测试编译代码也有些问题,include上少了类。略作修改,在UOS社…...
AI学习第二天--大模型压缩(量化、剪枝、蒸馏、低秩分解)
目录 1. 量化:压缩大象的“脂肪” 比喻 技术逻辑 2. 剪枝:修剪大象的“无效毛发” 比喻 技术逻辑 3. 知识蒸馏:让大象“师从巨象” 比喻 技术逻辑 4. 低秩分解:把大象“折叠成纸偶” 比喻 技术逻辑 5. 推理优化&#…...
C++ —— 线程同步(互斥锁)
C —— 线程同步(互斥锁) 线程同步互斥锁(互斥量)测试代码mutex互斥锁 线程同步 线程同步:多线程协同工作,协商如何使用共享资源。 C11线程同步包含三部分内容: 互斥锁(互斥量&…...
相对路径跳转和绝对路径跳转有什么区别?
在 Vue 3 中使用路由跳转时,相对路径跳转和绝对路径跳转在使用方式、适用场景等方面存在明显区别,以下为你详细介绍: 定义 绝对路径跳转:指的是使用完整的路径来进行路由导航,路径以 / 开头,无论当前处于…...
Flume详解——介绍、部署与使用
1. Flume 简介 Apache Flume 是一个专门用于高效地 收集、聚合、传输 大量日志数据的 分布式、可靠 的系统。它特别擅长将数据从各种数据源(如日志文件、消息队列等)传输到 HDFS、HBase、Kafka 等大数据存储系统。 特点: 可扩展࿱…...
笔记类AI应用体验
笔记类AI应用体验 叮当好记视频一键转笔记, 祝你学习效率起飞 IMAGet笔记印象笔记(Evernote):Notion:Trilium Notes:二、开始搭建三、搭建步骤四、创建博客 Obsidian:案例让ai帮我执行大模型学习…...
Mysql篇——SQL优化
本篇将带领各位了解一些常见的sql优化方法,学到就是赚到,一起跟着练习吧~ SQL优化 准备工作 准备的话我们肯定是需要一张表的,什么表都可以,这里先给出我的表结构(表名:userinfo) 通过sql查看…...
【css酷炫效果】纯CSS实现故障文字特效
【css酷炫效果】纯CSS实现故障文字特效 缘创作背景html结构css样式完整代码基础版进阶版(3D效果) 效果图 想直接拿走的老板,链接放在这里:https://download.csdn.net/download/u011561335/90492053 缘 创作随缘,不定时更新。 创作背景 刚…...
【Java】链表(LinkedList)(图文版)
本博客总结了Java当中链表的实现,以及相关方法的使用,在最后附带了一些常见链表相关处理技巧,希望对你有帮助! ps:可拷贝到IDEA上自行测试,代码全部完成测试。 一.链表概述 1.什么是链表? 链…...
审批工作流系统xFlow
WorkFlow-审批流程系统 该项目为完全开源免费项目 可用于学习或搭建初始化审批流程系统 希望有用的小伙伴记得点个免费的star gitee仓库地址 仿钉钉飞书工作审批流系统 介绍 前端技术栈: vue3 ts vite arcodesign eslint 后端技术栈:springbootspring mvc mybatis mavenmysq…...
UNION,UNION ALL 的详细用法
目录 一、基本概念 二、核心区别 三、语法使用规则 四、代码实演示 4.1 两张表字段相同,字段顺序也相同 4.2 两张表字段相同。但字段顺序不同 4.3 两张表存在相同字段,但一张表字段多,一张表字段少 一、基本概念 操作符功能描述去重处…...
Java 集合遍历过程中修改数据触发 Fail-Fast 机制 ,导致报ConcurrentModificationException异常
Java Fail-Fast 机制 Fail-Fast 机制是 Java 集合框架中的一种错误检测机制,用于在遍历集合时检测结构修改。如果在迭代器创建之后,集合被修改(例如添加或删除元素),并且这种修改不是通过迭代器自身的 remove() 方法进…...
Javascript 日期相关计算
1、获取当前日期的前一天 // 获取当前日期let today new Date();today.setDate(today.getDate() - 1);// 转换为本地日期字符串格式let yesterdayStr today.toISOString().slice(0, 10);console.log(yesterdayStr); // 例如: "2023-04-03" (格式取决于地区设置) 2…...
自动驾驶背后的数学:特征提取中的线性变换与非线性激活
在上一篇博客「自动驾驶背后的数学:从传感器数据到控制指令的函数嵌套」—— 揭秘人工智能中的线性函数、ReLU 与复合函数中,我们初步探讨了自动驾驶技术中从传感器数据到控制指令的函数嵌套流程,其中提到了特征提取模块对传感器数据进行线性…...
DNS解析查询工具
dig命令 1 常用命令 命令:dig 您的域名(示例:dig www.baidu.com) 2、根据解析记录查询,比如MX,CNAME,NS,PTR等,只需将类型加在命令后面即可。 示例:dig bai…...
【eNSP实战】(续)一个AC多个VAP的实现—将隧道转发改成直接转发
在 一个AC多个VAP的实现—CAPWAP隧道转发 此篇文章配置的基础上,将隧道转发改成直接转发 一、改成直接转发需要改动的配置 (一)将连接AP的接口改成trunk口,并允许vlan100、101、102通过 [AC1]interface GigabitEthernet 0/0/8 …...
解决远程卡在下载vscode-server的问题,一键安装脚本
vscode-server 下载与安装脚本 vscode-server一键安装脚本 简介 此脚本用于下载并安装指定提交 ID 和架构的 VS Code Server。用户可以选择不同的架构,并输入对应的 VS Code 提交 ID 来下载和安装 vscode-server。VS Code提交ID可以在VS Code界面“帮助>关于…...
【unity实战】用unity封装一个复杂全面且带不同射击模式的飞机大战射击系统
考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、流程控制、面向对象等,适合没有编程基础的…...
LeetCode[42] 接雨水
动态规划 left_max[i] height[i]左侧的最高高度right_max[i] height[i]右侧的最高高度height[i]能接多少水?min(left_max[i], right_max[i])-height[i] class Solution { public:int trap(vector<int>& height) {int len height.size();vector<in…...
c++ 基础题目lambda
1. auto lambda = [](double x) { return static_cast<int>(x); }; 是 匿名函数对象 ,不可直接声明 a.可以赋值给一个与其类型兼容的 std::function 类型的对象 std::function<int(int, int)> lambda = [](int x, int y) { return x + y; }; b.使用具体的 lambda …...
RTSP/Onvif安防视频EasyNVR平台 vs.多协议接入视频汇聚EasyCVR平台:设备分组的区别
EasyNVR安防视频云平台是旭帆科技TSINGSEE青犀旗下支持RTSP/Onvif协议接入的安防监控流媒体视频云平台。平台具备视频实时监控直播、云端录像、云存储、录像检索与回看、告警等视频能力,能对接入的视频流进行处理与多端分发,包括RTSP、RTMP、HTTP-FLV、W…...
网络编程套接字【端口号/TCPUDP/网络字节序/socket编程接口/UDPTCP网络实验】
网络编程套接字 0. 前言1. 认识端口号2. 认识TCP和UDP协议3. 网络字节序4. socket编程接口5. 实现一个简单的UDP网络程序5.1 需求分析5.2 头文件准备5.3 服务器端设计5.4 客户端设计5.5 本地测试5.6 跨网络测试5.7 UDP小应用——客户端输入命令,服务器端执行 6. 地址…...
【C语言预编译处理精选题】
C语言预编译处理精选题 一、选择易错题1.1 纯文本替换,注意优先级!1.2 再来一道文本替换,别马虎1.3 宏定义的替换1.4带参数宏定义的空格问题1.5 " "的include1.6 条件编译1.7 预编译概念 二、填空易错题2.1 注意两个连续的 i2.2 异…...
云钥科技工业相机定制服务,助力企业实现智能智造
在工业自动化、智能制造和机器视觉快速发展的今天,工业相机作为核心感知设备,其性能直接决定了检测精度、生产效率和产品质量。然而,标准化工业相机往往难以满足复杂多样的应用场景需求,工业相机定制逐渐成为企业突破技术瓶颈…...
用了Cline和华为云的大模型,再也回不去了
这两年AI火热,受影响最大的还是程序员群体,因为编程语言是高度形式化的,完全可以用BNF等形式精确地定义,不像自然语言那样,容易出现歧义。另外开源是软件界的潮流,GitHub上有海量的开源代码可供AI来训练&am…...
vs2017版本与arcgis10.1的ArcObject SDK for .NET兼容配置终结解决方案
因电脑用的arcgis10.1,之前安装的vs2010正常能使用AO和AE,安装vs2017后无法使用了,在重新按照新版本arcgis engine或者arcObject费时费力,还需要重新查找资源。 用vs2017与arc10.1的集成主要两个问题,1:安装后vs中没有…...
Java对接微信支付全过程详解
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
微软 System Center Configuration Manager(SCCM)的组件文件
微软 System Center Configuration Manager(SCCM) 或 Microsoft Endpoint Configuration Manager(MECM) 的组件文件,属于企业级设备管理工具的一部分。以下是具体说明: C:\Windows\CCM\smsswd.exe C:\Windows\CCM\tsmanager.exe smsswd.exe 和 tsmanager.exe 是 Micros…...
C语言和C++到底有什么关系?
C 读作“C 加加”,是“C Plus Plus”的简称。 顾名思义,C 就是在 C 语言的基础上增加了新特性,玩出了新花样,所以才说“Plus”,就像 Win11 和 Win10、iPhone 15 和 iPhone 15 Pro 的关系。 C 语言是 1972 年由美国贝…...
10.PE导出表
一:定位导出表 PIMAGE_NT_HEADERS->OptionalHeader->DataDirectory[0] typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress; // 导出表的RVADWORD Size; // 导出表大小(没用) } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 该结构的Vi…...
springBoot中不添加依赖 , 手动生成一个token ,并校验token,在统一拦截器中进行校验 (使用简单 , 但是安全性会低一点)
要在 Spring Boot 里实现接口统一拦截并校验 Token,可以借助 Spring 的拦截器机制。下面是具体的实现步骤和代码示例。 1. 创建 Token 工具类 import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgori…...
VSCode C/C++ 环境搭建指南
一、前言 Visual Studio Code(简称 VSCode)是一款轻量级且功能强大的跨平台代码编辑器,凭借丰富的插件生态和高度的可定制性,深受开发者喜爱。对于 C/C 开发者而言,在 VSCode 中搭建开发环境,能够获得灵活…...
ES6(4) Map 集合详解
1. Map 集合简介 Map 是 ES6 提供的一种新的键值对数据结构,与普通对象(Object)不同,Map 的键可以是任意类型(包括对象、函数等)。 2. 创建 Map 集合 可以使用 new Map() 创建一个 Map,并在括…...
DeepSeek私有化部署与安装浏览器插件内网穿透远程访问实战
文章目录 前言1. 本地部署OllamaDeepSeek2. Page Assist浏览器插件安装与配置3. 简单使用演示4. 远程调用大模型5. 安装内网穿透6. 配置固定公网地址 前言 最近,国产AI大模型Deepseek成了网红爆款,大家纷纷想体验它的魅力。但随着热度的攀升,…...
【设计模式】建造者模式
三、建造者模式 3.3 建造者模式 建造者(Builder) 模式也称构建器模式、构建者模式或生成器模式,同工厂模式或原型 模式一样,也是一种创建型模式。建造者模式比较复杂,不太常用,但这并不表示不需要了 解和掌握该模式。建造者模式…...
一场由 ES 分片 routing 引发的问题
一场由 ES 分片 routing 引发的问题 ES 结构 {"poroperties": {"joinType": {"type": "join","eager_global_ordinals": true,"relations": {"spu": "sku"}},"id":{"type&q…...
搭建Python量化开发环境:从零开始的完整指南
搭建Python量化开发环境:从零开始的完整指南 在量化投资领域,一个稳定且高效的开发环境是成功的关键。本文将引导你一步步搭建起自己的Python量化开发环境,确保你能够顺利开始编写和运行量化策略。 🚀量化软件开通 Ὠ…...
JavaScript日期区间计算:精准解析年月日差异
一、应用场景与功能概述 在日常的制作项目或者是练习,我们经常需要计算两个日期之间的精确时间差。本文将通过一个JavaScript日期计算函数,详细解析如何实现精准的年/月/日差异计算,并探讨实际开发中的常见问题和解决方案。 二、核心功能解…...
大数据学习(71)-三范式构成
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
el-table 插槽踩过的坑 :slot-scope 和#default的区别
slot-scope和#default是Vue中用于定义插槽的两种不同语法,它们在Vue 2和Vue 3中有不同的应用场景和语法规则。 slot-scope 在Vue 2.x中,slot-scope是用于声明具名插槽并获取父组件传递过来的数据的主要方式。通过slot-scope可以定义一个变量scop…...
Linux一键安装node.js【脚本】
node.js一般不用系统的apt安装,而是用nvm这个前端的应用商店安装 node.js是js环境,nvm是安装nodejs管理器。npm是nodejs里的包管理器,安装模块的,类似于python的pip 把以下代码复制保存在一个文件里 比如nano install_nodejs.sh …...
vue3:pinia安装及其使用
一、安装 Pinia 的步骤 1、安装 Pinia npm install pinia 2、在 Vue 应用中引入 Pinia 在 main.js 中引入并注册 Pinia: import { createApp } from vue; import { createPinia } from pinia; import App from ./App.vue;const app createApp(App); app…...
vue2升级Vue3--native、对inheritAttrs作用做以解释、声明的prop属性和未声明prop的属性
native取消 在 Vue 3 中,v-on 的 .native 修饰符已经被移除。在 Vue 2 中,.native 修饰符用于在组件的根元素上监听原生 DOM 事件,但在 Vue 3 中,这一行为发生了变化。 在 Vue 3 中,所有未在子组件的 emits 选项中定…...
【漫话机器学习系列】146.Softmax 激活函数(Softmax Activation Function)
Softmax 激活函数详解 1. Softmax 函数概述 Softmax 函数(Softmax Activation Function)是一种常用于多分类任务的激活函数,广泛应用于机器学习和深度学习模型,特别是在神经网络的输出层。它的主要作用是将输入的多个实数值转换…...
解决:ModuleNotFoundError: No module named ‘_sqlite3‘
报错: from _sqlite3 import * ModuleNotFoundError: No module named _sqlite3安装sqlite3支持组件: sudo apt-get install libsqlite3-dev进入之前下载的python包下,重新编译和安装Python ./configure --enable-loadable-sqlite-extensions make &a…...
C++差分风暴:区间修改终极模板
目录 🔥 差分核心价值 🌟 一维差分模板 1. 核心思想 2. 代码实现 3. 动态图示 📦 二维差分模板 1. 核心公式 2. 代码实现 3. 二维修改示意图 🚨 六大避坑指南 💡 复杂度对比 🌈 LeetCode实战 &…...
easypoi导入Excel兼容日期和字符串格式的日期和时间
问题场景 在使用easypoi导入Excel时,涉及到的常用日期会有yyyy-MM-dd HH:mm:ss、yyyy-MM-dd和HH:mm:ss,但是Excel上面的格式可不止这些,用户总会输入一些其他格式,如 如果在定义verify时用下面这种格式定义,那么总会…...
《保险科技》
自己在保险行业工作很多年,只是接触了一些数据的内容,对于保险业务的知识了解的很少,想通过这本书补充一下,但是发现这本书就是一些知识的拼接。 先将保险的历史,后讲保险的定义,然后就是吹嘘保险行业和互联…...