24 changed files with 3578 additions and 210 deletions
@ -0,0 +1,426 @@ |
|||
// Core code comes from https://github.com/davidshimjs/qrcodejs
|
|||
var QRCode; |
|||
|
|||
(function () { |
|||
/** |
|||
* Get the type by string length |
|||
* |
|||
* @private |
|||
* @param {String} sText |
|||
* @param {Number} nCorrectLevel |
|||
* @return {Number} type |
|||
*/ |
|||
function _getTypeNumber(sText, nCorrectLevel) { |
|||
var nType = 1; |
|||
var length = _getUTF8Length(sText); |
|||
|
|||
for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) { |
|||
var nLimit = 0; |
|||
|
|||
switch (nCorrectLevel) { |
|||
case QRErrorCorrectLevel.L: |
|||
nLimit = QRCodeLimitLength[i][0]; |
|||
break; |
|||
case QRErrorCorrectLevel.M: |
|||
nLimit = QRCodeLimitLength[i][1]; |
|||
break; |
|||
case QRErrorCorrectLevel.Q: |
|||
nLimit = QRCodeLimitLength[i][2]; |
|||
break; |
|||
case QRErrorCorrectLevel.H: |
|||
nLimit = QRCodeLimitLength[i][3]; |
|||
break; |
|||
} |
|||
|
|||
if (length <= nLimit) { |
|||
break; |
|||
} else { |
|||
nType++; |
|||
} |
|||
} |
|||
|
|||
if (nType > QRCodeLimitLength.length) { |
|||
throw new Error("Too long data"); |
|||
} |
|||
|
|||
return nType; |
|||
} |
|||
|
|||
function _getUTF8Length(sText) { |
|||
var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a'); |
|||
return replacedText.length + (replacedText.length != sText ? 3 : 0); |
|||
} |
|||
|
|||
function QR8bitByte(data) { |
|||
this.mode = QRMode.MODE_8BIT_BYTE; |
|||
this.data = data; |
|||
this.parsedData = []; |
|||
|
|||
// Added to support UTF-8 Characters
|
|||
for (var i = 0, l = this.data.length; i < l; i++) { |
|||
var byteArray = []; |
|||
var code = this.data.charCodeAt(i); |
|||
|
|||
if (code > 0x10000) { |
|||
byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18); |
|||
byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12); |
|||
byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6); |
|||
byteArray[3] = 0x80 | (code & 0x3F); |
|||
} else if (code > 0x800) { |
|||
byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12); |
|||
byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6); |
|||
byteArray[2] = 0x80 | (code & 0x3F); |
|||
} else if (code > 0x80) { |
|||
byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6); |
|||
byteArray[1] = 0x80 | (code & 0x3F); |
|||
} else { |
|||
byteArray[0] = code; |
|||
} |
|||
|
|||
this.parsedData.push(byteArray); |
|||
} |
|||
|
|||
this.parsedData = Array.prototype.concat.apply([], this.parsedData); |
|||
|
|||
if (this.parsedData.length != this.data.length) { |
|||
this.parsedData.unshift(191); |
|||
this.parsedData.unshift(187); |
|||
this.parsedData.unshift(239); |
|||
} |
|||
} |
|||
|
|||
QR8bitByte.prototype = { |
|||
getLength: function (buffer) { |
|||
return this.parsedData.length; |
|||
}, |
|||
write: function (buffer) { |
|||
for (var i = 0, l = this.parsedData.length; i < l; i++) { |
|||
buffer.put(this.parsedData[i], 8); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
|
|||
// QRCodeModel
|
|||
function QRCodeModel(typeNumber, errorCorrectLevel) { |
|||
this.typeNumber = typeNumber; |
|||
this.errorCorrectLevel = errorCorrectLevel; |
|||
this.modules = null; |
|||
this.moduleCount = 0; |
|||
this.dataCache = null; |
|||
this.dataList = []; |
|||
} |
|||
QRCodeModel.prototype = { |
|||
addData: function (data) { var newData = new QR8bitByte(data); this.dataList.push(newData); this.dataCache = null; }, isDark: function (row, col) { |
|||
if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { throw new Error(row + "," + col); } |
|||
return this.modules[row][col]; |
|||
}, getModuleCount: function () { return this.moduleCount; }, make: function () { this.makeImpl(false, this.getBestMaskPattern()); }, makeImpl: function (test, 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); for (var col = 0; col < this.moduleCount; col++) { this.modules[row][col] = null; } } |
|||
this.setupPositionProbePattern(0, 0); this.setupPositionProbePattern(this.moduleCount - 7, 0); this.setupPositionProbePattern(0, this.moduleCount - 7); this.setupPositionAdjustPattern(); this.setupTimingPattern(); this.setupTypeInfo(test, maskPattern); if (this.typeNumber >= 7) { this.setupTypeNumber(test); } |
|||
if (this.dataCache == null) { this.dataCache = QRCodeModel.createData(this.typeNumber, this.errorCorrectLevel, this.dataList); } |
|||
this.mapData(this.dataCache, maskPattern); |
|||
}, 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; } } } }, getBestMaskPattern: function () { |
|||
var minLostPoint = 0; var pattern = 0; for (var i = 0; i < 8; i++) { this.makeImpl(true, i); var lostPoint = QRUtil.getLostPoint(this); if (i == 0 || minLostPoint > lostPoint) { minLostPoint = lostPoint; pattern = i; } } |
|||
return pattern; |
|||
}, createMovieClip: function (target_mc, instance_name, depth) { |
|||
var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth); var cs = 1; this.make(); for (var row = 0; row < this.modules.length; row++) { var y = row * cs; for (var col = 0; col < this.modules[row].length; col++) { var x = col * cs; var dark = this.modules[row][col]; if (dark) { qr_mc.beginFill(0, 100); qr_mc.moveTo(x, y); qr_mc.lineTo(x + cs, y); qr_mc.lineTo(x + cs, y + cs); qr_mc.lineTo(x, y + cs); qr_mc.endFill(); } } } |
|||
return qr_mc; |
|||
}, 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); |
|||
} |
|||
for (var c = 8; c < this.moduleCount - 8; c++) { |
|||
if (this.modules[6][c] != null) { continue; } |
|||
this.modules[6][c] = (c % 2 == 0); |
|||
} |
|||
}, 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; } } } |
|||
} |
|||
} |
|||
}, 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; } |
|||
for (var i = 0; i < 18; i++) { var mod = (!test && ((bits >> i) & 1) == 1); this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod; } |
|||
}, setupTypeInfo: function (test, maskPattern) { |
|||
var data = (this.errorCorrectLevel << 3) | maskPattern; var bits = QRUtil.getBCHTypeInfo(data); 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; } } |
|||
for (var i = 0; i < 15; i++) { 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; } } |
|||
this.modules[this.moduleCount - 8][8] = (!test); |
|||
}, 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; } |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
QRCodeModel.PAD0 = 0xEC; |
|||
QRCodeModel.PAD1 = 0x11; |
|||
QRCodeModel.createData = function (typeNumber, errorCorrectLevel, dataList) { |
|||
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); var buffer = new QRBitBuffer(); for (var i = 0; i < dataList.length; i++) { var data = dataList[i]; buffer.put(data.mode, 4); buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber)); data.write(buffer); } |
|||
var totalDataCount = 0; for (var i = 0; i < rsBlocks.length; i++) { totalDataCount += rsBlocks[i].dataCount; } |
|||
if (buffer.getLengthInBits() > totalDataCount * 8) { |
|||
throw new Error("code length overflow. (" |
|||
+ buffer.getLengthInBits() |
|||
+ ">" |
|||
+ totalDataCount * 8 |
|||
+ ")"); |
|||
} |
|||
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { buffer.put(0, 4); } |
|||
while (buffer.getLengthInBits() % 8 != 0) { buffer.putBit(false); } |
|||
while (true) { |
|||
if (buffer.getLengthInBits() >= totalDataCount * 8) { break; } |
|||
buffer.put(QRCodeModel.PAD0, 8); if (buffer.getLengthInBits() >= totalDataCount * 8) { break; } |
|||
buffer.put(QRCodeModel.PAD1, 8); |
|||
} |
|||
return QRCodeModel.createBytes(buffer, rsBlocks); |
|||
}; |
|||
QRCodeModel.createBytes = function (buffer, rsBlocks) { |
|||
var offset = 0; var maxDcCount = 0; var maxEcCount = 0; var dcdata = new Array(rsBlocks.length); var ecdata = new Array(rsBlocks.length); for (var r = 0; r < rsBlocks.length; r++) { |
|||
var dcCount = rsBlocks[r].dataCount; var ecCount = rsBlocks[r].totalCount - 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 totalCodeCount = 0; for (var i = 0; i < rsBlocks.length; i++) { totalCodeCount += rsBlocks[i].totalCount; } |
|||
var data = new Array(totalCodeCount); 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; |
|||
}; |
|||
var QRMode = { MODE_NUMBER: 1 << 0, MODE_ALPHA_NUM: 1 << 1, MODE_8BIT_BYTE: 1 << 2, MODE_KANJI: 1 << 3 }; |
|||
var QRErrorCorrectLevel = { L: 1, M: 0, Q: 3, H: 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), 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; |
|||
}, 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; |
|||
}, 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); } }, 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; |
|||
}, getLengthInBits: function (mode, type) { if (1 <= type && type < 10) { switch (mode) { case QRMode.MODE_NUMBER: return 10; case QRMode.MODE_ALPHA_NUM: return 9; case QRMode.MODE_8BIT_BYTE: return 8; case QRMode.MODE_KANJI: return 8; default: throw new Error("mode:" + mode); } } else if (type < 27) { switch (mode) { case QRMode.MODE_NUMBER: return 12; case QRMode.MODE_ALPHA_NUM: return 11; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 10; default: throw new Error("mode:" + mode); } } else if (type < 41) { switch (mode) { case QRMode.MODE_NUMBER: return 14; case QRMode.MODE_ALPHA_NUM: return 13; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 12; default: throw new Error("mode:" + mode); } } else { throw new Error("type:" + type); } }, getLostPoint: function (qrCode) { |
|||
var moduleCount = qrCode.getModuleCount(); var lostPoint = 0; for (var row = 0; row < moduleCount; row++) { |
|||
for (var col = 0; col < moduleCount; col++) { |
|||
var sameCount = 0; var dark = qrCode.isDark(row, col); for (var r = -1; r <= 1; r++) { |
|||
if (row + r < 0 || moduleCount <= row + r) { continue; } |
|||
for (var c = -1; c <= 1; c++) { |
|||
if (col + c < 0 || moduleCount <= col + c) { continue; } |
|||
if (r == 0 && c == 0) { continue; } |
|||
if (dark == qrCode.isDark(row + r, col + c)) { sameCount++; } |
|||
} |
|||
} |
|||
if (sameCount > 5) { lostPoint += (3 + sameCount - 5); } |
|||
} |
|||
} |
|||
for (var row = 0; row < moduleCount - 1; row++) { for (var col = 0; col < moduleCount - 1; col++) { var count = 0; if (qrCode.isDark(row, col)) count++; if (qrCode.isDark(row + 1, col)) count++; if (qrCode.isDark(row, col + 1)) count++; if (qrCode.isDark(row + 1, col + 1)) count++; if (count == 0 || count == 4) { lostPoint += 3; } } } |
|||
for (var row = 0; row < moduleCount; row++) { for (var col = 0; col < moduleCount - 6; col++) { if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(row, col + 2) && qrCode.isDark(row, col + 3) && qrCode.isDark(row, col + 4) && !qrCode.isDark(row, col + 5) && qrCode.isDark(row, col + 6)) { lostPoint += 40; } } } |
|||
for (var col = 0; col < moduleCount; col++) { for (var row = 0; row < moduleCount - 6; row++) { if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(row + 2, col) && qrCode.isDark(row + 3, col) && qrCode.isDark(row + 4, col) && !qrCode.isDark(row + 5, col) && qrCode.isDark(row + 6, col)) { lostPoint += 40; } } } |
|||
var darkCount = 0; for (var col = 0; col < moduleCount; col++) { for (var row = 0; row < moduleCount; row++) { if (qrCode.isDark(row, col)) { darkCount++; } } } |
|||
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; lostPoint += ratio * 10; return lostPoint; |
|||
} |
|||
}; |
|||
var QRMath = { |
|||
glog: function (n) { |
|||
if (n < 1) { throw new Error("glog(" + n + ")"); } |
|||
return QRMath.LOG_TABLE[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; } |
|||
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; }, 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); |
|||
}, mod: function (e) { |
|||
if (this.getLength() - e.getLength() < 0) { return this; } |
|||
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0)); var num = new Array(this.getLength()); for (var i = 0; i < this.getLength(); i++) { num[i] = this.get(i); } |
|||
for (var i = 0; i < e.getLength(); i++) { num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio); } |
|||
return new QRPolynomial(num, 0).mod(e); |
|||
} |
|||
}; |
|||
function QRRSBlock(totalCount, dataCount) { this.totalCount = totalCount; this.dataCount = dataCount; } |
|||
QRRSBlock.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]]; |
|||
QRRSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) { |
|||
var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel); if (rsBlock == undefined) { throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel); } |
|||
var length = rsBlock.length / 3; var list = []; for (var i = 0; i < length; i++) { var count = rsBlock[i * 3 + 0]; var totalCount = rsBlock[i * 3 + 1]; var dataCount = rsBlock[i * 3 + 2]; for (var j = 0; j < count; j++) { list.push(new QRRSBlock(totalCount, dataCount)); } } |
|||
return list; |
|||
}; |
|||
QRRSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) { switch (errorCorrectLevel) { case QRErrorCorrectLevel.L: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; case QRErrorCorrectLevel.M: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; case QRErrorCorrectLevel.Q: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; case QRErrorCorrectLevel.H: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; default: return undefined; } }; |
|||
function QRBitBuffer() { this.buffer = []; this.length = 0; } |
|||
QRBitBuffer.prototype = { |
|||
get: function (index) { var bufIndex = Math.floor(index / 8); return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1; }, put: function (num, length) { for (var i = 0; i < length; i++) { this.putBit(((num >>> (length - i - 1)) & 1) == 1); } }, getLengthInBits: function () { return this.length; }, 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++; |
|||
} |
|||
}; |
|||
var QRCodeLimitLength = [[17, 14, 11, 7], [32, 26, 20, 14], [53, 42, 32, 24], [78, 62, 46, 34], [106, 84, 60, 44], [134, 106, 74, 58], [154, 122, 86, 64], [192, 152, 108, 84], [230, 180, 130, 98], [271, 213, 151, 119], [321, 251, 177, 137], [367, 287, 203, 155], [425, 331, 241, 177], [458, 362, 258, 194], [520, 412, 292, 220], [586, 450, 322, 250], [644, 504, 364, 280], [718, 560, 394, 310], [792, 624, 442, 338], [858, 666, 482, 382], [929, 711, 509, 403], [1003, 779, 565, 439], [1091, 857, 611, 461], [1171, 911, 661, 511], [1273, 997, 715, 535], [1367, 1059, 751, 593], [1465, 1125, 805, 625], [1528, 1190, 868, 658], [1628, 1264, 908, 698], [1732, 1370, 982, 742], [1840, 1452, 1030, 790], [1952, 1538, 1112, 842], [2068, 1628, 1168, 898], [2188, 1722, 1228, 958], [2303, 1809, 1283, 983], [2431, 1911, 1351, 1051], [2563, 1989, 1423, 1093], [2699, 2099, 1499, 1139], [2809, 2213, 1579, 1219], [2953, 2331, 1663, 1273]]; |
|||
|
|||
// QRCode object
|
|||
QRCode = function (canvasId, vOption) { |
|||
this._htOption = { |
|||
width: 256, |
|||
height: 256, |
|||
typeNumber: 4, |
|||
colorDark: "#000000", |
|||
colorLight: "#ffffff", |
|||
correctLevel: QRErrorCorrectLevel.H |
|||
}; |
|||
|
|||
if (typeof vOption === 'string') { |
|||
vOption = { |
|||
text: vOption |
|||
}; |
|||
} |
|||
|
|||
// Overwrites options
|
|||
if (vOption) { |
|||
for (var i in vOption) { |
|||
this._htOption[i] = vOption[i]; |
|||
} |
|||
} |
|||
|
|||
this._oQRCode = null; |
|||
this.canvasId = canvasId |
|||
|
|||
if (this._htOption.text && this.canvasId) { |
|||
this.makeCode(this._htOption.text); |
|||
} |
|||
}; |
|||
|
|||
QRCode.prototype.makeCode = function (sText) { |
|||
this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel); |
|||
this._oQRCode.addData(sText); |
|||
this._oQRCode.make(); |
|||
this.makeImage(); |
|||
}; |
|||
|
|||
QRCode.prototype.makeImage = function () { |
|||
var _oContext |
|||
if (this._htOption.usingIn) { |
|||
_oContext = wx.createCanvasContext(this.canvasId, this._htOption.usingIn) |
|||
} |
|||
else { |
|||
_oContext = wx.createCanvasContext(this.canvasId) |
|||
} |
|||
var _htOption = this._htOption; |
|||
var oQRCode = this._oQRCode |
|||
|
|||
var nCount = oQRCode.getModuleCount(); |
|||
var nWidth = _htOption.padding ? (_htOption.width - 2 * _htOption.padding) / nCount : _htOption.width / nCount; |
|||
var nHeight = _htOption.padding ? (_htOption.height - 2 * _htOption.padding) / nCount : _htOption.height / nCount; |
|||
var nRoundedHeight = Math.round(nHeight); |
|||
var nRoundedWidth = Math.round(nWidth); |
|||
|
|||
if (_htOption.image && _htOption.image != '') { |
|||
_oContext.drawImage(_htOption.image, 0, 0, _htOption.width, _htOption.height) |
|||
} |
|||
_oContext.setFillStyle('#fff') |
|||
_oContext.fillRect(0, 0, _htOption.width, _htOption.height) |
|||
_oContext.save() |
|||
for (var row = 0; row < nCount; row++) { |
|||
for (var col = 0; col < nCount; col++) { |
|||
var bIsDark = oQRCode.isDark(row, col); |
|||
var nLeft = _htOption.padding ? col * nWidth + _htOption.padding : col * nWidth; |
|||
var nTop = _htOption.padding ? row * nHeight + _htOption.padding : row * nHeight; |
|||
_oContext.setStrokeStyle(bIsDark ? _htOption.colorDark : _htOption.colorLight) |
|||
// _oContext.setStrokeStyle('red')
|
|||
_oContext.setLineWidth(1) |
|||
_oContext.setFillStyle(bIsDark ? _htOption.colorDark : _htOption.colorLight) |
|||
// _oContext.setFillStyle('red')
|
|||
// if (bIsDark) {
|
|||
_oContext.fillRect(nLeft, nTop, nWidth, nHeight); |
|||
// }
|
|||
|
|||
// 안티 앨리어싱 방지 처리
|
|||
// if (bIsDark) {
|
|||
_oContext.strokeRect( |
|||
Math.floor(nLeft) + 0.5, |
|||
Math.floor(nTop) + 0.5, |
|||
nRoundedHeight |
|||
); |
|||
|
|||
_oContext.strokeRect( |
|||
Math.ceil(nLeft) - 0.5, |
|||
Math.ceil(nTop) - 0.5, |
|||
nRoundedWidth, |
|||
nRoundedHeight |
|||
); |
|||
// }
|
|||
// _oContext.fillRect(
|
|||
// Math.floor(nLeft) + 0.5,
|
|||
// Math.floor(nTop) + 0.5,
|
|||
// nRoundedWidth,
|
|||
// nRoundedHeight
|
|||
// );
|
|||
// _oContext.fillRect(
|
|||
// Math.ceil(nLeft) - 0.5,
|
|||
// Math.ceil(nTop) - 0.5,
|
|||
// nRoundedWidth,
|
|||
// nRoundedHeight
|
|||
// );
|
|||
// _oContext.clearRect(
|
|||
// Math.floor(nLeft) + 0.5,
|
|||
// Math.floor(nTop) + 0.5,
|
|||
// nRoundedWidth,
|
|||
// nRoundedHeight
|
|||
// );
|
|||
// _oContext.clearRect(
|
|||
// Math.ceil(nLeft) - 0.5,
|
|||
// Math.ceil(nTop) - 0.5,
|
|||
// nRoundedWidth,
|
|||
// nRoundedHeight
|
|||
// );
|
|||
} |
|||
} |
|||
|
|||
_oContext.draw(false, () => { |
|||
setTimeout(() => { |
|||
this.exportImage() |
|||
}, 800) |
|||
}) |
|||
}; |
|||
|
|||
// 保存为图片,将临时路径传给回调
|
|||
QRCode.prototype.exportImage = function (callback) { |
|||
if (this._htOption.callback && typeof this._htOption.callback === 'function') { |
|||
wx.canvasToTempFilePath({ |
|||
x: 0, |
|||
y: 0, |
|||
width: this._htOption.width, |
|||
height: this._htOption.height, |
|||
destWidth: this._htOption.width, |
|||
destHeight: this._htOption.height, |
|||
canvasId: this.canvasId, |
|||
success: (res) => { |
|||
this._htOption.callback({path: res.tempFilePath}) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
QRCode.CorrectLevel = QRErrorCorrectLevel; |
|||
})(); |
|||
|
|||
module.exports = QRCode |
@ -0,0 +1,214 @@ |
|||
<template> |
|||
<view class="bg"> |
|||
<view class="swipe-box" v-if="info.images"> |
|||
<swiper class="swiper" :autoplay="true" :interval="3000" :duration="1000" circular indicator-dots indicator-color="rgba(255,255,255,.5)" |
|||
indicator-active-color="#fff" @change="swiperChange"> |
|||
<swiper-item v-for="(item, index) in info.images.split(',')" :key="item.id"> |
|||
<view class="swiper-item"> |
|||
<image class="item-img" :src="showImg(item)" mode="aspectFill"></image> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<view class="swiper-pointer"> |
|||
{{swiperCurrent}}/{{info.images.split(',').length}} |
|||
</view> |
|||
|
|||
<view class="collect" style="right: 100rpx;" @click.stop="collect()"> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240829/9dbb80cafe8375bd6d5c96fccb617962.png" v-if="info.is_collect"></image> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240826/564af778708591f5de29174d3b14bbff.png" v-else></image> |
|||
</view> |
|||
|
|||
<view class="collect" @click.stop="share()"> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240902/8eb614e4ea41db655a4e6da2cc0ca954.png"></image> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
<view class="common-container info-container"> |
|||
<view class="info-title text-overflowRows">{{info.title}}</view> |
|||
<view class="text-overflow" style="padding: 40rpx 0 16rpx;">{{info.address}}</view> |
|||
<view class="text-overflow" v-if="info.start_time && info.end_time">时间:{{info.start_time}} - {{info.end_time}}</view> |
|||
</view> |
|||
<view class="common-container detail-container"> |
|||
<view class="" id="cpts" v-html="formateRichText(info.detail)"></view> |
|||
</view> |
|||
|
|||
|
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
id: null, |
|||
swiperCurrent: 1, |
|||
info: {}, |
|||
} |
|||
}, |
|||
onShow(options) { |
|||
}, |
|||
onLoad(options) { |
|||
this.id = options.id; |
|||
this.getInfo(); |
|||
}, |
|||
methods: { |
|||
swiperChange (e) { |
|||
this.swiperCurrent = e.detail.current+1 |
|||
}, |
|||
// 获取信息 |
|||
getInfo() { |
|||
this.Post({id: this.id},'/api/activity/getActivityCalendarDetail').then(res => { |
|||
if (res.data.flag == 0) { |
|||
uni.showToast({title: '商品不存在或已下架',icon: 'none'}) |
|||
setTimeout(() => {this.goBack()}, 2000) |
|||
} |
|||
this.info = res.data; |
|||
}); |
|||
}, |
|||
// 收藏 |
|||
collect() { |
|||
this.Post({type: 7,id: this.id},'/api/scenic/collect').then(res => { |
|||
if (res) { |
|||
uni.showToast({title: res.msg,icon: 'none'}); |
|||
this.info.is_collect = !this.info.is_collect |
|||
} |
|||
}); |
|||
}, |
|||
share () { |
|||
var pages = getCurrentPages() //获取加载的页面 |
|||
var view = pages[pages.length - 1] //获取当前页面的对象 |
|||
uni.share({ |
|||
provider: "weixin", |
|||
scene: "WXSceneSession", |
|||
type: 0, |
|||
href: `${view.route}`, |
|||
title: this.info.title, |
|||
imageUrl: this.showImg(this.info.image), |
|||
success: function (res) { |
|||
console.log("success:" + JSON.stringify(res)); |
|||
}, |
|||
fail: function (err) { |
|||
console.log("fail:" + JSON.stringify(err)); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
*{ |
|||
box-sizing: border-box; |
|||
} |
|||
.bg{ |
|||
min-height: 100vh; |
|||
background: #F8F8F8; |
|||
} |
|||
|
|||
.swipe-box { |
|||
height: 867rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item-num { |
|||
width: 90rpx; |
|||
height: 40rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-family: PingFangSC-Regular, PingFang SC; |
|||
font-weight: 400; |
|||
color: #ffffff; |
|||
text-align: center; |
|||
line-height: 40rpx; |
|||
position: absolute; |
|||
right: 30rpx; |
|||
bottom: 50rpx; |
|||
} |
|||
|
|||
.collect{ |
|||
position: absolute; |
|||
width: 51rpx; |
|||
height: 51rpx; |
|||
right: 20rpx; |
|||
top: 20rpx; |
|||
font-size: 23rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
image{ |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
} |
|||
} |
|||
.swiper-pointer{ |
|||
position: absolute; |
|||
right: 20rpx; |
|||
bottom: 20rpx; |
|||
background: rgba(22,22,22,0.5); |
|||
border-radius: 23rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #FFFFFF; |
|||
padding: 6rpx 14rpx; |
|||
|
|||
} |
|||
} |
|||
|
|||
.swiper { |
|||
height: 867rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item { |
|||
width: 100%; |
|||
height: 867rpx; |
|||
|
|||
.item-img { |
|||
width: 750rpx; |
|||
height: 867rpx; |
|||
} |
|||
.item-img.pop-swiper-image{ |
|||
height: 347rpx; |
|||
border-radius: 13rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.common-container{ |
|||
background: white; |
|||
width: 100%; |
|||
} |
|||
|
|||
|
|||
.info-container{ |
|||
padding: 34rpx 25rpx; |
|||
width: 100%; |
|||
margin-bottom: 21rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 25rpx; |
|||
color: #666666; |
|||
.info-title{ |
|||
|
|||
font-weight: bold; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
} |
|||
|
|||
} |
|||
|
|||
.detail-container{ |
|||
width: 100%; |
|||
padding: 28rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 28rpx; |
|||
color: #333333; |
|||
} |
|||
</style> |
@ -0,0 +1,769 @@ |
|||
<template> |
|||
<view class="bg"> |
|||
<view class="swipe-box"> |
|||
<swiper class="swiper" :autoplay="true" :interval="3000" :duration="1000" circular indicator-dots indicator-color="rgba(255,255,255,.5)" |
|||
indicator-active-color="#fff" @change="swiperChange"> |
|||
<swiper-item v-for="(item, index) in info.list_images.split(',')" :key="item.id"> |
|||
<view class="swiper-item"> |
|||
<image class="item-img" :src="showImg(item)" mode="aspectFill"></image> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<view class="swiper-pointer"> |
|||
{{swiperCurrent}}/{{info.list_images.split(',').length}} |
|||
</view> |
|||
|
|||
<view class="collect" @click.stop="collect()"> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240829/9dbb80cafe8375bd6d5c96fccb617962.png" v-if="info.is_collect"></image> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240826/564af778708591f5de29174d3b14bbff.png" v-else></image> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail-container"> |
|||
<view class="common-container info-container"> |
|||
<view> |
|||
<view class="info-title text-overflowRows">{{info.title}}</view> |
|||
</view> |
|||
<view class="flex-between time-container"> |
|||
<view class="flex-1 w-1rpx"> |
|||
<view class="tag-container no-scrollbar" v-if="info.label"> |
|||
<view class="tag" v-for="(tag,tagIndex) in info.label.split(',')" :key="tagIndex">{{tag}}</view> |
|||
</view> |
|||
</view> |
|||
<view class="policy-btn flex-shrink-0 flex flex-items-center" style="justify-content: flex-end;" @click="goPolicy"> |
|||
设施/政策 |
|||
<uni-icons color="#71B580" style="height: 35rpx;" type="right" size="12"></uni-icons> |
|||
</view> |
|||
</view> |
|||
<view class="flex-between"> |
|||
<view class="address-container flex flex-shrink-0 flex-between" @click="goMap"> |
|||
<view class="address-title text-overflowRows"> |
|||
{{info.address}} |
|||
</view> |
|||
<view class="flex-column flex-center"> |
|||
<image :src="showImg('/uploads/20240827/5b19517f2a630f3a766ea03ac621a3be.png')"> |
|||
<view class="tip" style="padding-top: 11rpx;">地图</view> |
|||
</view> |
|||
</view> |
|||
<view class="phone-container flex-column flex-center" @click="callPhone"> |
|||
<image :src="showImg('/uploads/20240829/b38a84c91eef12d3040deaef50eaf697.png')"> |
|||
<view class="tip" style="padding-top: 11rpx;">电话</view> |
|||
</view> |
|||
|
|||
</view> |
|||
</view> |
|||
|
|||
<view class="common-container hotel-container" style="padding: 0;background: none;"> |
|||
<view class="order-time flex-between" style="justify-content: space-around;" |
|||
@click="openCalendar()"> |
|||
<view class="time"> |
|||
<view style="font-size: 35rpx;font-weight: bold;">{{new Date(selectDate.startDay).Format('MM-dd')}}</view> |
|||
<view style="padding:0 14rpx">{{ ShowDateDay(new Date(selectDate.startDay).getDay()) }}</view> |
|||
<view style="color: #71B580;">入住</view> |
|||
</view> |
|||
<view class="cal-day"> |
|||
共{{selectDate.differDays}}晚 |
|||
</view> |
|||
<view class="time"> |
|||
<view style="font-size: 35rpx;font-weight: bold;">{{new Date(selectDate.endDay).Format('MM-dd')}}</view> |
|||
<view style="padding:0 14rpx">{{ShowDateDay(new Date(selectDate.endDay).getDay()) }}</view> |
|||
<view style="color: #71B580;">离店</view> |
|||
</view> |
|||
</view> |
|||
<view style="padding:0 34rpx;"> |
|||
<view v-for="(item, index) in sku" :key="index"> |
|||
<view class="scenic-item flex" v-if="(!showMore&&index<=2)||showMore"> |
|||
<view class="scenic-image flex-shrink-0"> |
|||
<image style="width: 100%;height: 100%;" :src="showImg(item.image)"></image> |
|||
</view> |
|||
<view class="flex-1 w-1rpx flex-column" style="padding: 14rpx;justify-content: space-between;"> |
|||
<view class="flex flex-items-center" @click="viewDetail(item)"> |
|||
<view class="scenic-title text-overflow">{{item.title}}</view> |
|||
<uni-icons style="height: 40rpx;font-weight: bold;" type="right" size="15"></uni-icons> |
|||
</view> |
|||
<view> |
|||
<view class="scenic-content text-overflowRows"> |
|||
<text>{{item.showTag}}</text> |
|||
<text>{{item.lastShowTag}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="price-container"> |
|||
<view class="price">{{item.price/100}}</view> |
|||
<view class="btn" @click="goOrder(item)">预订</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="showMore" v-if="!showMore&&sku.length>3" @click="changeShowMore"> |
|||
查看剩余房型 |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
<view style="padding: 0rpx 29rpx;"> |
|||
<view class="box-title">酒店介绍</view> |
|||
<view class="common-container" style="padding: 30rpx 40rpx;"> |
|||
<view class="" id="cpts" v-html="formateRichText(info.feature_content)"></view> |
|||
</view> |
|||
<view class="box-title">设施与政策</view> |
|||
<view class="common-container policy-container" style="padding: 30rpx 40rpx;"> |
|||
<view class="common-container1"> |
|||
<view class="info-title1 box-title1 text-overflowRows">{{info.title}}</view> |
|||
<view class="flex-between phone-container1"> |
|||
<view> |
|||
联系电话:{{info.tel}} |
|||
</view> |
|||
<view @click="callPhone"> |
|||
<image :src="showImg('/uploads/20240830/8b5750ff23837fe4a187e13775f9424c.png')"> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="common-container1"> |
|||
<view class="box-title1">服务设施</view> |
|||
<view style="padding-top: 34rpx;" v-html="formateRichText(info.cost_content)"></view> |
|||
</view> |
|||
<view class="common-container1"> |
|||
<view class="box-title1">入住须知</view> |
|||
<view style="padding-top: 34rpx;" v-html="formateRichText(info.describe_content)"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
<!-- 酒店详情弹窗 --> |
|||
<uni-popup ref="popupRule" type="bottom" :safe-area="false" background-color="#F7F7F7"> |
|||
<view class="popup-content-date" > |
|||
<view class="popup-content-title flex"> |
|||
<view class="flex-1 w-1rpx text-overflow"> |
|||
{{skuInfo.title}} |
|||
</view> |
|||
<img src="https://static.ticket.sz-trip.com/taizhou/images/cha.png" @click="closePopupRule" |
|||
style="width: 31rpx;height: 31rpx;" class="flex-shrink-0"> |
|||
</view> |
|||
<view class="content"> |
|||
<view class="swipe-box" style="height: 347rpx;" v-if="skuInfo.list_images"> |
|||
<swiper class="swiper" :autoplay="true" :interval="3000" :duration="1000" circular indicator-dots indicator-color="rgba(255,255,255,.5)" |
|||
indicator-active-color="#fff" @change="popSwiperChange" :current="0"> |
|||
<swiper-item v-for="(item, index) in skuInfo.list_images.split(',')" :key="item.id"> |
|||
<view class="swiper-item" style="height: 347rpx;"> |
|||
<image class="item-img pop-swiper-image" :src="showImg(item)" mode="aspectFill"></image> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<view class="swiper-pointer" style="right:30rpx;bottom: 20rpx;"> |
|||
{{popSwiperCurrent}}/{{skuInfo.list_images.split(',').length}} |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="pop-detail-container"> |
|||
<view class="pop-detail-title">房型信息</view> |
|||
<view v-html="formateRichText(skuInfo.use_explain)"></view> |
|||
|
|||
</view> |
|||
</view> |
|||
</view> |
|||
</uni-popup> |
|||
|
|||
<uni-calendar ref="calendar" class="uni-calendar--hook" :clear-date="false" |
|||
:insert="false" :range="true" :startDate="calendarParam.startDate" |
|||
:endDate="calendarParam.endDate" @confirm="confirmCalendar"/> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
headImg: "https://tongli.sz-trip.com/uploads/20240826/8653c32761e01ee683505eddba1ae22b.png", |
|||
id: null, |
|||
swiperCurrent: 1, |
|||
popSwiperCurrent: 1, |
|||
info: {list_images:''}, |
|||
sku: [], |
|||
showMore: false, |
|||
skuInfo: {}, // 预定门票 |
|||
|
|||
selectDate: { |
|||
startDay:new Date().Format('yyyy-MM-dd'), |
|||
endDay:new Date((new Date()).getFullYear(), (new Date()).getMonth(), new Date().getDate()+1).Format('yyyy-MM-dd'), |
|||
differDays: 1 |
|||
}, |
|||
|
|||
minSeldDate: new Date().Format('yyyy-MM-dd'), |
|||
maxSeldDate: new Date((new Date()).getFullYear(), (new Date()).getMonth() + 3, 0).Format('yyyy-MM-dd'), |
|||
calendarParam: { |
|||
stratDate:'',endDate: '', selected: [] |
|||
}, |
|||
|
|||
} |
|||
}, |
|||
onShow(options) { |
|||
}, |
|||
onLoad(options) { |
|||
this.id = options.id; |
|||
this.getInfo(); |
|||
this.getGoodsList() |
|||
}, |
|||
methods: { |
|||
swiperChange (e) { |
|||
this.swiperCurrent = e.detail.current+1 |
|||
}, |
|||
popSwiperChange (e) { |
|||
this.popSwiperCurrent = e.detail.current+1 |
|||
}, |
|||
changeShowMore () { |
|||
this.showMore = !this.showMore |
|||
}, |
|||
goPolicy () { |
|||
uni.navigateTo({ |
|||
url:`/subPackages/hotelHomestay/policy?id=${this.id}` |
|||
}) |
|||
}, |
|||
callPhone () { |
|||
let _this = this |
|||
uni.showActionSheet({ |
|||
itemList: [this.info.tel,'呼叫'], |
|||
success: function (res) { |
|||
_this.clickPhone(_this.info.tel) |
|||
} |
|||
}); |
|||
// this.clickPhone(this.info.tel) |
|||
}, |
|||
|
|||
goMap () { |
|||
uni.openLocation({ |
|||
latitude: Number(this.info.lat), |
|||
longitude: Number(this.info.lon), |
|||
name: this.info.title, |
|||
address: this.info.address, |
|||
success: function () { |
|||
console.log('success'); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 获取景点信息 |
|||
getInfo() { |
|||
this.Post({id: this.id},'/api/scenic/getScenicById').then(res => { |
|||
if (res.data.flag == 0) { |
|||
uni.showToast({title: '商品不存在或已下架',icon: 'none'}) |
|||
setTimeout(() => {this.goBack()}, 2000) |
|||
} |
|||
let info = res.data; |
|||
try { |
|||
info.times_list_info = JSON.parse(info.times_list)[0] |
|||
} catch(e) { |
|||
console.log(e) |
|||
info.times_list_info = {start:'',end:''} |
|||
} |
|||
this.info = info |
|||
console.log(info) |
|||
}); |
|||
}, |
|||
// 根据景点id获取商品列表 |
|||
getGoodsList(){ |
|||
this.Post({ |
|||
scenic_id: this.id |
|||
},'/api/scenic/getGoodsByScenicId').then(res => { |
|||
let result = res.data || [] |
|||
let data = [] |
|||
result.forEach(item=>{ |
|||
item.specifications.forEach(v=>{ |
|||
let showTag = '' |
|||
let lastShowTag = '' |
|||
if (v.specifications_new_tag) { |
|||
let tags = v.specifications_new_tag.split(',') |
|||
showTag = (tags.slice(0,-1)||[]).join(' | ') |
|||
if (tags.length>1) { |
|||
showTag += ' | ' |
|||
} |
|||
lastShowTag = tags[tags.length-1]||'' |
|||
} |
|||
v.showTag = showTag |
|||
v.lastShowTag = lastShowTag |
|||
data.push(v) |
|||
}) |
|||
|
|||
}) |
|||
this.sku = data |
|||
// this.sku = [...data,...data,...data,...data] |
|||
}) |
|||
}, |
|||
// 收藏 |
|||
collect() { |
|||
this.Post({type: 4,id: this.id},'/api/scenic/collect').then(res => { |
|||
if (res) { |
|||
uni.showToast({title: res.msg,icon: 'none'}); |
|||
this.info.is_collect = !this.info.is_collect |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
calDate (startDate, endDate) { |
|||
var start = new Date(startDate); |
|||
var end = new Date(endDate); |
|||
var diff = end - start; |
|||
var days = Math.floor(diff / (1000 * 60 * 60 * 24)); // 计算天数差 |
|||
return days; |
|||
}, |
|||
openCalendar () { |
|||
this.calendarParam = { |
|||
startDate: this.minSeldDate, |
|||
endDate: this.maxSeldDate, |
|||
// selected: this.allSeldDate.filter(v=>v.store>0).map(v=>{ |
|||
// return { |
|||
// date: v.date, |
|||
// info: '¥'+this.showNoPriceNew(v.money), |
|||
// notNeedDot:true, |
|||
// } |
|||
// }) |
|||
} |
|||
this.$refs.calendar.open(); |
|||
}, |
|||
confirmCalendar (val) { |
|||
if (val.range.data.length<2) { |
|||
uni.showToast({ |
|||
title:'请选择日期', |
|||
icon:'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.selectDate.startDay = val.range.before |
|||
this.selectDate.endDay = val.range.after |
|||
this.selectDate.differDays = this.calDate(this.selectDate.startDay,this.selectDate.endDay) |
|||
}, |
|||
|
|||
// 查看房型 |
|||
viewDetail (item) { |
|||
this.skuInfo = item |
|||
this.popSwiperCurrent = 1 |
|||
this.openPopRule() |
|||
}, |
|||
|
|||
// 预定 |
|||
goOrder (item) { |
|||
// if (item.xx) { |
|||
// return |
|||
// } |
|||
|
|||
let param = { |
|||
skuInfo: item, |
|||
selectDate: this.selectDate |
|||
} |
|||
uni.setStorageSync('hotelOrderInfo', JSON.stringify(param)) |
|||
uni.navigateTo({ |
|||
url:`/subPackages/hotelHomestay/order` |
|||
}) |
|||
}, |
|||
|
|||
|
|||
closePopupRule() { |
|||
this.$refs.popupRule.close() |
|||
}, |
|||
openPopRule(){ |
|||
this.$refs.popupRule.open() |
|||
}, |
|||
// 价格格式 |
|||
showNoPriceNew(price) { |
|||
if (price && price > 0) { |
|||
return (price / 100) |
|||
} else { |
|||
return '0' |
|||
} |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
*{ |
|||
box-sizing: border-box; |
|||
} |
|||
.bg{ |
|||
min-height: 100vh; |
|||
background: #F8F8F8; |
|||
} |
|||
|
|||
.swipe-box { |
|||
height: 484rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item-num { |
|||
width: 90rpx; |
|||
height: 40rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-family: PingFangSC-Regular, PingFang SC; |
|||
font-weight: 400; |
|||
color: #ffffff; |
|||
text-align: center; |
|||
line-height: 40rpx; |
|||
position: absolute; |
|||
right: 30rpx; |
|||
bottom: 50rpx; |
|||
} |
|||
|
|||
.collect{ |
|||
position: absolute; |
|||
width: 51rpx; |
|||
height: 51rpx; |
|||
right: 36rpx; |
|||
top: 36rpx; |
|||
font-size: 23rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
image{ |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
} |
|||
} |
|||
.swiper-pointer{ |
|||
position: absolute; |
|||
right: 36rpx; |
|||
bottom: 80rpx; |
|||
background: rgba(22,22,22,0.5); |
|||
border-radius: 23rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #FFFFFF; |
|||
padding: 6rpx 14rpx; |
|||
|
|||
} |
|||
} |
|||
|
|||
.swiper { |
|||
height: 484rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item { |
|||
width: 100%; |
|||
height: 484rpx; |
|||
|
|||
.item-img { |
|||
width: 750rpx; |
|||
height: 484rpx; |
|||
} |
|||
.item-img.pop-swiper-image{ |
|||
height: 347rpx; |
|||
border-radius: 13rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
.detail-container{ |
|||
width: 100%; |
|||
z-index: 2; |
|||
position: relative; |
|||
top: -52rpx; |
|||
} |
|||
|
|||
.common-container{ |
|||
background: white; |
|||
border-radius: 20rpx; |
|||
margin-bottom: 20rpx; |
|||
padding: 22rpx 26rpx; |
|||
} |
|||
|
|||
|
|||
.info-container{ |
|||
font-family: PingFang; |
|||
font-weight: 500; |
|||
font-size: 27rpx; |
|||
color: #000000; |
|||
padding: 36rpx 29rpx; |
|||
.time-container{ |
|||
padding: 22rpx 0; |
|||
|
|||
.tag-container{ |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
overflow-x: auto; |
|||
.tag{ |
|||
border-radius: 5rpx; |
|||
border: 1px solid #71B580; |
|||
|
|||
padding: 7rpx 14rpx; |
|||
|
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 25rpx; |
|||
color: #71B580; |
|||
text-align: center; |
|||
margin-right: 15rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
.policy-btn{ |
|||
width: 160rpx; |
|||
height: 35rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 27rpx; |
|||
text-align: right; |
|||
color: #71B580; |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
.info-title{ |
|||
width: 100%; |
|||
height: 85rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
} |
|||
|
|||
.address-container{ |
|||
width: 587rpx; |
|||
height: 107rpx; |
|||
border-radius: 20rpx; |
|||
padding: 25rpx 20rpx 25rpx 33rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 27rpx; |
|||
color: #000000; |
|||
background-color: #E8F6EB; |
|||
background-image: url('https://tongli.sz-trip.com/uploads/20240830/e8089886a03786e925bd4d4bbc441320.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
image{ |
|||
width: 30rpx; |
|||
height: 30rpx; |
|||
} |
|||
.tip{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #333333; |
|||
} |
|||
} |
|||
.phone-container{ |
|||
width: 93rpx; |
|||
height: 107rpx; |
|||
background: #E8F6EB; |
|||
border-radius: 20rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #333333; |
|||
image{ |
|||
width: 30rpx; |
|||
height: 30rpx; |
|||
} |
|||
.tip{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #333333; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.hotel-container{ |
|||
.order-time{ |
|||
height: 107rpx; |
|||
background: linear-gradient(180deg, #FFFFFF, #F7F7F7); |
|||
.time{ |
|||
display: flex; |
|||
font-family: PingFang SC; |
|||
font-size: 24rpx; |
|||
color: #000000; |
|||
align-items: center; |
|||
} |
|||
} |
|||
.cal-day{ |
|||
background: #E8F6EB; |
|||
border-radius: 17rpx; |
|||
padding: 5rpx 16rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #000000; |
|||
} |
|||
.showMore{ |
|||
width: 100%; |
|||
height: 67rpx; |
|||
background: #FFFFFF; |
|||
border-radius: 20rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 27rpx; |
|||
color: #666666; |
|||
line-height: 67rpx; |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
.box-title { |
|||
margin: 52rpx 0 26rpx 12rpx; |
|||
font-family: PingFang; |
|||
font-weight: bold; |
|||
font-size: 37rpx; |
|||
color: #000000; |
|||
} |
|||
|
|||
.address-icon{ |
|||
margin-right: 9rpx; |
|||
width: 26rpx; |
|||
height: 26rpx; |
|||
} |
|||
|
|||
.scenic-item{ |
|||
display: flex; |
|||
width: 100%; |
|||
height: 213rpx; |
|||
background: #FFFFFF; |
|||
border-radius: 20rpx; |
|||
padding: 7rpx; |
|||
margin-bottom: 20rpx; |
|||
.scenic-image{ |
|||
width: 173rpx; |
|||
height: 100%; |
|||
flex-shrink: 0; |
|||
image{ |
|||
border-radius: 20rpx; |
|||
} |
|||
} |
|||
.scenic-title { |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 31rpx; |
|||
color: #000000; |
|||
flex-shrink: 0; |
|||
max-width: 90%; |
|||
} |
|||
.scenic-content{ |
|||
width: 100%; |
|||
height: 66rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #666666; |
|||
line-height: 33rpx; |
|||
text:last-of-type{ |
|||
color: #71B580; |
|||
} |
|||
} |
|||
|
|||
.price-container{ |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: flex-end; |
|||
.price{ |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 37rpx; |
|||
color: #D60000; |
|||
padding-right: 20rpx; |
|||
} |
|||
.price::before{ |
|||
content: "¥"; |
|||
font-size: 24rpx; |
|||
} |
|||
.btn{ |
|||
width: 120rpx; |
|||
height: 47rpx; |
|||
background: linear-gradient(90deg, #FD6F34, #F4A61F); |
|||
border-radius: 23rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 29rpx; |
|||
color: #FFFFFF; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
.bottom { |
|||
.notice { |
|||
font-weight: 400; |
|||
color: #0B898E; |
|||
} |
|||
} |
|||
|
|||
.popup-content-date{ |
|||
position: relative; |
|||
background: #F7F7F7; |
|||
top: -33rpx; |
|||
padding: 25rpx 44rpx; |
|||
border-radius: 33rpx; |
|||
.popup-content-title{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
text-align: center; |
|||
padding-bottom: 26rpx; |
|||
} |
|||
|
|||
.pop-detail-container{ |
|||
background: #FFFFFF; |
|||
border-radius: 13rpx; |
|||
padding: 20rpx; |
|||
margin-top: 26rpx; |
|||
max-height: 800rpx; |
|||
overflow-y: auto; |
|||
.pop-detail-title{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 32rpx; |
|||
color: #000000; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
.policy-container{ |
|||
.common-container1{ |
|||
width: 100%; |
|||
background: white; |
|||
border-radius: 13rpx; |
|||
margin-bottom: 20rpx; |
|||
padding: 20rpx 18rpx; |
|||
} |
|||
|
|||
.box-title1 { |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
} |
|||
.info-title1{ |
|||
height: 88rpx; |
|||
} |
|||
.phone-container1{ |
|||
padding-top: 20rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 400; |
|||
font-size: 28rpx; |
|||
color: #000000; |
|||
image{ |
|||
width: 33.33rpx; |
|||
height: 33.33rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
</style> |
@ -0,0 +1,601 @@ |
|||
<template> |
|||
<view class="bg"> |
|||
<view class="detail-container"> |
|||
<view class="common-container info-container"> |
|||
<view class="flex-between" style="align-items: flex-start;"> |
|||
<view class="info-title text-overflowRows">{{skuInfo.title}}</view> |
|||
<view class="policy-btn flex-shrink-0 flex flex-items-center" @click="openPopRule"> |
|||
房型信息 |
|||
<uni-icons color="#71B580" style="height: 32rpx;" type="right" size="12"></uni-icons> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="tag-container text-overflow"> |
|||
<text>{{skuInfo.showTag}}</text> |
|||
<text>{{skuInfo.lastShowTag}}</text> |
|||
</view> |
|||
|
|||
<view class="order-time flex-between" style="justify-content: space-around;"> |
|||
<view class="time"> |
|||
<view style="font-size: 35rpx;font-weight: bold;">{{new Date(selectDate.startDay).Format('MM-dd')}}</view> |
|||
<view style="padding-left:14rpx">{{ ShowDateDay(new Date(selectDate.startDay).getDay()) }}</view> |
|||
<view>入住</view> |
|||
</view> |
|||
<view class="cal-day"> |
|||
共{{selectDate.differDays}}晚 |
|||
</view> |
|||
<view class="time"> |
|||
<view style="font-size: 35rpx;font-weight: bold;">{{new Date(selectDate.endDay).Format('MM-dd')}}</view> |
|||
<view style="padding-left:14rpx">{{ShowDateDay(new Date(selectDate.endDay).getDay()) }}</view> |
|||
<view>离店</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="notice-container"> |
|||
<view class="flex"> |
|||
<view class="flex-shrink-0" style="padding-top: 4rpx;"> |
|||
<image :src="showImg('/uploads/20240830/aa84c8b1de80651f645fed67a1997ced.png')"></image> |
|||
</view> |
|||
<view class="flex-1 w-1rpx" style="padding-left: 14rpx;"> |
|||
<view>订单确认后,您在{{new Date(selectDate.startDay).Format('yyyy年MM月dd日')}}20:00前之免费取消或 变更订单,20:00后将收取30%房费作为违约费用。</view> |
|||
<view style="color: #F84A56;">预期不可取消或/修改,扣取全额房费</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="flex" style="padding-top: 22rpx;"> |
|||
<view class="flex-shrink-0" style="padding-top: 4rpx;"> |
|||
<image :src="showImg('/uploads/20240830/59d2f01f0391f72a537cc9004bc2071d.png')"></image> |
|||
</view> |
|||
<view class="flex-1 w-1rpx" style="padding-left: 14rpx;"> |
|||
<view>证件要求:大陆居民身份证登记入住</view> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
<view class="common-container form-container"> |
|||
<view class="form-title"> |
|||
<text style="font-weight: bold;font-size: 35rpx;padding-right: 28rpx;">预订信息</text> |
|||
<text>姓名需与证件一致</text> |
|||
</view> |
|||
<view class="btn-box flex-column"> |
|||
<view class="line w-full" style="border-top: none;"> |
|||
<view class="left">房间数</view> |
|||
<view class="input flex" style="justify-content: flex-end;"> |
|||
<view class="num-box"> |
|||
<view :class="['ctrl',buyNum>1?'':'disabled']" @click="reduce()">-</view> |
|||
<input class="num" type="text" v-model="buyNum" :disabled='true' /> |
|||
<view :class="['ctrl']" @click="plus()">+</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="line w-full" v-for="(user,index) in roomUser" :key="index"> |
|||
<view class="left">房间{{index+1}}</view> |
|||
<input class="input" type="text" placeholder="请输入住客姓名" v-model="user.name" /> |
|||
</view> |
|||
<view class="line w-full"> |
|||
<view class="left">联系电话</view> |
|||
<view class="flex flex-items-center input"> |
|||
<input class="input" type="text" placeholder="请输入联系手机号" v-model="phone" /> |
|||
<uni-icons v-if="phone.length>0" style="margin-left: 30rpx;" |
|||
type="closeempty" size="14" @click="phone = ''"></uni-icons> |
|||
</view> |
|||
|
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
|
|||
<view class="btn-list"> |
|||
<view class="price-box"> |
|||
<view class="text">合计:</view> |
|||
<view class="price">{{ total() }}</view> |
|||
<!-- <view class="post-text" v-if="sendType==1&&post">含邮费:¥{{ post / 100 }}</view> --> |
|||
</view> |
|||
<view class="btn" @click="order()">立即预订</view> |
|||
</view> |
|||
|
|||
<!-- 酒店详情弹窗 --> |
|||
<uni-popup ref="popupRule" type="bottom" :safe-area="false" background-color="#F7F7F7"> |
|||
<view class="popup-content-date" > |
|||
<view class="popup-content-title flex"> |
|||
<view class="flex-1 w-1rpx text-overflow"> |
|||
{{skuInfo.title}} |
|||
</view> |
|||
<img src="https://static.ticket.sz-trip.com/taizhou/images/cha.png" @click="closePopupRule" |
|||
style="width: 31rpx;height: 31rpx;" class="flex-shrink-0"> |
|||
</view> |
|||
<view class="content"> |
|||
<view class="swipe-box" style="height: 347rpx;" v-if="skuInfo.list_images"> |
|||
<swiper class="swiper" :autoplay="true" :interval="3000" :duration="1000" circular indicator-dots indicator-color="rgba(255,255,255,.5)" |
|||
indicator-active-color="#fff" @change="popSwiperChange" :current="0"> |
|||
<swiper-item v-for="(item, index) in skuInfo.list_images.split(',')" :key="item.id"> |
|||
<view class="swiper-item" style="height: 347rpx;"> |
|||
<image class="item-img pop-swiper-image" :src="showImg(item)" mode="aspectFill"></image> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<view class="swiper-pointer" style="right:30rpx;bottom: 20rpx;"> |
|||
{{popSwiperCurrent}}/{{skuInfo.list_images.split(',').length}} |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="pop-detail-container"> |
|||
<view class="pop-detail-title">房型信息</view> |
|||
<view v-html="formateRichText(skuInfo.use_explain)"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</uni-popup> |
|||
|
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
id: null, |
|||
popSwiperCurrent: 1, |
|||
|
|||
skuInfo: {}, // 酒店信息 |
|||
|
|||
selectDate: { |
|||
startDay:new Date().Format('yyyy-MM-dd'), |
|||
endDay:new Date((new Date()).getFullYear(), (new Date()).getMonth(), new Date().getDate()+1).Format('yyyy-MM-dd'), |
|||
differDays: 1 |
|||
}, |
|||
|
|||
phone: '', |
|||
buyNum: 1, |
|||
roomUser: [{ |
|||
name: '' |
|||
}] |
|||
|
|||
|
|||
} |
|||
}, |
|||
onShow(options) { |
|||
}, |
|||
onLoad(options) { |
|||
this.initOrderDate() |
|||
}, |
|||
methods: { |
|||
popSwiperChange (e) { |
|||
this.popSwiperCurrent = e.detail.current+1 |
|||
}, |
|||
|
|||
// 初始化 |
|||
initOrderDate () { |
|||
let data = {} |
|||
try { |
|||
data = JSON.parse(uni.getStorageSync('hotelOrderInfo')); |
|||
} catch (e) { |
|||
data = {} |
|||
setTimeout(()=>{ |
|||
uni.navigateBack() |
|||
},1000) |
|||
} |
|||
|
|||
this.skuInfo = data.skuInfo |
|||
this.selectDate = data.selectDate |
|||
this.buyNum = 1 |
|||
this.roomUser = [{name: ''}] |
|||
|
|||
}, |
|||
// 查看房型 |
|||
viewDetail () { |
|||
this.popSwiperCurrent = 1 |
|||
this.openPopRule() |
|||
}, |
|||
|
|||
plus() { |
|||
this.buyNum += 1; |
|||
this.roomUser.push({name: ''}) |
|||
}, |
|||
reduce() { |
|||
if (this.buyNum > 1) { |
|||
this.buyNum -= 1; |
|||
this.roomUser.pop() |
|||
} |
|||
}, |
|||
|
|||
total () { |
|||
return this.skuInfo.price/100 * this.buyNum || 0 |
|||
}, |
|||
|
|||
order () { |
|||
// 校验 |
|||
if (!this.isTel(this.phone)){ |
|||
uni.showToast({ |
|||
title: '请输入自提人手机号', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
if (this.roomUser.some(v=>v.trim().length<0)) { |
|||
uni.showToast({ |
|||
title: '请输入住客姓名', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
let goods = [] |
|||
let goodsItem = { |
|||
num:this.buyNum, |
|||
specifications_id: this.skuInfo.id, |
|||
start_date: this.selectDate.startDay, |
|||
end_date: this.selectDate.endDay, |
|||
customer_name: this.roomUser.map(v=>v.name).join('、'), |
|||
reserve_mobile: this.phone, |
|||
} |
|||
goods.push(goodsItem) |
|||
|
|||
let data = { |
|||
goods: goods, |
|||
reserve_phone: this.phone, |
|||
} |
|||
|
|||
return |
|||
this.Post({ |
|||
method: 'POST', |
|||
data: JSON.stringify(data) |
|||
},'/api/order/place').then(res => { |
|||
console.log('成功'); |
|||
if(res.code == 200){ |
|||
uni.removeStorageSync('hotelOrderInfo') |
|||
this.Post({ |
|||
order_id: res.data.order_id, |
|||
type: "miniprogram", |
|||
platform: 'miniprogram' |
|||
},'/api/pay/unify').then(res => { |
|||
if(res.data){ |
|||
uni.requestPayment({ |
|||
nonceStr: res.data.nonceStr, |
|||
package: res.data.package, |
|||
paySign: res.data.paySign, |
|||
signType: res.data.signType, |
|||
timeStamp: res.data.timeStamp, |
|||
complete() { |
|||
uni.navigateTo({ |
|||
url: '/subPackages/order/trades' |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
|
|||
}, |
|||
|
|||
closePopupRule() { |
|||
this.$refs.popupRule.close() |
|||
}, |
|||
openPopRule(){ |
|||
this.$refs.popupRule.open() |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
*{ |
|||
box-sizing: border-box; |
|||
} |
|||
.bg{ |
|||
min-height: 100vh; |
|||
background: #F8F8F8; |
|||
} |
|||
|
|||
.swipe-box { |
|||
height: 484rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item-num { |
|||
width: 90rpx; |
|||
height: 40rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-family: PingFangSC-Regular, PingFang SC; |
|||
font-weight: 400; |
|||
color: #ffffff; |
|||
text-align: center; |
|||
line-height: 40rpx; |
|||
position: absolute; |
|||
right: 30rpx; |
|||
bottom: 50rpx; |
|||
} |
|||
|
|||
.collect{ |
|||
position: absolute; |
|||
right: 36rpx; |
|||
top: 36rpx; |
|||
font-size: 23rpx; |
|||
image{ |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
} |
|||
} |
|||
.swiper-pointer{ |
|||
position: absolute; |
|||
right: 36rpx; |
|||
bottom: 80rpx; |
|||
background: rgba(22,22,22,0.5); |
|||
border-radius: 23rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #FFFFFF; |
|||
padding: 6rpx 14rpx; |
|||
|
|||
} |
|||
} |
|||
|
|||
.swiper { |
|||
height: 484rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item { |
|||
width: 100%; |
|||
height: 484rpx; |
|||
|
|||
.item-img { |
|||
width: 750rpx; |
|||
height: 484rpx; |
|||
} |
|||
.item-img.pop-swiper-image{ |
|||
height: 347rpx; |
|||
border-radius: 13rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
.detail-container{ |
|||
width: 100%; |
|||
padding: 26rpx; |
|||
padding-bottom: 200rpx; |
|||
} |
|||
|
|||
.common-container{ |
|||
background: white; |
|||
border-radius: 20rpx; |
|||
margin-bottom: 20rpx; |
|||
padding: 22rpx 26rpx; |
|||
} |
|||
|
|||
|
|||
.info-container{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #666666; |
|||
|
|||
.info-title{ |
|||
width: 100%; |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
} |
|||
|
|||
.policy-btn{ |
|||
width: 160rpx; |
|||
height: 35rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #666666; |
|||
text-align: right; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: flex-end; |
|||
} |
|||
.tag-container{ |
|||
width: 100%; |
|||
padding: 35rpx 0; |
|||
} |
|||
|
|||
.time-container{ |
|||
padding: 22rpx 0; |
|||
|
|||
} |
|||
|
|||
.notice-container{ |
|||
background:rgba(232, 246, 235, 0.7); |
|||
border-radius: 13rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 23rpx; |
|||
color: #000000; |
|||
padding: 22rpx 16rpx; |
|||
margin-top: 28rpx; |
|||
image{ |
|||
width: 25rpx; |
|||
height: 25rpx; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
.order-time{ |
|||
.time{ |
|||
display: flex; |
|||
font-family: PingFang SC; |
|||
font-size: 24rpx; |
|||
color: #000000; |
|||
align-items: center; |
|||
} |
|||
} |
|||
.cal-day{ |
|||
background: #E8F6EB; |
|||
border-radius: 17rpx; |
|||
padding: 5rpx 16rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #000000; |
|||
} |
|||
|
|||
|
|||
.popup-content-date{ |
|||
position: relative; |
|||
background: #F7F7F7; |
|||
top: -33rpx; |
|||
padding: 25rpx 44rpx; |
|||
border-radius: 33rpx; |
|||
.popup-content-title{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
text-align: center; |
|||
padding-bottom: 26rpx; |
|||
} |
|||
|
|||
.pop-detail-container{ |
|||
background: #FFFFFF; |
|||
border-radius: 13rpx; |
|||
padding: 20rpx; |
|||
margin-top: 26rpx; |
|||
max-height: 800rpx; |
|||
overflow-y: auto; |
|||
.pop-detail-title{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 32rpx; |
|||
color: #000000; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
.btn-list { |
|||
width: 100%; |
|||
height: 166rpx; |
|||
background: #ffffff; |
|||
box-shadow: 0rpx -3rpx 9rpx 1rpx rgba(227, 229, 232, 0.5); |
|||
display: flex; |
|||
position: fixed; |
|||
bottom: 0; |
|||
padding: 20rpx 20rpx; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
.btn { |
|||
width: 294rpx; |
|||
height: 88rpx; |
|||
background: #F84A56; |
|||
border-radius: 43rpx; |
|||
text-align: center; |
|||
line-height: 88rpx; |
|||
|
|||
font-size: 36rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
color: #FFFFFF; |
|||
|
|||
} |
|||
.price-box { |
|||
display: flex; |
|||
align-items: center; |
|||
.text { |
|||
font-size: 29rpx; |
|||
font-family: PingFangSC-Regular, PingFang SC; |
|||
font-weight: 500; |
|||
color: #333; |
|||
} |
|||
.price { |
|||
margin-left: 15rpx; |
|||
font-size: 36rpx; |
|||
font-family: PingFangSC-Regular, PingFang SC; |
|||
font-weight: 400; |
|||
color: #fc5109; |
|||
&:before { |
|||
content: '¥'; |
|||
display: inline-block; |
|||
color: #fc5109; |
|||
font-size: 36rpx; |
|||
} |
|||
} |
|||
.post-text { |
|||
margin-left: 15rpx; |
|||
color: #fc5109; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.form-container{ |
|||
.form-title{ |
|||
font-family: PingFang SC; |
|||
color: #000000; |
|||
font-weight: 500; |
|||
font-size: 23rpx; |
|||
display: flex; |
|||
align-items: baseline; |
|||
} |
|||
.left{ |
|||
width: 160rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 29rpx; |
|||
color: #000000; |
|||
flex-shrink: 0; |
|||
} |
|||
.line{ |
|||
display: flex; |
|||
align-items: center; |
|||
border-top:1rpx solid #D8D8D8; |
|||
height: 92rpx; |
|||
} |
|||
.input{ |
|||
flex: 1; |
|||
} |
|||
input{ |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 29rpx; |
|||
color: #000000; |
|||
} |
|||
} |
|||
|
|||
.num-box { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-left: 20rpx; |
|||
width: 160rpx; |
|||
justify-content: space-between; |
|||
.num { |
|||
text-align: center; |
|||
width: 50rpx; |
|||
} |
|||
.ctrl { |
|||
width: 47rpx; |
|||
height: 47rpx; |
|||
background: #71B580; |
|||
border-radius: 50%; |
|||
font-family: PingFang SC; |
|||
font-weight: 400; |
|||
font-size: 34rpx; |
|||
color: #FFFFFF; |
|||
line-height: 42rpx; |
|||
text-align: center; |
|||
} |
|||
.ctrl.disabled{ |
|||
background: #E8E8E8; |
|||
color: #999999; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,110 @@ |
|||
<template> |
|||
<view class="bg"> |
|||
|
|||
<view class="common-container"> |
|||
<view class="info-title box-title text-overflowRows">{{info.title}}</view> |
|||
<view class="flex-between phone-container"> |
|||
<view> |
|||
联系电话:{{info.tel}} |
|||
</view> |
|||
<view @click="callPhone"> |
|||
<image :src="showImg('/uploads/20240830/8b5750ff23837fe4a187e13775f9424c.png')"> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="common-container"> |
|||
<view class="box-title">服务设施</view> |
|||
<view style="padding-top: 34rpx;" v-html="formateRichText(info.cost_content)"></view> |
|||
</view> |
|||
<view class="common-container"> |
|||
<view class="box-title">入住须知</view> |
|||
<view style="padding-top: 34rpx;" v-html="formateRichText(info.describe_content)"></view> |
|||
</view> |
|||
|
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
id: null, |
|||
info: {}, |
|||
} |
|||
}, |
|||
onShow(options) { |
|||
}, |
|||
onLoad(options) { |
|||
this.id = options.id; |
|||
this.getInfo(); |
|||
}, |
|||
methods: { |
|||
callPhone () { |
|||
let _this = this |
|||
uni.showActionSheet({ |
|||
itemList: [this.info.tel,'呼叫'], |
|||
success: function (res) { |
|||
_this.clickPhone(_this.info.tel) |
|||
} |
|||
}); |
|||
// this.clickPhone(this.info.tel) |
|||
}, |
|||
// 获取景点信息 |
|||
getInfo() { |
|||
this.Post({id: this.id},'/api/scenic/getScenicById').then(res => { |
|||
if (res.data.flag == 0) { |
|||
uni.showToast({title: '商品不存在或已下架',icon: 'none'}) |
|||
setTimeout(() => {this.goBack()}, 2000) |
|||
} |
|||
this.info = res.data |
|||
}); |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
*{ |
|||
box-sizing: border-box; |
|||
} |
|||
.bg{ |
|||
min-height: 100vh; |
|||
background: #F7F7F7; |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
|
|||
.common-container{ |
|||
width: 100%; |
|||
background: white; |
|||
border-radius: 13rpx; |
|||
margin-bottom: 20rpx; |
|||
padding: 20rpx 18rpx; |
|||
} |
|||
|
|||
.box-title { |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
} |
|||
.info-title{ |
|||
height: 88rpx; |
|||
} |
|||
.phone-container{ |
|||
padding-top: 20rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 400; |
|||
font-size: 28rpx; |
|||
color: #000000; |
|||
image{ |
|||
width: 33.33rpx; |
|||
height: 33.33rpx; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
</style> |
@ -0,0 +1,184 @@ |
|||
<template> |
|||
<view class="bg"> |
|||
<view class="swipe-box"> |
|||
<swiper class="swiper" :autoplay="true" :interval="3000" :duration="1000" circular indicator-dots indicator-color="rgba(255,255,255,.5)" |
|||
indicator-active-color="#fff" @change="swiperChange"> |
|||
<swiper-item v-for="(item, index) in info.list_images.split(',')" :key="item.id"> |
|||
<view class="swiper-item"> |
|||
<image class="item-img" :src="showImg(item)" ></image> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<view class="swiper-pointer"> |
|||
{{swiperCurrent}}/{{info.list_images.split(',').length}} |
|||
</view> |
|||
|
|||
<view class="collect" @click.stop="collect()"> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240829/9dbb80cafe8375bd6d5c96fccb617962.png" v-if="info.is_collect"></image> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240826/564af778708591f5de29174d3b14bbff.png" v-else></image> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
<view class="common-container info-container"> |
|||
<view class="info-title text-overflowRows">{{info.title}}</view> |
|||
</view> |
|||
<view class="common-container detail-container"> |
|||
<view class="" id="cpts" v-html="formateRichText(info.feature_content)"></view> |
|||
</view> |
|||
|
|||
|
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
id: null, |
|||
swiperCurrent: 1, |
|||
info: {list_images:''}, |
|||
} |
|||
}, |
|||
onShow(options) { |
|||
}, |
|||
onLoad(options) { |
|||
this.id = options.id; |
|||
this.getInfo(); |
|||
}, |
|||
methods: { |
|||
swiperChange (e) { |
|||
this.swiperCurrent = e.detail.current+1 |
|||
}, |
|||
// 获取信息 |
|||
getInfo() { |
|||
this.Post({goods_id: this.id},'/api/goods/getGoodDetail').then(res => { |
|||
if (res.data.flag == 0) { |
|||
uni.showToast({title: '商品不存在或已下架',icon: 'none'}) |
|||
setTimeout(() => {this.goBack()}, 2000) |
|||
} |
|||
this.info = res.data; |
|||
}); |
|||
}, |
|||
// 收藏 |
|||
collect() { |
|||
this.Post({type: 6,id: this.id},'/api/scenic/collect').then(res => { |
|||
if (res) { |
|||
uni.showToast({title: res.msg,icon: 'none'}); |
|||
this.info.is_collect = !this.info.is_collect |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
*{ |
|||
box-sizing: border-box; |
|||
} |
|||
.bg{ |
|||
min-height: 100vh; |
|||
background: #F8F8F8; |
|||
} |
|||
|
|||
.swipe-box { |
|||
height: 867rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item-num { |
|||
width: 90rpx; |
|||
height: 40rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-family: PingFangSC-Regular, PingFang SC; |
|||
font-weight: 400; |
|||
color: #ffffff; |
|||
text-align: center; |
|||
line-height: 40rpx; |
|||
position: absolute; |
|||
right: 30rpx; |
|||
bottom: 50rpx; |
|||
} |
|||
|
|||
.collect{ |
|||
position: absolute; |
|||
width: 51rpx; |
|||
height: 51rpx; |
|||
right: 36rpx; |
|||
top: 36rpx; |
|||
font-size: 23rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
image{ |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
} |
|||
} |
|||
.swiper-pointer{ |
|||
position: absolute; |
|||
right: 36rpx; |
|||
bottom: 80rpx; |
|||
background: rgba(22,22,22,0.5); |
|||
border-radius: 23rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #FFFFFF; |
|||
padding: 6rpx 14rpx; |
|||
|
|||
} |
|||
} |
|||
|
|||
.swiper { |
|||
height: 867rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item { |
|||
width: 100%; |
|||
height: 867rpx; |
|||
|
|||
.item-img { |
|||
width: 750rpx; |
|||
height: 867rpx; |
|||
} |
|||
.item-img.pop-swiper-image{ |
|||
height: 347rpx; |
|||
border-radius: 13rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.common-container{ |
|||
background: white; |
|||
width: 100%; |
|||
} |
|||
|
|||
|
|||
.info-container{ |
|||
padding: 34rpx 25rpx; |
|||
width: 100%; |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
margin-bottom: 21rpx; |
|||
} |
|||
|
|||
.detail-container{ |
|||
width: 100%; |
|||
padding: 28rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 28rpx; |
|||
color: #333333; |
|||
} |
|||
</style> |
@ -0,0 +1,200 @@ |
|||
<template> |
|||
<view class="bg"> |
|||
<view class="swipe-box" v-if="info.images"> |
|||
<swiper class="swiper" :autoplay="true" :interval="3000" :duration="1000" circular indicator-dots indicator-color="rgba(255,255,255,.5)" |
|||
indicator-active-color="#fff" @change="swiperChange"> |
|||
<swiper-item v-for="(item, index) in info.images.split(',')" :key="item.id"> |
|||
<view class="swiper-item"> |
|||
<image class="item-img" :src="showImg(item)" mode="aspectFill"></image> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<view class="swiper-pointer"> |
|||
{{swiperCurrent}}/{{info.images.split(',').length}} |
|||
</view> |
|||
|
|||
<view class="collect" @click.stop="collect()"> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240829/9dbb80cafe8375bd6d5c96fccb617962.png" v-if="info.is_collect"></image> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240826/564af778708591f5de29174d3b14bbff.png" v-else></image> |
|||
</view> |
|||
|
|||
<!-- <view class="collect" @click.stop="share()"> |
|||
<image src="https://tongli.sz-trip.com/uploads/20240902/8eb614e4ea41db655a4e6da2cc0ca954.png"></image> |
|||
</view> --> |
|||
</view> |
|||
|
|||
|
|||
<view class="common-container info-container"> |
|||
<view class="info-title text-overflowRows">{{info.title}}</view> |
|||
<view class="flex-between" style="padding: 40rpx 0 0;"> |
|||
<view>浏览 {{info.view}}</view> |
|||
<view>发布时间:{{info.create_time}}</view> |
|||
</view> |
|||
|
|||
<!-- <view class="text-overflow" v-if="info.start_time && info.end_time">时间:{{info.start_time}} - {{info.end_time}}</view> --> |
|||
</view> |
|||
<view class="common-container detail-container"> |
|||
<view class="" id="cpts" v-html="formateRichText(info.detail)"></view> |
|||
</view> |
|||
|
|||
|
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
id: null, |
|||
swiperCurrent: 1, |
|||
info: {}, |
|||
} |
|||
}, |
|||
onShow(options) { |
|||
}, |
|||
onLoad(options) { |
|||
this.id = options.id; |
|||
this.getInfo(); |
|||
}, |
|||
methods: { |
|||
swiperChange (e) { |
|||
this.swiperCurrent = e.detail.current+1 |
|||
}, |
|||
// 获取信息 |
|||
getInfo() { |
|||
this.Post({id: this.id},'/api/article/getArticleById').then(res => { |
|||
if (res.data.flag == 0) { |
|||
uni.showToast({title: '文章不存在或已下架',icon: 'none'}) |
|||
setTimeout(() => {this.goBack()}, 2000) |
|||
} |
|||
this.info = res.data; |
|||
}); |
|||
}, |
|||
// 收藏 |
|||
collect() { |
|||
this.Post({type: 1,id: this.id},'/api/scenic/collect').then(res => { |
|||
if (res) { |
|||
uni.showToast({title: res.msg,icon: 'none'}); |
|||
this.info.is_collect = !this.info.is_collect |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
*{ |
|||
box-sizing: border-box; |
|||
} |
|||
.bg{ |
|||
min-height: 100vh; |
|||
background: #F8F8F8; |
|||
} |
|||
|
|||
.swipe-box { |
|||
height: 867rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item-num { |
|||
width: 90rpx; |
|||
height: 40rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-family: PingFangSC-Regular, PingFang SC; |
|||
font-weight: 400; |
|||
color: #ffffff; |
|||
text-align: center; |
|||
line-height: 40rpx; |
|||
position: absolute; |
|||
right: 30rpx; |
|||
bottom: 50rpx; |
|||
} |
|||
|
|||
.collect{ |
|||
position: absolute; |
|||
width: 51rpx; |
|||
height: 51rpx; |
|||
right: 20rpx; |
|||
top: 20rpx; |
|||
font-size: 23rpx; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
image{ |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
} |
|||
} |
|||
.swiper-pointer{ |
|||
position: absolute; |
|||
right: 20rpx; |
|||
bottom: 20rpx; |
|||
background: rgba(22,22,22,0.5); |
|||
border-radius: 23rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
color: #FFFFFF; |
|||
padding: 6rpx 14rpx; |
|||
|
|||
} |
|||
} |
|||
|
|||
.swiper { |
|||
height: 867rpx; |
|||
position: relative; |
|||
|
|||
.swiper-item { |
|||
width: 100%; |
|||
height: 867rpx; |
|||
|
|||
.item-img { |
|||
width: 750rpx; |
|||
height: 867rpx; |
|||
} |
|||
.item-img.pop-swiper-image{ |
|||
height: 347rpx; |
|||
border-radius: 13rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.common-container{ |
|||
background: white; |
|||
width: 100%; |
|||
} |
|||
|
|||
|
|||
.info-container{ |
|||
padding: 34rpx 25rpx; |
|||
width: 100%; |
|||
margin-bottom: 21rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 25rpx; |
|||
color: #666666; |
|||
.info-title{ |
|||
|
|||
font-weight: bold; |
|||
font-size: 35rpx; |
|||
color: #000000; |
|||
} |
|||
|
|||
} |
|||
|
|||
.detail-container{ |
|||
width: 100%; |
|||
padding: 28rpx; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 28rpx; |
|||
color: #333333; |
|||
} |
|||
</style> |
@ -0,0 +1,234 @@ |
|||
<template> |
|||
<view class="bg"> |
|||
<view class="topImg relative"> |
|||
<img v-if="headImg" :src="showImg(headImg)" class="topImg" mode="aspectFill"> |
|||
<view class="icon-back" :style="{top:systemInfo.textTop,left:'19rpx'}" @click="goBack()" > |
|||
<uni-icons type="left" size="24" color="#242424"></uni-icons> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="tag-container no-scrollbar"> |
|||
<view :class="['tag', type_id==0?'active':'']" @click="setTag(0)">全部</view> |
|||
<view :class="['tag', type_id==1?'active':'']" v-for="(item,i) in 10" :key="i" @click="setTag(i)">景点</view> |
|||
</view> |
|||
|
|||
<view class="strategy-box box"> |
|||
<view v-for="(num,nums) in 2" :key="nums"> |
|||
<view v-for="(item,index) in list" :key="index" class="strategy-item" v-if="index % 2 === nums" @click="viewDetail(item)"> |
|||
<!-- <view class="title-tag" v-if="item.image_tag">{{item.image_tag}}</view> --> |
|||
<image class="strategy-image" :src="showImg(item.image)" mode="widthFix"></image> |
|||
<view class="strategy-content"> |
|||
<view class="strategy-title">{{item.title}}</view> |
|||
<view class="flex-between"> |
|||
<view class="text-overflow" style="display: flex;align-items: center;width: 200rpx;"> |
|||
<image :src="showImg(item.author_img)" mode="" class="author-img"></image> |
|||
{{item.author}} |
|||
</view> |
|||
<view style="display: flex;align-items: center;"> |
|||
<image src="https://static.ticket.sz-trip.com/yandu/images/index/eye.png" mode="" class="eye-img"></image> |
|||
{{item.view}} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default{ |
|||
data(){ |
|||
return { |
|||
systemInfo: { |
|||
height:"0px", |
|||
textHeight:"0px", |
|||
textTop:"0px", |
|||
contentTop: '0px', |
|||
}, |
|||
|
|||
list: [], |
|||
showMore: true, |
|||
headImg:"https://tongli.sz-trip.com/uploads/20240902/55c5cbe137790323cc719b5c83d5e2e9.png", |
|||
|
|||
type_id: 0,//景点分类id |
|||
} |
|||
}, |
|||
onShow() { |
|||
this.showMore = true |
|||
}, |
|||
onReady() { |
|||
this.getArticleByType() |
|||
}, |
|||
onLoad(options) { |
|||
let that = this |
|||
uni.getSystemInfo({ |
|||
success(res) { |
|||
console.log(res) |
|||
that.systemInfo.height =res.windowHeight+'px' |
|||
const menu=uni.getMenuButtonBoundingClientRect() |
|||
that.systemInfo.textHeight=menu.height+"px" |
|||
that.systemInfo.textTop=menu.top+"px" |
|||
that.systemInfo.contentTop = (menu.height + menu.top)+"px" |
|||
console.log(that.systemInfo) |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
methods: { |
|||
setTag (item) { |
|||
this.type_id = item |
|||
// this.getArticleByType() |
|||
}, |
|||
|
|||
// 游记攻略 |
|||
getArticleByType() { |
|||
this.Post({ |
|||
type_id: 41, |
|||
offset: this.list.length, |
|||
limit: 10 |
|||
},'/api/Article/getArticleByType').then(res => { |
|||
this.list = [...this.list, ...res.data] |
|||
|
|||
if(res.data.length < 10) this.showMore = false |
|||
}) |
|||
}, |
|||
// 游记攻略查看更多 |
|||
strategyMore() { |
|||
this.getArticleByType() |
|||
}, |
|||
viewDetail (item) { |
|||
uni.navigateTo({ |
|||
url:'/subPackages/travelGuide/detail?id='+item.id |
|||
}) |
|||
}, |
|||
}, |
|||
onReachBottom() { |
|||
setTimeout(() => { |
|||
if (!this.showMore) this.getList() |
|||
},1000) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
*{ |
|||
box-sizing: border-box; |
|||
font-family: PingFangSC; |
|||
} |
|||
.bg{ |
|||
min-height: 100vh; |
|||
background: #FFFFFF; |
|||
} |
|||
.topImg{ |
|||
width: 100%; |
|||
height: 440rpx; |
|||
.icon-back{ |
|||
position: absolute; |
|||
display: flex; |
|||
align-items: center; |
|||
z-index: 50; |
|||
} |
|||
} |
|||
|
|||
.tag-container{ |
|||
margin: 42rpx 26rpx; |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
overflow-y: auto; |
|||
.tag{ |
|||
flex-shrink: 0; |
|||
border-radius: 13rpx; |
|||
border: 1px solid #999999; |
|||
font-family: PingFang SC; |
|||
font-weight: 500; |
|||
font-size: 28rpx; |
|||
color: #666666; |
|||
padding: 13rpx 29rpx; |
|||
margin-right: 20rpx; |
|||
} |
|||
.tag.active{ |
|||
background: linear-gradient(90deg, #9EE4FE, #7FD491); |
|||
border: none; |
|||
font-weight: bold; |
|||
color: #000000; |
|||
} |
|||
} |
|||
|
|||
.strategy-box { |
|||
padding: 0 26rpx; |
|||
padding-bottom: 50rpx; |
|||
box-sizing: border-box; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.strategy-item { |
|||
width: 339rpx; |
|||
border-radius: 13rpx; |
|||
overflow: hidden; |
|||
margin-bottom: 28rpx; |
|||
position: relative; |
|||
box-shadow: 0rpx 0rpx 20rpx 0rpx rgba(153,153,153,0.38); |
|||
|
|||
.title-tag { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
width: 153rpx; |
|||
line-height: 47rpx; |
|||
background: #71B580; |
|||
border-radius: 20rpx 0rpx 20rpx 0rpx; |
|||
text-align: center; |
|||
font-weight: 500; |
|||
font-size: 27rpx; |
|||
color: #FFFFFF; |
|||
overflow-x: auto; |
|||
white-space: nowrap; |
|||
} |
|||
.title-tag::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
|
|||
.strategy-image { |
|||
width: 337rpx; |
|||
display: block; |
|||
} |
|||
|
|||
.strategy-content { |
|||
background: #FFFFFF; |
|||
padding: 20rpx; |
|||
|
|||
.strategy-title { |
|||
font-family: PingFang SC; |
|||
font-weight: bold; |
|||
font-size: 31rpx; |
|||
color: #2C2C2C; |
|||
} |
|||
|
|||
.flex-between { |
|||
font-weight: 500; |
|||
font-family: PingFang SC; |
|||
font-size: 24rpx; |
|||
color: #000000; |
|||
|
|||
margin-top: 14rpx; |
|||
|
|||
.author-img { |
|||
width: 37rpx; |
|||
height: 37rpx; |
|||
margin-right: 6rpx; |
|||
} |
|||
|
|||
.eye-img { |
|||
width: 26.67rpx; |
|||
height: 18.67rpx; |
|||
margin-right: 6rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
</style> |
Loading…
Reference in new issue