金橘社区1.0版本
- (一)、SpringBoot整合SpringSecurity
- 1.导入依赖
- 2.数据库
- 3.登入表单
- 4. 添加配置类 SecurityConfig
- 5.接口实现类 CkqnUserServiceImpl
- 6.前端认证问题
- (二)、SpringBoot整合Ajax
- 1.登入表单
- 2. JavaScript
- (三)、SpringBoot整合editor.md
- 1.编写页面
- (1).前端页面
- (2).后端页面
- 2.修改文件
- (1).前端页面
- (2).后端页面
- 3.文章展示
- (1).前端页面
- (2).后端页面
- 4.Aax 密码验证
- (三)、知识点新发现
- 1. template包中创建包静态资源路径一样.
- 2. SpringBoot自定义400、500错误。只需要在template包下创建error包即可。
- 3. 加入我们要使用mybatis-plus那么条件构造其实际上就是对SQL的拼接操作。构造器的泛型是: 实体类
- 4.我们在验证登入操作的时候,只需要根据username进行判断即可,因为这样可以减少对数据库的一次访问
- (四)、金橘社区前端设计
- 1. 渐变动态背景CSS .
- 2. sleep() 休眠的工具类
- 3. 利用JS页面的跳转,我们一定要用异步跳转
- 4. 好看的动态背景推荐
- 6. 炫酷的搜索框
- 7. 返回顶部
- 8. 特效烟花点击
- 9. 前端引入 markdown编辑器 ---- 一定要和html处于同级目录下.
- 10. 百度搜索框
- 11. 二维码弹窗!!!!!
- 12. 文本框不能为空格的属性,一个属性就可以
(一)、SpringBoot整合SpringSecurity
金橘社区官网: http://www.jsxs1.cn/
1.导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jsxs</groupId>
<artifactId>Kumquat</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Kumquat</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--测试启动类 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- JDBC驱动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--mysql连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--自动生成代码依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.22</version>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.7.7</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- JSON-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.26</version>
</dependency>
<!--SpringSecurity -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.数据库
CREATE DATABASE CQAN;
USE CQAN;
# 用户表
CREATE TABLE `ckqn_user` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '自增id',
`uid` varchar(200) NOT NULL COMMENT '用户编号',
`role_id` int(10) NOT NULL COMMENT '角色编号',
`username` varchar(100) NOT NULL COMMENT '用户名',
`password` varchar(200) NOT NULL COMMENT '密码',
`avatar` varchar(500) NOT NULL DEFAULT '/images/avatar/avatar-1.jpg' COMMENT '头像',
`login_date` datetime NOT NULL COMMENT '登录时间',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=840 DEFAULT CHARSET=utf8;
# 用户角色
CREATE TABLE `ckqn_user_role` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '角色编号',
`name` varchar(200) NOT NULL COMMENT '角色名称',
`description` varchar(500) NOT NULL DEFAULT '无描述...' COMMENT '角色描述',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
3.登入表单
<form th:action="@{/code}" method="post">
<div class="inputBox">
<input type="text" placeholder="账户ID" name="username" id="name" required onblur="a1()" onkeyup="this.value=this.value.replace(/\s+/g,'')">
<i id="username" style="z-index: 9999"></i>
</div>
<div class="inputBox">
<input type="password" placeholder="密码" name="password" required onkeyup="this.value=this.value.replace(/\s+/g,'')">
</div>
<div class="inputBox">
<input type="submit" value="登录">
</div>
<p class="forget">忘记密码?<a href="#">
点击这里
</a></p>
<p class="forget">没有账户?<a href="#">
注册
</a></p>
</form>
4. 添加配置类 SecurityConfig
package com.jsxs.kumquat.config;
import com.jsxs.kumquat.service.impl.CkqnUserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author Jsxs
* @Date 2023/4/10 20:56
* @PackageName:com.jsxs.kumquat.config
* @ClassName: SecurityConfig
* @Description: TODO 授权文件
* @Version 1.0
*/
@EnableWebSecurity
@Configuration
@Component
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 调用的是业务实现类
@Resource
private CkqnUserServiceImpl ckqnUserService;
// 密码加密方式
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 认证
*
* @param http
* @throws Exception permitAll -> 无条件访问
* authenticated -> 需要认证才能访问, /* 除了无条件的,其他都加上锁
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 首页所有人可以访问,但是功能页只有对应有权限的人才能访问
http.authorizeRequests()
.antMatchers("/", "/index.html", "/login.html", "/mainMenu.html", "/JsxsRoad.html", "/recommendedArticles.html", "/code", "/a3").permitAll()
.antMatchers("/*").authenticated();
super.configure(http);
http.formLogin().loginPage("/login.html");
// 登录配置
http.formLogin()
.usernameParameter("username") // 这个参数必须为username (约定大于配置)
.passwordParameter("password") // 这个参数必须为password (约定大于配置)
.loginPage("/login.html") //登入页面是哪个?
.loginProcessingUrl("/code") // 点击提交的时候跳转到哪里?
.defaultSuccessUrl("/"); // 密码验证通过后我们跳转到哪里?
// 注销配置
http.headers().contentTypeOptions().disable();
http.headers().frameOptions().disable(); // 图片跨域
http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求(ajax如果要使用一定开启)
http.logout().logoutSuccessUrl("/");
}
/**
*
* @param auth
* @throws Exception userDetailsService-》对哪个业务进行操作,passwordEncoder-》密码加密方式
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(ckqnUserService).passwordEncoder(passwordEncoder());
}
/**
*
* @param web
* @throws Exception 对静态资源不拦截: static目录可以省略
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/assets/**", "/plugins/**", "/img/**", "/editor.md/**", "/Jquery/**", "/layui/**", "/qrcode/**");
}
}
5.接口实现类 CkqnUserServiceImpl
CkqnUserServiceImpl
package com.jsxs.kumquat.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jsxs.kumquat.pojo.CkqnUser;
import com.jsxs.kumquat.mapper.CkqnUserMapper;
import com.jsxs.kumquat.pojo.CkqnUserRole;
import com.jsxs.kumquat.service.CkqnUserRoleService;
import com.jsxs.kumquat.service.CkqnUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author 吉士先生
* @since 2023-04-09
* TODO 这里需要继承一个接口: UserDetailsService
*/
@Service
public class CkqnUserServiceImpl extends ServiceImpl<CkqnUserMapper, CkqnUser> implements CkqnUserService, UserDetailsService {
// 1. 业务层接口
@Resource
CkqnUserService userService;
// 2. 认证角色层接口
@Resource
CkqnUserRoleService roleService;
// 3.session
@Resource
HttpSession session;
/**
* TODO 用户逻辑和认证
*
* @param uid
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String uid) throws UsernameNotFoundException {
// 4. 通过用户名查询用户
CkqnUser user = userService.getOne(new QueryWrapper<CkqnUser>().eq("uid", uid));
// 5. 查询到的角色放入session
session.setAttribute("loginUser", user);
// 7.创建一个新的UserDetails对象,最后验证登陆的需要
UserDetails userDetails = null;
if (user != null) {
System.out.println("未加密:" + user.getPassword());
// 8.加密密码
String BCryptPassword = new BCryptPasswordEncoder().encode(user.getPassword());
// 登录后会将登录密码进行加密,然后比对数据库中的密码,数据库密码需要加密存储!
// String password = user.getPassword();
// 9. 创建一个集合来存放权限
Collection<GrantedAuthority> authorities = getAuthorities(user);
/**
* TODO 实例化UserDetails对象
* uid-> 前端username框传过来的数据
* BCryptPassword-> 加密后的密码
* authorities -》 认证列表
*/
userDetails = new org.springframework.security.core.userdetails.User(uid, BCryptPassword,
true,
true,
true,
true, authorities);
}
return userDetails;
}
/**
* @param user ->实体类
* @return
*/
private Collection<GrantedAuthority> getAuthorities(CkqnUser user) {
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
// 1. 通过用户的第三个字段roleID再另一个表中查找数据据
CkqnUserRole role = roleService.getById(user.getRoleId());
// 2. 注意:这里每个权限前面都要加ROLE_。否在最后验证不会通过
authList.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
return authList;
}
}
两张表
6.前端认证问题
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
sec:authorize="!isAuthenticated()" 没有被认证就是显示
sec:authorize="isAuthenticated()" 认证了才被显示
(二)、SpringBoot整合Ajax
1.登入表单
<form th:action="@{/code}" method="post">
<div class="inputBox">
<input type="text" placeholder="账户ID" name="username" id="name" required onblur="a1()" onkeyup="this.value=this.value.replace(/\s+/g,'')">
<--*******id 为username, 上面id为name-->
<i id="username" style="z-index: 9999"></i>
</div>
<div class="inputBox">
<input type="password" placeholder="密码" name="password" required onkeyup="this.value=this.value.replace(/\s+/g,'')">
</div>
<div class="inputBox">
<input type="submit" value="登录">
</div>
<p class="forget">忘记密码?<a href="#">
点击这里
</a></p>
<p class="forget">没有账户?<a href="#">
注册
</a></p>
</form>
2. JavaScript
// AJAX -js
<script src="Jquery/jquery-3.6.1.js"></script>
function a1(){
$.post({
url:"http://localhost:8080/a3",
data:{'name':$("#name").val()},
success:function(data){
if(data.toString()=='OK'){
$("#username").css("color","green");
}else{
$("#username").css("color","red");
}
$("#username").html(data);
}
});
}
function a2(){
$.post({
url: "http://localhost:8080/a3",
data: {'pwd': $("#pwd").val()},
success: function (data) {
if (data.toString()=='OK') {
$("#userpwd").css("color", "green");
} else {
$("#userpwd").css("color", "red");
}
$("#userpwd").html(data);
}
});
}
(三)、SpringBoot整合editor.md
前提需要我们导入editor.md的插件文件
1.编写页面
(1).前端页面
引入文件
<link rel="stylesheet" href="{/editor.md/examples/css/style.css" th:href="@{/editor.md/examples/css/style.css/}">
<link rel="stylesheet" href="/editor.md/css/editormd.css" th:href="@{/editor.md/css/editormd.css}"/>
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon"/>
<script src="Jquery/jquery-3.6.1.js"></script>
在这里编写文件
<div id="test-editormd">
<textarea style="display:none;" name="content"></textarea>
</div>
配置JS
<script src="/editor.md/examplesjs/jquery.min.js" th:src="@{/editor.md/examples/js/jquery.min.js}"></script>
<script src="/editor.md/editormd.js" th:src="@{/editor.md/editormd.js}"></script>
<script type="text/javascript">
var testEditor;
$(function () {
$.get('/editor.md/examples/test.md', function (md) {
// 这里的第一个参数,指的是:"编写页面的id": 第二个参数是: "配置"
testEditor = editormd("test-editormd", {
width: "100%", // 1.宽度
height: "750px", // 2.高度
path: '/editor.md/lib/', //3.配置文件
theme: "dark", //4.主题
markdown:'#欢迎您来到金橘社区!!', //5.默认编辑页面
// previewTheme: "dark",
editorTheme: "pastel-on-dark", // 6.编辑页面主题
imageUpload: true, //7.文件上传
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL: "/article/image/upload", //8.图片上传URL
toolbarIcons: function () { //9.组件
return ["undo", "redo", "|", "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", "help", "info", "||", "publish"];
},
toolbarIconTexts: { //10.自定义发布按钮的组件
publish: "<span bgcolor='gray' style='font-family: 微软雅黑;font-weight: bold;color: #00FF00'>发布</span>"
},
toolbarHandlers: { //11. 提交文章上传的路径
publish: function (cm, icon, cursor, selection) {
mdEditorForm.method = "post";
mdEditorForm.action = "/article/publish";//提交至服务器的路径
mdEditorForm.submit();
}
}
});
});
});
</script>
全部文档信息
<!DOCTYPE html>
<html lang="zh" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="utf-8"/>
<title>金橘社区-文章编写</title>
<link rel="stylesheet" href="{/editor.md/examples/css/style.css" th:href="@{/editor.md/examples/css/style.css/}">
<link rel="stylesheet" href="/editor.md/css/editormd.css" th:href="@{/editor.md/css/editormd.css}"/>
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" href="layui/css/layui.css" media="all">
<script src="layui/layui.js" charset="utf-8"></script>
<script src="Jquery/jquery-3.6.1.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
.top-search {
width: 680px;
height: 45px;
margin: 30px auto;
}
.search-box {
display: flex;
position: relative;
}
.search-left {
width: 545px;
height: 45px;
border: 2px solid rgb(196, 199, 206);
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
outline-color: rgb(242, 78, 130);
}
.icon-xiangji {
position: absolute;
right: 150px;
top: 12px;
font-size: 24px;
color: rgb(196, 199, 206);
}
.search-right {
color: #fff;
font-size: 18px;
width: 110px;
height: 49px;
border: 0px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
background-color: rgb(242, 78, 130);
}
#su:hover {
background: #14dc99;
}
</style>
</head>
<body>
<form name="mdEditorForm" method="post">
<div class="top-search" style="margin-top: 13px;">
<button type="button" class="layui-btn layui-btn-primary1 layui-btn-lg" style="margin-top: 7px;margin-left: -1200px;"
onclick="document.location.href='MyArticals.html'"><i class="layui-icon layui-icon-return"
style="font-size: 20px; color: #1E9FFF;"></i><i
style="color: #0e0c0d;font-family: 微软雅黑;font-weight: bold" onclick="return confirm('您还未保存,如果直接退出将不会保存数据。您确定直接退出?')">文章管理</i></button>
<div class="search-box" style=" top: -45px;">
<input type="text" name="title" class="search-left" placeholder=" 请 输 入 文 章 标 题" required
style="font-size: 20px;font-family: 微软雅黑;font-weight: bold;"
onkeyup="this.value=this.value.replace(/\s+/g,'')"
value="【无标题】">
<span class="iconfont icon-xiangji"></span>
</div>
<ul sec:authorize="!isAuthenticated()" class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999">
<li class="layui-nav-item" lay-unselect="">
<a href="javascript:;"><img src="/img/login.png" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='login.html'"></a>
<dl class="layui-nav-child">
<dd><a href="javascript:;" onclick="document.location.href='login.html'">登入</a></dd>
</dl>
</li>
</ul>
<ul sec:authorize="hasRole('ROLE_A')" class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999">
<li class="layui-nav-item" lay-unselect="">
<a href="javascript:;"><img src="/img/user.jpg" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='#'"></a>
<dl class="layui-nav-child">
<dd><a href="javascript:;">个人中心</a></dd>
<dd><a href="javascript:;" th:href="@{/login.html}">切换账号</a></dd>
<dd><a href="javascript:;" th:href="@{/logout}">注销</a></dd>
</dl>
</li>
</ul>
<ul class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999">
<li sec:authorize="hasRole('ROLE_B')" class="layui-nav-item" lay-unselect="">
<a href="javascript:;"><img src="/img/user2.jpg" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='#'"></a>
<dl class="layui-nav-child">
<dd><a href="javascript:;">个人中心</a></dd>
<dd><a href="javascript:;" onclick="document.location.href='login.html'">切换账号</a></dd>
<dd><a href="javascript:;" th:href="@{/logout}">注销</a></dd>
</dl>
</li>
</ul>
</div>
<div id="layout" style="margin-top: -41px;">
<header>
</header>
<div id="test-editormd">
<textarea style="display:none;" name="content"></textarea>
</div>
</div>
</form>
<script src="/editor.md/examplesjs/jquery.min.js" th:src="@{/editor.md/examples/js/jquery.min.js}"></script>
<script src="/editor.md/editormd.js" th:src="@{/editor.md/editormd.js}"></script>
<script type="text/javascript">
var testEditor;
$(function () {
$.get('/editor.md/examples/test.md', function (md) {
testEditor = editormd("test-editormd", {
width: "100%",
height: "750px",
path: '/editor.md/lib/',
theme: "dark",
markdown:'#欢迎您来到金橘社区!!',
// previewTheme: "dark",
editorTheme: "pastel-on-dark",
imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL: "/article/image/upload",
toolbarIcons: function () {
return ["undo", "redo", "|", "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", "help", "info", "||", "publish"];
},
toolbarIconTexts: {
publish: "<span bgcolor='gray' style='font-family: 微软雅黑;font-weight: bold;color: #00FF00'>发布</span>"
},
toolbarHandlers: {
publish: function (cm, icon, cursor, selection) {
mdEditorForm.method = "post";
mdEditorForm.action = "/article/publish";//提交至服务器的路径
mdEditorForm.submit();
}
}
});
});
});
</script>
</body>
</html>
(2).后端页面
工具类
package com.jsxs.kumquat.utils;
/**
* @Author Jsxs
* @Date 2023/4/11 15:50
* @PackageName:com.jsxs.kumquat.utils
* @ClassName: FileUtils
* @Description: TODO
* @Version 1.0
*/
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* 文件上传工具类
*/
public class FileUtils {
private static final String prePath = System.getProperty("user.dir") + "/src/main/resources/static/upload/";
/**
* 上传文件
* @param file
* @return 返回文件路径(以相对路径放回)
*/
public static String uploadFile(MultipartFile file) {
if(file.isEmpty()) {
return "";
}
// 获取原文件名
String originFileName = file.getOriginalFilename();
// 我们通过UUID 来重新重组文件名
String uid = UUID.randomUUID().toString();
assert originFileName != null;
String suffix = originFileName.substring(originFileName.lastIndexOf('.') + 1);
String path = prePath + uid + "." + suffix;
String returnPath = "/upload/" + uid + "." + suffix;
File newFile = new File(path);
if(newFile.getParentFile() != null && !newFile.getParentFile().exists()) {
System.out.println("创建目录ing");
// 上面的 newFile.getParentFile() 已经保证了不为null.
if(newFile.getParentFile().mkdirs()) {
System.out.println("创建目录成功");
}else {
System.out.println("创建目录失败");
return "";
}
}
try {
file.transferTo(newFile);
} catch (IOException e) {
e.printStackTrace();
return "";
}
return returnPath;
}
}
文件上传
package com.jsxs.kumquat.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jsxs.kumquat.pojo.CkqnBlog;
import com.jsxs.kumquat.pojo.CkqnUser;
import com.jsxs.kumquat.service.CkqnBlogService;
import com.jsxs.kumquat.utils.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.UUID;
/**
* <p>
* 前端控制器
* </p>
*
* @author 吉士先生
* @since 2023-04-09
*/
@Controller
@RequestMapping("/article")
public class CkqnBlogController {
@Resource
CkqnBlogService ckqnBlogService;
@Resource
HttpSession session;
// 文章上传
@RequestMapping("/publish")
public String publishArticle(CkqnBlog article) {
CkqnUser user = (CkqnUser) session.getAttribute("loginUser");
// 1.作者信息
article.setAuthorId(user.getUid());
article.setAuthorName(user.getUsername());
article.setAuthorAvatar(user.getAvatar());
// 2.博客ID
String replace = UUID.randomUUID().toString().replace("-", "");
article.setBid(replace);
if (article.getTitle() == null) {
System.out.println("标题为空?????????");
}
boolean res = ckqnBlogService.save(article);
if (res) {
return "tipSuccess";
}
return "tipFalse";
}
// 图片上传
@RequestMapping("/image/upload")
@ResponseBody
public JSONObject imageUpload(@RequestParam("editormd-image-file") MultipartFile image) {
JSONObject jsonObject = new JSONObject();
if (image != null) {
String path = FileUtils.uploadFile(image);
// 回调给 editor
System.out.println(path);
jsonObject.put("url", path);
jsonObject.put("success", 1);
jsonObject.put("message", "upload success!");
return jsonObject;
}
jsonObject.put("success", 0);
jsonObject.put("message", "upload error!");
return jsonObject;
}
// 1.一篇详细文章
@RequestMapping("/get/{id}")
public ModelAndView getArticleById(@PathVariable(name = "id") int id) {
ModelAndView modelAndView = new ModelAndView();
CkqnBlog article = ckqnBlogService.getById(id);
modelAndView.setViewName("WriteCenter/article");
if (article == null) {
modelAndView.addObject("article", new CkqnBlog());
}
modelAndView.addObject("article", article);
return modelAndView;
}
2.修改文件
(1).前端页面
需要引入数据
<link rel="stylesheet" th:href="@{/editor.md/examples/css/style.css/}">
<link rel="stylesheet" th:href="@{/editor.md/css/editormd.css}"/>
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon"/>
<script th:src="@{/Jquery/jquery-3.6.1.js}"></script>
被修改页面的展示
<div class="col-md-12 mb-3">
<div id="blog-content">
<textarea required name="content" th:text="${blog.getContent()}" id="content" style="display:none;" rows="3" class="form-control"> </textarea>
</div>
</div>
js
<script th:src="@{/editor.md/examples/js/jquery.min.js}"></script>
<script th:src="@{/editor.md/editormd.js}"></script>
<script type="text/javascript">
var testEditor;
$(function() {
testEditor = editormd("blog-content", {
width : "100%", //1.
height : "750px", //2.
syncScrolling : "single", //3.单页
path: '/editor.md/lib/', //4.路径
theme: "dark", //5.黑暗主题
previewTheme: "gary",
editorTheme: "pastel-on-dark",
imageUpload: true, //6.文件上传
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL: "/article/image/upload", //7.文件上传对象
onload : function() {
console.log('onload', this);
},
toolbarIcons: function () {
return ["undo", "redo", "|", "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", "help", "info", "||", "publish"];
},
toolbarIconTexts: { //8. 提交修改的按钮
publish: "<span bgcolor='gray' style='color: #00FF00;font-weight: bold;font-family: 微软雅黑'>提交修改</span>"
},
toolbarHandlers: { //9.修改文章的路径
publish: function (cm, icon, cursor, selection) {
mdEditorForm.method = "post";
mdEditorForm.action = "/article/toupdate";//提交至服务器的路径
mdEditorForm.submit();
}
},
onfullscreen : function() {
console.log("onfullscreen");
document.getElementsByClassName("navbar")[0].style.display="none";
},
onfullscreenExit : function() {
console.log("onfullscreenExit");
document.getElementsByClassName("navbar")[0].style.display="";
}
});
});
</script>
全部前端
<!DOCTYPE html>
<html lang="zh" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>金橘社区-文章修改</title>
<link rel="stylesheet" th:href="@{/editor.md/examples/css/style.css/}">
<link rel="stylesheet" th:href="@{/editor.md/css/editormd.css}"/>
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}" media="all">
<script th:src="@{/layui/layui.js}" charset="utf-8"></script>
<script th:src="@{/Jquery/jquery-3.6.1.js}"></script>
<style>
* {
margin: 0;
padding: 0;
}
.top-search {
width: 680px;
height: 45px;
margin: 30px auto;
}
.search-box {
display: flex;
position: relative;
}
.search-left {
width: 545px;
height: 45px;
border: 2px solid rgb(196, 199, 206);
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
outline-color: rgb(242, 78, 130);
}
.icon-xiangji {
position: absolute;
right: 150px;
top: 12px;
font-size: 24px;
color: rgb(196, 199, 206);
}
.search-right {
color: #fff;
font-size: 18px;
width: 110px;
height: 49px;
border: 0px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
background-color: rgb(242, 78, 130);
}
#su:hover {
background: #14dc99;
}
</style>
</head>
<body>
<form name="mdEditorForm" method="post">
<!-- 隐藏域 uid -->
<input type="hidden" name="bid" th:value="${blog.getBid()}">
<div class="top-search" style="margin-top: 13px;">
<a type="button" class="layui-btn layui-btn-primary1 layui-btn-lg" style="margin-top: 7px;margin-left: -1200px;"
th:href="@{/MyArticals.html}"><i class="layui-icon layui-icon-return"
style="font-size: 20px; color: #1E9FFF;"></i><i
style="color: #0e0c0d;font-family: 微软雅黑;font-weight: bold" onclick="return confirm('您还未保存更改数据,如果直接退出将不会保存更改的数据。您确定直接退出?')">文章管理</i></a>
<div class="search-box" style=" top: -45px;">
<!-- 标题-->
<input type="text" name="title" class="search-left" placeholder=" 请 输 入 文 章 标 题" required
style="font-size: 20px;font-family: 微软雅黑;font-weight: bold;"
onkeyup="this.value=this.value.replace(/\s+/g,'')"
th:value="${blog.title}">
<span class="iconfont icon-xiangji"></span>
</div>
<ul class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999">
<li sec:authorize="!isAuthenticated()" class="layui-nav-item" lay-unselect="">
<a href="javascript:;"><img src="/img/login.png" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='login.html'"></a>
<dl class="layui-nav-child">
<dd><a href="javascript:;" onclick="document.location.href='login.html'">登入</a></dd>
</dl>
</li>
</ul>
<ul class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999">
<li sec:authorize="isAuthenticated()" class="layui-nav-item" lay-unselect="">
<a href="javascript:;"><img src="/img/user.jpg" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='#'"></a>
<dl class="layui-nav-child">
<dd><a href="javascript:;">个人中心</a></dd>
<dd><a href="javascript:;" th:href="@{/login.html}">切换账号</a></dd>
<dd><a href="javascript:;" th:href="@{/logout}">注销</a></dd>
</dl>
</li>
</ul>
</div>
<div id="layout" style="margin-top: -41px;">
<header>
</header>
<div class="col-md-12 mb-3">
<div id="blog-content">
<textarea required name="content" th:text="${blog.getContent()}" id="content" style="display:none;" rows="3" class="form-control"> </textarea>
</div>
</div>
</div>
</div>
</form>
<script th:src="@{/editor.md/examples/js/jquery.min.js}"></script>
<script th:src="@{/editor.md/editormd.js}"></script>
<script type="text/javascript">
var testEditor;
$(function() {
testEditor = editormd("blog-content", {
width : "100%",
height : "750px",
syncScrolling : "single",
path: '/editor.md/lib/',
theme: "dark",
previewTheme: "gary",
editorTheme: "pastel-on-dark",
imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL: "/article/image/upload",
onload : function() {
console.log('onload', this);
},
toolbarIcons: function () {
return ["undo", "redo", "|", "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", "help", "info", "||", "publish"];
},
toolbarIconTexts: {
publish: "<span bgcolor='gray' style='color: #00FF00;font-weight: bold;font-family: 微软雅黑'>提交修改</span>"
},
toolbarHandlers: {
publish: function (cm, icon, cursor, selection) {
mdEditorForm.method = "post";
mdEditorForm.action = "/article/toupdate";//提交至服务器的路径
mdEditorForm.submit();
}
},
onfullscreen : function() {
console.log("onfullscreen");
document.getElementsByClassName("navbar")[0].style.display="none";
},
onfullscreenExit : function() {
console.log("onfullscreenExit");
document.getElementsByClassName("navbar")[0].style.display="";
}
});
});
</script>
</body>
</html>
(2).后端页面
package com.jsxs.kumquat.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jsxs.kumquat.pojo.CkqnBlog;
import com.jsxs.kumquat.pojo.CkqnUser;
import com.jsxs.kumquat.service.CkqnBlogService;
import com.jsxs.kumquat.utils.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.UUID;
/**
* <p>
* 前端控制器
* </p>
*
* @author 吉士先生
* @since 2023-04-09
*/
@Controller
@RequestMapping("/article")
public class CkqnBlogController {
@Resource
CkqnBlogService ckqnBlogService;
@Resource
HttpSession session;
// 文章上传
@RequestMapping("/publish")
public String publishArticle(CkqnBlog article) {
CkqnUser user = (CkqnUser) session.getAttribute("loginUser");
// 1.作者信息
article.setAuthorId(user.getUid());
article.setAuthorName(user.getUsername());
article.setAuthorAvatar(user.getAvatar());
// 2.博客ID
String replace = UUID.randomUUID().toString().replace("-", "");
article.setBid(replace);
if (article.getTitle() == null) {
System.out.println("标题为空?????????");
}
boolean res = ckqnBlogService.save(article);
if (res) {
return "tipSuccess";
}
return "tipFalse";
}
// 文件上传
@RequestMapping("/image/upload")
@ResponseBody
public JSONObject imageUpload(@RequestParam("editormd-image-file") MultipartFile image) {
JSONObject jsonObject = new JSONObject();
if (image != null) {
String path = FileUtils.uploadFile(image);
// 回调给 editor
System.out.println(path);
jsonObject.put("url", path);
jsonObject.put("success", 1);
jsonObject.put("message", "upload success!");
return jsonObject;
}
jsonObject.put("success", 0);
jsonObject.put("message", "upload error!");
return jsonObject;
}
// 1.一篇详细文章
@RequestMapping("/get/{id}")
public ModelAndView getArticleById(@PathVariable(name = "id") int id) {
ModelAndView modelAndView = new ModelAndView();
CkqnBlog article = ckqnBlogService.getById(id);
modelAndView.setViewName("WriteCenter/article");
if (article == null) {
modelAndView.addObject("article", new CkqnBlog());
}
modelAndView.addObject("article", article);
return modelAndView;
}
// 开始修改
@RequestMapping("/update/editor/{authorId}/{bid}")
public String updateArticleByUser(@PathVariable("authorId") String authorId, @PathVariable("bid") String bid, Model model) {
// CkqnUser user = (CkqnUser)session.getAttribute("loginUser");
QueryWrapper<CkqnBlog> wrapper = new QueryWrapper<>();
// 查询uid
CkqnBlog blog = ckqnBlogService.getOne(wrapper.eq("bid", bid));
System.out.println(authorId + " " + bid);
// 2.查询作者
if (!blog.getAuthorId().equals(authorId)) {
System.out.println("对不起,你在非法编辑");
// 直接注销账号
return "redirect:/index.html";
}
// 3. 存放文章
model.addAttribute("blog", blog);
// 4.没有重定向
return "WriteCenter/update";
}
// 修改的操作
@PostMapping("/toupdate")
public String editor(CkqnBlog blog) {
QueryWrapper<CkqnBlog> wrapper = new QueryWrapper<>();
System.out.println(blog);
CkqnBlog queryBlog = ckqnBlogService.getOne(wrapper.eq("bid", blog.getBid()));
queryBlog.setTitle(blog.getTitle());
queryBlog.setCategoryId(blog.getCategoryId());
queryBlog.setContent(blog.getContent());
boolean b = ckqnBlogService.updateById(queryBlog);
if (b) {
return "tipSuccess";
}
return "tipFalse";
}
}
3.文章展示
(1).前端页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>金橘社区-文章展示</title>
<link rel="stylesheet" th:href="@{/editor.md/css/examples/style.css}" />
<link rel="stylesheet" th:href="@{/editor.md/css/editormd.css}" />
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />
<script th:src="@{/Jquery/jquery-3.6.1.js}"></script>
<link rel="stylesheet" th:href="@{/css/commons.css}">
<link rel="stylesheet" th:href="@{/css/blog_detail.css}">
<link rel="stylesheet" th:href="@{/layui/css/layui.css}" media="all">
<style>
.arrow{
border: 9px solid transparent;
border-bottom-color: #3DA0DB;
width: 0px;
height: 0px;
top:0px
}
.stick{
width: 8px;
height: 14px;
border-radius: 1px;
background-color: #3DA0DB;
top:15px;
}
#back_top div{
position: absolute;
margin: auto;
right: 0px;
left: 0px;
}
#back_top{
background-color: #dddddd;
height: 38px;
width: 38px;
border-radius: 3px;
display: block;
cursor: pointer;
position: fixed;
right: 50px;
bottom: 100px;
display: none;
}
</style>
</head>
<body>
<div class="top-search" style="margin-top: 12px;">
<a type="button" class="layui-btn layui-btn-primary1 layui-btn-lg" style="margin-top: 42px;margin-left: 1px;"
th:href="@{/MyArticals.html}" ><i class="layui-icon layui-icon-return"
style="font-size: 20px; color: #1E9FFF;"></i><i
style="color: #0e0c0d;font-family: 微软雅黑;font-weight: bold">文章管理</i></a>
</div>
<span class="js-cursor-container"></span>
<div id="article"></div>
<div id="back_top">
<div class="arrow"></div>
<div class="stick"></div>
</div>
<!-- .container 作为页面的版心 -->
<div class="container">
<!-- 左侧个人信息 -->
<div class="left">
<!-- 整个用户信息区 -->
<div class="card">
<img th:src="@{/img/login.png}" alt="">
<h3 th:text="${article.getAuthorName()}"></h3>
<a href="#">github 地址</a>
<div class="counter">
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span>X</span>
<span>X</span>
</div>
</div>
</div>
<!-- 右侧内容详情 -->
<div class="right" style="overflow: auto">
<!-- 包裹整个博客的内容详情 -->
<div class="blog-content">
<h3 style="color: #e8136f" th:text="${article.title}"></h3>
<div class="date" th:text="${#dates.format(article.getGmtCreate(),'yyyy-MM-dd HH:mm:ss')}"></div>
<!-- <h2 style="align-content: center" th:text="${article.authorName}"></h2>-->
<div id="layout">
<div id="test-editormd">
<textarea style="display:none;" th:text="${article.content}"></textarea>
</div>
</div>
</div>
</div>
</div>
<script th:src="@{/editor.md/examples/js/jquery.min.js}"></script>
<script th:src="@{/editor.md/lib/marked.min.js}"></script>
<script th:src="@{/editor.md/lib/prettify.min.js}"></script>
<script th:src="@{/editor.md/lib/raphael.min.js}"></script>
<script th:src="@{/editor.md/lib/underscore.min.js}"></script>
<script th:src="@{/editor.md/lib/sequence-diagram.min.js}"></script>
<script th:src="@{/editor.md/lib/flowchart.min.js}"></script>
<script th:src="@{/editor.md/lib/jquery.flowchart.min.js}"></script>
<script th:src="@{/editor.md/editormd.js}"></script>
<script type="text/javascript">
var testEditor;
$(function () {
testEditor = editormd.markdownToHTML("test-editormd", {
width: "90%",
height: 700,
path: "/editor.md/lib/",
preview: true,
watch: true,
editor: false,
})
})
! function (e, t, a) {
function r() {
for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[
e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x +
"px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e]
.scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");
requestAnimationFrame(r)
}
function n() {
var t = "function" == typeof e.onclick && e.onclick;
e.onclick = function (e) {
t && t(), o(e)
}
}
function o(e) {
var a = t.createElement("div");
a.className = "heart", s.push({
el: a,
x: e.clientX - 5,
y: e.clientY - 5,
scale: 1,
alpha: 1,
color: c()
}), t.body.appendChild(a)
}
function i(e) {
var a = t.createElement("style");
a.type = "text/css";
try {
a.appendChild(t.createTextNode(e))
} catch (t) {
a.styleSheet.cssText = e
}
t.getElementsByTagName("head")[0].appendChild(a)
}
function c() {
return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math
.random()) + ")"
}
var s = [];
e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e
.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) {
setTimeout(e, 1e3 / 60)
}, i(
".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"
), n(), r()
}(window, document);
//-----------------------------
(function fairyDustCursor() {
var possibleColors = ["#D61C59", "#E7D84B", "#1B8798"]
var width = window.innerWidth;
var height = window.innerHeight;
var cursor = { x: width / 2, y: width / 2 };
var particles = [];
function init() {
bindEvents();
loop();
}
// Bind events that are needed
function bindEvents() {
document.addEventListener('mousemove', onMouseMove);
window.addEventListener('resize', onWindowResize);
}
function onWindowResize(e) {
width = window.innerWidth;
height = window.innerHeight;
}
function onMouseMove(e) {
cursor.x = e.clientX;
cursor.y = e.clientY;
addParticle(cursor.x, cursor.y, possibleColors[Math.floor(Math.random() * possibleColors.length)]);
}
function addParticle(x, y, color) {
var particle = new Particle();
particle.init(x, y, color);
particles.push(particle);
}
function updateParticles() {
// Updated
for (var i = 0; i < particles.length; i++) {
particles[i].update();
}
// Remove dead particles
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i].lifeSpan < 0) {
particles[i].die();
particles.splice(i, 1);
}
}
}
function loop() {
requestAnimationFrame(loop);
updateParticles();
}
/**
* Particles
*/
function Particle() {
this.character = "*";
this.lifeSpan = 120; //ms
this.initialStyles = {
"position": "fixed",
"display": "inline-block",
"top": "0px",
"left": "0px",
"pointerEvents": "none",
"touch-action": "none",
"z-index": "10000000",
"fontSize": "25px",
"will-change": "transform"
};
// Init, and set properties
this.init = function (x, y, color) {
this.velocity = {
x: (Math.random() < 0.5 ? -1 : 1) * (Math.random() / 2),
y: 1
};
this.position = { x: x + 10, y: y + 10 };
this.initialStyles.color = color;
this.element = document.createElement('span');
this.element.innerHTML = this.character;
applyProperties(this.element, this.initialStyles);
this.update();
document.querySelector('.js-cursor-container').appendChild(this.element);
};
this.update = function () {
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
this.lifeSpan--;
this.element.style.transform = "translate3d(" + this.position.x + "px," + this.position.y + "px, 0) scale(" + (this.lifeSpan / 120) + ")";
}
this.die = function () {
this.element.parentNode.removeChild(this.element);
}
}
/**
* Utils
*/
// Applies css `properties` to an element.
function applyProperties(target, properties) {
for (var key in properties) {
target.style[key] = properties[key];
}
}
if (!('ontouchstart' in window || navigator.msMaxTouchPoints)) init();
})();
$(function(){
$(window).scroll(function(){ //只要窗口滚动,就触发下面代码
var scrollt = document.documentElement.scrollTop + document.body.scrollTop; //获取滚动后的高度
if( scrollt >200 ){ //判断滚动后高度超过200px,就显示
$("#back_top").fadeIn(400); //淡入
}else{
$("#back_top").stop().fadeOut(400); //如果返回或者没有超过,就淡出.必须加上stop()停止之前动画,否则会出现闪动
}
});
$("#back_top").click(function(){ //当点击标签的时候,使用animate在200毫秒的时间内,滚到顶部
$("html,body").animate({scrollTop:"0px"},200);
});
});
</script>
</body>
</html>
(2).后端页面
package com.jsxs.kumquat.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jsxs.kumquat.pojo.CkqnBlog;
import com.jsxs.kumquat.pojo.CkqnUser;
import com.jsxs.kumquat.service.CkqnBlogService;
import com.jsxs.kumquat.utils.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.UUID;
/**
* <p>
* 前端控制器
* </p>
*
* @author 吉士先生
* @since 2023-04-09
*/
@Controller
@RequestMapping("/article")
public class CkqnBlogController {
@Resource
CkqnBlogService ckqnBlogService;
@Resource
HttpSession session;
// 文章上传
@RequestMapping("/publish")
public String publishArticle(CkqnBlog article) {
CkqnUser user = (CkqnUser) session.getAttribute("loginUser");
// 1.作者信息
article.setAuthorId(user.getUid());
article.setAuthorName(user.getUsername());
article.setAuthorAvatar(user.getAvatar());
// 2.博客ID
String replace = UUID.randomUUID().toString().replace("-", "");
article.setBid(replace);
if (article.getTitle() == null) {
System.out.println("标题为空?????????");
}
boolean res = ckqnBlogService.save(article);
if (res) {
return "tipSuccess";
}
return "tipFalse";
}
// 文件上传
@RequestMapping("/image/upload")
@ResponseBody
public JSONObject imageUpload(@RequestParam("editormd-image-file") MultipartFile image) {
JSONObject jsonObject = new JSONObject();
if (image != null) {
String path = FileUtils.uploadFile(image);
// 回调给 editor
System.out.println(path);
jsonObject.put("url", path);
jsonObject.put("success", 1);
jsonObject.put("message", "upload success!");
return jsonObject;
}
jsonObject.put("success", 0);
jsonObject.put("message", "upload error!");
return jsonObject;
}
// 1.一篇详细文章
@RequestMapping("/get/{id}")
public ModelAndView getArticleById(@PathVariable(name = "id") int id) {
ModelAndView modelAndView = new ModelAndView();
CkqnBlog article = ckqnBlogService.getById(id);
modelAndView.setViewName("WriteCenter/article");
if (article == null) {
modelAndView.addObject("article", new CkqnBlog());
}
modelAndView.addObject("article", article);
return modelAndView;
}
// 开始修改
@RequestMapping("/update/editor/{authorId}/{bid}")
public String updateArticleByUser(@PathVariable("authorId") String authorId, @PathVariable("bid") String bid, Model model) {
// CkqnUser user = (CkqnUser)session.getAttribute("loginUser");
QueryWrapper<CkqnBlog> wrapper = new QueryWrapper<>();
// 查询uid
CkqnBlog blog = ckqnBlogService.getOne(wrapper.eq("bid", bid));
System.out.println(authorId + " " + bid);
// 2.查询作者
if (!blog.getAuthorId().equals(authorId)) {
System.out.println("对不起,你在非法编辑");
// 直接注销账号
return "redirect:/index.html";
}
// 3. 存放文章
model.addAttribute("blog", blog);
// 4.没有重定向
return "WriteCenter/update";
}
}
4.Aax 密码验证
需要引入js- 一个都不能变,注意路径
<link rel="stylesheet" th:href="@{/editor.md/css/examples/style.css}" />
<link rel="stylesheet" th:href="@{/editor.md/css/editormd.css}" />
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />
<script th:src="@{/Jquery/jquery-3.6.1.js}"></script>
<script th:src="@{/editor.md/examples/js/jquery.min.js}"></script>
<script th:src="@{/editor.md/lib/marked.min.js}"></script>
<script th:src="@{/editor.md/lib/prettify.min.js}"></script>
<script th:src="@{/editor.md/lib/raphael.min.js}"></script>
<script th:src="@{/editor.md/lib/underscore.min.js}"></script>
<script th:src="@{/editor.md/lib/sequence-diagram.min.js}"></script>
<script th:src="@{/editor.md/lib/flowchart.min.js}"></script>
<script th:src="@{/editor.md/lib/jquery.flowchart.min.js}"></script>
<script th:src="@{/editor.md/editormd.js}"></script>
文章主题部分
<div id="test-editormd">
<textarea style="display:none;" th:text="${article.content}"></textarea>
</div>
JS
<script type="text/javascript">
var testEditor;
$(function () {
testEditor = editormd.markdownToHTML("test-editormd", {
width: "90%",
height: 700,
path: "/editor.md/lib/",
preview: true,
watch: true,
editor: false,
})
})
! function (e, t, a) {
function r() {
for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[
e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x +
"px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e]
.scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");
requestAnimationFrame(r)
}
function n() {
var t = "function" == typeof e.onclick && e.onclick;
e.onclick = function (e) {
t && t(), o(e)
}
}
function o(e) {
var a = t.createElement("div");
a.className = "heart", s.push({
el: a,
x: e.clientX - 5,
y: e.clientY - 5,
scale: 1,
alpha: 1,
color: c()
}), t.body.appendChild(a)
}
function i(e) {
var a = t.createElement("style");
a.type = "text/css";
try {
a.appendChild(t.createTextNode(e))
} catch (t) {
a.styleSheet.cssText = e
}
t.getElementsByTagName("head")[0].appendChild(a)
}
function c() {
return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math
.random()) + ")"
}
var s = [];
e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e
.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) {
setTimeout(e, 1e3 / 60)
}, i(
".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"
), n(), r()
}(window, document);
</script>
全体文章
package com.jsxs.kumquat.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author Jsxs
* @Date 2023/4/10 17:07
* @PackageName:com.jsxs.kumquat.controller
* @ClassName: AjaxController
* @Description: TODO
* @Version 1.0
*/
@RestController
public class AjaxController {
@RequestMapping("/a3")
public String a3(String name,String pwd){
String msg="";
System.out.println(name);
System.out.println(pwd);
if (name!=null){
if ("吉士先生".equals(name)){
msg="OK";
}else {
msg="Error";
}
}
if (pwd!=null){
if ("121788".equals(pwd)){
msg="OK";
}else {
msg="Error";
}
}
return msg;
}
}
以上可以实现异步登入判断
(三)、知识点新发现
1. template包中创建包静态资源路径一样.
2. SpringBoot自定义400、500错误。只需要在template包下创建error包即可。
3. 加入我们要使用mybatis-plus那么条件构造其实际上就是对SQL的拼接操作。构造器的泛型是: 实体类
//1. 通过账户UserUid进行查找用户
@RequestMapping("/code")
public String SelectByUserUid(){
// 1.定义条件构造器-> 这里的泛型是实体类
QueryWrapper<CkqnUser> wrapper = new QueryWrapper<>();
wrapper.eq("uid","root");
CkqnUser one = ckqnUserService.getOne(wrapper.eq("uid", "root"));
return ""+one;
}
4.我们在验证登入操作的时候,只需要根据username进行判断即可,因为这样可以减少对数据库的一次访问
//1. 通过账户UserUid进行查找用户 -> 可以减少对数据库的访问频次(可以减少对密码的查询)
@PostMapping("/code")
public String SelectByUserUid() throws Exception {
// 1.定义条件构造器-> 这里的泛型是实体类
QueryWrapper<CkqnUser> wrapper = new QueryWrapper<>();
// 2.添加条件: 是否相等
wrapper.eq("uid","root");
// 3.执行查找数据的操作
CkqnUser ckqnUser = ckqnUserService.getOne(wrapper.eq("uid", "root"));
// 4.optional检测是否为空
Optional<CkqnUser> optional = Optional.ofNullable(ckqnUser);
// 5.假如为空直接抛出异常
optional.orElseThrow(()->new Exception("非常抱歉出现了空指针异常!!"));
// 6.查看密码
String password = ckqnUser.getPassword();
CkqnUser ckqnUser1 = ckqnUserService.getOne(wrapper.eq("password", password));
if (ckqnUser1.equals(ckqnUser)){
return "index";
}else {
return "login";
}
(四)、金橘社区前端设计
金橘社区
1. 渐变动态背景CSS .
<style>
body {
margin: 0;
padding: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #34ac40, #2c8ae8, #dc47cd, #e7e373);
background-size: 500%;
animation: bgAnimation 6s linear infinite;
}
h1 {
color: white;
letter-spacing: 5px;
}
@keyframes bgAnimation {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
</style>
2. sleep() 休眠的工具类
function sleep(numberMillis) {
var now = new Date();
var exitTime = now.getTime() + numberMillis;
while (true) {
now = new Date();
if (now.getTime() > exitTime)
return true;
}
}
3. 利用JS页面的跳转,我们一定要用异步跳转
// 这里一定要异步对其进行跳转页面
setTimeout(()=>{
document.location.href="two.html"
},600)
4. 好看的动态背景推荐
css
/* 清除浏览器默认边距,
使边框和内边距的值包含在元素的width和height内 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 使用flex布局,让内容垂直和水平居中 */
section {
/* 相对定位 */
position: relative;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
/* linear-gradient() 函数用于创建一个表示两种或多种颜色线性渐变的图片 */
background: linear-gradient(to bottom, #f1f4f9, #dff1ff);
}
/* 背景颜色 */
section .color {
/* 绝对定位 */
position: absolute;
/* 使用filter(滤镜) 属性,给图像设置高斯模糊*/
filter: blur(200px);
}
/* :nth-child(n) 选择器匹配父元素中的第 n 个子元素 */
section .color:nth-child(1) {
top: -350px;
width: 600px;
height: 600px;
background: #ff359b;
}
section .color:nth-child(2) {
bottom: -150px;
left: 100px;
width: 500px;
height: 500px;
background: #fffd87;
}
section .color:nth-child(3) {
bottom: 50px;
right: 100px;
width: 500px;
height: 500px;
background: #00d2ff;
}
.box {
position: relative;
}
/* 背景圆样式 */
.box .circle {
position: absolute;
background: rgba(255, 255, 255, 0.1);
/* backdrop-filter属性为一个元素后面区域添加模糊效果 */
backdrop-filter: blur(5px);
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.5);
border-right: 1px solid rgba(255, 255, 255, 0.2);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 50%;
/* 使用filter(滤镜) 属性,改变颜色。
hue-rotate(deg) 给图像应用色相旋转
calc() 函数用于动态计算长度值
var() 函数调用自定义的CSS属性值x*/
filter: hue-rotate(calc(var(--x) * 70deg));
/* 调用动画animate,需要10s完成动画,
linear表示动画从头到尾的速度是相同的,
infinite指定动画应该循环播放无限次*/
animation: animate 10s linear infinite;
/* 动态计算动画延迟几秒播放 */
animation-delay: calc(var(--x) * -1s);
}
/* 背景圆动画 */
@keyframes animate {
0%, 100%, {
transform: translateY(-50px);
}
50% {
transform: translateY(50px);
}
}
.box .circle:nth-child(1) {
top: -50px;
right: -60px;
width: 100px;
height: 100px;
}
.box .circle:nth-child(2) {
top: 150px;
left: -100px;
width: 120px;
height: 120px;
z-index: 2;
}
.box .circle:nth-child(3) {
bottom: 50px;
right: -60px;
width: 80px;
height: 80px;
z-index: 2;
}
.box .circle:nth-child(4) {
bottom: -80px;
left: 100px;
width: 60px;
height: 60px;
}
.box .circle:nth-child(5) {
top: -80px;
left: 140px;
width: 60px;
height: 60px;
}
/* 登录框样式 */
.container {
position: relative;
width: 400px;
min-height: 400px;
background: rgba(255, 255, 255, 0.1);
display: flex;
justify-content: center;
align-items: center;
backdrop-filter: blur(5px);
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.5);
border-right: 1px solid rgba(255, 255, 255, 0.2);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.form {
position: relative;
width: 100%;
height: 100%;
padding: 50px;
}
/* 登录标题样式 */
.form h2 {
position: relative;
color: #fff;
font-size: 24px;
font-weight: 600;
letter-spacing: 5px;
margin-bottom: 30px;
cursor: pointer;
}
/* 登录标题的下划线样式 */
.form h2::before {
content: "";
position: absolute;
left: 0;
bottom: -10px;
width: 0px;
height: 3px;
background: #fff;
transition: 0.5s;
}
.form h2:hover:before {
width: 53px;
}
.form .inputBox {
width: 100%;
margin-top: 20px;
}
/* 输入框样式 */
.form .inputBox input {
width: 100%;
padding: 10px 20px;
background: rgba(255, 255, 255, 0.2);
outline: none;
border: none;
border-radius: 30px;
border: 1px solid rgba(255, 255, 255, 0.5);
border-right: 1px solid rgba(255, 255, 255, 0.2);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
font-size: 16px;
letter-spacing: 1px;
color: #fff;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
}
.form .inputBox input::placeholder {
color: #fff;
}
/* 登录按钮样式 */
.form .inputBox input[type="submit"] {
background: #fff;
color: #666;
max-width: 100px;
margin-bottom: 20px;
font-weight: 600;
cursor: pointer;
}
.forget {
margin-top: 6px;
color: #fff;
letter-spacing: 1px;
}
.forget a {
color: #fff;
font-weight: 600;
text-decoration: none;
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>登录:微信公众号AlbertYang</title>
</head>
<body>
<section>
<!-- 背景颜色 -->
<div class="color"></div>
<div class="color"></div>
<div class="color"></div>
<div class="box">
<!-- 背景圆 -->
<div class="circle" style="--x:0"></div>
<div class="circle" style="--x:1"></div>
<div class="circle" style="--x:2"></div>
<div class="circle" style="--x:3"></div>
<div class="circle" style="--x:4"></div>
<!-- 登录框 -->
<div class="container">
<div class="form">
<h2>登录</h2>
<form>
<div class="inputBox">
<input type="text" placeholder="姓名">
</div>
<div class="inputBox">
<input type="password" placeholder="密码">
</div>
<div class="inputBox">
<input type="submit" value="登录">
</div>
<p class="forget">忘记密码?<a href="#">
点击这里
</a></p>
<p class="forget">没有账户?<a href="#">
注册
</a></p>
</form>
</div>
</div>
</div>
</section>
</body>
</html>
6. 炫酷的搜索框
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title> 搜索框 </title>
<link rel="stylesheet" href="http://at.alicdn.com/t/font_1309180_m0vigzfu7y.css">
<style type="text/css">
.bod1 {
/*background: -webkit-linear-gradient(to right, #f7797d, #FBD786, #C6FFDD);*/
/*background-image: url(" 2558.jpg");*/
/*background-size:cover;*/
background: #aa4b6b;
/* fallback for old browsers */
background: -webkit-linear-gradient(to right, #3b8d99, #6b6b83, #aa4b6b);
/* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to right, #3b8d99, #6b6b83, #aa4b6b);
/* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}
.search-box {
position: absolute;
top: 10%;
left: 60%;
transform: translate(-10%, -50%);
background: #FD2C6B;
height: 40px;
border-radius: 30px;
/*圆角边框*/
padding: 10px;
}
.search-box:hover>.search-text {
width: 400px;
}
.search-box:hover>.search-btn {
background: white;
color: #26087b;
}
.search-btn {
color: white;
float: right;
width: 40px;
height: 40px;
border-radius: 50%;
background: #FD2C6B;
display: flex;
justify-content: center;
align-items: center;
transition: 0.5s;
}
.search-text {
border: none;
background: none;
outline: none;
float: left;
padding: 0;
color: white;
font-size: 20px;
font-family: "Microsoft YaHei UI Light";
transition: 0.5s;
line-height: 40px;
width: 0px;
}
</style>
</head>
<body class="bod1">
<div class="search-box">
<input class="search-text" name="inputs" placeholder="在这里输入你想要搜的的东西" >
<a class="search-btn" onclick="as()">
<i class="iconfont iconchazhao"></i>
</a>
</div>
</body>
<script>
function as() {
let e = document.getElementsByClassName('search-text').inputs.value
window.location.href = `https://www.baidu.com/s?wd=${e}`
console.log("fsfs",e)
}
</script>
</html>
7. 返回顶部
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>doc</title>
<style>
.arrow{
border: 9px solid transparent;
border-bottom-color: #3DA0DB;
width: 0px;
height: 0px;
top:0px
}
.stick{
width: 8px;
height: 14px;
border-radius: 1px;
background-color: #3DA0DB;
top:15px;
}
#back_top div{
position: absolute;
margin: auto;
right: 0px;
left: 0px;
}
#back_top{
background-color: #dddddd;
height: 38px;
width: 38px;
border-radius: 3px;
display: block;
cursor: pointer;
position: fixed;
right: 50px;
bottom: 100px;
display: none;
}
</style>
</head>
<body>
<div id="article"></div>
<div id="back_top">
<div class="arrow"></div>
<div class="stick"></div>
</div>
<script src="http://cdn.staticfile.org/jquery/1.11.1-rc2/jquery.min.js"></script>
<script>
$(function(){
for(var i =0 ;i <100;i++){
$("#article").append("<p>xxxxxxxxxx<br></p>")
}
})
</script>
<script>
$(function(){
$(window).scroll(function(){ //只要窗口滚动,就触发下面代码
var scrollt = document.documentElement.scrollTop + document.body.scrollTop; //获取滚动后的高度
if( scrollt >200 ){ //判断滚动后高度超过200px,就显示
$("#back_top").fadeIn(400); //淡入
}else{
$("#back_top").stop().fadeOut(400); //如果返回或者没有超过,就淡出.必须加上stop()停止之前动画,否则会出现闪动
}
});
$("#back_top").click(function(){ //当点击标签的时候,使用animate在200毫秒的时间内,滚到顶部
$("html,body").animate({scrollTop:"0px"},200);
});
});
</script>
</body>
</html>
8. 特效烟花点击
<span class="js-cursor-container"></span>
<!-- 网页鼠标点击特效(爱心) -->
<script>
! function (e, t, a) {
function r() {
for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[
e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x +
"px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e]
.scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");
requestAnimationFrame(r)
}
function n() {
var t = "function" == typeof e.onclick && e.onclick;
e.onclick = function (e) {
t && t(), o(e)
}
}
function o(e) {
var a = t.createElement("div");
a.className = "heart", s.push({
el: a,
x: e.clientX - 5,
y: e.clientY - 5,
scale: 1,
alpha: 1,
color: c()
}), t.body.appendChild(a)
}
function i(e) {
var a = t.createElement("style");
a.type = "text/css";
try {
a.appendChild(t.createTextNode(e))
} catch (t) {
a.styleSheet.cssText = e
}
t.getElementsByTagName("head")[0].appendChild(a)
}
function c() {
return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math
.random()) + ")"
}
var s = [];
e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e
.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) {
setTimeout(e, 1e3 / 60)
}, i(
".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"
), n(), r()
}(window, document);
//-----------------------------
(function fairyDustCursor() {
var possibleColors = ["#D61C59", "#E7D84B", "#1B8798"]
var width = window.innerWidth;
var height = window.innerHeight;
var cursor = { x: width / 2, y: width / 2 };
var particles = [];
function init() {
bindEvents();
loop();
}
// Bind events that are needed
function bindEvents() {
document.addEventListener('mousemove', onMouseMove);
window.addEventListener('resize', onWindowResize);
}
function onWindowResize(e) {
width = window.innerWidth;
height = window.innerHeight;
}
function onMouseMove(e) {
cursor.x = e.clientX;
cursor.y = e.clientY;
addParticle(cursor.x, cursor.y, possibleColors[Math.floor(Math.random() * possibleColors.length)]);
}
function addParticle(x, y, color) {
var particle = new Particle();
particle.init(x, y, color);
particles.push(particle);
}
function updateParticles() {
// Updated
for (var i = 0; i < particles.length; i++) {
particles[i].update();
}
// Remove dead particles
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i].lifeSpan < 0) {
particles[i].die();
particles.splice(i, 1);
}
}
}
function loop() {
requestAnimationFrame(loop);
updateParticles();
}
/**
* Particles
*/
function Particle() {
this.character = "*";
this.lifeSpan = 120; //ms
this.initialStyles = {
"position": "fixed",
"display": "inline-block",
"top": "0px",
"left": "0px",
"pointerEvents": "none",
"touch-action": "none",
"z-index": "10000000",
"fontSize": "25px",
"will-change": "transform"
};
// Init, and set properties
this.init = function (x, y, color) {
this.velocity = {
x: (Math.random() < 0.5 ? -1 : 1) * (Math.random() / 2),
y: 1
};
this.position = { x: x + 10, y: y + 10 };
this.initialStyles.color = color;
this.element = document.createElement('span');
this.element.innerHTML = this.character;
applyProperties(this.element, this.initialStyles);
this.update();
document.querySelector('.js-cursor-container').appendChild(this.element);
};
this.update = function () {
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
this.lifeSpan--;
this.element.style.transform = "translate3d(" + this.position.x + "px," + this.position.y + "px, 0) scale(" + (this.lifeSpan / 120) + ")";
}
this.die = function () {
this.element.parentNode.removeChild(this.element);
}
}
/**
* Utils
*/
// Applies css `properties` to an element.
function applyProperties(target, properties) {
for (var key in properties) {
target.style[key] = properties[key];
}
}
if (!('ontouchstart' in window || navigator.msMaxTouchPoints)) init();
})();
</script>
9. 前端引入 markdown编辑器 ---- 一定要和html处于同级目录下.
[Editor.md - 开源在线 Markdown 编辑器 (pandao.github.io)](https://pandao.github.io/editor.md/)
首先我们必须先要打开上面的网站: 然后点击安装下载: 就会跳转到一个Github项目中。安装一个压缩包
然后解压到我们的项目中:
<link rel="stylesheet" href="editor.md/css/editormd.min.css">
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>
<!-- 正文: -->
<div id="editor">
</div>
<script>
var editor = editormd("editor", {
// 这里的尺寸必须在这里设置,设置样式会被 editormd 自动覆盖
width: "100%",
// 设置高度
height: "500px",
// 编辑器中初始内容
markdown: "# 在这里写下一篇博客",
// 指定插件路径
path: "editor.md/lib/"
});
</script>
10. 百度搜索框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Serach</title>
<style>
* {
margin: 0;
padding: 0;
}
.top-search {
width: 680px;
height: 45px;
margin: 30px auto;
}
.search-box {
display: flex;
position: relative;
}
.search-left {
width: 545px;
height: 45px;
border: 2px solid rgb(196, 199, 206);
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
outline-color: rgb(78, 110, 242);
}
.icon-xiangji {
position: absolute;
right: 150px;
top: 12px;
font-size: 24px;
color: rgb(196, 199, 206);
}
.search-right {
color: #fff;
font-size: 18px;
width: 110px;
height: 49px;
border: 0px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
background-color: rgb(78, 110, 242);
}
#su:hover{
background: #14dc99;
}
</style>
</head>
<body>
<form action="mainMenu.html">
<div class="top-img"></div>
<div class="top-search">
<div class="search-box">
<input type="text" name="search" class="search-left">
<span class="iconfont icon-xiangji"></span>
<input class="search-right" type="subbmit" name="btn" id="su" value='发表文章'>
</div>
</div>
</form>
</body>
</html>
11. 二维码弹窗!!!
<!DOCTYPE html>
<html>
<head>
<title>test1</title>
<meta charset="UTF-8">
<%--注意引用和地址--%>
<script src="static/Jquery/jquery-3.6.1.js"></script>
<script src="static/layui/layui.js" charset="utf-8"></script>
<script src="https://static.runoob.com/assets/qrcode/qrcode.min.js"></script>
<link rel="stylesheet" href="static/layui/css/layui.css" media="all">
</head>
<body>
<div align="center">
<button type="button" class="layui-btn layui-btn-normal layui-btn-radius" onclick="skipHandle()">预览</button>
</div>
<div id="code" style="display: none;">
<div id="qrcode" style="margin-left: 75px;margin-top: 20px">
<h1 style="color: #e8136f">你好</h1>
</div>
</div>
<script>
layui.use(['layer'], function () {
var layer = layui.layer
});
// 设置要生成二维码的链接
new QRCode(document.getElementById("qrcode"), {
text: "https://img-home.csdnimg.cn/images/20230407093553.jpg", //设置二维码内容
canvas: "table", //设置渲染方式
width: 250,
height: 250
});
//预览等弹出框
function skipHandle() {
layer.open({
type: 1,
title: "bug_producter的博客",//标题
area: ['400px', '400px'],
content: $('#code').html(),
});
}
</script>
</body>
</html>
12. 文本框不能为空格的属性,一个属性就可以
οnkeyup="this.value=this.value.replace(/\s+/g,'')"