生成图片方法更改
This commit is contained in:
parent
5b37d7962d
commit
4a992c9847
@ -12,7 +12,6 @@
|
||||
<body>
|
||||
<div id="app" v-cloak></div>
|
||||
<script src="/js/echarts.min.js"></script>
|
||||
<script src="/js/html2canvas.min.js"></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<style>
|
||||
[v-cloak] {
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@vant/area-data": "^2.1.0",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"html-to-image": "^1.11.13",
|
||||
"image-conversion": "^2.1.1",
|
||||
"pinia": "^3.0.4",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
@ -822,6 +823,12 @@
|
||||
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/html-to-image": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.13.tgz",
|
||||
"integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@vant/area-data": "^2.1.0",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"html-to-image": "^1.11.13",
|
||||
"image-conversion": "^2.1.1",
|
||||
"pinia": "^3.0.4",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 16 KiB |
@ -1,235 +0,0 @@
|
||||
/*
|
||||
* Canvas2Image v0.1
|
||||
* Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk
|
||||
* MIT License [http://www.opensource.org/licenses/mit-license.php]
|
||||
*/
|
||||
|
||||
var Canvas2Image = (function() {
|
||||
|
||||
// check if we have canvas support
|
||||
var bHasCanvas = false;
|
||||
var oCanvas = document.createElement("canvas");
|
||||
if (oCanvas.getContext("2d")) {
|
||||
bHasCanvas = true;
|
||||
}
|
||||
|
||||
// no canvas, bail out.
|
||||
if (!bHasCanvas) {
|
||||
return {
|
||||
saveAsBMP : function(){},
|
||||
saveAsPNG : function(){},
|
||||
saveAsJPEG : function(){}
|
||||
}
|
||||
}
|
||||
|
||||
var bHasImageData = !!(oCanvas.getContext("2d").getImageData);
|
||||
var bHasDataURL = !!(oCanvas.toDataURL);
|
||||
var bHasBase64 = !!(window.btoa);
|
||||
|
||||
var strDownloadMime = "image/octet-stream";
|
||||
|
||||
// ok, we're good
|
||||
var readCanvasData = function(oCanvas) {
|
||||
var iWidth = parseInt(oCanvas.width);
|
||||
var iHeight = parseInt(oCanvas.height);
|
||||
return oCanvas.getContext("2d").getImageData(0,0,iWidth,iHeight);
|
||||
}
|
||||
|
||||
// base64 encodes either a string or an array of charcodes
|
||||
var encodeData = function(data) {
|
||||
var strData = "";
|
||||
if (typeof data == "string") {
|
||||
strData = data;
|
||||
} else {
|
||||
var aData = data;
|
||||
for (var i=0;i<aData.length;i++) {
|
||||
strData += String.fromCharCode(aData[i]);
|
||||
}
|
||||
}
|
||||
return btoa(strData);
|
||||
}
|
||||
|
||||
// creates a base64 encoded string containing BMP data
|
||||
// takes an imagedata object as argument
|
||||
var createBMP = function(oData) {
|
||||
var aHeader = [];
|
||||
|
||||
var iWidth = oData.width;
|
||||
var iHeight = oData.height;
|
||||
|
||||
aHeader.push(0x42); // magic 1
|
||||
aHeader.push(0x4D);
|
||||
|
||||
var iFileSize = iWidth*iHeight*3 + 54; // total header size = 54 bytes
|
||||
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
||||
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
||||
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
||||
aHeader.push(iFileSize % 256);
|
||||
|
||||
aHeader.push(0); // reserved
|
||||
aHeader.push(0);
|
||||
aHeader.push(0); // reserved
|
||||
aHeader.push(0);
|
||||
|
||||
aHeader.push(54); // dataoffset
|
||||
aHeader.push(0);
|
||||
aHeader.push(0);
|
||||
aHeader.push(0);
|
||||
|
||||
var aInfoHeader = [];
|
||||
aInfoHeader.push(40); // info header size
|
||||
aInfoHeader.push(0);
|
||||
aInfoHeader.push(0);
|
||||
aInfoHeader.push(0);
|
||||
|
||||
var iImageWidth = iWidth;
|
||||
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
||||
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
||||
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
||||
aInfoHeader.push(iImageWidth % 256);
|
||||
|
||||
var iImageHeight = iHeight;
|
||||
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
||||
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
||||
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
||||
aInfoHeader.push(iImageHeight % 256);
|
||||
|
||||
aInfoHeader.push(1); // num of planes
|
||||
aInfoHeader.push(0);
|
||||
|
||||
aInfoHeader.push(24); // num of bits per pixel
|
||||
aInfoHeader.push(0);
|
||||
|
||||
aInfoHeader.push(0); // compression = none
|
||||
aInfoHeader.push(0);
|
||||
aInfoHeader.push(0);
|
||||
aInfoHeader.push(0);
|
||||
|
||||
var iDataSize = iWidth*iHeight*3;
|
||||
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
||||
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
||||
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
||||
aInfoHeader.push(iDataSize % 256);
|
||||
|
||||
for (var i=0;i<16;i++) {
|
||||
aInfoHeader.push(0); // these bytes not used
|
||||
}
|
||||
|
||||
var iPadding = (4 - ((iWidth * 3) % 4)) % 4;
|
||||
|
||||
var aImgData = oData.data;
|
||||
|
||||
var strPixelData = "";
|
||||
var y = iHeight;
|
||||
do {
|
||||
var iOffsetY = iWidth*(y-1)*4;
|
||||
var strPixelRow = "";
|
||||
for (var x=0;x<iWidth;x++) {
|
||||
var iOffsetX = 4*x;
|
||||
|
||||
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+2]);
|
||||
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+1]);
|
||||
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX]);
|
||||
}
|
||||
for (var c=0;c<iPadding;c++) {
|
||||
strPixelRow += String.fromCharCode(0);
|
||||
}
|
||||
strPixelData += strPixelRow;
|
||||
} while (--y);
|
||||
|
||||
var strEncoded = encodeData(aHeader.concat(aInfoHeader)) + encodeData(strPixelData);
|
||||
|
||||
return strEncoded;
|
||||
}
|
||||
|
||||
|
||||
// sends the generated file to the client
|
||||
var saveFile = function(strData) {
|
||||
document.location.href = strData;
|
||||
}
|
||||
|
||||
var makeDataURI = function(strData, strMime) {
|
||||
return "data:" + strMime + ";base64," + strData;
|
||||
}
|
||||
|
||||
// generates a <img> object containing the imagedata
|
||||
var makeImageObject = function(strSource) {
|
||||
var oImgElement = document.createElement("img");
|
||||
oImgElement.src = strSource;
|
||||
return oImgElement;
|
||||
}
|
||||
|
||||
var scaleCanvas = function(oCanvas, iWidth, iHeight) {
|
||||
if (iWidth && iHeight) {
|
||||
var oSaveCanvas = document.createElement("canvas");
|
||||
oSaveCanvas.width = iWidth;
|
||||
oSaveCanvas.height = iHeight;
|
||||
oSaveCanvas.style.width = iWidth+"px";
|
||||
oSaveCanvas.style.height = iHeight+"px";
|
||||
|
||||
var oSaveCtx = oSaveCanvas.getContext("2d");
|
||||
|
||||
oSaveCtx.drawImage(oCanvas, 0, 0, oCanvas.width, oCanvas.height, 0, 0, iWidth, iHeight);
|
||||
return oSaveCanvas;
|
||||
}
|
||||
return oCanvas;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
saveAsPNG : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
||||
if (!bHasDataURL) {
|
||||
return false;
|
||||
}
|
||||
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
|
||||
var strData = oScaledCanvas.toDataURL("image/png");
|
||||
if (bReturnImg) {
|
||||
return makeImageObject(strData);
|
||||
} else {
|
||||
saveFile(strData.replace("image/png", strDownloadMime));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
saveAsJPEG : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
||||
if (!bHasDataURL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
|
||||
var strMime = "image/jpeg";
|
||||
var strData = oScaledCanvas.toDataURL(strMime);
|
||||
|
||||
// check if browser actually supports jpeg by looking for the mime type in the data uri.
|
||||
// if not, return false
|
||||
if (strData.indexOf(strMime) != 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bReturnImg) {
|
||||
return makeImageObject(strData);
|
||||
} else {
|
||||
saveFile(strData.replace(strMime, strDownloadMime));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
saveAsBMP : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
||||
if (!(bHasImageData && bHasBase64)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
|
||||
|
||||
var oData = readCanvasData(oScaledCanvas);
|
||||
var strImgData = createBMP(oData);
|
||||
if (bReturnImg) {
|
||||
return makeImageObject(makeDataURI(strImgData, "image/bmp"));
|
||||
} else {
|
||||
saveFile(makeDataURI(strImgData, strDownloadMime));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
22
public/js/html2canvas.min.js
vendored
22
public/js/html2canvas.min.js
vendored
File diff suppressed because one or more lines are too long
@ -268,6 +268,12 @@ const routes = [
|
||||
component: () => import('./views/Merchant/MerchantIntroduction.vue'),
|
||||
meta: { title: '商家资料' }
|
||||
},
|
||||
{
|
||||
path: '/PayCode',
|
||||
name: 'PayCode',
|
||||
component: () => import('./views/Merchant/PayCode.vue'),
|
||||
meta: { title: '收款码' }
|
||||
},
|
||||
{
|
||||
path: '/QrReader',
|
||||
name: 'QrReader',
|
||||
|
||||
@ -5566,3 +5566,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.paycode-page {
|
||||
.f5;
|
||||
}
|
||||
@ -2298,26 +2298,37 @@ img {
|
||||
.box;
|
||||
.box-tb;
|
||||
.box-align-center;
|
||||
padding: 6.8vw 9.2vw;
|
||||
width: 80vw;
|
||||
min-width: 80vw;
|
||||
max-width: 80vw;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
|
||||
border-radius: 2.67vw;
|
||||
|
||||
.share_box {
|
||||
.box;
|
||||
.box-tb;
|
||||
.box-align-center;
|
||||
padding: 6.8vw 9.2vw;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
background-image: linear-gradient(180deg,
|
||||
#f5cfd8 0%,
|
||||
#fff4f4 100%);
|
||||
border-radius: 2.67vw;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 10.53vw;
|
||||
margin-bottom: 4vw;
|
||||
width: 18.93vw;
|
||||
height: 22.27vw;
|
||||
}
|
||||
|
||||
.bottom_box {
|
||||
.box;
|
||||
.box-align-center;
|
||||
.box-pack-around;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.share_userinfo {
|
||||
@ -2335,10 +2346,10 @@ img {
|
||||
.name {
|
||||
.box;
|
||||
.box-center-center;
|
||||
color: #222222;
|
||||
.bs;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: #fde5eb;
|
||||
background-color: #841e36;
|
||||
border-radius: 3.33vw;
|
||||
// padding: .667vw 0;
|
||||
// padding: 1.2vw 4vw 6vw;
|
||||
@ -2350,7 +2361,7 @@ img {
|
||||
|
||||
span {
|
||||
line-height: 8vw;
|
||||
margin-bottom: 2.4vw;
|
||||
// margin-bottom: 2.4vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2377,9 +2388,25 @@ img {
|
||||
height: 8vw;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
.box;
|
||||
.box-align-center;
|
||||
.box-pack-between;
|
||||
width: 100%;
|
||||
padding-bottom: 3.33vw;
|
||||
border-bottom: 1px #fff solid;
|
||||
|
||||
.goods_price {
|
||||
margin-top: 1.33vw;
|
||||
font-size: 4vw;
|
||||
font-weight: bold;
|
||||
color: #ea3e23;
|
||||
}
|
||||
|
||||
.o_price {
|
||||
color: #b1b1b1;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2398,7 +2425,7 @@ img {
|
||||
}
|
||||
|
||||
.share_result {
|
||||
margin-top: 4vw;
|
||||
// margin-top: 4vw;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
|
||||
@ -1,159 +1,94 @@
|
||||
/**
|
||||
* HTML转图片工具
|
||||
* 基于 html2canvas + canvas2image
|
||||
* 基于 html-to-image 将DOM元素转换为图片
|
||||
*/
|
||||
|
||||
import { toCanvas } from 'html-to-image'
|
||||
|
||||
/**
|
||||
* 将HTML元素转换为canvas
|
||||
* @param {HTMLElement} element - DOM元素
|
||||
* @param {Object} options - 配置参数
|
||||
* @param {number} [options.scale=2] - 缩放比例
|
||||
* @param {number} [options.width] - 指定宽度
|
||||
* @param {number} [options.height] - 指定高度
|
||||
* @param {boolean} [options.useCORS=true] - 是否支持跨域
|
||||
* @param {number} [options.dpi=window.devicePixelRatio] - DPI
|
||||
* @returns {Promise<HTMLCanvasElement>}
|
||||
* 将DOM元素转换为图片
|
||||
* @param {string|HTMLElement} selector - DOM选择器或元素
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {number} [options.width] - 自定义宽度(会应用到元素上)
|
||||
* @param {number} [options.height] - 自定义高度(会应用到元素上)
|
||||
* @param {number} [options.canvasWidth] - 输出画布宽度
|
||||
* @param {number} [options.canvasHeight] - 输出画布高度
|
||||
* @param {number} [options.pixelRatio] - 像素比例,默认2
|
||||
* @param {boolean} [options.useCORS] - 是否允许跨域,默认true
|
||||
* @param {string} [options.format] - 图片格式,'png'或'jpeg',默认'png'
|
||||
* @param {number} [options.quality] - 图片质量,0-1,仅jpeg有效,默认1.0
|
||||
* @returns {Promise<string>} - 返回base64图片数据URL
|
||||
*/
|
||||
export async function html2canvas(element, options = {}) {
|
||||
export async function toDataURL(selector, options = {}) {
|
||||
const {
|
||||
scale = 2,
|
||||
width,
|
||||
height,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
pixelRatio = 2,
|
||||
useCORS = true,
|
||||
dpi = window.devicePixelRatio,
|
||||
onclone,
|
||||
} = options;
|
||||
format = 'png',
|
||||
quality = 1.0
|
||||
} = options
|
||||
|
||||
const opts = {
|
||||
scale,
|
||||
useCORS,
|
||||
dpi,
|
||||
logging: false,
|
||||
backgroundColor: '#ffffff',
|
||||
};
|
||||
|
||||
if (width) opts.width = width;
|
||||
if (height) opts.height = height;
|
||||
|
||||
// 添加 onclone 回调用于调试
|
||||
if (onclone) {
|
||||
opts.onclone = onclone;
|
||||
const element = typeof selector === 'string' ? document.querySelector(selector) : selector
|
||||
if (!element) {
|
||||
throw new Error('Element not found')
|
||||
}
|
||||
|
||||
const html2canvasFn = window.html2canvas;
|
||||
if (!html2canvasFn) {
|
||||
throw new Error('html2canvas not loaded, please include html2canvas.min.js');
|
||||
}
|
||||
const canvasEl = await toCanvas(element, {
|
||||
width,
|
||||
height,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
pixelRatio,
|
||||
cors: useCORS,
|
||||
backgroundColor: '#ffffff'
|
||||
})
|
||||
|
||||
return html2canvasFn(element, opts);
|
||||
const mimeType = format === 'jpeg' ? 'image/jpeg' : 'image/png'
|
||||
return canvasEl.toDataURL(mimeType, quality)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将canvas转换为dataURL
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {string} [type='image/png']
|
||||
* @param {number} [quality=1.0]
|
||||
* @returns {string}
|
||||
* 将DOM元素转换为图片并下载
|
||||
* @param {string|HTMLElement} selector - DOM选择器或元素
|
||||
* @param {string} [filename] - 下载文件名,不含扩展名
|
||||
* @param {Object} options - 配置选项,同toDataURL
|
||||
*/
|
||||
export function canvasToDataURL(canvas, type = 'image/png', quality = 1.0) {
|
||||
return canvas.toDataURL(type, quality);
|
||||
export async function toImageDownload(selector, filename = 'download', options = {}) {
|
||||
const dataUrl = await toDataURL(selector, options)
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.download = filename + '.png'
|
||||
link.href = dataUrl
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将canvas转换为Blob
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {string} [type='image/png']
|
||||
* @param {number} [quality=1.0]
|
||||
* 将DOM元素转换为Blob
|
||||
* @param {string|HTMLElement} selector - DOM选择器或元素
|
||||
* @param {Object} options - 配置选项,同toDataURL
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
export function canvasToBlob(canvas, type = 'image/png', quality = 1.0) {
|
||||
return new Promise((resolve, reject) => {
|
||||
canvas.toBlob(blob => {
|
||||
if (blob) {
|
||||
resolve(blob);
|
||||
} else {
|
||||
reject(new Error('Canvas to Blob failed'));
|
||||
export async function toBlob(selector, options = {}) {
|
||||
const dataUrl = await toDataURL(selector, options)
|
||||
|
||||
const arr = dataUrl.split(',')
|
||||
const mime = arr[0].match(/:(.*?);/)[1]
|
||||
const bstr = atob(arr[1])
|
||||
let n = bstr.length
|
||||
const u8arr = new Uint8Array(n)
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n)
|
||||
}
|
||||
}, type, quality);
|
||||
});
|
||||
return new Blob([u8arr], { type: mime })
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HTML元素直接转成dataURL图片
|
||||
* @param {HTMLElement} element - DOM元素
|
||||
* @param {Object} options - html2canvas配置
|
||||
* @param {string} [type='image/png'] - 图片类型
|
||||
* @param {number} [quality=1.0] - 图片质量
|
||||
* @returns {Promise<string>} dataURL
|
||||
*/
|
||||
export async function htmlToDataURL(element, options = {}, type = 'image/png', quality = 1.0) {
|
||||
const canvas = await html2canvas(element, options);
|
||||
return canvasToDataURL(canvas, type, quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HTML元素直接转成Blob
|
||||
* @param {HTMLElement} element - DOM元素
|
||||
* @param {Object} options - html2canvas配置
|
||||
* @param {string} [type='image/png'] - 图片类型
|
||||
* @param {number} [quality=1.0] - 图片质量
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
export async function htmlToBlob(element, options = {}, type = 'image/png', quality = 1.0) {
|
||||
const canvas = await html2canvas(element, options);
|
||||
return canvasToBlob(canvas, type, quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载canvas为图片
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {string} filename - 文件名(不带扩展名)
|
||||
* @param {string} [type='png'] - 图片格式 ('png' | 'jpeg' | 'webp')
|
||||
*/
|
||||
export function downloadCanvas(canvas, filename, type = 'png') {
|
||||
const mimeType = `image/${type}`;
|
||||
const dataURL = canvasToDataURL(canvas, mimeType, 1.0);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.download = `${filename}.${type}`;
|
||||
link.href = dataURL;
|
||||
link.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HTML元素转成图片并下载
|
||||
* @param {HTMLElement} element - DOM元素
|
||||
* @param {string} filename - 文件名(不带扩展名)
|
||||
* @param {Object} options - html2canvas配置
|
||||
* @param {string} [type='png'] - 图片格式
|
||||
*/
|
||||
export async function htmlToImage(element, filename, options = {}, type = 'png') {
|
||||
const canvas = await html2canvas(element, options);
|
||||
downloadCanvas(canvas, filename, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将dataURL保存为图片文件
|
||||
* @param {string} dataURL - 图片dataURL
|
||||
* @param {string} filename - 文件名(不带扩展名)
|
||||
*/
|
||||
export function downloadDataURL(dataURL, filename) {
|
||||
const link = document.createElement('a');
|
||||
link.download = filename;
|
||||
link.href = dataURL;
|
||||
link.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Blob保存为图片文件
|
||||
* @param {Blob} blob - 图片Blob
|
||||
* @param {string} filename - 文件名(不带扩展名)
|
||||
*/
|
||||
export async function downloadBlob(blob, filename) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.download = filename;
|
||||
link.href = url;
|
||||
link.click();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 100);
|
||||
export default {
|
||||
toDataURL,
|
||||
toImageDownload,
|
||||
toBlob
|
||||
}
|
||||
@ -125,7 +125,7 @@
|
||||
|
||||
<van-popup v-model:show="show" class="shareimgbox" style="max-width: 80vw;">
|
||||
<div class="share_box" id="bsCard">
|
||||
<img class="logo" src="/img/logo-lr.png" alt="">
|
||||
|
||||
|
||||
<div class="share_userinfo">
|
||||
<img :src="$file($userInfo.userimg)" alt="">
|
||||
@ -135,18 +135,24 @@
|
||||
<div class="goods_info">
|
||||
<img class="goods_img" :src="$file(data.img)" alt="">
|
||||
<div class="goods_name">{{ data.name }}</div>
|
||||
<div class="goods_price">¥{{ data.saleprice?.toFixed(2) || '0.00' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="price">
|
||||
<div class="goods_price">¥{{ data.saleprice?.toFixed(2) || '0.00' }}</div>
|
||||
<div class="o_price">¥{{ data.originalprice?.toFixed(2) || '0.00' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="bottom_box">
|
||||
<div class="qrcode">
|
||||
<vue-qr :margin="0" :text="shareLink" backgroundColor="rgb(255,255,255)"
|
||||
colorLight="rgb(255,255,255)"></vue-qr>
|
||||
<vue-qr :margin="0" :text="shareLink" backgroundColor="#ffffff00" colorLight="rgb(255,255,255)"></vue-qr>
|
||||
长按扫码购买
|
||||
|
||||
</div>
|
||||
<img class="logo" src="/img/logo.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="share_result" v-if="ShareImg">
|
||||
<img :src="ShareImg" alt="分享海报">
|
||||
<!-- <div class="tips">长按保存图片</div> -->
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
@ -158,7 +164,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { html2canvas, canvasToDataURL } from '@/utils/html2image';
|
||||
|
||||
import { toDataURL } from '@/utils/html2image.js'
|
||||
|
||||
export default {
|
||||
name: 'GoodsDetail',
|
||||
@ -182,7 +189,8 @@ export default {
|
||||
],
|
||||
shareLink: '',
|
||||
ShareImg: '',
|
||||
loading: false
|
||||
loading: false,
|
||||
transparent: 'rgba(0,0,0,0)'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -242,7 +250,7 @@ export default {
|
||||
// 等待 DOM 渲染后再生成
|
||||
setTimeout(() => {
|
||||
this.generateShareImg();
|
||||
}, 200);
|
||||
}, 500);
|
||||
} else if (option.name == "复制链接") {
|
||||
this.$copyText(location.href);
|
||||
this.$showToast({ type: "success", message: "复制成功" });
|
||||
@ -263,19 +271,29 @@ export default {
|
||||
async generateShareImg() {
|
||||
var shareContent = document.querySelector('#bsCard');
|
||||
if (!shareContent) return;
|
||||
var width = shareContent.offsetWidth;
|
||||
var height = shareContent.offsetHeight;
|
||||
try {
|
||||
var canvas = await html2canvas(shareContent, {
|
||||
scale: 2,
|
||||
width: width,
|
||||
height: height,
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
// Wait for all images inside shareContent to load
|
||||
const images = shareContent.querySelectorAll('img');
|
||||
for (const img of images) {
|
||||
if (!img.complete) {
|
||||
await new Promise((resolve) => {
|
||||
img.onload = resolve;
|
||||
img.onerror = resolve;
|
||||
});
|
||||
this.ShareImg = canvasToDataURL(canvas, "image/png", 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for vue-qr to render
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
this.ShareImg = await toDataURL(shareContent, {
|
||||
pixelRatio: 2,
|
||||
useCORS: true,
|
||||
format: 'png'
|
||||
});
|
||||
document.querySelector('.share_box').style = 'display: none';
|
||||
this.loading = false;
|
||||
this.loading = false;
|
||||
document.querySelector('.share_box').style.display = 'none';
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.loading = false;
|
||||
|
||||
@ -79,7 +79,8 @@ export default {
|
||||
onTabClick(name) {
|
||||
if (this.$route.name === name) {
|
||||
// 当前路由,双击刷新
|
||||
this.$router.replace({ name })
|
||||
// this.$router.replace({ name })
|
||||
location.reload()
|
||||
} else {
|
||||
this.$router.push({ name })
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BasePage>
|
||||
<!-- <BasePage> -->
|
||||
<ManagerPopup v-model="managerPopupVisible" />
|
||||
<div class="merchant">
|
||||
<div class="shopinfo">
|
||||
@ -74,7 +74,7 @@
|
||||
</div> -->
|
||||
|
||||
<div class="fastTo">
|
||||
<div class="ft1" @click="showBottom = true">
|
||||
<div class="ft1" @click="$navigate('PayCode?id=' + id)">
|
||||
<span>收款码</span>
|
||||
<img src="/img/ft_i1.png" alt="">
|
||||
</div>
|
||||
@ -138,32 +138,11 @@
|
||||
|
||||
</div>
|
||||
|
||||
<van-action-sheet v-model:show="showBottom" title="收款码" @click-overlay="onCloseBottom" @opened="onOpenBottom">
|
||||
<div class="paycode-wrap">
|
||||
<div class="paycode-original" ref="paycodeOriginal" v-show="!paycodeImg">
|
||||
<img src="/img/paycode.jpg" alt="" style="width: 100%; display: block;">
|
||||
<div class="info">
|
||||
<img :src="$file(data.shopimg)" alt="" style="border-radius: 50%;">
|
||||
</div>
|
||||
<div class="name">
|
||||
<span>{{ data.shopname }}</span>
|
||||
</div>
|
||||
<div class="code">
|
||||
<vue-qr v-if="!loading" :margin="0" :text="link" backgroundColor="rgb(255,255,255)"
|
||||
colorLight="rgb(255,255,255)"></vue-qr>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paycode-result" v-if="paycodeImg">
|
||||
<img :src="paycodeImg" style="width: 100%;" alt="收款码">
|
||||
</div>
|
||||
</div>
|
||||
</van-action-sheet>
|
||||
</BasePage>
|
||||
<!-- </BasePage> -->
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { html2canvas } from '@/utils/html2image'
|
||||
import ManagerPopup from "@/components/ManagerPopup.vue"
|
||||
|
||||
export default {
|
||||
@ -181,33 +160,11 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
managerPopupVisible: false,
|
||||
showInfo: false,
|
||||
showBottom: false,
|
||||
paycodeImg: '',
|
||||
isCapturing: false,
|
||||
link: '',
|
||||
id: this.$route.query.id,
|
||||
data: {
|
||||
id: 'merchant_001',
|
||||
MerchantAvatar: '',
|
||||
MerchantName: '泰古润直营店',
|
||||
MerchantPhone: '13800138000',
|
||||
Income: 12868.50,
|
||||
Point: 5680.00,
|
||||
TodayAmount: 5680.00,
|
||||
YesterdayAmount: 4200.00,
|
||||
TodayDeduct: 1280.00,
|
||||
YesterdayDeduct: 980.00,
|
||||
TodayQty: 45,
|
||||
YesterdayQty: 38,
|
||||
MonthIncome: 45600.00,
|
||||
PrevMonthIncome: 38500.00,
|
||||
endTimes: new Date().toLocaleString('zh-CN', { hour12: false })
|
||||
},
|
||||
countData: {},
|
||||
States: 0,
|
||||
expiring: 3,
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -226,87 +183,14 @@ export default {
|
||||
init() {
|
||||
Promise.all([
|
||||
this.$get(`/v1/client/DShopsClient/${this.id}`).then(res => {
|
||||
// console.log(res);
|
||||
this.data = res.data;
|
||||
this.data.endTimes = new Date().toLocaleString('zh-CN', { hour12: false });
|
||||
this.$ls.set('merchant_shopname', res.data.shopname);
|
||||
this.$ls.set('merchant_shopimg', res.data.shopimg);
|
||||
this.$ls.set('merchant_cellphone', res.data.cellphone);
|
||||
|
||||
this.link = `${location.origin}${location.pathname}#/Checkout?id=${res.data.userid}`
|
||||
}),
|
||||
this.getCount()
|
||||
]
|
||||
)
|
||||
},
|
||||
async onOpenBottom() {
|
||||
this.paycodeImg = ''
|
||||
this.$nextTick(async () => {
|
||||
const el = this.$refs.paycodeOriginal
|
||||
if (!el) return
|
||||
|
||||
const scale = 2
|
||||
const elRect = el.getBoundingClientRect()
|
||||
|
||||
// 不画全图,改为各部分分别截图再合成
|
||||
// 创建最终画布
|
||||
const canvas = document.createElement('canvas')
|
||||
const w = el.offsetWidth * scale
|
||||
const h = el.offsetHeight * scale
|
||||
canvas.width = w
|
||||
canvas.height = h
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
// 画背景图(只截取背景img)
|
||||
const bg = el.querySelector('img')
|
||||
const bgCanvas = await html2canvas(bg, { scale, useCORS: true, width: el.offsetWidth, height: el.offsetHeight })
|
||||
ctx.drawImage(bgCanvas, 0, 0, w, h)
|
||||
|
||||
// 截取头像并画成圆形
|
||||
const infoEl = el.querySelector('.info')
|
||||
const avatarEl = infoEl.querySelector('img')
|
||||
const avatarCanvas = await html2canvas(avatarEl, { scale, useCORS: true, backgroundColor: 'transparent' })
|
||||
|
||||
const infoRect = infoEl.getBoundingClientRect()
|
||||
const avatarSize = 30 * scale
|
||||
const avatarX = (infoRect.left - elRect.left) * scale
|
||||
const avatarY = (infoRect.top - elRect.top) * scale
|
||||
|
||||
ctx.save()
|
||||
ctx.beginPath()
|
||||
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2)
|
||||
ctx.clip()
|
||||
ctx.drawImage(avatarCanvas, avatarX, avatarY, avatarSize, avatarSize)
|
||||
ctx.restore()
|
||||
|
||||
// 截取二维码
|
||||
const codeEl = el.querySelector('.code')
|
||||
const codeCanvas = await html2canvas(codeEl, { scale, useCORS: true, backgroundColor: '#ffffff' })
|
||||
const codeRect = codeEl.getBoundingClientRect()
|
||||
ctx.drawImage(
|
||||
codeCanvas,
|
||||
(codeRect.left - elRect.left) * scale,
|
||||
(codeRect.top - elRect.top) * scale,
|
||||
codeRect.width * scale,
|
||||
codeRect.height * scale
|
||||
)
|
||||
|
||||
// 画商户名文字 (根据.name样式定位)
|
||||
const nameEl = el.querySelector('.name')
|
||||
const nameRect = nameEl.getBoundingClientRect()
|
||||
const fontSize = 20 * scale
|
||||
ctx.font = `${fontSize}px 'PingFang SC'`
|
||||
ctx.fillStyle = '#333'
|
||||
ctx.textBaseline = 'middle'
|
||||
if (this.data.shopname)
|
||||
ctx.fillText(this.truncatedShopname, (nameRect.left - elRect.left) * scale, (nameRect.top - elRect.top) * scale + fontSize / 2)
|
||||
|
||||
this.paycodeImg = canvas.toDataURL('image/png')
|
||||
})
|
||||
},
|
||||
onCloseBottom() {
|
||||
this.showBottom = false
|
||||
// this.paycodeImg = ''
|
||||
])
|
||||
},
|
||||
getCount() {
|
||||
this.$get('/v1/client/DShopsClient/statistics').then(res => {
|
||||
@ -320,51 +204,4 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.paycode-original {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
font-family: 'PingFang SC';
|
||||
|
||||
>img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
left: 30.67vw;
|
||||
top: 32.27vw;
|
||||
|
||||
img {
|
||||
width: 6.4vw;
|
||||
height: 6.4vw;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
position: absolute;
|
||||
left: 39.33vw;
|
||||
top: 33.47vw;
|
||||
|
||||
span {
|
||||
font-size: 4vw;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.code {
|
||||
position: absolute;
|
||||
top: 42.4vw;
|
||||
left: 23.33vw;
|
||||
|
||||
canvas,
|
||||
img {
|
||||
width: 53.33vw;
|
||||
height: 53.33vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
196
src/views/Merchant/PayCode.vue
Normal file
196
src/views/Merchant/PayCode.vue
Normal file
@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<BasePage>
|
||||
<div class="paycode-page">
|
||||
<div class="paycode-wrap">
|
||||
<!-- 生成后的图片 -->
|
||||
<div v-if="generatedImage" class="paycode-generated">
|
||||
<img :src="generatedImage" alt="收款码">
|
||||
</div>
|
||||
<!-- 原始元素 -->
|
||||
<div v-else class="paycode-original" ref="paycodeOriginalRef">
|
||||
<img src="/img/paycode.jpg" alt="" style="width: 100%; display: block;" class="bg-img" @load="onBgLoad">
|
||||
<div class="user-info">
|
||||
<div class="info">
|
||||
<img :src="$file(shopimg)" alt="" style="border-radius: 50%;">
|
||||
</div>
|
||||
<div class="name">
|
||||
<span>{{ shopname?.length > 8 ? shopname.slice(0, 8) + '...' : shopname }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code">
|
||||
<vue-qr v-if="link" :margin="0" :text="link" backgroundColor="rgb(255,255,255)"
|
||||
colorLight="rgb(255,255,255)"></vue-qr>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="save-btn">
|
||||
<van-button type="primary" color="#ca2904" round block :loading="loading" @click="handleSave">{{ loading ?
|
||||
'生成中...' : ('保存图片')
|
||||
}}</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toDataURL } from '@/utils/html2image'
|
||||
|
||||
export default {
|
||||
name: 'PayCode',
|
||||
data() {
|
||||
return {
|
||||
shopname: '',
|
||||
shopimg: '',
|
||||
link: '',
|
||||
loading: false,
|
||||
generatedImage: '',
|
||||
bgLoaded: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const id = this.$route.query.id
|
||||
this.$get(`/v1/client/DShopsClient/${id}`).then(res => {
|
||||
this.shopname = res.data.shopname
|
||||
this.shopimg = res.data.shopimg
|
||||
this.link = `${location.origin}${location.pathname}#/Checkout?id=${res.data.userid}`
|
||||
}).catch(err => {
|
||||
this.$showFailToast('加载失败')
|
||||
})
|
||||
},
|
||||
|
||||
onBgLoad() {
|
||||
this.bgLoaded = true
|
||||
// 背景图加载完成后自动生成图片
|
||||
this.generateImage()
|
||||
},
|
||||
|
||||
async generateImage() {
|
||||
if (!this.bgLoaded || this.loading) return
|
||||
this.loading = true
|
||||
try {
|
||||
await this.$nextTick()
|
||||
await document.fonts.ready
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
const el = this.$refs.paycodeOriginalRef
|
||||
this.generatedImage = await toDataURL(el, { format: 'png', pixelRatio: 3, useCORS: true })
|
||||
} catch (e) {
|
||||
this.$showFailToast('生成失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async handleSave() {
|
||||
if (this.loading) return
|
||||
// 如果已生成图片,直接下载
|
||||
if (this.generatedImage) {
|
||||
this.downloadByBlob(this.generatedImage, `收款码_${this.shopname}.png`)
|
||||
this.$showSuccessToast('保存成功')
|
||||
return
|
||||
}
|
||||
// 否则生成图片
|
||||
await this.generateImage()
|
||||
if (this.generatedImage) {
|
||||
this.downloadByBlob(this.generatedImage, `收款码_${this.shopname}.png`)
|
||||
this.$showSuccessToast('保存成功')
|
||||
}
|
||||
},
|
||||
|
||||
downloadByBlob(dataUrl, filename) {
|
||||
const arr = dataUrl.split(',')
|
||||
const mime = arr[0].match(/:(.*?);/)[1]
|
||||
const bstr = atob(arr[1])
|
||||
let n = bstr.length
|
||||
const u8arr = new Uint8Array(n)
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n)
|
||||
}
|
||||
const blob = new Blob([u8arr], { type: mime })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.download = filename
|
||||
link.href = url
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.paycode-page {
|
||||
// min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.paycode-generated {
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.paycode-original {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
font-family: 'PingFang SC';
|
||||
|
||||
>img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: 32.27vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 6.4vw;
|
||||
height: 6.4vw;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-left: 2.67vw;
|
||||
// margin-bottom: 2.4vw;
|
||||
|
||||
span {
|
||||
font-size: 4vw;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.code {
|
||||
position: absolute;
|
||||
top: 42.4vw;
|
||||
left: 23.33vw;
|
||||
|
||||
canvas,
|
||||
img {
|
||||
width: 53.33vw;
|
||||
height: 53.33vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
margin: 20px;
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BasePage>
|
||||
<!-- <BasePage> -->
|
||||
<ManagerPopup v-model="managerPopupVisible" />
|
||||
<div class="opera">
|
||||
|
||||
@ -84,7 +84,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasePage>
|
||||
<!-- </BasePage> -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<img src="/img/search.png" alt="">
|
||||
<input v-model="searchParams.Key" placeholder="请输入关键词搜索"
|
||||
@search="$navigate('Category?Key=' + searchParams.Key)">
|
||||
<button @click="$navigate('Category?Key=' + searchParams.Key)">搜索</button>
|
||||
<button @click="$navigate('Category?Key=' + searchParams.Key)" :style="btnStyle">搜索</button>
|
||||
</div>
|
||||
<div class="banner">
|
||||
<van-swipe :autoplay="3000" indicator-color="white" @change="onSwipeChange">
|
||||
@ -288,6 +288,14 @@ export default {
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
btnStyle() {
|
||||
if (this.bgcolor) {
|
||||
return {
|
||||
background: this.bgcolor
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -27,8 +27,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { html2canvas, canvasToDataURL } from '@/utils/html2image';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { toDataURL } from '@/utils/html2image';
|
||||
|
||||
export default {
|
||||
name: 'Invite',
|
||||
@ -41,7 +40,7 @@ export default {
|
||||
NickName: '',
|
||||
},
|
||||
link: '',
|
||||
userStore: useUserStore(),
|
||||
userStore: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -77,8 +76,6 @@ export default {
|
||||
},
|
||||
async CreatePoster(divID, targetID) {
|
||||
var shareContent = document.querySelector(divID);
|
||||
var width = shareContent.offsetWidth;
|
||||
var height = shareContent.offsetHeight;
|
||||
|
||||
try {
|
||||
// 等待头像图片加载完成
|
||||
@ -91,18 +88,11 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
var canvas = await html2canvas(shareContent, {
|
||||
scale: 2,
|
||||
width: width,
|
||||
height: height,
|
||||
useCORS: true,
|
||||
allowTaint: false,
|
||||
});
|
||||
var dataUrl = canvasToDataURL(canvas, "image/png", 1.0);
|
||||
var dataUrl = await toDataURL(shareContent, { format: 'png', pixelRatio: 2, useCORS: true });
|
||||
var newImg = document.createElement("img");
|
||||
newImg.src = dataUrl;
|
||||
newImg.width = width;
|
||||
newImg.height = height;
|
||||
newImg.width = shareContent.offsetWidth;
|
||||
newImg.height = shareContent.offsetHeight;
|
||||
document.querySelector(targetID).appendChild(newImg);
|
||||
} catch (err) {
|
||||
console.error('生成海报失败', err);
|
||||
@ -128,7 +118,7 @@ export default {
|
||||
.name {
|
||||
position: absolute;
|
||||
left: 20.27vw;
|
||||
bottom: 25.6vw;
|
||||
bottom: 23.6vw;
|
||||
line-height: 6vw;
|
||||
width: 100%;
|
||||
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
<div class="verif">
|
||||
<div class="search_box">
|
||||
<img src="/img/search.png" alt="">
|
||||
<input type="text" placeholder="输入用户账号/礼品券码搜索" v-model="keyword1">
|
||||
<input type="text" placeholder="输入用户账号/礼品券码搜索" v-model="keyword1" v-if="type === 'verif'">
|
||||
<input type="text" placeholder="输入用户账号/会员卡号搜索" v-model="keyword1" v-else>
|
||||
</div>
|
||||
|
||||
<BaseList ref="listRef"
|
||||
@ -17,9 +18,12 @@
|
||||
<span>{{ item.nickname || item.username || '未知用户' }}</span>
|
||||
<p>账号:{{ item.cellphone || item.username }}</p>
|
||||
</div>
|
||||
<span class="r tt">
|
||||
<span class="r tt" v-if="type === 'verif'">
|
||||
已核销
|
||||
</span>
|
||||
<span class="r pre" v-else>
|
||||
增送额度:<b style="color: f00;">500.00</b>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info_box">
|
||||
<div>
|
||||
@ -28,17 +32,27 @@
|
||||
src="/img/copy_b.png" alt=""></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div v-if="type === 'verif'">
|
||||
<span class="left">礼品券码:</span>
|
||||
<span class="r rg">{{ item.sysid }}<img @click="$copyText(item.sysid); $showSuccessToast('复制成功')"
|
||||
src="/img/copy_b.png" alt=""></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div v-else>
|
||||
<span class="left">会员卡号:</span>
|
||||
<span class="r rg">{{ item.huiyuankaid }}<img
|
||||
@click="$copyText(item.huiyuankaid); $showSuccessToast('复制成功')" src="/img/copy_b.png" alt=""></span>
|
||||
</div>
|
||||
|
||||
<div v-if="type === 'verif'">
|
||||
<span class="left">核销时间:</span>
|
||||
<span class="r rg">{{ formatTime(item.writeofftime) }}</span>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<span class="left">赠送时间:</span>
|
||||
<span class="r rg">{{ formatTime(item.addtime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user