# SN
# chrom 添加模拟 app 的设备
在 User agent string 一栏中填写
Mozilla/5.0 (iPod touch; CPU iPhone OS 9_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13C75 2.0.24 CK 2.0
# 移动端横屏
@media screen and (orientation: landscape) {
/*横屏*/
main {
width: 100vw;
height: 100vh;
background-color: #bfa;
}
}
# 上拉加载分页
文件在旅行世界中
/public_html/addons/ewei_shopv2/static/js/util.js
// 全局变量,两个函数
let getLog;
let bindScroller;
require(["tpl", "core"], function (tpl, core) {
// 页码数
var page = 1;
// 滚动加载
/**
* author: gauharchan http://person.gauhar.top
* @param {sting} url 请求地址
* @param {object} obj 请求数据对象
* @param {string} container 内容容器,jq选择器,如:.container
* @param {string} dataTemplate 数据‘不’为空时的模板,id,如:tpl-goods
*/
bindScroller = function (url, obj, container, dataTemplate) {
var loaded = false; // 加载状态
var stop = true; // 双重保险
$(window).scroll(function () {
if (loaded) {
// true的时候代表加载中
return;
}
totalheight =
parseFloat($(window).height()) + parseFloat($(window).scrollTop());
if ($(document).height() - totalheight <= 30) {
if (stop == true) {
stop = false;
// 加载提示
core.loading();
// 节流
loaded = true;
obj.page++;
core.json(
url,
obj,
function (json) {
var morejson = json;
stop = true;
// 删除加载提示
core.removeLoading();
// $("#container").append(tpl('tpl_log', morejson));
// 加载渲染完成
loaded = false;
// 渲染
$(container).append(tpl(dataTemplate, json));
if (!morejson.result || morejson.result.length < 20) {
$(container).append(
'<div id="credit_loading" style="width:100%;text-align:center;margin:10px 0;">已经全部加载完成</div>'
);
loaded = true;
$(window).scroll = null;
return;
}
},
true,
true
);
}
}
});
};
/**
* author: gauharchan https://gauhar.gitee.io/
* @param {sting} url 请求地址
* @param {object} obj 请求数据对象
* @param {string} container 内容容器,jq选择器,如:.container
* @param {string} empty 数据为空时的模板,id,如:tpl_empty
* @param {string} dataTemplate 数据‘不’为空时的模板,id,如:tpl-goods
*/
getLog = function (url, obj, container, empty, dataTemplate) {
// 去除浏览器滚动监听
$(window).off("scroll");
obj.page = page;
core.json(
url,
obj,
function (json) {
console.log(json);
if (!json.result || json.result.length <= 0) {
$(container).html(tpl(empty));
return;
}
$(container).html(tpl(dataTemplate, json));
if (!json.result || json.result.length < 20) {
$(container).append(
'<div id="credit_loading" style="width:100%;text-align:center;margin:10px 0;">已经全部加载完成</div>'
);
return;
}
bindScroller(url, obj, container, dataTemplate);
},
true,
true
);
};
});
# 记住密码
html
<!-- 记住密码 -->
<div class="password-box">
<div class="right-box flex flex-ver">
<div class="img-box">
<img
class="img-remeber"
src="..//addons/sz_yi/template/mobile/style1/static/images/newImage/btn_xuanze.png"
alt=""
/>
</div>
<span>记住密码</span>
</div>
</div>
css
.password-box {
display: flex;
justify-content: flex-end;
margin: 2% 10% 8%;
}
.img-box {
display: flex;
align-items: center;
width: 20%;
}
.img-box .img-remeber {
width: 100%;
}
.right-box span {
color: #fff;
margin-left: 3px;
}
js
let remeber = true;
// 記住密碼
$(".img-remeber").click(function () {
remeber = !remeber;
let src = remeber
? "..//addons/sz_yi/template/mobile/style1/static/images/newImage/btn_xuanze.png"
: "..//addons/sz_yi/template/mobile/style1/static/images/newImage/btn_weixuanze.png";
$(this).attr("src", src);
});
// 检测是否有记录密码
let pass = localStorage.getItem("password");
let pho = localStorage.getItem("phone");
if (pass && pho) {
$("#mobile").val(pho);
$("#password").val(pass);
}
// 登录按钮的请求
$(".info_sub").click(function () {
if (!$("#mobile").isMobile()) {
core.tip.show("请输入正确手机号码!");
return;
}
if ($("#password").isEmpty()) {
core.tip.show("请输入密码!");
return;
}
let pass = $("#password").val();
let pho = $("#mobile").val();
core.json(
"member/login",
{
memberdata: {
mobile: $("#mobile").val(),
password: pass,
},
},
function (json) {
if (json.status == 1) {
// 記住密碼
if (remeber) {
localStorage.setItem("password", pass);
localStorage.setItem("phone", pho);
} else {
localStorage.removeItem("password");
localStorage.removeItem("phone");
}
core.tip.show("登录成功");
location.href = json.result.preurl;
} else {
core.tip.show("用户不存在或密码错误!");
}
},
true,
true
);
});
# 密码是否可见
/// 密码的显示和隐藏
let open =
"../addons/ewei_shopv2/template/mobile/default/static/images/newImage/login/icon3-d.png";
let close =
"../addons/ewei_shopv2/template/mobile/default/static/images/newImage/login/closeeye.png";
// 眼的点击方法
$(".eye").on("click", function () {
let user = $(this).data("status");
user = !user;
user ? $(".eye").attr("src", open) : $(".eye").attr("src", close);
user ? $("#pwd").attr("type", "text") : $("#pwd").attr("type", "password");
$(this).data("status", user);
});
# 验证码
html:
<canvas id="canvas" width="100" height="40"></canvas>
js:
var show_num = [];
draw(show_num);
$("#canvas").on("click", function () {
draw(show_num);
});
function draw(show_num) {
var canvas_width = $("#canvas").width();
var canvas_height = $("#canvas").height();
var canvas = document.getElementById("canvas"); //获取到canvas的对象,演员
var context = canvas.getContext("2d"); //获取到canvas画图的环境,演员表演的舞台
canvas.width = canvas_width;
canvas.height = canvas_height;
var sCode = "A,B,C,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,W,X,Y,Z";
// var sCode = "1,2,3,4,5,6,7,8,9,0";
var aCode = sCode.split(",");
var aLength = aCode.length; //获取到数组的长度
for (var i = 0; i <= 3; i++) {
var j = Math.floor(Math.random() * aLength); //获取到随机的索引值
var deg = (Math.random() * 30 * Math.PI) / 180; //产生0~30之间的随机弧度
var txt = aCode[j]; //得到随机的一个内容
show_num[i] = txt.toLowerCase();
var x = 10 + i * 20; //文字在canvas上的x坐标
var y = 20 + Math.random() * 8; //文字在canvas上的y坐标
context.font = "bold 23px 微软雅黑";
context.translate(x, y);
context.rotate(deg);
context.fillStyle = randomColor();
context.fillText(txt, 0, 0);
context.rotate(-deg);
context.translate(-x, -y);
}
for (var i = 0; i <= 5; i++) {
//验证码上显示线条
context.strokeStyle = randomColor();
context.beginPath();
context.moveTo(Math.random() * canvas_width, Math.random() * canvas_height);
context.lineTo(Math.random() * canvas_width, Math.random() * canvas_height);
context.stroke();
}
for (var i = 0; i <= 30; i++) {
//验证码上显示小点
context.strokeStyle = randomColor();
context.beginPath();
var x = Math.random() * canvas_width;
var y = Math.random() * canvas_height;
context.moveTo(x, y);
context.lineTo(x + 1, y + 1);
context.stroke();
}
}
function randomColor() {
//得到随机的颜色值
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
return "rgb(" + r + "," + g + "," + b + ")";
}
# 文件上传
ajaxfileupload.js (opens new window)
/*图片上传*/
$("#imgFile0").change(function () {
// 加载提示
FoxUI.loader.show("mini");
$.ajaxFileUpload({
url: core.getUrl("util/uploader"), // 请求地址
data: { file: "qrcode" }, // input框的name属性
secureuri: false, // 启用安全模式
fileElementId: "imgFile0", // input id
dataType: "json", // 响应数据格式
success: function (res, status) {
FoxUI.loader.hide();
$("#image").attr("src", res.url);
$("#qcode").val(res.filename);
},
error: function (data, status, e) {
FoxUI.loader.hide();
FoxUI.toast.show("上传失败!");
},
});
});
/*图片上传*/
# 模板语法
<!-- 有数据的模板 -->
<script type="text/html" id="tpl-goods">
<%each result as log%>
<div class="list-box gh-between">
<div class="ball"></div>
<div class="list-text">
<div class="num-text"><%log.title%></div>
<!-- <div class="info-text">颜色随机设置自动出价1次,扣除购物币5</div> -->
</div>
<div class="right-box">
<%if log.in_or_out == 'in'%>+<%else%>-<%/if%><%log.money%>佣金
</div>
</div>
<%/each%>
</script>
# workman
PHP webscoket
/**
*
* @param {sting} container 商品item容器选择器
* @param {sting} time 商品倒计时
* @param {sting} price 商品价格
* @param {sting} status 商品状态,是否结束
*/
function getSecond(container, time, price, status) {
require(["socket.io.js"], function (io) {
$(() => {
// 初始化io对象
var socket = io("http://" + document.domain + ":2120");
// uid 可以为网站用户的uid,作为例子这里用session_id代替
var uid = '{php echo $_W["openid"]}';
// 当socket连接后发送登录请求
socket.on("connect", function () {
socket.emit("login", uid);
});
// 当服务端推送来消息时触发,这里简单的aler出来,用户可做成自己的展示效果
socket.on("new_msg", function (msg) {
let message = JSON.parse(msg);
// console.log(message);
// 获取当前页面商品id
let arr = [];
$(container).each(function (i, e) {
// message.forEach(item => {
// if (item.goods_id == $(e).data('goods_id')) {
// $(e).find(time).text(item.second.padStart(2, '0'))
// $(e).find(price).text(item.price)
// }
// });
for (let i = 0; i < message.length; i++) {
if (message[i].goods_id == $(e).data("goods_id")) {
$(e).find(time).text(message[i].second.padStart(2, "0"));
$(e).find(price).text(message[i].price);
message[i].status == 0
? $(e).find(status).show()
: $(e).find(status).hide();
break; // break 用于原生for循环中
}
}
});
});
});
});
}
/**
*
* @param {sting} name 请求服务端(方法)的名称
* @param {sting} callback 处理的回调函数
*/
function getDetail(name, callback) {
require(["socket.io.js"], function (io) {
$(() => {
// 初始化io对象
var socket = io("http://" + document.domain + ":2120");
// uid 可以为网站用户的uid,作为例子这里用session_id代替
var uid = '{php echo $_W["openid"]}';
// 当socket连接后发送登录请求
socket.on("connect", function () {
socket.emit("login", uid);
});
// 当服务端推送来消息时触发,这里简单的aler出来,用户可做成自己的展示效果
socket.on(name, function (msg) {
callback(msg);
});
});
});
}
# 复制文本
clipboard.js (opens new window)
<div class="copy-btn" data-clipboard-text="123">复制</div>
const btnCopy = new ClipboardJS(".copy-btn");
btnCopy.on("success", () => {
core.tip.show("复制成功");
});
# 全选与全不选
$(".all_check").on("change", function () {
// 全选按钮
const status = $(this).prop("checked");
$(".checkone").prop("checked", status);
});
$(".checkone").on("change", function () {
// 每一个子按钮
const temp = Array.from($(".checkone"));
const status = temp.every((ele) => {
return $(ele).prop("checked");
});
$(".all_check").prop("checked", status);
});
# moment.js (opens new window)
时间戳转换格式
moment(time).format("YYYY-MM-DD HH:mm:ss");
# 高仿 ios 数量选择器
// 数量选择器
var picker1 = myPicker({
cols, // 数量数组 [1000,2000...]
title: "",
onOkClick: function (values) {
document.querySelector("#number").textContent = values[0]; // 将选择的数量写入dom节点
// 项目逻辑代码
let selector = values[0];
let num = selector * box77;
$(".price").text(num.toFixed(8));
},
});
document.querySelector(".selector").addEventListener("click", function () {
picker1.show();
});
# 滑动切换
html
<main>
<nav class="tab gh-around">
<div class="tab-text tab-active" data-type="1">预热中</div>
<div class="tab-text" data-type="2">进行中</div>
<div class="tab-text" data-type="3">已结束</div>
</nav>
</main>
<div class="container"></div>
js
let start = null;
let endPos = { x: 0, y: 0 };
let isScrolling = 0; //这个参数判断是垂直滚动还是水平滚动
document.body.addEventListener("touchstart", function (e) {
let touch = e.targetTouches[0];
start = {
// 记录开始的位置
x: touch.clientX,
y: touch.clientY,
};
});
document.body.addEventListener("touchmove", function (e) {
let touch = e.targetTouches[0];
endPos = { x: touch.clientX - start.x, y: touch.clientY - start.y }; // 记录偏差值,判断移动
// Math.abs 取绝对值
// isScrolling = Math.abs(endPos.x) > Math.abs(endPos.y) ? 0 : 1; //isScrolling为1时,表示纵向滑动,0为横向滑动
// 如果上下滑动超过100就是纵向
isScrolling = Math.abs(endPos.y) > 100 ? 1 : 0; //isScrolling为1时,表示纵向滑动,0为横向滑动
});
document.body.addEventListener("touchend", function (e) {
let type = $(".tab-active").data("type");
if (isScrolling === 0) {
//当为水平滚动时
//判断是左移还是右移,当偏移量大于10时执行
if (endPos.x > 10) {
// 右移手势
// 业务逻辑
if (type == 1) {
return;
} else {
type--;
$(".tab-text")
.eq(type - 1)
.siblings()
.removeClass("tab-active");
$(".tab-text")
.eq(type - 1)
.addClass("tab-active");
getLog(
"member/gongzhen",
{
op: "getlist",
page: 1,
type,
},
".container",
"tpl_empty",
"tpl_data"
);
}
} else if (endPos.x < -10) {
// 左移手势
if (type == 3) {
return;
} else {
$(".tab-text").eq(type).siblings().removeClass("tab-active");
$(".tab-text").eq(type).addClass("tab-active");
type++;
getLog(
"member/gongzhen",
{
op: "getlist",
page: 1,
type,
},
".container",
"tpl_empty",
"tpl_data"
);
}
}
}
});
# 进度条
添加类名
progress-bar
.progress-bar {
width: 40%;
height: 6px;
border-radius: 25px;
background: linear-gradient(#ffa800, #ffa800) no-repeat #fff;
/*控制进度条进度*/
background-size: 80% 100%;
margin-left: 0.5rem;
}
# 禁止用户打开控制台
通过修改 get,得知是否有在控制台打印
兼容性不太好
// 阻止用户
document.oncontextmenu = (e) => {
e.preventDefault();
};
document.onkeydown =
document.onkeyup =
document.onkeypress =
(e) => {
if (e.which == 123) {
e.preventDefault();
// alert('没事别老研究别人的接口,好好做站去吧')
}
};
let element = document.createElement("checkDevTools");
Object.defineProperty(element, "id", {
get: function () {
/* TODO */
// 死循环
// while(true){}
},
});
console.debug(element); //使用console.debug()使打印出的辅助信息默认隐藏
# 文字滚动
marquee 标签
<!-- 公告 -->
<div class="gonggao gh-between">
<img
src="../addons/sz_yi/template/mobile/style1/static/images/newImage/home_icon_a3.png"
alt=""
/>
<marquee
height="40"
direction="up"
scrolldelay="200"
class="bd"
style="padding-left: 1rem;"
>
<?php foreach ($notice_list as $k =>
$row): ?>
<div style="height: 100%;">{php echo $row['content']}</div>
<?php endforeach ?>
</marquee>
</div>
# 倒计时
fnTimeCountDown(结束时间,{sec, mini, hour, month, year, day})
第一个参数,是一个毫秒数时间
第二个参数是一个对象,存放想要显示的 dom 节点。如只想要时分秒,如下代码
var obj = {
sec: document.getElementById("sec"), // 秒
mini: document.getElementById("mini"), // 分
hour: document.getElementById("hour"), // 时
};
fnTimeCountDown(adopt_begin_time * 1000, obj);
# 修改placeholder
样式
::-webkit-input-placeholder {
color: #09d9ec;
}
:-moz-placeholder {
/* Firefox 18- */
color: #09d9ec;
}
::-moz-placeholder {
/* Firefox 19+ */
color: #09d9ec;
}
:-ms-input-placeholder {
color: #09d9ec;
}
# emmet 语法生成无意义文字
lorem
# 文本溢出,以...表示
# 单行
<style>
div {
width: 40px;
overflow: hidden;
white-space: nowrap; /*不换行*/
text-overflow: ellipsis; /*省略号*/
}
</style>
<div>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus facilis
iure quos aliquam, tempore repellat explicabo officiis ducimus ullam
voluptatum assumenda voluptate mollitia sapiente pariatur. Labore laboriosam
reprehenderit placeat possimus?
</div>
# 多行
div {
display: -moz-box; /* Mozilla */
display: -webkit-box; /* WebKit */
display: box; /* As specified */
overflow: hidden;
white-space: normal !important;
text-overflow: ellipsis;
word-wrap: break-word; /*单词断句*/
-webkit-line-clamp: 2; /*第几行省略*/
-webkit-box-orient: vertical; /*水平布局*/
-moz-box-orient: vertical; /* Mozilla */
box-orient: vertical; /* As specified */
}
# 判断对象有无指定属性
hasOwnProperty()方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是是否有指定的键)
/**
* 判断对象有无指定属性
* @param {Object} obj 判断的对象
* @param {String} key 判断的属性名(键名)
*/
function hasKey(obj, key) {
let status = obj.hasOwnProperty(key) && key in obj ? true : false;
return status;
}
# 判断手机(安卓,苹果)
返回
true
为 ios 系统
function checkPhone() {
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
return true;
} else if (/(Android)/i.test(navigator.userAgent)) {
return false;
}
}
# LC
# 权限
开发的时候遇到,
sideBar
新增了东西,但要等后端配置增加权限(单单前端初始化加了,在checkPermission
方法会被重新赋值),索性不执行checkPermission
,自己写个递归把所有的auth
都设置为true
// 初始化
init () {
this.initTab()
this.recursion()
// this.checkPermission()
},
recursion (arr = this.tabsList) {
arr.forEach((item) => {
item.auth = true
if (!item.children || item.children.length === 0) return
this.recursion(item.children)
})
},
# element
# table
使用展开行的表格时,默认使用 id 作为标识。如果 tableData 中没有 id,或者是别的名字,会报错
Duplicate keys detected: 'expanded-row__undefined'. This may cause an update error.
# HONGDU
# 禁止开发者工具调试
(() => {
function block() {
if (
window.outerHeight - window.innerHeight > 200 ||
window.outerWidth - window.innerWidth > 200
) {
document.body.innerHTML = "检测到非法调试,请关闭后刷新重试!";
}
setInterval(() => {
(function () {
return false;
}
["constructor"]("debugger")
["call"]());
}, 50);
}
try {
block();
} catch (err) {}
})();
# 获取当前日期的一周时间
示例
import dayjs from 'dayjs'
const zhCn = require('dayjs/locale/zh-cn')
// 设置中文
dayjs.locale(zhCn)
/**
* @description 获取当前日期的一周
* @param {string | Date} date 能够转化的日期
*/
getWeek(date) {
// 中文包一周的开始是周一,因此这里要减去一天
const sunday = dayjs(date).startOf('week').subtract(1, 'day')
const week = Array.from({ length: 7 }).map((item, index) => {
const current = sunday.add(index, 'day')
return {
day: current.format('YYYY-MM-DD'),
week: current.format('dddd'),
current
}
})
return week
}
# 通过 Blob 下载文件
初始化 api 要设置一下
export const postExportPushRecord = (params: IFailRecordParams) => {
return attendanceHttp.post<object>(
{
url: "/",
params,
responseType: "blob",
},
{ isExport: true }
);
};
具体方法
警告 ⚠️
我们需要拿到响应头的Content-Disposition
,并解析出文件名;因此要注意接口返回的响应头的Access-Control-Expose-Header
字段有没有指定Content-Disposition
,如果没有指定,客户端是获取不到的
/**
* @author GauharChan
* @description 把文档数据流下载,api接口需特殊处理(包括responseType: 'blob',isExport: false)
* @param { object } res 完整的响应信息,包括header、data
*/
export function downLoadByBlob(res) {
if (
!res.headers["content-disposition"] &&
!res.headers["Content-Disposition"]
) {
createMessage.error("下载失败");
return;
}
const contentDisposition = decodeURIComponent(
res.headers["content-disposition"] || res.headers["Content-Disposition"]
); // 从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
const patt = new RegExp("filename\\*?=([^;]+\\.[^\\.;]+);*");
const result: any = patt.exec(contentDisposition);
const params = result[1].split("'");
const filename: string = params[params.length - 1];
const fileNameList = filename.split(".");
const suffix = fileNameList[fileNameList.length - 1];
const typeEumn = {
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8",
xls: "application/vnd.ms-excel;charset=utf-8",
doc: "application/msword",
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
};
const blob = new Blob([res.data], {
type: typeEumn[suffix],
});
const downloadElement = document.createElement("a");
const href = window.URL.createObjectURL(blob);
downloadElement.style.display = "none";
downloadElement.href = href;
downloadElement.download = filename; // 下载后文件名
document.body.appendChild(downloadElement);
downloadElement.click(); // 点击下载
document.body.removeChild(downloadElement); // 下载完成移除元素
window.URL.revokeObjectURL(href); // 释放掉blob对象
}