技术标签: 实战项目 layui 框架实战开发
<!-- 导入 jQuery -->
<script src="/assets/lib/jquery.js"></script>
<!-- 导入自己封装的 baseAPI -->
<script src="/assets/js/baseAPI.js"></script>
<!-- 导入自己的 js 文件 -->
<script src="/assets/js/index.js"></script>
// 获取用户的基本信息
function getUserInfo() {
$.ajax({
method: 'GET',
url: '/my/userinfo',
success: function(res) {
if (res.status !== 0) {
return layui.layer.msg('获取用户信息失败!')
}
// 调用 renderAvatar 渲染用户的头像
renderAvatar(res.data)
}
// 渲染用户的头像
function renderAvatar(user) {
1. 获取用户的名称
var name = user.nickname || user.username
2. 设置欢迎的文本
$('#welcome').html('欢迎 ' + name)
3. 按需渲染用户的头像
if (user.user_pic !== null) {
// 3.1 渲染图片头像
$('.layui-nav-img')
.attr('src', user.user_pic)
.show()
$('.text-avatar').hide()
} else {
// 3.2 渲染文本头像
$('.layui-nav-img').hide()
var first = name[0].toUpperCase()
$('.text-avatar')
.html(first)
.show()
}
}
// 统一为有权限的接口,设置 headers 请求头
if (options.url.indexOf('/my/') !== -1) {
options.headers = {
Authorization: localStorage.getItem('token') || ''
}
}
<a href="javascript:;" id="btnLogout">
<span class="iconfont icon-tuichu"> </span>
退出
</a>
// 点击按钮,实现退出功能
$('#btnLogout').on('click', function() {
// 提示用户是否确认退出
layer.confirm('确定退出登录?', {
icon: 3, title: '提示' }, function(index) {
//do something
// 1. 清空本地存储中的 token
localStorage.removeItem('token')
// 2. 重新跳转到登录页面
location.href = '/login.html'
// 关闭 confirm 询问框
layer.close(index)
})
})
})
重点!!!! 不论成功还是失败,最终都会调用 complete 回调函数!!!!
complete: function(res) {
// console.log('执行了 complete 回调:')
// console.log(res)
// 在 complete 回调函数中,可以使用 res.responseJSON 拿到服务器响应回来的数据
if (res.responseJSON.status === 1 && res.responseJSON.message === '身份认证失败!') {
// 1. 强制清空 token
localStorage.removeItem('token')
// 2. 强制跳转到登录页面
location.href = '/login.html'
}
}
})
}
注意:每次调用 $.get() 或 $.post() 或 $.ajax() 的时候,
会先调用 ajaxPrefilter 这个函数
// 在这个函数中,可以拿到我们给Ajax提供的配置对象
$.ajaxPrefilter(function(options) {
1. 在发起真正的 Ajax 请求之前,统一拼接请求的根路径
// options.url = 'http://ajax.frontend.itheima.net' + options.url
options.url = 'http://www.liulongbin.top:3007' + options.url
2. 统一为有权限的接口,设置 headers 请求头
if (options.url.indexOf('/my/') !== -1) {
options.headers = {
Authorization: localStorage.getItem('token') || ''
}
}
3. 全局统一挂载 complete 回调函数
options.complete = function(res) {
// console.log('执行了 complete 回调:')
// console.log(res)
// 在 complete 回调函数中,可以使用 res.responseJSON 拿到服务器响应回来的数据
if (res.responseJSON.status === 1 && res.responseJSON.message === '身份认证失败!') {
// 1. 强制清空 token
localStorage.removeItem('token')
// 2. 强制跳转到登录页面
location.href = '/login.html'
}
}
})
先用 git 保存 刚刚的那个 分支, 然后新建 user分支
html,
body {
margin: 0;
padding: 0;
}
body {
background-color: #f2f3f5;
padding: 15px;
}
form.verify({
nickname: function(value) {
if (value.length > 6) {
return '昵称长度必须在 1 ~ 6 个字符之间!'
}
}
})
!!!! layui 的form.val()方法,可以快速为表单赋值
var form = layui.form
// 初始化用户的基本信息
function initUserInfo() {
$.ajax({
method: 'GET',
url: '/my/userinfo',
success: function(res) {
if (res.status !== 0) {
return layer.msg('获取用户信息失败!')
}
// console.log(res)
// 调用 form.val() 快速为表单赋值
form.val('formUserInfo', res.data)
}
})
}
<form class="layui-form" lay-filter="formUserInfo"></form>
var form = layui.form
form.val('formUserInfo', res.data)
<!-- form 表单区域 -->
<form class="layui-form" lay-filter="formUserInfo">
<!-- 这是隐藏域 -->
<input type="hidden" name="id" value="" />
<!-- 省略其他代码 -->
</form>
// 重置表单的数据
$('#btnReset').on('click', function(e) {
// 阻止表单的默认重置行为
e.preventDefault()
initUserInfo()
})
// 监听表单的提交事件
$('.layui-form').on('submit', function(e) {
1. 阻止表单的默认提交行为
e.preventDefault()
2. 发起 ajax 数据请求
$.ajax({
method: 'POST',
url: '/my/userinfo',
data: $(this).serialize(), // 提交表单中的 数据 给 服务器
success: function(res) {
if (res.status !== 0) {
return layer.msg('更新用户信息失败!')
}
layer.msg('更新用户信息成功!')
3. 调用父页面中的方法,重新渲染用户的头像和用户的信息 重要!!!!!!
这个相当于 这个 iframe 页面的 父亲index 来调用 这个 函数
window.parent.getUserInfo()
}
})
})
var form = layui.form
form.verify({
pwd: [/^[\S]{6,12}$/, '密码必须6到12位,且不能出现空格'],
samePwd: function(value) {
if (value === $('[name=oldPwd]').val()) {
return '新旧密码不能相同!'
}
},
rePwd: function(value) {
if (value !== $('[name=newPwd]').val()) {
return '两次密码不一致!'
}
}
})
$('.layui-form').on('submit', function(e) {
e.preventDefault()
$.ajax({
method: 'POST',
url: '/my/updatepwd',
data: $(this).serialize(), 提交表单数据
success: function(res) {
if (res.status !== 0) {
return layui.layer.msg('更新密码失败!')
}
layui.layer.msg('更新密码成功!')
// 重置表单
$('.layui-form')[0].reset() 这个是重置表单的方法(因为是 原生的,所以jQ的对象要转换为原生的对象才能使用)
}
})
})
// 1.1 获取裁剪区域的 DOM 元素
var $image = $('#image')
// 1.2 配置选项
const options = {
// 纵横比
aspectRatio: 1,
// 指定预览区域
preview: '.img-preview'
}
// 1.3 创建裁剪区域
$image.cropper(options)
// 为上传按钮绑定点击事件
$('#btnChooseImage').on('click', function() {
$('#file').click()
})
// 为文件选择框绑定 change 事件
$('#file').on('change', function(e) {
// 获取用户选择的文件
var filelist = e.target.files
if (filelist.length === 0) {
return layer.msg('请选择照片!')
}
// 1. 拿到用户选择的文件
var file = e.target.files[0]
// 2. 将文件,转化为路径
var imgURL = URL.createObjectURL(file)
// 3. 重新初始化裁剪区域
$image
.cropper('destroy') // 销毁旧的裁剪区域
.attr('src', imgURL) // 重新设置图片路径
.cropper(options) // 重新初始化裁剪区域
})
// 为确定按钮,绑定点击事件
$('#btnUpload').on('click', function() {
// 1. 要拿到用户裁剪之后的头像
var dataURL = $image
.cropper('getCroppedCanvas', {
// 创建一个 Canvas 画布
width: 100,
height: 100
})
.toDataURL('image/png') // 将 Canvas 画布上的内容,转化为 base64 格式的字符串
// 2. 调用接口,把头像上传到服务器
$.ajax({
method: 'POST',
url: '/my/update/avatar',
data: {
avatar: dataURL
},
success: function(res) {
if (res.status !== 0) {
return layer.msg('更换头像失败!')
}
layer.msg('更换头像成功!')
window.parent.getUserInfo()
}
})
})
<dl class="layui-nav-child">
<dd><a href="/user/user_info.html" target="fm">基本资料</a></dd>
<dd><a href="/user/user_avatar.html" target="fm">更换头像</a></dd>
<dd><a href="/user/user_pwd.html" target="fm">重置密码</a></dd>
</dl>
<link rel="stylesheet" href="/assets/lib/cropper/cropper.css" /> 1
<script src="/assets/lib/jquery.js"></script>
<script src="/assets/lib/cropper/Cropper.js"></script>
<script src="/assets/lib/cropper/jquery-cropper.js"></script>
<!-- 第一行的图片裁剪和预览区域 -->
<div class="row1">
<!-- 图片裁剪区域 -->
<div class="cropper-box">
<!-- 这个 img 标签很重要,将来会把它初始化为裁剪区域 -->
<img id="image" src="/assets/images/sample.jpg" />
</div>
<!-- 图片的预览区域 -->
<div class="preview-box">
<div>
<!-- 宽高为 100px 的预览区域 -->
<div class="img-preview w100"></div>
<p class="size">100 x 100</p>
</div>
<div>
<!-- 宽高为 50px 的预览区域 -->
<div class="img-preview w50"></div>
<p class="size">50 x 50</p>
</div>
</div>
</div>
<!-- 第二行的按钮区域 -->
<div class="row2">
<!-- 通过 accept 属性,可以指定,允许用户选择什么类型的文件 -->
<input type="file" id="file" accept="image/png,image/jpeg" />
<button type="button" class="layui-btn" id="btnChooseImage">上传</button>
<button type="button" class="layui-btn layui-btn-danger" id="btnUpload">确定</button>
</div>
/* 设置卡片主体区域的宽度 */
.layui-card-body {
width: 500px;
}
/* 设置按钮行的样式 */
.row2 {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
/* 设置裁剪区域的样式 */
.cropper-box {
width: 350px;
height: 350px;
background-color: cyan;
overflow: hidden;
}
/* 设置第一个预览区域的样式 */
.w100 {
width: 100px;
height: 100px;
background-color: gray;
}
/* 设置第二个预览区域的样式 */
.w50 {
width: 50px;
height: 50px;
background-color: gray;
margin-top: 50px;
}
/* 设置预览区域下方文本的样式 */
.size {
font-size: 12px;
color: gray;
text-align: center;
}
/* 设置图片行的样式 */
.row1 {
display: flex;
}
/* 设置 preview-box 区域的的样式 */
.preview-box {
display: flex;
flex-direction: column;
flex: 1;
align-items: center;
}
/* 设置 img-preview 区域的样式 */
.img-preview {
overflow: hidden;
border-radius: 50%;
}
#file {
display: none;
}
// 1.1 获取裁剪区域的 DOM 元素
var $image = $('#image')
// 1.2 配置选项
const options = {
// 纵横比
aspectRatio: 1, 调整图片里面剪裁的 比例 也可以3/4 或者 2/3
// 指定预览区域
preview: '.img-preview'
}
// 1.3 创建裁剪区域
$image.cropper(options)
var file = e.target.files[0]
var newImgURL = URL.createObjectURL(file)
$image
.cropper('destroy') // 销毁旧的裁剪区域
.attr('src', newImgURL) // 重新设置图片路径
.cropper(options) // 重新初始化裁剪区域
var dataURL = $image
.cropper('getCroppedCanvas', {
// 创建一个 Canvas 画布
width: 100,
height: 100
})
.toDataURL('image/png') // 将 Canvas 画布上的内容,转化为 base64 格式的字符 串
<script src="/assets/lib/template-web.js"></script>
<!-- 表格数据的模板 -->
<script type="text/html" id="tpl-table">
{
{
each data}}
<tr>
<td>{
{
$value.name}}</td>
<td>{
{
$value.alias}}</td>
<td>
<button type="button" class="layui-btn layui-btn-xs btn-edit" data-id="{
{$value.Id}}">编辑</button>
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs btn-delete" data-id="{
{$value.Id}}">删除</button>
</td>
</tr>
{
{
/each}}
</script>
initArtCateList()
// 获取文章分类的列表
function initArtCateList() {
$.ajax({
method: 'GET',
url: '/my/article/cates',
success: function(res) {
// !!!!获取模板引擎的数据, 然后存在 tbody 中
var htmlStr = template('tpl-table', res)
$('tbody').html(htmlStr)
}
})
}
var layer = layui.layer
<button type="button" class="layui-btn layui-btn-normal layui-btn-sm" id="btnAddCate">添加类别</button>
// 为添加类别按钮绑定点击事件
var indexAdd = null
$('#btnAddCate').on('click', function() {
indexAdd = layer.open({
type: 1,
area: ['500px', '250px'],
title: '添加文章分类',
content: $('#dialog-add').html()
})
})
这里没有使用模板引擎,至少用 script 的 type,更方便的渲染结构
<!-- 添加分类的弹出层 -->
<script type="text/html" id="dialog-add">
<form class="layui-form" id="form-add">
<div class="layui-form-item">
<label class="layui-form-label">分类名称</label>
<div class="layui-input-block">
<input type="text" name="name" required lay-verify="required" placeholder="请输入分类名称" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">分类别名</label>
<div class="layui-input-block">
<input type="text" name="alias" required lay-verify="required" placeholder="请输入分类别名" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formDemo">确认添加</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</script>
indexAdd = layer.open({
type: 1,
area: ['500px', '250px'],
title: '添加文章分类',
content: $('#dialog-add').html()
})
// 通过代理的形式,为 form-add 表单绑定 submit 事件
$('body').on('submit', '#form-add', function(e) {
e.preventDefault()
$.ajax({
method: 'POST',
url: '/my/article/addcates',
data: $(this).serialize(),
success: function(res) {
if (res.status !== 0) {
return layer.msg('新增分类失败!')
}
initArtCateList()
layer.msg('新增分类成功!')
// 根据索引,关闭对应的弹出层
layer.close(indexAdd)
}
})
})
// 为添加类别按钮绑定点击事件
var indexAdd = null
$('#btnAddCate').on('click', function() {
indexAdd = layer.open({
type: 1,
area: ['500px', '250px'],
title: '添加文章分类',
content: $('#dialog-add').html()
})
})
好题,真难,都看一天了,总算是AC了,真够纠结的啊,参考博客http://www.notonlysuccess.com/index.php/segment-tree-complete/1、http://poj.org/problem?id=32252、题目大意:给一个空的区间,经过给定的五种操作,输出最终的结果,五种操作分别是CommandSemantics_interval补集
mysql root 密码随便输入,都能连接成功1.mysql数据库别人安装好之后,我尝试root账号密码登录,ok.但是我随便输入了一段密码,也是可以成功connection的,我很纳闷。查阅了好多资料。包括skip-grant-tables#跳过数据库权限验证意思就是在my.ini中添加的这个代码之后,就可以不用密码随便输入。没有加的话,只有正确密码才能connection成功。..._mysql root密码输入任意数字都可以进去
主要功能与目的利用RFID+GPS实现待定位车辆的实时定位于轨迹跟踪实现了基于Gmap.net控件Map常规应用利用外接短信设备实现车辆打卡信息实时通报登录主页信息管理代码分析:Gmap部分#region gmap初始化 SuspendLayout(); MainMap = new GMapControl(); ...
http://en.wikipedia.org/wiki/Comparison_of_remote_desktop_software
JavaWeb综合项目实战—第三天购物车模块
是数据结构而非类型很多文章都会说,redis支持5种常用的数据类型,这其实是存在很大的歧义。redis里存的都是二进制数据,其实就是字节数组(byte[]),这些字节数据是没有数据类型的,只有把它们按照合理的格式解码后,可以变成一个字符串,整数或对象,此时才具有数据类型。这一点必须要记住。所以任何东西只要能转化成字节数组(byte[])的,都可以存到redis里。管你是字符串、数字、对象、图片、声音、视频、还是文件,只要变成byte数组。 因此redis里的String指的并不是字符串,它其实.._redis 支持 5 种常用的数据类型,这其实是存在很大的歧义
一、特征选择和特征提取 特征选择(feature selection)和特征提取(Feature extraction)都属于降维(Dimension reduction)这两者达到的效果是一样的,就是试图去减少特征数据集中的属性(或者称为特征)的数目;但是两者所采用的方式方法却不同。 特征提取的方法主要是通过属性间的关系,如组合不同的属性得到新的属性,这样就改变了原来的特征空间。 ..._特征提取
前言目标:掌握sqlite节点中各个节点的基本使用方法。懂的朋友欢迎订阅。一、演示环境win10系统,Node-RED最新版本2.1.3(2021年11月21号)。二、模块与节点_node-red系列教程-11扩展节点之sqlite节点
spring boot 的项目在idea下报错,错误信息如下:java.lang.IllegalStateException: Could not evaluate condition on org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration#propertySourcesPlaceholder_could not evaluate condition on org.springframework.boot.autoconfigure.cache
public static void main(String[] args) { Properties props = System.getProperties(); System.out.println("Java的运行环境版本:"+props.getProperty("java.version")); System.out.println("Ja..._getproperties
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>vue跑马灯</title> <script src="vue/vue.min.js" type="text/javascript" charset="utf-8"></script> <style type="text/css"> p{ border_uniapp 滚动广告
P2365 任务安排 题目描述 N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完..._有n个工作,分成j批,机器启动时间