diff --git a/App.vue b/App.vue
index ed1993c..4a362c5 100644
--- a/App.vue
+++ b/App.vue
@@ -25,6 +25,16 @@ export default {
});
let uuid = plus.device.uuid;
// #endif
+ // #ifdef H5
+ if (typeof window !== "undefined" && window.location) {
+ const pathname = window.location.pathname || "";
+ if (pathname && pathname !== "/" && pathname.includes("pages/")) {
+ const cleanPath = pathname.startsWith("/") ? pathname.slice(1) : pathname;
+ const search = window.location.search || "";
+ window.location.replace(`${window.location.origin}/#/${cleanPath}${search}`);
+ }
+ }
+ // #endif
},
onShow: function () {
console.log("App Show");
diff --git a/api/cart.js b/api/cart.js
index 67de22f..b9013a7 100644
--- a/api/cart.js
+++ b/api/cart.js
@@ -42,23 +42,23 @@ export function updateGoodsNum(data) {
// 查询结算列表
-export function getSettleList(data) {
+export function getSettleList(data) {
// return request.post('/common/getSmsCode',qs.stringify(params));
return request({
url: "/cart/getSettleList",
method: "post",
data,
});
-}
-
-// 立即购买预结算(含优惠券可用性和抵扣后金额)
-export function getDirectSettleList(data) {
- return request({
- url: "/cart/getDirectSettleList",
- method: "post",
- data,
- });
-}
+}
+
+// 立即购买预结算(含优惠券可用性和抵扣后金额)
+export function getDirectSettleList(data) {
+ return request({
+ url: "/cart/getDirectSettleList",
+ method: "post",
+ data,
+ });
+}
// 结算下单
export function settle(data) {
@@ -82,13 +82,20 @@ export function setSeckillOrder(data) {
url: "/seckill/order",
method: "post",
data,
- // cha:1
- // headers: {
- // "Content-Type": "application/x-www-form-urlencoded"
- // }
});
}
+// 秒杀下单
+export function setGroupBuyOrder(data) {
+ return request({
+ url: "/groupBuy/order/0910",
+ method: "post",
+ data,
+ });
+}
+
+setGroupBuyOrder
+
// 查询运费
export function getPostageQuery(data) {
diff --git a/api/product.js b/api/product.js
index 0f3e70b..73448d3 100644
--- a/api/product.js
+++ b/api/product.js
@@ -145,3 +145,42 @@ export function getSearchGoodsList(params) {
});
}
+// 拼团商品列表
+export function getGroupBuyList(params) {
+ return request({
+ url: "/groupBuy/getList/0910",
+ method: "get",
+ data: params,
+ });
+}
+
+// 可直接参加的拼团列表
+export function getGroupBuyJoinableList(params) {
+ return request({
+ url: "/groupBuy/getJoinableList/0910",
+ method: "get",
+ data: params,
+ });
+}
+
+// 当前拼团活动信息 (倒计时、startTime, endTime等)
+export function getGroupBuyCurrent(params) {
+ return request({
+ url: "/groupBuy/current/0910",
+ method: "get",
+ data: params,
+ });
+}
+
+// 获取拼团详情
+export function getTeamDetail(params) {
+ return request({
+ url: "/groupBuy/getTeamDetail/0910",
+ method: "get",
+ data: params,
+ });
+}
+
+
+
+
diff --git a/components/common/product-group-card.vue b/components/common/product-group-card.vue
new file mode 100644
index 0000000..dc317a3
--- /dev/null
+++ b/components/common/product-group-card.vue
@@ -0,0 +1,344 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.goodsName || item.title || item.name || '商品名称' }}
+
+
+
+
+
+ {{ item.groupSize || item.groupNum || item.groupCount ||
+ item.groupPersonCount || 2 }}人团
+ |
+ 已拼{{ item.soldNum !== undefined ? item.soldNum : (item.groupSales ||
+ item.groupSalesCount || item.sales || item.salesVolume || 12) }}份
+
+
+
+ {{ countdownDisplay }}
+
+
+
+
+
+
+ ⚡
+ 立省¥{{ discountAmount }}
+
+
+
+
+
+
+ ¥
+ {{ item.groupPrice !== undefined ? item.groupPrice : (item.price ||
+ '88.88') }}
+
+ ¥{{ originalPriceDisplay }}
+
+
+
+
+
+ 去拼团
+
+
+
+
+
+
+
+
+
+
diff --git a/components/common/product-seckill-card.vue b/components/common/product-seckill-card.vue
index a1be351..fdf6bdf 100644
--- a/components/common/product-seckill-card.vue
+++ b/components/common/product-seckill-card.vue
@@ -32,6 +32,10 @@
⚡
加倍补{{ item.subsidy }}积分
+
+ ⚡
+ 立省{{ item.secDiscount || item.subsidy || item.discount || 100 }}元
+
@@ -121,6 +125,8 @@ export default {
let url = "/pages/other_package/productInfo/productInfo?id=" + item.id;
if (this.activedType === 'seckill') {
url = "/pages/other_package/productInfo/productInfo?id=" + item.id + "&activedType=" + this.activedType + "&activityId=" + item.activityId;
+ } else if (this.activedType === 'group') {
+ url = "/pages/other_package/productInfo/productInfo?id=" + item.id + "&activedType=" + this.activedType;
}
uni.navigateTo({
url: url,
diff --git a/components/header.vue b/components/header.vue
index dc1c2e3..96c3c1a 100644
--- a/components/header.vue
+++ b/components/header.vue
@@ -48,11 +48,33 @@
const leftPath = this.leftPath;
const leftPathType = this.leftPathType;
if(leftPathType === "navigateTo"){
- uni.navigateBack();
+ const pages = getCurrentPages();
+ if (pages && pages.length > 1) {
+ const prevPage = pages[pages.length - 2];
+ const route = prevPage ? (prevPage.route || (prevPage.$page && prevPage.$page.route)) : '';
+ if (!leftPath || (route && leftPath.includes(route))) {
+ uni.navigateBack();
+ return;
+ }
+ }
+ if (leftPath) {
+ uni.redirectTo({
+ url: leftPath,
+ fail: () => {
+ uni.navigateTo({ url: leftPath });
+ }
+ });
+ } else {
+ uni.navigateBack();
+ }
}else if(leftPathType === "switchTab" && leftPath){
uni.switchTab({
url: leftPath,
});
+ }else if(leftPathType === "redirectTo" && leftPath){
+ uni.redirectTo({
+ url: leftPath,
+ });
}
},
rightJump() {
diff --git a/pages.json b/pages.json
index d0e56ed..5323d4d 100644
--- a/pages.json
+++ b/pages.json
@@ -698,6 +698,18 @@
"style": {
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "group-buying/group-buying",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "group-detail/group-detail",
+ "style": {
+ "navigationStyle": "custom"
+ }
}
]
}
diff --git a/pages/active/components/share-popup.vue b/pages/active/components/share-popup.vue
new file mode 100644
index 0000000..d6f98ac
--- /dev/null
+++ b/pages/active/components/share-popup.vue
@@ -0,0 +1,1092 @@
+
+
+
+
+
+
+
+
+
diff --git a/pages/active/components/tki-qrcode/qrcode.js b/pages/active/components/tki-qrcode/qrcode.js
new file mode 100644
index 0000000..6ea6553
--- /dev/null
+++ b/pages/active/components/tki-qrcode/qrcode.js
@@ -0,0 +1,1201 @@
+let QRCode = {};
+(function () {
+ /**
+ * 获取单个字符的utf8编码
+ * unicode BMP平面约65535个字符
+ * @param {num} code
+ * return {array}
+ */
+ function unicodeFormat8(code) {
+ // 1 byte
+ var c0, c1, c2;
+ if (code < 128) {
+ return [code];
+ // 2 bytes
+ } else if (code < 2048) {
+ c0 = 192 + (code >> 6);
+ c1 = 128 + (code & 63);
+ return [c0, c1];
+ // 3 bytes
+ } else {
+ c0 = 224 + (code >> 12);
+ c1 = 128 + (code >> 6 & 63);
+ c2 = 128 + (code & 63);
+ return [c0, c1, c2];
+ }
+ }
+ /**
+ * 获取字符串的utf8编码字节串
+ * @param {string} string
+ * @return {array}
+ */
+ function getUTF8Bytes(string) {
+ var utf8codes = [];
+ for (var i = 0; i < string.length; i++) {
+ var code = string.charCodeAt(i);
+ var utf8 = unicodeFormat8(code);
+ for (var j = 0; j < utf8.length; j++) {
+ utf8codes.push(utf8[j]);
+ }
+ }
+ return utf8codes;
+ }
+ /**
+ * 二维码算法实现
+ * @param {string} data 要编码的信息字符串
+ * @param {num} errorCorrectLevel 纠错等级
+ */
+ function QRCodeAlg(data, errorCorrectLevel) {
+ this.typeNumber = -1; //版本
+ this.errorCorrectLevel = errorCorrectLevel;
+ this.modules = null; //二维矩阵,存放最终结果
+ this.moduleCount = 0; //矩阵大小
+ this.dataCache = null; //数据缓存
+ this.rsBlocks = null; //版本数据信息
+ this.totalDataCount = -1; //可使用的数据量
+ this.data = data;
+ this.utf8bytes = getUTF8Bytes(data);
+ this.make();
+ }
+ QRCodeAlg.prototype = {
+ constructor: QRCodeAlg,
+ /**
+ * 获取二维码矩阵大小
+ * @return {num} 矩阵大小
+ */
+ getModuleCount: function () {
+ return this.moduleCount;
+ },
+ /**
+ * 编码
+ */
+ make: function () {
+ this.getRightType();
+ this.dataCache = this.createData();
+ this.createQrcode();
+ },
+ /**
+ * 设置二位矩阵功能图形
+ * @param {bool} test 表示是否在寻找最好掩膜阶段
+ * @param {num} maskPattern 掩膜的版本
+ */
+ makeImpl: function (maskPattern) {
+ this.moduleCount = this.typeNumber * 4 + 17;
+ this.modules = new Array(this.moduleCount);
+ for (var row = 0; row < this.moduleCount; row++) {
+ this.modules[row] = new Array(this.moduleCount);
+ }
+ this.setupPositionProbePattern(0, 0);
+ this.setupPositionProbePattern(this.moduleCount - 7, 0);
+ this.setupPositionProbePattern(0, this.moduleCount - 7);
+ this.setupPositionAdjustPattern();
+ this.setupTimingPattern();
+ this.setupTypeInfo(true, maskPattern);
+ if (this.typeNumber >= 7) {
+ this.setupTypeNumber(true);
+ }
+ this.mapData(this.dataCache, maskPattern);
+ },
+ /**
+ * 设置二维码的位置探测图形
+ * @param {num} row 探测图形的中心横坐标
+ * @param {num} col 探测图形的中心纵坐标
+ */
+ setupPositionProbePattern: function (row, col) {
+ for (var r = -1; r <= 7; r++) {
+ if (row + r <= -1 || this.moduleCount <= row + r) continue;
+ for (var c = -1; c <= 7; c++) {
+ if (col + c <= -1 || this.moduleCount <= col + c) continue;
+ if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ },
+ /**
+ * 创建二维码
+ * @return {[type]} [description]
+ */
+ createQrcode: function () {
+ var minLostPoint = 0;
+ var pattern = 0;
+ var bestModules = null;
+ for (var i = 0; i < 8; i++) {
+ this.makeImpl(i);
+ var lostPoint = QRUtil.getLostPoint(this);
+ if (i == 0 || minLostPoint > lostPoint) {
+ minLostPoint = lostPoint;
+ pattern = i;
+ bestModules = this.modules;
+ }
+ }
+ this.modules = bestModules;
+ this.setupTypeInfo(false, pattern);
+ if (this.typeNumber >= 7) {
+ this.setupTypeNumber(false);
+ }
+ },
+ /**
+ * 设置定位图形
+ * @return {[type]} [description]
+ */
+ setupTimingPattern: function () {
+ for (var r = 8; r < this.moduleCount - 8; r++) {
+ if (this.modules[r][6] != null) {
+ continue;
+ }
+ this.modules[r][6] = (r % 2 == 0);
+ if (this.modules[6][r] != null) {
+ continue;
+ }
+ this.modules[6][r] = (r % 2 == 0);
+ }
+ },
+ /**
+ * 设置矫正图形
+ * @return {[type]} [description]
+ */
+ setupPositionAdjustPattern: function () {
+ var pos = QRUtil.getPatternPosition(this.typeNumber);
+ for (var i = 0; i < pos.length; i++) {
+ for (var j = 0; j < pos.length; j++) {
+ var row = pos[i];
+ var col = pos[j];
+ if (this.modules[row][col] != null) {
+ continue;
+ }
+ for (var r = -2; r <= 2; r++) {
+ for (var c = -2; c <= 2; c++) {
+ if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ }
+ }
+ },
+ /**
+ * 设置版本信息(7以上版本才有)
+ * @param {bool} test 是否处于判断最佳掩膜阶段
+ * @return {[type]} [description]
+ */
+ setupTypeNumber: function (test) {
+ var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+ for (var i = 0; i < 18; i++) {
+ var mod = (!test && ((bits >> i) & 1) == 1);
+ this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
+ this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+ }
+ },
+ /**
+ * 设置格式信息(纠错等级和掩膜版本)
+ * @param {bool} test
+ * @param {num} maskPattern 掩膜版本
+ * @return {}
+ */
+ setupTypeInfo: function (test, maskPattern) {
+ var data = (QRErrorCorrectLevel[this.errorCorrectLevel] << 3) | maskPattern;
+ var bits = QRUtil.getBCHTypeInfo(data);
+ // vertical
+ for (var i = 0; i < 15; i++) {
+ var mod = (!test && ((bits >> i) & 1) == 1);
+ if (i < 6) {
+ this.modules[i][8] = mod;
+ } else if (i < 8) {
+ this.modules[i + 1][8] = mod;
+ } else {
+ this.modules[this.moduleCount - 15 + i][8] = mod;
+ }
+ // horizontal
+ var mod = (!test && ((bits >> i) & 1) == 1);
+ if (i < 8) {
+ this.modules[8][this.moduleCount - i - 1] = mod;
+ } else if (i < 9) {
+ this.modules[8][15 - i - 1 + 1] = mod;
+ } else {
+ this.modules[8][15 - i - 1] = mod;
+ }
+ }
+ // fixed module
+ this.modules[this.moduleCount - 8][8] = (!test);
+ },
+ /**
+ * 数据编码
+ * @return {[type]} [description]
+ */
+ createData: function () {
+ var buffer = new QRBitBuffer();
+ var lengthBits = this.typeNumber > 9 ? 16 : 8;
+ buffer.put(4, 4); //添加模式
+ buffer.put(this.utf8bytes.length, lengthBits);
+ for (var i = 0, l = this.utf8bytes.length; i < l; i++) {
+ buffer.put(this.utf8bytes[i], 8);
+ }
+ if (buffer.length + 4 <= this.totalDataCount * 8) {
+ buffer.put(0, 4);
+ }
+ // padding
+ while (buffer.length % 8 != 0) {
+ buffer.putBit(false);
+ }
+ // padding
+ while (true) {
+ if (buffer.length >= this.totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCodeAlg.PAD0, 8);
+ if (buffer.length >= this.totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCodeAlg.PAD1, 8);
+ }
+ return this.createBytes(buffer);
+ },
+ /**
+ * 纠错码编码
+ * @param {buffer} buffer 数据编码
+ * @return {[type]}
+ */
+ createBytes: function (buffer) {
+ var offset = 0;
+ var maxDcCount = 0;
+ var maxEcCount = 0;
+ var length = this.rsBlock.length / 3;
+ var rsBlocks = new Array();
+ for (var i = 0; i < length; i++) {
+ var count = this.rsBlock[i * 3 + 0];
+ var totalCount = this.rsBlock[i * 3 + 1];
+ var dataCount = this.rsBlock[i * 3 + 2];
+ for (var j = 0; j < count; j++) {
+ rsBlocks.push([dataCount, totalCount]);
+ }
+ }
+ var dcdata = new Array(rsBlocks.length);
+ var ecdata = new Array(rsBlocks.length);
+ for (var r = 0; r < rsBlocks.length; r++) {
+ var dcCount = rsBlocks[r][0];
+ var ecCount = rsBlocks[r][1] - dcCount;
+ maxDcCount = Math.max(maxDcCount, dcCount);
+ maxEcCount = Math.max(maxEcCount, ecCount);
+ dcdata[r] = new Array(dcCount);
+ for (var i = 0; i < dcdata[r].length; i++) {
+ dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+ }
+ offset += dcCount;
+ var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+ var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+ var modPoly = rawPoly.mod(rsPoly);
+ ecdata[r] = new Array(rsPoly.getLength() - 1);
+ for (var i = 0; i < ecdata[r].length; i++) {
+ var modIndex = i + modPoly.getLength() - ecdata[r].length;
+ ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
+ }
+ }
+ var data = new Array(this.totalDataCount);
+ var index = 0;
+ for (var i = 0; i < maxDcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < dcdata[r].length) {
+ data[index++] = dcdata[r][i];
+ }
+ }
+ }
+ for (var i = 0; i < maxEcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < ecdata[r].length) {
+ data[index++] = ecdata[r][i];
+ }
+ }
+ }
+ return data;
+
+ },
+ /**
+ * 布置模块,构建最终信息
+ * @param {} data
+ * @param {} maskPattern
+ * @return {}
+ */
+ mapData: function (data, maskPattern) {
+ var inc = -1;
+ var row = this.moduleCount - 1;
+ var bitIndex = 7;
+ var byteIndex = 0;
+ for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+ if (col == 6) col--;
+ while (true) {
+ for (var c = 0; c < 2; c++) {
+ if (this.modules[row][col - c] == null) {
+ var dark = false;
+ if (byteIndex < data.length) {
+ dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
+ }
+ var mask = QRUtil.getMask(maskPattern, row, col - c);
+ if (mask) {
+ dark = !dark;
+ }
+ this.modules[row][col - c] = dark;
+ bitIndex--;
+ if (bitIndex == -1) {
+ byteIndex++;
+ bitIndex = 7;
+ }
+ }
+ }
+ row += inc;
+ if (row < 0 || this.moduleCount <= row) {
+ row -= inc;
+ inc = -inc;
+ break;
+ }
+ }
+ }
+ }
+ };
+ /**
+ * 填充字段
+ */
+ QRCodeAlg.PAD0 = 0xEC;
+ QRCodeAlg.PAD1 = 0x11;
+ //---------------------------------------------------------------------
+ // 纠错等级对应的编码
+ //---------------------------------------------------------------------
+ var QRErrorCorrectLevel = [1, 0, 3, 2];
+ //---------------------------------------------------------------------
+ // 掩膜版本
+ //---------------------------------------------------------------------
+ var QRMaskPattern = {
+ PATTERN000: 0,
+ PATTERN001: 1,
+ PATTERN010: 2,
+ PATTERN011: 3,
+ PATTERN100: 4,
+ PATTERN101: 5,
+ PATTERN110: 6,
+ PATTERN111: 7
+ };
+ //---------------------------------------------------------------------
+ // 工具类
+ //---------------------------------------------------------------------
+ var QRUtil = {
+ /*
+ 每个版本矫正图形的位置
+ */
+ PATTERN_POSITION_TABLE: [
+ [],
+ [6, 18],
+ [6, 22],
+ [6, 26],
+ [6, 30],
+ [6, 34],
+ [6, 22, 38],
+ [6, 24, 42],
+ [6, 26, 46],
+ [6, 28, 50],
+ [6, 30, 54],
+ [6, 32, 58],
+ [6, 34, 62],
+ [6, 26, 46, 66],
+ [6, 26, 48, 70],
+ [6, 26, 50, 74],
+ [6, 30, 54, 78],
+ [6, 30, 56, 82],
+ [6, 30, 58, 86],
+ [6, 34, 62, 90],
+ [6, 28, 50, 72, 94],
+ [6, 26, 50, 74, 98],
+ [6, 30, 54, 78, 102],
+ [6, 28, 54, 80, 106],
+ [6, 32, 58, 84, 110],
+ [6, 30, 58, 86, 114],
+ [6, 34, 62, 90, 118],
+ [6, 26, 50, 74, 98, 122],
+ [6, 30, 54, 78, 102, 126],
+ [6, 26, 52, 78, 104, 130],
+ [6, 30, 56, 82, 108, 134],
+ [6, 34, 60, 86, 112, 138],
+ [6, 30, 58, 86, 114, 142],
+ [6, 34, 62, 90, 118, 146],
+ [6, 30, 54, 78, 102, 126, 150],
+ [6, 24, 50, 76, 102, 128, 154],
+ [6, 28, 54, 80, 106, 132, 158],
+ [6, 32, 58, 84, 110, 136, 162],
+ [6, 26, 54, 82, 110, 138, 166],
+ [6, 30, 58, 86, 114, 142, 170]
+ ],
+ G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
+ G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
+ G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+ /*
+ BCH编码格式信息
+ */
+ getBCHTypeInfo: function (data) {
+ var d = data << 10;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+ d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15)));
+ }
+ return ((data << 10) | d) ^ QRUtil.G15_MASK;
+ },
+ /*
+ BCH编码版本信息
+ */
+ getBCHTypeNumber: function (data) {
+ var d = data << 12;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+ d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18)));
+ }
+ return (data << 12) | d;
+ },
+ /*
+ 获取BCH位信息
+ */
+ getBCHDigit: function (data) {
+ var digit = 0;
+ while (data != 0) {
+ digit++;
+ data >>>= 1;
+ }
+ return digit;
+ },
+ /*
+ 获取版本对应的矫正图形位置
+ */
+ getPatternPosition: function (typeNumber) {
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+ },
+ /*
+ 掩膜算法
+ */
+ getMask: function (maskPattern, i, j) {
+ switch (maskPattern) {
+ case QRMaskPattern.PATTERN000:
+ return (i + j) % 2 == 0;
+ case QRMaskPattern.PATTERN001:
+ return i % 2 == 0;
+ case QRMaskPattern.PATTERN010:
+ return j % 3 == 0;
+ case QRMaskPattern.PATTERN011:
+ return (i + j) % 3 == 0;
+ case QRMaskPattern.PATTERN100:
+ return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
+ case QRMaskPattern.PATTERN101:
+ return (i * j) % 2 + (i * j) % 3 == 0;
+ case QRMaskPattern.PATTERN110:
+ return ((i * j) % 2 + (i * j) % 3) % 2 == 0;
+ case QRMaskPattern.PATTERN111:
+ return ((i * j) % 3 + (i + j) % 2) % 2 == 0;
+ default:
+ throw new Error("bad maskPattern:" + maskPattern);
+ }
+ },
+ /*
+ 获取RS的纠错多项式
+ */
+ getErrorCorrectPolynomial: function (errorCorrectLength) {
+ var a = new QRPolynomial([1], 0);
+ for (var i = 0; i < errorCorrectLength; i++) {
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
+ }
+ return a;
+ },
+ /*
+ 获取评价
+ */
+ getLostPoint: function (qrCode) {
+ var moduleCount = qrCode.getModuleCount(),
+ lostPoint = 0,
+ darkCount = 0;
+ for (var row = 0; row < moduleCount; row++) {
+ var sameCount = 0;
+ var head = qrCode.modules[row][0];
+ for (var col = 0; col < moduleCount; col++) {
+ var current = qrCode.modules[row][col];
+ //level 3 评价
+ if (col < moduleCount - 6) {
+ if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) {
+ if (col < moduleCount - 10) {
+ if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) {
+ lostPoint += 40;
+ }
+ } else if (col > 3) {
+ if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) {
+ lostPoint += 40;
+ }
+ }
+ }
+ }
+ //level 2 评价
+ if ((row < moduleCount - 1) && (col < moduleCount - 1)) {
+ var count = 0;
+ if (current) count++;
+ if (qrCode.modules[row + 1][col]) count++;
+ if (qrCode.modules[row][col + 1]) count++;
+ if (qrCode.modules[row + 1][col + 1]) count++;
+ if (count == 0 || count == 4) {
+ lostPoint += 3;
+ }
+ }
+ //level 1 评价
+ if (head ^ current) {
+ sameCount++;
+ } else {
+ head = current;
+ if (sameCount >= 5) {
+ lostPoint += (3 + sameCount - 5);
+ }
+ sameCount = 1;
+ }
+ //level 4 评价
+ if (current) {
+ darkCount++;
+ }
+ }
+ }
+ for (var col = 0; col < moduleCount; col++) {
+ var sameCount = 0;
+ var head = qrCode.modules[0][col];
+ for (var row = 0; row < moduleCount; row++) {
+ var current = qrCode.modules[row][col];
+ //level 3 评价
+ if (row < moduleCount - 6) {
+ if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) {
+ if (row < moduleCount - 10) {
+ if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) {
+ lostPoint += 40;
+ }
+ } else if (row > 3) {
+ if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) {
+ lostPoint += 40;
+ }
+ }
+ }
+ }
+ //level 1 评价
+ if (head ^ current) {
+ sameCount++;
+ } else {
+ head = current;
+ if (sameCount >= 5) {
+ lostPoint += (3 + sameCount - 5);
+ }
+ sameCount = 1;
+ }
+ }
+ }
+ // LEVEL4
+ var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+ lostPoint += ratio * 10;
+ return lostPoint;
+ }
+
+ };
+ //---------------------------------------------------------------------
+ // QRMath使用的数学工具
+ //---------------------------------------------------------------------
+ var QRMath = {
+ /*
+ 将n转化为a^m
+ */
+ glog: function (n) {
+ if (n < 1) {
+ throw new Error("glog(" + n + ")");
+ }
+ return QRMath.LOG_TABLE[n];
+ },
+ /*
+ 将a^m转化为n
+ */
+ gexp: function (n) {
+ while (n < 0) {
+ n += 255;
+ }
+ while (n >= 256) {
+ n -= 255;
+ }
+ return QRMath.EXP_TABLE[n];
+ },
+ EXP_TABLE: new Array(256),
+ LOG_TABLE: new Array(256)
+
+ };
+ for (var i = 0; i < 8; i++) {
+ QRMath.EXP_TABLE[i] = 1 << i;
+ }
+ for (var i = 8; i < 256; i++) {
+ QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
+ }
+ for (var i = 0; i < 255; i++) {
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
+ }
+ //---------------------------------------------------------------------
+ // QRPolynomial 多项式
+ //---------------------------------------------------------------------
+ /**
+ * 多项式类
+ * @param {Array} num 系数
+ * @param {num} shift a^shift
+ */
+ function QRPolynomial(num, shift) {
+ if (num.length == undefined) {
+ throw new Error(num.length + "/" + shift);
+ }
+ var offset = 0;
+ while (offset < num.length && num[offset] == 0) {
+ offset++;
+ }
+ this.num = new Array(num.length - offset + shift);
+ for (var i = 0; i < num.length - offset; i++) {
+ this.num[i] = num[i + offset];
+ }
+ }
+ QRPolynomial.prototype = {
+ get: function (index) {
+ return this.num[index];
+ },
+ getLength: function () {
+ return this.num.length;
+ },
+ /**
+ * 多项式乘法
+ * @param {QRPolynomial} e 被乘多项式
+ * @return {[type]} [description]
+ */
+ multiply: function (e) {
+ var num = new Array(this.getLength() + e.getLength() - 1);
+ for (var i = 0; i < this.getLength(); i++) {
+ for (var j = 0; j < e.getLength(); j++) {
+ num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
+ }
+ }
+ return new QRPolynomial(num, 0);
+ },
+ /**
+ * 多项式模运算
+ * @param {QRPolynomial} e 模多项式
+ * @return {}
+ */
+ mod: function (e) {
+ var tl = this.getLength(),
+ el = e.getLength();
+ if (tl - el < 0) {
+ return this;
+ }
+ var num = new Array(tl);
+ for (var i = 0; i < tl; i++) {
+ num[i] = this.get(i);
+ }
+ while (num.length >= el) {
+ var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0));
+
+ for (var i = 0; i < e.getLength(); i++) {
+ num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
+ }
+ while (num[0] == 0) {
+ num.shift();
+ }
+ }
+ return new QRPolynomial(num, 0);
+ }
+ };
+
+ //---------------------------------------------------------------------
+ // RS_BLOCK_TABLE
+ //---------------------------------------------------------------------
+ /*
+ 二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数]
+ */
+ var RS_BLOCK_TABLE = [
+ // L
+ // M
+ // Q
+ // H
+ // 1
+ [1, 26, 19],
+ [1, 26, 16],
+ [1, 26, 13],
+ [1, 26, 9],
+
+ // 2
+ [1, 44, 34],
+ [1, 44, 28],
+ [1, 44, 22],
+ [1, 44, 16],
+
+ // 3
+ [1, 70, 55],
+ [1, 70, 44],
+ [2, 35, 17],
+ [2, 35, 13],
+
+ // 4
+ [1, 100, 80],
+ [2, 50, 32],
+ [2, 50, 24],
+ [4, 25, 9],
+
+ // 5
+ [1, 134, 108],
+ [2, 67, 43],
+ [2, 33, 15, 2, 34, 16],
+ [2, 33, 11, 2, 34, 12],
+
+ // 6
+ [2, 86, 68],
+ [4, 43, 27],
+ [4, 43, 19],
+ [4, 43, 15],
+
+ // 7
+ [2, 98, 78],
+ [4, 49, 31],
+ [2, 32, 14, 4, 33, 15],
+ [4, 39, 13, 1, 40, 14],
+
+ // 8
+ [2, 121, 97],
+ [2, 60, 38, 2, 61, 39],
+ [4, 40, 18, 2, 41, 19],
+ [4, 40, 14, 2, 41, 15],
+
+ // 9
+ [2, 146, 116],
+ [3, 58, 36, 2, 59, 37],
+ [4, 36, 16, 4, 37, 17],
+ [4, 36, 12, 4, 37, 13],
+
+ // 10
+ [2, 86, 68, 2, 87, 69],
+ [4, 69, 43, 1, 70, 44],
+ [6, 43, 19, 2, 44, 20],
+ [6, 43, 15, 2, 44, 16],
+
+ // 11
+ [4, 101, 81],
+ [1, 80, 50, 4, 81, 51],
+ [4, 50, 22, 4, 51, 23],
+ [3, 36, 12, 8, 37, 13],
+
+ // 12
+ [2, 116, 92, 2, 117, 93],
+ [6, 58, 36, 2, 59, 37],
+ [4, 46, 20, 6, 47, 21],
+ [7, 42, 14, 4, 43, 15],
+
+ // 13
+ [4, 133, 107],
+ [8, 59, 37, 1, 60, 38],
+ [8, 44, 20, 4, 45, 21],
+ [12, 33, 11, 4, 34, 12],
+
+ // 14
+ [3, 145, 115, 1, 146, 116],
+ [4, 64, 40, 5, 65, 41],
+ [11, 36, 16, 5, 37, 17],
+ [11, 36, 12, 5, 37, 13],
+
+ // 15
+ [5, 109, 87, 1, 110, 88],
+ [5, 65, 41, 5, 66, 42],
+ [5, 54, 24, 7, 55, 25],
+ [11, 36, 12],
+
+ // 16
+ [5, 122, 98, 1, 123, 99],
+ [7, 73, 45, 3, 74, 46],
+ [15, 43, 19, 2, 44, 20],
+ [3, 45, 15, 13, 46, 16],
+
+ // 17
+ [1, 135, 107, 5, 136, 108],
+ [10, 74, 46, 1, 75, 47],
+ [1, 50, 22, 15, 51, 23],
+ [2, 42, 14, 17, 43, 15],
+
+ // 18
+ [5, 150, 120, 1, 151, 121],
+ [9, 69, 43, 4, 70, 44],
+ [17, 50, 22, 1, 51, 23],
+ [2, 42, 14, 19, 43, 15],
+
+ // 19
+ [3, 141, 113, 4, 142, 114],
+ [3, 70, 44, 11, 71, 45],
+ [17, 47, 21, 4, 48, 22],
+ [9, 39, 13, 16, 40, 14],
+
+ // 20
+ [3, 135, 107, 5, 136, 108],
+ [3, 67, 41, 13, 68, 42],
+ [15, 54, 24, 5, 55, 25],
+ [15, 43, 15, 10, 44, 16],
+
+ // 21
+ [4, 144, 116, 4, 145, 117],
+ [17, 68, 42],
+ [17, 50, 22, 6, 51, 23],
+ [19, 46, 16, 6, 47, 17],
+
+ // 22
+ [2, 139, 111, 7, 140, 112],
+ [17, 74, 46],
+ [7, 54, 24, 16, 55, 25],
+ [34, 37, 13],
+
+ // 23
+ [4, 151, 121, 5, 152, 122],
+ [4, 75, 47, 14, 76, 48],
+ [11, 54, 24, 14, 55, 25],
+ [16, 45, 15, 14, 46, 16],
+
+ // 24
+ [6, 147, 117, 4, 148, 118],
+ [6, 73, 45, 14, 74, 46],
+ [11, 54, 24, 16, 55, 25],
+ [30, 46, 16, 2, 47, 17],
+
+ // 25
+ [8, 132, 106, 4, 133, 107],
+ [8, 75, 47, 13, 76, 48],
+ [7, 54, 24, 22, 55, 25],
+ [22, 45, 15, 13, 46, 16],
+
+ // 26
+ [10, 142, 114, 2, 143, 115],
+ [19, 74, 46, 4, 75, 47],
+ [28, 50, 22, 6, 51, 23],
+ [33, 46, 16, 4, 47, 17],
+
+ // 27
+ [8, 152, 122, 4, 153, 123],
+ [22, 73, 45, 3, 74, 46],
+ [8, 53, 23, 26, 54, 24],
+ [12, 45, 15, 28, 46, 16],
+
+ // 28
+ [3, 147, 117, 10, 148, 118],
+ [3, 73, 45, 23, 74, 46],
+ [4, 54, 24, 31, 55, 25],
+ [11, 45, 15, 31, 46, 16],
+
+ // 29
+ [7, 146, 116, 7, 147, 117],
+ [21, 73, 45, 7, 74, 46],
+ [1, 53, 23, 37, 54, 24],
+ [19, 45, 15, 26, 46, 16],
+
+ // 30
+ [5, 145, 115, 10, 146, 116],
+ [19, 75, 47, 10, 76, 48],
+ [15, 54, 24, 25, 55, 25],
+ [23, 45, 15, 25, 46, 16],
+
+ // 31
+ [13, 145, 115, 3, 146, 116],
+ [2, 74, 46, 29, 75, 47],
+ [42, 54, 24, 1, 55, 25],
+ [23, 45, 15, 28, 46, 16],
+
+ // 32
+ [17, 145, 115],
+ [10, 74, 46, 23, 75, 47],
+ [10, 54, 24, 35, 55, 25],
+ [19, 45, 15, 35, 46, 16],
+
+ // 33
+ [17, 145, 115, 1, 146, 116],
+ [14, 74, 46, 21, 75, 47],
+ [29, 54, 24, 19, 55, 25],
+ [11, 45, 15, 46, 46, 16],
+
+ // 34
+ [13, 145, 115, 6, 146, 116],
+ [14, 74, 46, 23, 75, 47],
+ [44, 54, 24, 7, 55, 25],
+ [59, 46, 16, 1, 47, 17],
+
+ // 35
+ [12, 151, 121, 7, 152, 122],
+ [12, 75, 47, 26, 76, 48],
+ [39, 54, 24, 14, 55, 25],
+ [22, 45, 15, 41, 46, 16],
+
+ // 36
+ [6, 151, 121, 14, 152, 122],
+ [6, 75, 47, 34, 76, 48],
+ [46, 54, 24, 10, 55, 25],
+ [2, 45, 15, 64, 46, 16],
+
+ // 37
+ [17, 152, 122, 4, 153, 123],
+ [29, 74, 46, 14, 75, 47],
+ [49, 54, 24, 10, 55, 25],
+ [24, 45, 15, 46, 46, 16],
+
+ // 38
+ [4, 152, 122, 18, 153, 123],
+ [13, 74, 46, 32, 75, 47],
+ [48, 54, 24, 14, 55, 25],
+ [42, 45, 15, 32, 46, 16],
+
+ // 39
+ [20, 147, 117, 4, 148, 118],
+ [40, 75, 47, 7, 76, 48],
+ [43, 54, 24, 22, 55, 25],
+ [10, 45, 15, 67, 46, 16],
+
+ // 40
+ [19, 148, 118, 6, 149, 119],
+ [18, 75, 47, 31, 76, 48],
+ [34, 54, 24, 34, 55, 25],
+ [20, 45, 15, 61, 46, 16]
+ ];
+
+ /**
+ * 根据数据获取对应版本
+ * @return {[type]} [description]
+ */
+ QRCodeAlg.prototype.getRightType = function () {
+ for (var typeNumber = 1; typeNumber < 41; typeNumber++) {
+ var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel];
+ if (rsBlock == undefined) {
+ throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel);
+ }
+ var length = rsBlock.length / 3;
+ var totalDataCount = 0;
+ for (var i = 0; i < length; i++) {
+ var count = rsBlock[i * 3 + 0];
+ var dataCount = rsBlock[i * 3 + 2];
+ totalDataCount += dataCount * count;
+ }
+ var lengthBytes = typeNumber > 9 ? 2 : 1;
+ if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) {
+ this.typeNumber = typeNumber;
+ this.rsBlock = rsBlock;
+ this.totalDataCount = totalDataCount;
+ break;
+ }
+ }
+ };
+
+ //---------------------------------------------------------------------
+ // QRBitBuffer
+ //---------------------------------------------------------------------
+ function QRBitBuffer() {
+ this.buffer = new Array();
+ this.length = 0;
+ }
+ QRBitBuffer.prototype = {
+ get: function (index) {
+ var bufIndex = Math.floor(index / 8);
+ return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1);
+ },
+ put: function (num, length) {
+ for (var i = 0; i < length; i++) {
+ this.putBit(((num >>> (length - i - 1)) & 1));
+ }
+ },
+ putBit: function (bit) {
+ var bufIndex = Math.floor(this.length / 8);
+ if (this.buffer.length <= bufIndex) {
+ this.buffer.push(0);
+ }
+ if (bit) {
+ this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
+ }
+ this.length++;
+ }
+ };
+
+
+
+ // xzedit
+ let qrcodeAlgObjCache = [];
+ /**
+ * 二维码构造函数,主要用于绘制
+ * @param {参数列表} opt 传递参数
+ * @return {}
+ */
+ QRCode = function (opt) {
+ //设置默认参数
+ this.options = {
+ text: '',
+ size: 256,
+ correctLevel: 3,
+ background: '#ffffff',
+ foreground: '#000000',
+ pdground: '#000000',
+ image: '',
+ imageSize: 30,
+ canvasId: opt.canvasId,
+ context: opt.context,
+ usingComponents: opt.usingComponents,
+ showLoading: opt.showLoading,
+ loadingText: opt.loadingText,
+ };
+ if (typeof opt === 'string') { // 只编码ASCII字符串
+ opt = {
+ text: opt
+ };
+ }
+ if (opt) {
+ for (var i in opt) {
+ this.options[i] = opt[i];
+ }
+ }
+ //使用QRCodeAlg创建二维码结构
+ var qrCodeAlg = null;
+ for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
+ if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) {
+ qrCodeAlg = qrcodeAlgObjCache[i].obj;
+ break;
+ }
+ }
+ if (i == l) {
+ qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel);
+ qrcodeAlgObjCache.push({
+ text: this.options.text,
+ correctLevel: this.options.correctLevel,
+ obj: qrCodeAlg
+ });
+ }
+ /**
+ * 计算矩阵点的前景色
+ * @param {Obj} config
+ * @param {Number} config.row 点x坐标
+ * @param {Number} config.col 点y坐标
+ * @param {Number} config.count 矩阵大小
+ * @param {Number} config.options 组件的options
+ * @return {String}
+ */
+ let getForeGround = function (config) {
+ var options = config.options;
+ if (options.pdground && (
+ (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
+ (config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
+ (config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
+ )) {
+ return options.pdground;
+ }
+ return options.foreground;
+ }
+ // 创建canvas
+ let createCanvas = function (options) {
+ if (options.showLoading) {
+ uni.showLoading({
+ title: options.loadingText,
+ mask: true
+ });
+ }
+ var ctx = uni.createCanvasContext(options.canvasId, options.context);
+ var count = qrCodeAlg.getModuleCount();
+ var ratioSize = options.size;
+ var ratioImgSize = options.imageSize;
+ //计算每个点的长宽
+ var tileW = (ratioSize / count).toPrecision(4);
+ var tileH = (ratioSize / count).toPrecision(4);
+ //绘制
+ for (var row = 0; row < count; row++) {
+ for (var col = 0; col < count; col++) {
+ var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
+ var h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW));
+ var foreground = getForeGround({
+ row: row,
+ col: col,
+ count: count,
+ options: options
+ });
+ ctx.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background);
+ ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
+ }
+ }
+ if (options.image) {
+ var x = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+ var y = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+ drawRoundedRect(ctx, x, y, ratioImgSize, ratioImgSize, 2, 6, true, true)
+ ctx.drawImage(options.image, x, y, ratioImgSize, ratioImgSize);
+ // 画圆角矩形
+ function drawRoundedRect(ctxi, x, y, width, height, r, lineWidth, fill, stroke) {
+ ctxi.setLineWidth(lineWidth);
+ ctxi.setFillStyle(options.background);
+ ctxi.setStrokeStyle(options.background);
+ ctxi.beginPath(); // draw top and top right corner
+ ctxi.moveTo(x + r, y);
+ ctxi.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner
+ ctxi.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner
+ ctxi.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner
+ ctxi.arcTo(x, y, x + r, y, r);
+ ctxi.closePath();
+ if (fill) {
+ ctxi.fill();
+ }
+ if (stroke) {
+ ctxi.stroke();
+ }
+ }
+ }
+ setTimeout(() => {
+ ctx.draw(true, () => {
+ // 保存到临时区域
+ setTimeout(() => {
+ uni.canvasToTempFilePath({
+ width: options.width,
+ height: options.height,
+ destWidth: options.width,
+ destHeight: options.height,
+ canvasId: options.canvasId,
+ quality: Number(1),
+ success: function (res) {
+ if (options.cbResult) {
+ options.cbResult(res.tempFilePath)
+ }
+ },
+ fail: function (res) {
+ if (options.cbResult) {
+ options.cbResult(res)
+ }
+ },
+ complete: function () {
+ if (options.showLoading){
+ uni.hideLoading();
+ }
+ },
+ }, options.context);
+ }, options.text.length + 100);
+ });
+ }, options.usingComponents ? 0 : 150);
+ }
+ createCanvas(this.options);
+ // 空判定
+ let empty = function (v) {
+ let tp = typeof v,
+ rt = false;
+ if (tp == "number" && String(v) == "") {
+ rt = true
+ } else if (tp == "undefined") {
+ rt = true
+ } else if (tp == "object") {
+ if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+ } else if (tp == "string") {
+ if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+ } else if (tp == "function") {
+ rt = false
+ }
+ return rt
+ }
+ };
+ QRCode.prototype.clear = function (fn) {
+ var ctx = uni.createCanvasContext(this.options.canvasId, this.options.context)
+ ctx.clearRect(0, 0, this.options.size, this.options.size)
+ ctx.draw(false, () => {
+ if (fn) {
+ fn()
+ }
+ })
+ };
+})()
+
+export default QRCode
\ No newline at end of file
diff --git a/pages/active/components/tki-qrcode/tki-qrcode.vue b/pages/active/components/tki-qrcode/tki-qrcode.vue
new file mode 100644
index 0000000..2eee5fc
--- /dev/null
+++ b/pages/active/components/tki-qrcode/tki-qrcode.vue
@@ -0,0 +1,211 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/active/group-buying/group-buying.vue b/pages/active/group-buying/group-buying.vue
new file mode 100644
index 0000000..2bb21b4
--- /dev/null
+++ b/pages/active/group-buying/group-buying.vue
@@ -0,0 +1,274 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/active/group-detail/group-detail.vue b/pages/active/group-detail/group-detail.vue
new file mode 100644
index 0000000..64b557c
--- /dev/null
+++ b/pages/active/group-detail/group-detail.vue
@@ -0,0 +1,1135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ navTitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ teamDetail.goodsName }}
+
+ {{ teamDetail.requiredNum }}人成团
+
+
+
+ 拼团价
+ ¥
+ {{ teamDetail.groupPrice }}
+
+
+ ¥{{ teamDetail.salePrice }}
+
+
+
+
+
+
+
+
+
+
+
+ 拼团剩余时间 {{ countdownText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 团长
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 邀请好友参团
+
+
+
+
+ 单独购买
+
+
+
+
+
+
+ 参加这个团
+
+
+
+
+
+
+ 查看订单
+
+
+
+
+
+
+
+ 拼团玩法
+
+
+
+
+
+ 开团/参加
+
+
+
+ 邀请好友参团
+
+
+
+ 成团发货
+
+
+
+
+
+ 购买须知:付款后等待成团,成团后 1-3 天发货。未成团且不支持自动成团时全额退款。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/active/static/group_banner_cart.jpg b/pages/active/static/group_banner_cart.jpg
new file mode 100644
index 0000000..4344a73
Binary files /dev/null and b/pages/active/static/group_banner_cart.jpg differ
diff --git a/pages/home/home.vue b/pages/home/home.vue
index 6da519f..1448e19 100644
--- a/pages/home/home.vue
+++ b/pages/home/home.vue
@@ -379,9 +379,9 @@ export default {
// #ifdef MP
const hasSeenGuide = uni.getStorageSync("has_seen_home_guide");
if (hasSeenGuide) {
- uni.showTabBar({ animation: false, fail: () => {} });
+ uni.showTabBar({ animation: false, fail: () => { } });
} else {
- uni.hideTabBar({ animation: false, fail: () => {} });
+ uni.hideTabBar({ animation: false, fail: () => { } });
}
// #endif
this.refreshKefuUnread();
@@ -721,7 +721,9 @@ export default {
uni.getClipboardData({
success: function (res) {
// http://localhost:5173/#/pages/other_package/productInfo/productInfo?id=1038
- console.log(res.data); // 剪贴板内容
+ console.log('剪贴板内容', res.data, res.data.indexOf(
+ `${SHARE_URL}/#/pages/other_package/productInfo/productInfo?id=`,
+ ) != -1); // 剪贴板内容
if (
res.data.indexOf(
`${SHARE_URL}/#/pages/other_package/productInfo/productInfo?id=`,
@@ -732,12 +734,25 @@ export default {
content: "当前APP想要粘贴商品详情,是否继续?",
success: (res1) => {
if (res1.confirm) {
- let id = res.data.match(/=(.*?)\s/);
- _this.curId = Number(id[1]);
- _this.getShop();
+ // https://h.o.tbmall.xin/#/pages/other_package/productInfo/productInfo?id=53641&activedType=groupBuy&activityId=1
+
+ const match = res.data.match(/[?&]id=([^& \s]+)/);
+ const id = match ? match[1] : "";
+ console.log("获取id", id, res.data.indexOf('activedType=groupBuy&activityId=1') != -1)
+ // 拼团
+ if (res.data.indexOf('activedType=groupBuy&activityId=1') != -1) {
+ uni.navigateTo({
+ url: `/pages/other_package/productInfo/productInfo?id=${id}&activedType=groupBuy&activityId=1`,
+ });
+ } else {
+ _this.curId = Number(id);
+ _this.getShop();
+
+ }
uni.setClipboardData({
data: "",
});
+
}
},
fail: (res2) => {
diff --git a/pages/login_package/login/login.vue b/pages/login_package/login/login.vue
index 8ecb993..0b05c00 100644
--- a/pages/login_package/login/login.vue
+++ b/pages/login_package/login/login.vue
@@ -167,7 +167,11 @@ export default {
});
// #endif
},
- onLoad() {
+ onLoad(options) {
+ if (options && options.redirect) {
+ this.redirect = decodeURIComponent(options.redirect);
+ console.log("登录后重定向目标页面:", this.redirect);
+ }
// #ifdef APP-PLUS
// 判断微信是否安装 (iOS 的 scheme 为 weixin://)
this.isWechatInstalled = plus.runtime.isApplicationExist({
@@ -201,18 +205,48 @@ export default {
loading: false,
showPolicyModal: false,
isWechatInstalled: false,
+ redirect: "",
};
},
methods: {
onBack() {
- console.log('onBack',)
- uni.switchTab({
- url: '/pages/home/home'
- })
+ console.log('onBack')
+ const pages = getCurrentPages();
+ if (pages && pages.length > 1) {
+ uni.navigateBack();
+ } else {
+ uni.switchTab({
+ url: '/pages/home/home'
+ });
+ }
+ },
+
+ jumpAfterLogin() {
+ if (this.redirect) {
+ uni.redirectTo({
+ url: this.redirect,
+ fail: () => {
+ uni.switchTab({
+ url: this.redirect,
+ fail: () => {
+ uni.switchTab({ url: "/pages/home/home" });
+ },
+ });
+ },
+ });
+ } else {
+ const pages = getCurrentPages();
+ if (pages && pages.length > 1) {
+ uni.navigateBack();
+ } else {
+ uni.switchTab({
+ url: "/pages/home/home",
+ });
+ }
+ }
},
jumpHome() {
-
uni.switchTab({
url: "/pages/home/home",
});
@@ -271,8 +305,8 @@ export default {
});
setTimeout(() => {
- then.jumpHome();
- }, 2000);
+ then.jumpAfterLogin();
+ }, 1500);
},
checkNameParam(val) {
// console.log("val",val);
@@ -647,9 +681,7 @@ export default {
message: response.msg,
});
setTimeout(() => {
- uni.switchTab({
- url: "/pages/home/home",
- });
+ this.jumpAfterLogin();
}, 1200);
} else if (response && response.msg) {
this.$refs.uToastRef.show({
diff --git a/pages/mine_package/mine_order/mine_order.vue b/pages/mine_package/mine_order/mine_order.vue
index 7e5da5f..33c2092 100644
--- a/pages/mine_package/mine_order/mine_order.vue
+++ b/pages/mine_package/mine_order/mine_order.vue
@@ -31,8 +31,9 @@
- {{ pItem.title }}
+
+
+ {{ pItem.title }}
+
{{ pItem.goodsSpece }}
+ 查看拼团
+
+
售后详情
@@ -381,6 +393,7 @@ import { CUSTOMER_SERVICE_PHONE_NUMBER } from "@/utils/config.js";
import afterSales from "./after-sales/index";
import refundPopup from "@/pages/mine_package/mine_order/after-sales/refund-popup.vue";
import { RETURN_REASON_LIST } from "@/pages/mine_package/mine_order/emun/index.js";
+import Decimal from "decimal.js";
export default {
components: { DownPopup, afterSales, refundPopup },
@@ -500,6 +513,19 @@ export default {
this.userinfo();
},
methods: {
+ calcOrderTotal(sumAmount, buyDeduction) {
+ try {
+ const sum = new Decimal(sumAmount || 0);
+ const deduction = new Decimal(buyDeduction || 0);
+ const total = sum.minus(deduction);
+ return (total.greaterThan(0) ? total : new Decimal(0)).toFixed(2);
+ } catch (e) {
+ const sum = Number(sumAmount) || 0;
+ const deduction = Number(buyDeduction) || 0;
+ const total = sum - deduction;
+ return (total > 0 ? total : 0).toFixed(2);
+ }
+ },
initMPHeader() {
// #ifdef MP-WEIXIN || MP
try {
@@ -788,6 +814,12 @@ export default {
"/pages/mine_package/mine_order_info/mine_order_info?id=" + item.id + '&type=' + this.form.type,
});
},
+ jumpGroupDetail(item) {
+ if (!item || !item.teamId) return;
+ uni.navigateTo({
+ url: `/pages/active/group-detail/group-detail?teamId=${item.teamId}`,
+ });
+ },
// 处理到期时间
handleExpressTime(expressTime) {
let resultM = 0;
@@ -1350,6 +1382,22 @@ export default {
.content_info_title {
width: 370rpx;
+ display: flex;
+ align-items: center;
+
+ .group_buy_tag {
+ height: 30rpx;
+ margin-right: 8rpx;
+ flex-shrink: 0;
+ }
+
+ .title_text {
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
}
.content_item_yd {
diff --git a/pages/mine_package/mine_order_info/mine_order_info.vue b/pages/mine_package/mine_order_info/mine_order_info.vue
index cd4dad4..5bf11ff 100644
--- a/pages/mine_package/mine_order_info/mine_order_info.vue
+++ b/pages/mine_package/mine_order_info/mine_order_info.vue
@@ -9,7 +9,7 @@
- {{ getValue(ORDER_STATUS, orderInfo.status) }}
+ {{ orderStatusText }}
@@ -78,7 +78,12 @@
- {{ pItem.title }}
+
+
+
+ {{ pItem.title }}
+
{{ pItem.goodsSpece }}
¥{{
pItem.totalPrice
- }}
+ }}
每件到手价¥{{ pItem.unitPrice }}
@@ -84,7 +84,7 @@
¥{{
pItem.totalPrice
- }}
+ }}
每件到手价¥{{ pItem.unitPrice }}
@@ -108,7 +108,7 @@
运费{{
getValue(PRODUCT_DELIVERY_TIME, addItem.deliveryTime)
- }}
+ }}
{{ orderInfo.postage ? "¥" + orderInfo.postage : "包邮" }}
@@ -351,7 +351,8 @@ import {
settle,
settlementOrder,
updateGoodsNum,
- setSeckillOrder
+ setSeckillOrder,
+ setGroupBuyOrder
} from "@/api/cart.js";
import { getSupportPay } from "@/api/order.js";
import { assembleAddress } from "@/utils/index.js";
@@ -365,6 +366,7 @@ import {
} from "@/utils/payUtils.js";
import CryptoJS from "crypto-js";
+import Decimal from "decimal.js";
import CouponDialog from "./coupon-dialog.vue";
export default {
@@ -478,6 +480,16 @@ export default {
this.sed = JSON.parse(option.sed);
this.leftPathType = "navigateTo";
this.leftPath = null;
+ if (this.sed?.activedType == 'groupBuy') {
+ const goodsId = this.sed.goodsId || this.sed.id;
+ const activityId = this.sed.activityId || 1;
+ let path = `/pages/other_package/productInfo/productInfo?id=${goodsId}&activedType=groupBuy&activityId=${activityId}`;
+ if (this.sed.teamId) {
+ path += `&teamId=${this.sed.teamId}`;
+ }
+ this.leftPath = path;
+ }
+
await this.addressList();
// console.log('addressListOne',this.addressListOne)
// if(this.addressListOne && this.addressListOne.length){
@@ -497,15 +509,36 @@ export default {
if (addressShowPop && addressShowPop !== "null") {
this.addressShow = addressShowPop;
}
- // 支付弹框
- // const payOpen = option.payOpen;
- // if (payOpen && payOpen !== "null") {
- // this.getWayOption();
- // this.wayShow = payOpen;
- // }
+
this.getWayOption();
this.getUserInfo();
},
+ onBackPress(options) {
+ if (this.sed?.activedType == 'groupBuy') {
+ const goodsId = this.sed.goodsId || this.sed.id;
+ const activityId = this.sed.activityId || 1;
+ let path = `/pages/other_package/productInfo/productInfo?id=${goodsId}&activedType=groupBuy&activityId=${activityId}`;
+ if (this.sed.teamId) {
+ path += `&teamId=${this.sed.teamId}`;
+ }
+ const pages = getCurrentPages();
+ if (pages && pages.length > 1) {
+ const prevPage = pages[pages.length - 2];
+ const route = prevPage ? (prevPage.route || (prevPage.$page && prevPage.$page.route)) : '';
+ if (route && path.includes(route)) {
+ return false;
+ }
+ }
+ uni.redirectTo({
+ url: path,
+ fail: () => {
+ uni.navigateTo({ url: path });
+ }
+ });
+ return true;
+ }
+ return false;
+ },
mounted() { },
methods: {
getValue,
@@ -727,15 +760,30 @@ export default {
params.activityId = Number(this.sed?.activityId);
params.activityType = "seckill";
}
+
+ if (this.sed?.activedType === "groupBuy") {
+ params.activityId = Number(this.sed?.activityId);
+ params.type = 5
+ }
const resp = await getGoodsDetail(params);
if (resp && resp.bizcode === 100) {
const data = resp.data;
this.isPoints = data.type == 3 ? 1 : 0;
- console.log("秦星星data", data);
+ console.log("秦星星data", data, this.sed);
+
+ const goodsNum = this.sed?.goodsNum || 1;
+ const unitPrice =
+ this.sed?.activedType == "seckill"
+ ? (data.activity?.activityPrice ?? 0)
+ : (this.sed?.specsId?.price ?? 0);
+ const amount = new Decimal(goodsNum).times(new Decimal(unitPrice || 0)).toFixed(2);
+
this.orderInfo = {
address: null,
- amount: this.sed?.activedType == "seckill" ? data.activity?.activityPrice : this.sed.goodsNum * this.sed.specsId.price || 0,
+ amount: amount,
postage: 0,
+ expendNum: data.expendNum,
+ groupPrice: data.groupPrice, //拼团标识
shops: [
{
addrTimes: [
@@ -756,13 +804,13 @@ export default {
id: this.sed.specsId.id,
img: data.goodsGraph.split(",")[0],
name: null,
- num: this.sed.goodsNum,
+ num: goodsNum,
phone: null,
provinceName: null,
specs: this.sed.specsId.combNames,
title: data.name,
- totalPrice: this.sed.goodsNum * this.sed.specsId.price,
- unitPrice: this.sed.specsId.price,
+ totalPrice: amount,
+ unitPrice: unitPrice,
},
],
},
@@ -777,6 +825,7 @@ export default {
"this.defaultAddress",
this.defaultAddress,
this.addressListOne,
+ this.orderInfo
);
if (
this.defaultAddress &&
@@ -805,28 +854,7 @@ export default {
if (!this.isCoupon && this.sed?.activedType !== "seckill") {
await this.refreshDirectSettle();
}
- // if(this.addressListOne && this.addressListOne.length){
- // this.orderInfo.address = this.addressListOne[0];
- // }
}
- // const params = {
- // cartIds: ids,
- // }
- // const resp = await getSettleList(params);
- // if (resp && resp.bizcode === 100) {
- // const data = resp.data;
- // const shops = data.shops;
- // if (shops && shops.length !== 0) {
- // shops.map(item => {
- // item.addrTimes.map(addItem => {
- // addItem.remark = null;
- // })
- // })
- // }
- // data.shops = shops;
- // data.paymentMethod = null;// 支付方式
- // this.orderInfo = data;
- // }
},
/**
* 数量进行增加删除
@@ -1028,10 +1056,8 @@ export default {
// #endif
}
- console.log("收货地址ID--params", params);
-
let resp = null;
- console.log("orderInfo--22--", this.sed, this.orderInfo);
+ console.log("收货地址ID--params", params, this.sed, this.orderInfo);
if (this.sed) {
if (
this.orderInfo.paymentMethod == "balance" ||
@@ -1044,11 +1070,12 @@ export default {
params["specsId"] = this.sed.specsId.id;
params["goodsNum"] = this.orderInfo.shops[0].addrTimes[0].goods[0].num;
-
- console.log("秦星星params-----", this.sed?.activedType);
if (this.sed?.activedType == "seckill") {
params["activityId"] = Number(this.sed?.activityId); // 秒杀活动ID
resp = await setSeckillOrder(params);
+ } else if (this.orderInfo.groupPrice && this.sed?.activedType == 'groupBuy') {
+ params["activityId"] = Number(this.sed?.activityId); // 秒杀活动ID
+ resp = await setGroupBuyOrder(params);
} else {
if (this.isCoupon) {
params.payway2 = "vcoin";
@@ -1095,22 +1122,37 @@ export default {
}
if (resp && resp.bizcode === 100) {
- console.log("提交订单成功", resp.data);
- const item = resp.data;
+ console.log("提交订单成功", resp.data, resp.data.teamId);
+ const isGroupBuy = Boolean(
+ this.orderInfo.groupPrice && this.sed?.activedType == "groupBuy"
+ );
+ const item = isGroupBuy ? resp.data?.payment : resp.data;
+
+ const groupDetailUrl = isGroupBuy
+ ? `/pages/active/group-detail/group-detail?teamId=${resp.data.teamId}`
+ : null;
+
if (item.status === 1) {
+ if (isGroupBuy) {
+ uni.redirectTo({
+ url: groupDetailUrl,
+ });
+ return;
+ }
jumpWayOk();
return;
}
if (item.douyin) {
+ if (groupDetailUrl) item.customSuccessUrl = groupDetailUrl;
await appDypayFun(item);
} else if (item.wechat && Object.keys(item.wechat).length !== 0) {
- this.appWxpay(item.wechat, item.orderId, item.orderNum, item);
+ this.appWxpay(item.wechat, item.orderId, item.orderNum, item, groupDetailUrl);
} else if (
item.alipay &&
Object.keys(item.alipay).length !== 0 &&
item.alipay
) {
- this.zfbPay(item.alipay, item.orderId, item.orderNum, item);
+ this.zfbPay(item.alipay, item.orderId, item.orderNum, item, groupDetailUrl);
} else {
this.$refs.uToastRef.show({
type: "error",
@@ -1128,9 +1170,9 @@ export default {
} finally { this._paySubmitting = false; }
},
// app 微信支付
- async appWxpay(alipay, orderId, orderNum, item) {
+ async appWxpay(alipay, orderId, orderNum, item, customSuccessUrl) {
try {
- const result = await appWxpayFun(alipay, orderId, orderNum, item);
+ const result = await appWxpayFun(alipay, orderId, orderNum, customSuccessUrl);
console.log("支付成功", result);
// 处理支付成功后的逻辑,如更新订单状态等
} catch (error) {
@@ -1139,9 +1181,9 @@ export default {
}
},
// 支付宝支付
- async zfbPay(alipay, orderId, orderNum, item) {
+ async zfbPay(alipay, orderId, orderNum, item, customSuccessUrl) {
try {
- const result = await zfbPayFun(alipay, orderId, orderNum, item);
+ const result = await zfbPayFun(alipay, orderId, orderNum, customSuccessUrl);
console.log("支付成功", result);
// 处理支付成功后的逻辑,如更新订单状态等
} catch (error) {
@@ -1227,7 +1269,7 @@ export default {
this.selectedCoupon = coupon;
},
async refreshDirectSettle(couponUserId) {
- if (!this.sed || !this.sed.specsId) return false;
+ if (!this.sed || !this.sed.specsId || !couponUserId) return false;
const resp = await getDirectSettleList({
goodsId: this.sed.id,
specsId: this.sed.specsId.id,
diff --git a/pages/other_package/components/share-popup.vue b/pages/other_package/components/share-popup.vue
index b5d9e0c..7fb0e63 100644
--- a/pages/other_package/components/share-popup.vue
+++ b/pages/other_package/components/share-popup.vue
@@ -35,7 +35,7 @@
-
+
@@ -45,7 +45,57 @@
取消
-
+
+
+
+
+
+
+ 拼团价
+ ¥
+ {{ groupPrice || price }}
+
+
+ {{ requiredNum }}人成团
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+ 超值拼团
+ 人多价更优 拼团更实惠
+
+
+ {{ currentInviterName }}
+
+
+
+
+
+
+ 长按识别 参与拼团
+
+
+
+
+
+
+
+
+
+
+
@@ -65,13 +124,16 @@
import Func from '/utils/func'
import tkiQrcode from './tki-qrcode/tki-qrcode.vue'
import { SHARE_URL } from '@/utils/config.js'
+import { getStorageFun, USER_DATA } from '@/utils/auth.js'
export default {
data() {
return {
btnList: ['微信', '朋友圈'],
appBtnList: ['微信', '朋友圈', '复制链接', '保存图片', 'QQ'],
- qrvalue: ''
-
+ qrvalue: '',
+ defaultAvatar: 'https://static.tbmall.xin/static/new/default_avatar.png',
+ groupQrPath: '',
+ isGeneratingPoster: false
}
},
@@ -82,7 +144,7 @@ export default {
default: false
},
id: {
- type: String,
+ type: [String, Number],
default: ''
},
tu: {
@@ -100,21 +162,50 @@ export default {
hideCopyAndQQ: {
type: Boolean,
default: false
+ },
+ sharePath: {
+ type: String,
+ default: ''
+ },
+ isGroupBuy: {
+ type: Boolean,
+ default: false
+ },
+ groupPrice: {
+ type: [String, Number],
+ default: ''
+ },
+ requiredNum: {
+ type: [String, Number],
+ default: 2
+ },
+ inviterName: {
+ type: String,
+ default: ''
+ },
+ inviterAvatar: {
+ type: String,
+ default: ''
+ }
+ },
+ watch: {
+ show(val) {
+ if (val) {
+ this.initQrCode();
+ }
+ },
+ sharePath() {
+ this.initQrCode();
+ },
+ id() {
+ this.initQrCode();
+ },
+ isGroupBuy() {
+ this.initQrCode();
}
},
mounted() {
- // #ifdef APP-PLUS || H5
- if (!this.isInvitePage) {
- this.qrvalue = `${SHARE_URL}/#/pages/other_package/productInfo/productInfo?id=${this.id}`
- this.$nextTick(() => {
- if (this.qrvalue) {
- this.creatQrcode();
- }
- })
- }
-
- // #endif
-
+ this.initQrCode();
},
computed: {
@@ -134,6 +225,9 @@ export default {
if (this.isInvitePage) {
return items.filter(item => item.name !== '微信');
}
+ if (this.isGroupBuy) {
+ return items.filter(item => item.name !== 'QQ');
+ }
return items;
},
filteredAppBtnList() {
@@ -141,14 +235,79 @@ export default {
if (this.isInvitePage) {
return items.filter(item => item.name !== '复制链接' && item.name !== 'QQ');
}
+ if (this.isGroupBuy) {
+ return items.filter(item => item.name !== 'QQ');
+ }
return items;
+ },
+ currentInviterName() {
+ if (this.inviterName) return this.inviterName;
+ try {
+ const u = getStorageFun(USER_DATA) || {};
+ return u.name || u.nickName || 'Haonan Mu';
+ } catch (e) {
+ return 'Haonan Mu';
+ }
+ },
+ currentInviterAvatar() {
+ if (this.inviterAvatar) return this.inviterAvatar;
+ try {
+ const u = getStorageFun(USER_DATA) || {};
+ return u.avatar || u.avatarUrl || this.defaultAvatar;
+ } catch (e) {
+ return this.defaultAvatar;
+ }
}
},
methods: {
+ initQrCode() {
+ if (!this.isInvitePage) {
+ this.groupQrPath = '';
+ if (this.isGroupBuy) {
+ // 拼团专属卡片二维码:路由后面加 id=${this.id}&activedType=groupBuy&activityId=1
+ let basePath = 'pages/other_package/productInfo/productInfo';
+ let targetId = this.id != null ? String(this.id) : '';
+ if (this.sharePath) {
+ const cleanPath = this.sharePath.startsWith('/') ? this.sharePath.slice(1) : this.sharePath;
+ const pathOnly = cleanPath.split('?')[0];
+ if (pathOnly) {
+ basePath = pathOnly;
+ }
+ if (!targetId) {
+ const idMatch = cleanPath.match(/[?&]id=([^&]+)/);
+ if (idMatch) {
+ targetId = idMatch[1];
+ }
+ }
+ }
+ // 拼团专属卡片二维码:扫码后直接打开 H5 对应商品详情页,路由后面加 id=${this.id}&activedType=groupBuy&activityId=1
+ this.qrvalue = `${SHARE_URL}/#/${basePath}?id=${targetId}&activedType=groupBuy&activityId=1`;
+ } else if (this.sharePath) {
+ const cleanPath = this.sharePath.startsWith('/') ? this.sharePath.slice(1) : this.sharePath;
+ this.qrvalue = `${SHARE_URL}/#/${cleanPath}`;
+ } else if (this.id) {
+ this.qrvalue = `${SHARE_URL}/#/pages/other_package/productInfo/productInfo?id=${this.id}`;
+ }
+ if (this.qrvalue) {
+ this.$nextTick(() => {
+ setTimeout(() => {
+ if (this.$refs.qrcode && this.$refs.qrcode._makeCode) {
+ this.$refs.qrcode._makeCode();
+ }
+ if (this.$refs.groupQrcode && this.$refs.groupQrcode._makeCode) {
+ this.$refs.groupQrcode._makeCode();
+ }
+ }, 50);
+ });
+ }
+ }
+ },
creatQrcode() {
- this.$refs.qrcode._makeCode();
+ if (this.$refs.qrcode && this.$refs.qrcode._makeCode) {
+ this.$refs.qrcode._makeCode();
+ }
},
onBtn(index) {
if (index == 2) {
@@ -197,10 +356,395 @@ export default {
Func.myToast('复制成功')
})
// #endif
+ } else if (index == 3) {
+ if (this.isGroupBuy) {
+ this.saveGroupCard();
+ return;
+ }
}
this.$emit('share', index)
},
+ onGroupQrResult(res) {
+ this.groupQrPath = res;
+ },
+
+ getLocalImage(url) {
+ return new Promise((resolve) => {
+ if (!url) {
+ resolve('');
+ return;
+ }
+ if (
+ url.startsWith('file://') ||
+ url.startsWith('_doc') ||
+ url.startsWith('/storage') ||
+ url.startsWith('blob:') ||
+ url.startsWith('data:')
+ ) {
+ resolve(url);
+ return;
+ }
+ uni.downloadFile({
+ url: url,
+ success: (res) => {
+ if (res.statusCode === 200 && res.tempFilePath) {
+ resolve(res.tempFilePath);
+ } else {
+ uni.getImageInfo({
+ src: url,
+ success: (info) => resolve(info.path),
+ fail: () => resolve('')
+ });
+ }
+ },
+ fail: () => {
+ uni.getImageInfo({
+ src: url,
+ success: (info) => resolve(info.path),
+ fail: () => resolve('')
+ });
+ }
+ });
+ });
+ },
+
+ base64ToTempFile(base64Data) {
+ return new Promise((resolve) => {
+ if (!base64Data || !base64Data.startsWith('data:image')) {
+ resolve(base64Data);
+ return;
+ }
+ // #ifdef APP-PLUS
+ try {
+ const bitmap = new plus.nativeObj.Bitmap('qr_' + Date.now());
+ bitmap.loadBase64Data(
+ base64Data,
+ () => {
+ const tempPath = `_doc/qr_${Date.now()}.png`;
+ bitmap.save(
+ tempPath,
+ { overwrite: true, format: 'png', quality: 100 },
+ (e) => {
+ bitmap.clear();
+ resolve(e.target);
+ },
+ () => {
+ bitmap.clear();
+ resolve(base64Data);
+ }
+ );
+ },
+ () => {
+ bitmap.clear();
+ resolve(base64Data);
+ }
+ );
+ } catch (e) {
+ resolve(base64Data);
+ }
+ // #endif
+ // #ifndef APP-PLUS
+ resolve(base64Data);
+ // #endif
+ });
+ },
+
+ drawRoundedRect(ctx, x, y, width, height, radius, fill, stroke) {
+ ctx.beginPath();
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(x + width - radius, y);
+ ctx.arcTo(x + width, y, x + width, y + radius, radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
+ ctx.lineTo(x + radius, y + height);
+ ctx.arcTo(x, y + height, x, y + height - radius, radius);
+ ctx.lineTo(x, y + radius);
+ ctx.arcTo(x, y, x + radius, y, radius);
+ ctx.closePath();
+ if (fill) {
+ ctx.fill();
+ }
+ if (stroke) {
+ ctx.stroke();
+ }
+ },
+
+ drawWrapText(ctx, text, x, y, maxWidth, lineHeight, maxLines = 2) {
+ if (!text) return y;
+ const chars = String(text).split('');
+ let line = '';
+ let currentLine = 1;
+ let currentY = y;
+
+ for (let n = 0; n < chars.length; n++) {
+ const testLine = line + chars[n];
+ const metrics = ctx.measureText ? ctx.measureText(testLine) : { width: testLine.length * 14 };
+ const testWidth = metrics.width;
+ if (testWidth > maxWidth && n > 0) {
+ if (currentLine === maxLines) {
+ let ellipsisLine = line;
+ while (ellipsisLine.length > 0) {
+ const lineWithEllipsis = ellipsisLine + '...';
+ const m = ctx.measureText ? ctx.measureText(lineWithEllipsis) : { width: lineWithEllipsis.length * 14 };
+ if (m.width <= maxWidth) {
+ ctx.fillText(lineWithEllipsis, x, currentY);
+ break;
+ }
+ ellipsisLine = ellipsisLine.slice(0, -1);
+ }
+ return currentY + lineHeight;
+ }
+ ctx.fillText(line, x, currentY);
+ line = chars[n];
+ currentY += lineHeight;
+ currentLine++;
+ } else {
+ line = testLine;
+ }
+ }
+ ctx.fillText(line, x, currentY);
+ return currentY + lineHeight;
+ },
+
+ async saveGroupCard() {
+ if (this.isGeneratingPoster) return;
+ this.isGeneratingPoster = true;
+ uni.showLoading({ title: '海报生成中...', mask: true });
+
+ try {
+ let localTu = '';
+ if (this.tu) {
+ localTu = await this.getLocalImage(this.tu);
+ }
+
+ const avatarUrl = this.currentInviterAvatar;
+ let localAvatar = '';
+ if (avatarUrl) {
+ localAvatar = await this.getLocalImage(avatarUrl);
+ }
+
+ let qrImg = this.groupQrPath || (this.$refs.groupQrcode && this.$refs.groupQrcode.result);
+ if (!qrImg && this.$refs.groupQrcode) {
+ if (this.$refs.groupQrcode._makeCode) {
+ this.$refs.groupQrcode._makeCode();
+ }
+ await new Promise((r) => setTimeout(r, 450));
+ qrImg = this.groupQrPath || (this.$refs.groupQrcode && this.$refs.groupQrcode.result);
+ }
+ if (!qrImg) {
+ await new Promise((r) => setTimeout(r, 300));
+ qrImg = this.groupQrPath || (this.$refs.groupQrcode && this.$refs.groupQrcode.result);
+ }
+ if (qrImg && qrImg.startsWith('data:image')) {
+ qrImg = await this.base64ToTempFile(qrImg);
+ }
+
+ const ctx = uni.createCanvasContext('groupCardCanvas', this);
+ const W = 560;
+ const H = 910;
+ const pad = 24;
+ const innerW = W - pad * 2; // 512
+
+ // 1. 卡片背景
+ ctx.fillStyle = '#FFFFFF';
+ this.drawRoundedRect(ctx, 0, 0, W, H, 24, true, false);
+
+ // 2. 商品主图
+ if (localTu) {
+ ctx.save();
+ this.drawRoundedRect(ctx, pad, pad, innerW, 500, 16, false, false);
+ ctx.clip();
+ ctx.drawImage(localTu, pad, pad, innerW, 500);
+ ctx.restore();
+ } else {
+ ctx.fillStyle = '#F7F7F7';
+ this.drawRoundedRect(ctx, pad, pad, innerW, 500, 16, true, false);
+ }
+
+ // 3. 价格行
+ const priceRowY = pad + 500 + 20; // 544
+ ctx.font = 'normal 500 24px sans-serif';
+ ctx.fillStyle = '#7F3BF5';
+ ctx.textBaseline = 'middle';
+ ctx.fillText('拼团价', pad, priceRowY + 18);
+ const labelWidth = ctx.measureText ? ctx.measureText('拼团价').width : 72;
+
+ ctx.font = 'bold 22px sans-serif';
+ ctx.fillStyle = '#7F3BF5';
+ const symbolX = pad + labelWidth + 6;
+ ctx.fillText('¥', symbolX, priceRowY + 19);
+ const symbolWidth = ctx.measureText ? ctx.measureText('¥').width : 16;
+
+ const displayPrice = String(this.groupPrice || this.price || '0');
+ ctx.font = 'bold 42px sans-serif';
+ ctx.fillStyle = '#7F3BF5';
+ const numX = symbolX + symbolWidth + 4;
+ ctx.fillText(displayPrice, numX, priceRowY + 16);
+
+ if (this.requiredNum) {
+ const tagText = `${this.requiredNum}人成团`;
+ ctx.font = 'normal 500 20px sans-serif';
+ const tagTextWidth = ctx.measureText ? ctx.measureText(tagText).width : 74;
+ const tagW = tagTextWidth + 24;
+ const tagH = 34;
+ const tagX = W - pad - tagW;
+ const tagY = priceRowY + 2;
+ ctx.fillStyle = '#F3EBFE';
+ this.drawRoundedRect(ctx, tagX, tagY, tagW, tagH, 8, true, false);
+ ctx.fillStyle = '#7F3BF5';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(tagText, tagX + 12, tagY + tagH / 2);
+ }
+
+ // 4. 商品名称 (最多2行,溢出显示省略号)
+ const titleY = priceRowY + 54;
+ ctx.fillStyle = '#222222';
+ ctx.font = 'bold 26px sans-serif';
+ ctx.textBaseline = 'top';
+ const nextY = this.drawWrapText(ctx, this.text || '', pad, titleY, innerW, 36, 2);
+
+ // 5. 分割线
+ const dividerY = Math.max(nextY + 12, titleY + 76);
+ ctx.strokeStyle = '#F0F0F0';
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(pad, dividerY);
+ ctx.lineTo(W - pad, dividerY);
+ ctx.stroke();
+
+ // 6. 底部区域
+ const bottomY = dividerY + 18;
+
+ ctx.fillStyle = '#1E1E1E';
+ ctx.font = 'bold 30px sans-serif';
+ ctx.textBaseline = 'top';
+ ctx.fillText('超值拼团', pad, bottomY);
+
+ ctx.fillStyle = '#999999';
+ ctx.font = 'normal 20px sans-serif';
+ ctx.fillText('人多价更优 拼团更实惠', pad, bottomY + 38);
+
+ const userRowY = bottomY + 74;
+ const avatarSize = 44;
+ if (localAvatar) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(pad + avatarSize / 2, userRowY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI);
+ ctx.closePath();
+ ctx.clip();
+ ctx.drawImage(localAvatar, pad, userRowY, avatarSize, avatarSize);
+ ctx.restore();
+ } else {
+ ctx.fillStyle = '#EEEEEE';
+ ctx.beginPath();
+ ctx.arc(pad + avatarSize / 2, userRowY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI);
+ ctx.fill();
+ }
+
+ ctx.fillStyle = '#333333';
+ ctx.font = 'normal 500 22px sans-serif';
+ ctx.textBaseline = 'middle';
+ const username = this.currentInviterName;
+ ctx.fillText(username, pad + avatarSize + 12, userRowY + avatarSize / 2);
+
+ // 二维码绘制(带纯白底色与四周静区 Padding,确保微信扫码/长按识别成功率)
+ const qrBoxSize = 144;
+ const qrInnerPad = 10;
+ const qrSize = qrBoxSize - qrInnerPad * 2; // 124px
+ const qrBoxX = W - pad - qrBoxSize;
+ const qrBoxY = bottomY;
+
+ // 绘制白色背景底卡
+ ctx.fillStyle = '#FFFFFF';
+ this.drawRoundedRect(ctx, qrBoxX, qrBoxY, qrBoxSize, qrBoxSize, 8, true, false);
+
+ if (qrImg) {
+ ctx.drawImage(qrImg, qrBoxX + qrInnerPad, qrBoxY + qrInnerPad, qrSize, qrSize);
+ }
+
+ ctx.fillStyle = '#999999';
+ ctx.font = 'normal 17px sans-serif';
+ ctx.textBaseline = 'top';
+ const tipText = '长按识别 参与拼团';
+ const tipWidth = ctx.measureText ? ctx.measureText(tipText).width : 120;
+ const tipX = qrBoxX + (qrBoxSize - tipWidth) / 2;
+ ctx.fillText(tipText, tipX, qrBoxY + qrBoxSize + 8);
+
+ // 7. 提交绘制
+ await new Promise((resolve) => {
+ ctx.draw(false, () => {
+ setTimeout(resolve, 200);
+ });
+ });
+
+ // 8. 导出临时图片
+ const tempFilePath = await new Promise((resolve, reject) => {
+ uni.canvasToTempFilePath(
+ {
+ canvasId: 'groupCardCanvas',
+ fileType: 'png',
+ width: W,
+ height: H,
+ destWidth: W * 2,
+ destHeight: H * 2,
+ quality: 1,
+ success: (res) => resolve(res.tempFilePath),
+ fail: (err) => reject(err)
+ },
+ this
+ );
+ });
+
+ uni.hideLoading();
+
+ // 9. 保存至相册
+ // #ifdef APP-PLUS || MP-WEIXIN
+ uni.saveImageToPhotosAlbum({
+ filePath: tempFilePath,
+ success: () => {
+ uni.showToast({
+ title: '已保存至手机相册',
+ icon: 'success',
+ duration: 2000
+ });
+ this.onClose();
+ },
+ fail: (err) => {
+ console.error('保存相册失败:', err);
+ if (err && err.errMsg && (err.errMsg.includes('auth') || err.errMsg.includes('authorize'))) {
+ uni.showModal({
+ title: '提示',
+ content: '需要保存图片到相册权限,请在系统设置中开启相册权限',
+ success: (res) => {
+ if (res.confirm) uni.openSetting();
+ }
+ });
+ } else {
+ uni.previewImage({ urls: [tempFilePath] });
+ }
+ }
+ });
+ // #endif
+
+ // #ifdef H5
+ uni.previewImage({ urls: [tempFilePath] });
+ uni.showToast({ title: '请长按图片保存到手机相册', icon: 'none' });
+ this.onClose();
+ // #endif
+
+ } catch (err) {
+ uni.hideLoading();
+ console.error('生成或保存海报失败:', err);
+ uni.showToast({
+ title: '生成海报失败,请重试',
+ icon: 'none'
+ });
+ } finally {
+ this.isGeneratingPoster = false;
+ }
+ },
+
onClose() {
this.$emit('close');
},
@@ -215,6 +759,181 @@ export default {
diff --git a/pages/other_package/productInfo/components/product-contribution-dialog.vue b/pages/other_package/productInfo/components/product-contribution-dialog.vue
new file mode 100644
index 0000000..85ed63d
--- /dev/null
+++ b/pages/other_package/productInfo/components/product-contribution-dialog.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/other_package/productInfo/components/product-coupon-popup.vue b/pages/other_package/productInfo/components/product-coupon-popup.vue
new file mode 100644
index 0000000..791597f
--- /dev/null
+++ b/pages/other_package/productInfo/components/product-coupon-popup.vue
@@ -0,0 +1,420 @@
+
+
+
+
+
+
+
+
+
diff --git a/pages/other_package/productInfo/components/product-group-rules.vue b/pages/other_package/productInfo/components/product-group-rules.vue
new file mode 100644
index 0000000..e162d3f
--- /dev/null
+++ b/pages/other_package/productInfo/components/product-group-rules.vue
@@ -0,0 +1,247 @@
+
+
+
+
+
+ 拼团玩法
+
+
+
+
+
+ 开团/参加
+
+
+
+
+
+
+ 邀请好友参团
+
+
+
+
+
+
+ 成团发货
+
+
+
+ 购买须知:付款后等待成团,成团后 1-3 天发货。未成团且不支持自动成团时全额退款。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ team.nickname }}
+
+
+ {{ team.countdownText }}
+
+ 去拼团
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/other_package/productInfo/components/product-joinable-popup.vue b/pages/other_package/productInfo/components/product-joinable-popup.vue
new file mode 100644
index 0000000..5d73794
--- /dev/null
+++ b/pages/other_package/productInfo/components/product-joinable-popup.vue
@@ -0,0 +1,199 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/other_package/productInfo/productInfo.vue b/pages/other_package/productInfo/productInfo.vue
index db41799..9f3f7c8 100644
--- a/pages/other_package/productInfo/productInfo.vue
+++ b/pages/other_package/productInfo/productInfo.vue
@@ -15,21 +15,54 @@
-
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+ 拼团价
+ ¥
+ {{ orderInfo.groupPrice }}
+ ¥{{ groupOriginPrice }}
+
+
+ 只剩 {{ groupCountdownText }}
+
+
+
+
+
+
+ {{ orderInfo.name }}
+
+
+
+
+
+ 下单送{{ orderInfo.contribution }}贡献值
+
+
+
+ 全网销量{{ limit999(orderInfo.sales) }}
+
+
-
-
-
+
+
+
-->
+
+
+
+
退货提示
@@ -201,17 +239,29 @@
购物车
-
-
- 加入购物车
-
-
-
- 立即购买
-
+
+
+
+
+ 单独购买
+
+
+ 发起拼团
+
+
+
+
+
+ 加入购物车
+
+
+
+ 立即购买
+
+
-
+
+{{ orderInfo.minPrice }}元
+
+
+ 拼团价¥
+
+ {{ (selectSpecificationTempl && selectSpecificationTempl.groupPrice) ?
+ selectSpecificationTempl.groupPrice
+ : orderInfo.groupPrice }}
+
+
+ {{ orderInfo.groupSize || 2 }}人团
+
+
+
¥
@@ -298,8 +363,6 @@
{{ skuBySpecInfo.stock || 0 }}件 -->
已选规格: {{ skuBySpecInfo.skuName }}
-
-
@@ -330,7 +393,12 @@
+
- 去支付
+
+
+ 去支付
+
加入购物车
@@ -399,121 +467,25 @@
+ :id="id" :text="orderInfo.name ? orderInfo.name : ''" :tu="swiperList.length && swiperList[0]"
+ :price="isGroupProduct ? String(groupActivityInfo?.groupPrice || orderInfo.price) : String(orderInfo.price || '')"
+ :groupPrice="groupActivityInfo?.groupPrice ? String(groupActivityInfo.groupPrice) : ''"
+ :requiredNum="groupActivityInfo?.requiredNum || 2"
+ :sharePath="isGroupProduct ? `pages/other_package/productInfo/productInfo?id=${id}&activedType=groupBuy&activityId=1` : ''">
+
-
-
-
-
-
-
-
+
+
+