123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- app.controller('qrCodeCtrl', ['$scope', '$http', '$timeout', '$q', 'toaster', function ($scope, $http, $timeout, $q, toaster) {
- moment.locale('zh-cn');
- function initColorPicker(initColor) {
- var colorPicker;
- if (window.Pickr) {
- colorPicker = Pickr.create({
- el: '#colorPicker',
- default: initColor || '#000',
- swatches: [
- '#000',
- '#fff',
- 'rgb(233, 30, 99)',
- 'rgb(244, 67, 54)',
- 'rgb(233, 30, 99)',
- 'rgb(156, 39, 176)',
- 'rgb(103, 58, 183)',
- 'rgb(63, 81, 181)',
- 'rgb(33, 150, 243)',
- 'rgb(3, 169, 244)',
- 'rgb(0, 188, 212)',
- 'rgb(0, 150, 136)',
- 'rgb(76, 175, 80)',
- 'rgb(255, 235, 59)',
- 'rgb(255, 193, 7)'
- ],
- components: {
- preview: true,
- opacity: true,
- hue: true,
- interaction: {
- rgba: true,
- hex: true,
- hsva: true,
- input: true,
- save: true
- }
- },
- strings: {
- save: '确定',
- }
- });
- colorPicker.on('save', function (args) {
- $scope.condition.labelColor = args.toRGBA().toString()
- $scope.$apply()
- })
- }
- }
- //圆角矩形
- CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
- if (w < 2 * r) r = w / 2;
- if (h < 2 * r) r = h / 2;
- this.beginPath();
- this.moveTo(x + r, y);
- this.arcTo(x + w, y, x + w, y + h, r);
- this.arcTo(x + w, y + h, x, y + h, r);
- this.arcTo(x, y + h, x, y, r);
- this.arcTo(x, y, x + w, y, r);
- this.closePath();
- return this;
- };
- var defaultCondition = {
- width: 200,//总画布宽度(有背景图时生效)
- height: 200,//总画布高度(有背景图时生效)
- qrcodeLeft: 0,//二维码位于背景left位置
- qrcodeTop: 0,//二维码位于背景top位置
- labelHeight: 30,//标签占的高度
- labelColor: "#000",//标签颜色
- size: 200,//二维码尺寸
- background: false,
- backgroundPath: "img/qrcode-back.png",
- backgroundScale: 100,
- logo: false,
- logoPath: "img/logo.png",
- logoSize: 40,
- domain: location.protocol + "//" + location.host,//网址,如 https://www.washpayer.com,这个可以自定义
- pathname: '/userLogin?l=',
- prefix: "",//二维码前缀,如蓝牙B开通,还有某些厂商自定义前缀
- dataMod: 'serial',// 默认连续serial,还有batch = logicalCode列表,换行分割
- start: 100,
- end: 200,
- logicalCodeList: "",
- subIndex: false,
- subStart: 1,
- subEnd: 40,
- fileName: "",
- fileSuffix: "jpeg",
- };
- $scope.condition = $.extend(true, {}, defaultCondition, JSON.parse(localStorage.getItem("QRCODE_CONDITION")));
- initColorPicker($scope.condition.labelColor)
- $scope.pageStatus = {
- progress: 0,
- progressShow: false,
- progressComplete: false
- }
- $scope.previewData = {
- url: ""
- }
- $scope.clickBackImgBtn = function () {
- }
- function getLogicalList() {
- var condition = $scope.condition;
- var needList = []
- if (condition.dataMod === 'serial') {
- for (var i = condition.start; i <= condition.end; i++) {
- needList.push(i);
- }
- } else {
- needList = condition.logicalCodeList.split("\n")
- for (var i = needList.length - 1; i >= 0; i--) {
- var item = needList[i];
- needList[i] = $.trim(item);
- // 去空和去重
- if (needList[i] === '') {
- // 必须倒删
- needList.splice(i, 1)
- }
- }
- // 去重复
- needList = needList.filter(function (item, i, self) {
- // 数组的indexOf是元素的全等匹配,如['1111','1'].indexOf('1')结果是1,而不是0
- // 当前遍历元素在整个数组中出现的index 等于 当前遍历序列,则说明这个元素是新元素 返回true(加入到新数组needList),否则false
- var flag = self.indexOf(item) === i
- return flag;
- })
- }
- return needList
- }
- //生成二维码
- function getQRCodeImageData(text, label) {
- var condition = $scope.condition;
- var size = condition.size;
- var labelHeight = condition.labelHeight;
- var logoSize = condition.logoSize;
- //生成二维码
- var qrDom = $("<div></div>");
- qrDom.qrcode({
- render: "canvas", // 渲染方式有table方式(IE兼容)和canvas方式
- width: size, //宽度
- height: size, //高度
- text: text, //内容
- typeNumber: -1,//计算模式
- correctLevel: 2,//二维码纠错级别
- background: "#ffffff",//背景颜色
- foreground: "#000000" //二维码颜色
- });
- var canvas = qrDom.find("canvas")[0];
- var context = canvas.getContext("2d");
- var imgData = context.getImageData(0, 0, size, size);//获取到二维码图片数据流
- //生成背景(用来绘制标签、背景图等)
- var backDom = $("<canvas></canvas>");
- var maxW = size;
- var maxH = size + labelHeight;
- var left = condition.qrcodeLeft;
- var top = condition.qrcodeTop;
- if (condition.background) {
- var backImgDom = $("#previewBackImg")[0];
- maxW = condition.width = backImgDom.naturalWidth * (condition.backgroundScale / 100);
- maxH = condition.height = backImgDom.naturalHeight * (condition.backgroundScale / 100);
- left = condition.qrcodeLeft;
- top = condition.qrcodeTop;
- } else {
- maxW = condition.width = size;
- maxH = condition.height = size + labelHeight;
- left = 0;
- top = 0;
- }
- backDom.attr({"width": maxW, "height": maxH});
- var backCanvas = backDom[0];
- var ctx = backCanvas.getContext("2d");
- //绘制背景图
- if (condition.background) {
- ctx.drawImage($("#previewBackImg")[0], 0, 0, maxW, maxH);
- $scope.condition.backWidth = parseInt(maxW)
- $scope.condition.backHeight = parseInt(maxH)
- }
- // ctx.clearRect(0, 0, size, size + labelHeight);//清空绘制标题区域
- ctx.putImageData(imgData, left, top);//合并二维码到带背景信息的画布
- if (condition.fileSuffix === 'jpeg') {
- //标题绘制白色背景,避免变黑色
- ctx.fillStyle = "#fff";
- ctx.fillRect(left, size + top, size, labelHeight);
- }
- //绘制标题
- ctx.font = "bold 24px Arial";//使用粗体,稍大的字号,否则打印出来的不清晰
- ctx.textAlign = "center";
- ctx.fillStyle = condition.labelColor;
- ctx.fillText(label, size / 2 + left, (size + labelHeight - 8) + top);
- //绘制logo
- if (condition.logo) {
- var logoBorder = 4;
- var centerSize = 2 * logoBorder + logoSize;
- //清空中心区域用来绘制logo
- ctx.rect((size - centerSize) / 2 + left, (size - centerSize) / 2 + top, centerSize, centerSize);
- ctx.fillStyle = "#fff";
- ctx.fill();
- //logo图片
- ctx.drawImage($("#previewLogo")[0], (size - logoSize) / 2 + left, (size - logoSize) / 2 + top, logoSize, logoSize);
- //边框 0.5px 修复
- ctx.lineWidth = 1;
- ctx.strokeStyle = "#aaa";
- ctx.roundRect((size - centerSize) / 2 + left + 0.5, (size - centerSize) / 2 + top + 0.5, centerSize, centerSize, 6).stroke();
- }
- var dataURL = backCanvas.toDataURL("image/" + condition.fileSuffix);
- return dataURL;
- }
- //转换base64到Blob对象,用以zip压缩
- function convertImgDataToBlob(base64Data, fileSuffix) {
- var format = "image/" + fileSuffix;
- var base64 = base64Data;
- var code = window.atob(base64.split(",")[1]);
- var aBuffer = new window.ArrayBuffer(code.length);
- var uBuffer = new window.Uint8Array(aBuffer);
- for (var i = 0; i < code.length; i++) {
- uBuffer[i] = code.charCodeAt(i) & 0xff;
- }
- var blob = null;
- try {
- blob = new Blob([uBuffer], {type: format});
- }
- catch (e) {
- window.BlobBuilder = window.BlobBuilder ||
- window.WebKitBlobBuilder ||
- window.MozBlobBuilder ||
- window.MSBlobBuilder;
- if (e.name == 'TypeError' && window.BlobBuilder) {
- var bb = new window.BlobBuilder();
- bb.append(uBuffer.buffer);
- blob = bb.getBlob("image/" + fileSuffix);
- }
- else if (e.name == "InvalidStateError") {
- blob = new Blob([aBuffer], {type: format});
- }
- else {
- }
- }
- return blob;
- }
- if (BROWSER_TYPE === 'wechat') {
- $scope.showOpenBrowser = true
- }
- //判断访问终端
- var browser = {
- versions: function () {
- var u = navigator.userAgent, app = navigator.appVersion;
- return {
- trident: u.indexOf('Trident') > -1, //IE内核
- presto: u.indexOf('Presto') > -1, //opera内核
- webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
- gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
- mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
- ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
- android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
- iPhone: u.indexOf('iPhone') > -1, //是否为iPhone或者QQHD浏览器
- iPad: u.indexOf('iPad') > -1, //是否iPad
- webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
- weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
- qq: u.match(/\sQQ/i) == " qq" //是否QQ
- };
- }(),
- language: (navigator.browserLanguage || navigator.language).toLowerCase()
- };
- //点击则生成图片:生成过程很慢,生成过程中请不要改表单数据!
- $scope.getZip = function () {
- if (BROWSER_TYPE === 'wechat') {
- $('#browser_type_error').modal();
- return;
- }
- var condition = $scope.condition;
- var needList = getLogicalList();
- if (needList.length === 0) {
- alert('没有需要生成的二维码')
- return;
- }
- var hasSubIndex = condition.subIndex;
- var subIndex = condition.subStart;
- var sumCodeCount = needList.length;
- if (hasSubIndex) {
- sumCodeCount = sumCodeCount * (condition.subEnd - condition.subStart + 1);
- }
- var index = 0;// 开始生成的index = 0
- var zip;
- var mask;
- if (browser.versions.mobile || browser.versions.android || browser.versions.ios) {
- if (sumCodeCount > 100) {
- alert("超过100个二维码,无法正常生成。手机端一次性最多只能生成100个二维码,请修改参数减少二维码数量。");
- return
- }
- }
- $.confirm({
- content: "将生成二维码" + sumCodeCount + "个,确定?",
- buttons: {
- ok: {
- action: function () {
- zip = new JSZip();
- //重置状态
- $scope.pageStatus.progressComplete = false;
- $scope.pageStatus.progressShow = true;
- index = 0;
- run();
- mask = new Mask("正在生成,请稍候,请不要切换或关闭页面。").show();
- }
- },
- }
- });
- //使用timeout避免网页卡死
- function run() {
- $timeout(function () {
- if (index >= needList.length) {
- over();
- return;
- }
- var nowLogical = needList[index]
- var progress = Math.round(index * 100 / needList.length);//刷新进度
- if (progress % 2 === 0) {
- //控制刷新次数
- $scope.pageStatus.progress = progress;
- }
- var currentDomain = condition.domain;
- var pathname = condition.pathname;
- var qrcode = condition.prefix + "" + nowLogical;
- var url = currentDomain + pathname + qrcode;
- var label = qrcode;
- if (hasSubIndex) {
- label = label + "-" + subIndex;
- url = url + "&chargeIndex=" + subIndex
- }
- var dataURL = getQRCodeImageData(url, label);
- var fileContent = dataURL.split('base64,')[1];
- zip.file(label + "." + condition.fileSuffix, fileContent, {base64: true});
- // 如果子编号遍历完一轮,则逻辑码+1,然后再重置子编号
- if (hasSubIndex) {
- subIndex++;
- if (subIndex > condition.subEnd) {
- index++;
- subIndex = condition.subStart;
- }
- } else {
- // 如果没有子编号,逻辑码+1
- index++;
- }
- run();//继续run
- });
- }
- function over() {
- $scope.pageStatus.progressComplete = true;
- var fileName = condition.fileName;
- if (!fileName) {
- fileName = "逻辑码"
- if (condition.dataMod === 'serial') {
- fileName = fileName + condition.start + (condition.end === condition.start ? "" : ("-" + condition.end))
- } else {
- fileName = fileName + '批量自定义' + moment().format("YYYY-MM-DD hh_mm_ss") // 文件系统不支持:
- }
- if (condition.subIndex) {
- fileName = fileName + " 端口" + condition.subStart + (condition.subEnd === condition.subStart ? "" : ("-" + condition.subEnd))
- }
- }
- zip.generateAsync({type: "blob"}).then(function (content) {
- // see FileSaver.js
- window.saveAs && saveAs(content, fileName + ".zip")
- });
- $timeout(function () {
- $scope.pageStatus.progressShow = false;
- }, 5000);
- mask.remove()
- }
- };
- //建立一個可存取到該file的url
- function getObjectURL(file) {
- var url = null;
- if (window.createObjectURL != undefined) { // basic
- url = window.createObjectURL(file);
- } else if (window.URL != undefined) { // mozilla(firefox)
- url = window.URL.createObjectURL(file);
- } else if (window.webkitURL != undefined) { // webkit or chrome
- url = window.webkitURL.createObjectURL(file);
- }
- return url;
- }
- //预览
- $scope.previewQRCode = function () {
- var condition = $scope.condition;
- var currentDomain = condition.domain;
- var pathname = condition.pathname;
- var qrcode = condition.prefix + "6666";
- var url = currentDomain + pathname + qrcode;
- var label = qrcode;
- if (condition.subIndex) {
- label = label + "-" + condition.subStart;
- url = url + "&chargeIndex=" + condition.subStart
- }
- var dataURL = getQRCodeImageData(url, label);
- $("#previewImg").attr({
- "width": condition.width,
- "height": condition.height,
- "src": dataURL,
- "title": url
- });
- $scope.previewData.url = url
- };
- //缓存二维码制作方案
- $scope.savePlan = function () {
- localStorage.setItem("QRCODE_CONDITION", JSON.stringify($scope.condition));
- };
- //事件绑定
- $timeout(function () {
- // 实时刷新预览,深度监听
- $scope.$watch("condition", function (newValue, oldValue) {
- if (newValue == oldValue) {
- // 如果两次值相等,则不刷新视图
- } else {
- $scope.previewQRCode();
- }
- }, true);
- $("#backImgFile").on("change", function () {
- var file = this.files[0];
- if (!file.type.includes("image")) {
- toaster.pop("info", "提示", "请选择图片文件!");
- } else {
- //预览logo
- var objUrl = getObjectURL(file);
- if (objUrl) {
- $("#previewBackImg").attr("src", objUrl);
- $scope.condition.backgroundPath = objUrl
- }
- }
- $scope.$apply();//刷新提示和数据
- });
- $("#logoFile").on("change", function () {
- //change说明用户需要logo
- $scope.condition.logo = true;
- var file = this.files[0];
- if (!file.type.includes("image")) {
- toaster.pop("info", "提示", "请选择图片文件!");
- } else {
- //预览logo
- var objUrl = getObjectURL(file);
- if (objUrl) {
- $("#previewLogo").attr("src", objUrl);
- $scope.condition.logoPath = objUrl
- }
- }
- $scope.$apply();//刷新提示和数据
- });
- $("#previewBackImg,#previewLogo").on('load', function () {
- // input file 没法绑定angular,当图片加载完后手动刷新预览
- $scope.previewQRCode();
- });
- });
- }]);
|